Skip to content

build(proto): resolve protoc-gen-es from the nix toolchain, not the bun store - #18

Merged
mattwilkinsonn merged 4 commits into
mainfrom
compass-agent-1405-protoc-gen-es
Jul 29, 2026
Merged

build(proto): resolve protoc-gen-es from the nix toolchain, not the bun store#18
mattwilkinsonn merged 4 commits into
mainfrom
compass-agent-1405-protoc-gen-es

Conversation

@mattwilkinsonn

@mattwilkinsonn mattwilkinsonn commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Ported from sealedsecurity/sealed#971 — the code half. The generated trees are regenerated here, not patched.

The defect

buf lazily requires a plugin's closure while executing it. A nix store path cannot be rewritten mid-execution; a package store can, and a torn read of that closure flakes the codegen gate with a fault that reads like a codegen error rather than a missing file.

protoc-gen-es was the last plugin still resolved from packages/compass-client/node_modules/.binbuf, protoc-gen-go and protoc-gen-connect-go already came from the pinned nixpkgs. This puts the TS plugin where the other three already are.

What changes

  • devenv.nix — bake protoc-gen-es.
  • proto/moon.ymlgen/drift drop the PATH= prefix and the compass-client:install dep; both gain a preflight. /package.json + /bun.lock leave the input sets, /devenv.lock joins them — the inputs now name what the output actually depends on.
  • package.json — the root overrides pin goes. It existed so a float could not restamp headers; a store path cannot float.
  • packages/compass-client/package.json + bun.lock — devDep removed, 14 lock lines drop.
  • 6 × *_pb.ts — regenerated.

Regenerated, not patched — and the versions differ

nixpkgs protoc-gen-es   2.12.0    ← /nix/store/4gcxiy…-protoc-gen-es-2.12.0
sealed's bun devDep     2.12.1    ← what this tree's headers said

moon run compass-proto:gen --force produced exactly the 6 files sealed#971 touches, with an identical 2.12.1 → 2.12.0 delta — derived from this repo's toolchain rather than copied. A literal port of the sealed diff would have asserted a version this tree does not produce; here it happens to agree, and the agreement is a result rather than an assumption.

Gate — run locally, in this repo, forced

This repo has no CI. Zero workflows, so CLEAN means nothing ran. Local testimony.

$ moon run :ci --force --concurrency 4

Tasks: 35 completed
 Time: 2m 799ms

The wide gate, not compass-proto:ci — a narrow green is evidence about the narrow thing, and this diff reaches the workspace root.

Both new mechanisms proven capable of failing

Drift — mutate a committed header and confirm it reds:

sed 's/v2.12.0/v9.9.9/' agent_pb.ts
  → +// @generated by protoc-gen-es v9.9.9 …
  → Error: task_runner::run_failed  (exit 1)

The preflight — the new code in this diff, so it gets the same treatment in both directions:

PATH=/nonexistent  → "protoc-gen-es is not on PATH: enter the devenv shell…"  exit=1
devenv PATH        → no-op, proceeds

A guard that has only ever been observed passing is indistinguishable from one that cannot fail.

Not in this PR

sealed#971 also adds assertGeneratorStamp to tools/root-checks — a gate asserting the @generated by headers agree with each other and with the baked plugin. sealedsecurity/compass has no tools/ directory and its CI is GitHub Actions rather than moon, so the harness is a rewrite, not a move. The gate's own logic is pure (runner and source texts injected) and ports cleanly; it follows in its own change.

Worth stating plainly rather than leaving implicit: until it lands, the version relationship this PR establishes is enforced by drift alone. Drift catches a gen tree that disagrees with the plugin currently on PATH. It does not name the skew — a devenv.lock bump surfaces as a comment-only diff on an unrelated PR, which is exactly the mystery red the stamp gate exists to convert into "baked X ≠ gen-tree stamp Y".

Spec-impact: none

Refs SEA-1405

The version delta is stamp-only today — measured, not assumed

The body above states 2.12.1 → 2.12.0 and calls it what it is. What it did not
state is the blast radius, which is the part a reviewer actually needs: does
generating with the older plugin change any code, or only the header?

Measured across every generated file in the repo, comparing main's body to
this branch's with the stamp line excluded:

SAME-BODY  packages/compass-agent/src/gen/compass/v1/agent_gateway_pb.ts
SAME-BODY  packages/compass-agent/src/gen/compass/v1/agent_pb.ts
SAME-BODY  packages/compass-agent/src/gen/compass/v1/comms_pb.ts
SAME-BODY  packages/compass-agent/src/gen/compass/v1/compass_pb.ts
SAME-BODY  packages/compass-client/src/gen/compass/v1/comms_pb.ts
SAME-BODY  packages/compass-client/src/gen/compass/v1/compass_pb.ts

6 files, 6 identical bodies, 6 changed stamps

So the delta is entirely the header for the protos we have today. That is a
measurement of this schema against these two versions, not a property of the
two versions
— a later minor may well emit different code, which is precisely
why the stamp is worth gating rather than normalising away.

compass#30 collides with this PR, and the merge is clean while being wrong

#30 regenerates comms_pb.ts in both packages with 2.12.1 (it predates
this PR's cutover). Textual overlap with this branch is 2 files; git merge-tree reports zero conflicts and produces a tree whose header says
2.12.0 over a body generated by 2.12.1.

Verified on the materialised merge, rather than reasoned about:

merge origin/compass-agent-1405-protoc-gen-es + origin/compass-server-port-drop
  conflicts                0
  merged stamp             2.12.0        ← from this branch
  merged `answered` field  present       ← from #30's body
  compass-proto:drift      PASS (659ms)
  calibration              perturb the merged file → drift RED
                           restore                → drift GREEN

Drift passes because 2.12.0 and 2.12.1 emit identical bodies for this proto, so
the merged tree is genuinely correct — the stamp is not lying about anything
observable. But it passes for a reason that holds only while the bodies agree.
Had #30 landed a proto whose output differed between the two versions, that
same merge would be textually clean, semantically wrong, and — since drift
compares against the plugin on PATH — green.

tools/stamp-gate (compass#20) is what names that class: it compares the
checked-in stamps against the plugin on PATH and against each other, so a
partial regeneration reads as baked X != gen-tree stamp Y instead of as a
comment-only diff on a PR that touched neither the schema nor the gen trees.

No action needed on either PR. Whichever lands second regenerates; if #30
lands second under this PR's toolchain, its regeneration restamps to 2.12.0 and
drift stays green. Recording it because a zero-conflict merge between two
generated-code PRs is the case least likely to be looked at, and here the clean
merge was load-bearing on a coincidence.

mattwilkinsonn commented Jul 28, 2026

Copy link
Copy Markdown
Contributor Author

@linear-code

linear-code Bot commented Jul 28, 2026

Copy link
Copy Markdown

SEA-1405

@greptile-apps

greptile-apps Bot commented Jul 28, 2026

Copy link
Copy Markdown

Greptile Summary

This PR moves TypeScript protobuf generation from the Bun dependency store to the pinned Nix toolchain.

  • Adds protoc-gen-es to the devenv packages and removes its Bun dependency and override.
  • Updates proto generation and drift tasks to resolve the plugin from PATH and fail early when it is missing.
  • Documents the non-Nix setup requirements.
  • Regenerates six TypeScript protobuf headers with the Nix-provided generator version.

Confidence Score: 3/5

The PR should not merge until the previously reported generator-version enforcement gap is fixed or the documentation stops claiming that a stamp gate enforces it.

The current generation and drift tasks accept any PATH-resolved protoc-gen-es, while the documented CI stamp comparison does not exist; the existing parity check validates CI's executable identity rather than checked-in generated stamps.

Files Needing Attention: README.md, proto/moon.yml

Important Files Changed

Filename Overview
proto/moon.yml Moves generation and drift to the PATH-resolved Nix plugin, adds presence checks, and updates task inputs.
devenv.nix Adds protoc-gen-es to the pinned development toolchain alongside the existing protobuf generators.
README.md Documents non-Nix installation but incorrectly states that CI has a generated-stamp comparison gate.
packages/compass-client/package.json Removes the Bun-managed generator development dependency after moving codegen to the Nix toolchain.
bun.lock Removes the generator and its now-unused transitive lockfile entries.
package.json Removes the obsolete exact generator override and updates workspace documentation.
packages/compass-agent/src/gen/compass/v1/agent_gateway_pb.ts Restamps generated output from protoc-gen-es 2.12.1 to 2.12.0 without changing its generated body.
packages/compass-agent/src/gen/compass/v1/agent_pb.ts Restamps generated output from protoc-gen-es 2.12.1 to 2.12.0 without changing its generated body.
packages/compass-agent/src/gen/compass/v1/comms_pb.ts Restamps generated output from protoc-gen-es 2.12.1 to 2.12.0 without changing its generated body.
packages/compass-agent/src/gen/compass/v1/compass_pb.ts Restamps generated output from protoc-gen-es 2.12.1 to 2.12.0 without changing its generated body.
packages/compass-client/src/gen/compass/v1/comms_pb.ts Restamps generated output from protoc-gen-es 2.12.1 to 2.12.0 without changing its generated body.
packages/compass-client/src/gen/compass/v1/compass_pb.ts Restamps generated output from protoc-gen-es 2.12.1 to 2.12.0 without changing its generated body.

Reviews (4): Last reviewed commit: "docs(compass): scope the stamp-gate reje..." | Re-trigger Greptile

Comment thread proto/moon.yml Outdated
mattwilkinsonn and others added 3 commits July 29, 2026 13:14
…un store

buf lazily requires a plugin's closure while executing it. A nix store path
cannot be rewritten mid-execution; a package store can, and a torn read of
that closure is what flakes the codegen gate with a fault that reads like a
codegen error rather than a missing file.

So protoc-gen-es joins buf and the Go plugins in devenv.nix and comes off
PATH like they do. The gen and drift tasks drop their PATH manipulation and
their compass-client:install dependency: codegen no longer depends on any
package-install state, and no concurrent writer can tear a plugin
mid-exec. Both tasks carry the same preflight, because a shell without the
plugin makes buf report `exec: executable file not found`, which reads as a
codegen fault; the check is a no-op whenever the plugin is present.

The trade is that the plugin version becomes the pinned nixpkgs's to choose
rather than a lockfile's. A nixpkgs bump that moves it restamps the
`@generated by` header, so drift reds with a comment-only diff on a PR that
touched neither the schema nor the gen trees. That is real drift, not a
flake: regenerate and commit the restamp alongside the bump. The root
override pinning the plugin exact goes with it — that pin existed to stop a
float from restamping headers, and a store path cannot float.

The generated trees are regenerated here rather than patched, so their
headers record this repo's toolchain. nixpkgs supplies 2.12.0 where the
bun devDep resolved 2.12.1; a literal port of the sealed diff would have
asserted a version this tree does not produce.

Ported from sealedsecurity/sealed#971. The generator-stamp gate that PR
adds lives in sealed's tools/root-checks, which has no counterpart here —
it follows in its own change.

Co-Authored-By: seal <noreply@sealedsecurity.com>
Moving the plugin to a store path took it out of the lockfile, so
`bun install` no longer supplies it — but the README's without-nix path
still listed only the four Go-lane plugins, and the preflight's error
named only the devenv fix. A contributor following the documented path
hit a hard stop whose message pointed somewhere they were not.

State why it is resolved from PATH rather than node_modules: the version
is stamped into every generated header, so it is pinned by the toolchain
instead of by a lockfile that can float underneath it.

Co-Authored-By: seal <noreply@sealedsecurity.com>
The non-nix install pointer named no version; point it at the in-repo
generated-header stamp (v2.12.0) the drift gate enforces.

Co-Authored-By: seal <noreply@sealedsecurity.com>
@seal-agent
seal-agent force-pushed the compass-agent-1405-protoc-gen-es branch from b4b85cf to fc8d0c7 Compare July 29, 2026 17:17
Comment thread README.md Outdated
…ugin

A locally-consistent off-version regenerate passes on the contributor's
machine; the gate rejects it in CI against the nix-pinned plugin.

Co-Authored-By: seal <noreply@sealedsecurity.com>
@mattwilkinsonn
mattwilkinsonn merged commit 0feb34b into main Jul 29, 2026
4 checks passed
@mattwilkinsonn
mattwilkinsonn deleted the compass-agent-1405-protoc-gen-es branch July 29, 2026 22:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants