From 686b8ec251e7fbc36ceb42ac8e14307632ef419e Mon Sep 17 00:00:00 2001 From: mintaka Date: Sun, 6 Sep 2026 02:00:29 -0400 Subject: [PATCH 1/2] refactor(store): read the credential denylist from the installed SDK, not the vendored subtree (RIG-2407) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR2 of 3. Repoints the credential-denylist generator off `forks/oh-my-pi/` onto the installed `@oh-my-pi/pi-coding-agent`, removing the last **build-time** dependency on the vendored subtree so PR3 can delete it. ### What moves `schemaRelPath` (`gen_credential_keys.go`) now resolves the settings schema from the installed package. Note bun's isolated linker keeps packages under `node_modules/.bun/node_modules/`, not at the plain top level, so that prefix is load-bearing — and `bun install` must have run before `go generate`. The parser itself is unchanged, and deliberately so: `isCredential` in 18.0.11 (`src/config/settings-schema.ts:6064-6071`) still marks a path credential on `credential: true` at the def level **or** `ui.secret === true`, which is exactly the rule `extractCredentialKeys` reproduces structurally. This stays a path change, not a semantics change. Also fixed the **hardcoded** provenance literal the generator emits into `credential_keys_gen.go`. Regenerating alone would not have corrected it — it would have re-emitted `forks/oh-my-pi/...` into a file whose source no longer exists after PR3. ### The denylist grows by one, which is the fail-safe direction ``` + images.urls.credentials ``` 7 → 8 paths, **nothing removed**. Verified the new path is genuinely credential-marked upstream (`settings-schema.ts:1054`, `credential: true`), so this is the generator correctly tracking the pinned dependency rather than drift: one more credential path the store door rejects. `TestCredentialKeysMatchSchema`'s `want` set is updated in the same commit — a denylist diff is the expected outcome of an SDK bump, not a failure. I also sharpened that test's docstring to say which direction of diff is safe: a path appearing is adopted, but a path **disappearing** means the door stopped rejecting something it used to, and that is a policy regression to investigate before accepting. The old wording ("regenerate and re-review") did not distinguish them. ### Verification - `go generate ./...` from `go/internal/store` — clean, writes 8 paths - `go test ./internal/store/` — ok (full package, `-count=1`) - `go test ./internal/store/ -run 'TestCredentialKeys|TestValidateConfigBundleRejectsCredentialKeys'` — ok - `gofmt -l internal/store/` — clean; `go vet ./internal/store/` — clean - `grep -rn 'forks/oh-my-pi' go/internal/store/` — no matches `forks/oh-my-pi/` is still present after this lands; PR3 deletes the tree, its `.moon/workspace.yml` registration, and the remaining prose references. Spec-impact: none. Refs RIG-2407 Co-authored-by: Matt Wilkinson --- go/internal/store/agent_config.go | 3 ++- go/internal/store/agent_config_test.go | 11 +++++++--- go/internal/store/credential_keys_gen.go | 7 +++--- go/internal/store/gen_credential_keys.go | 28 +++++++++++++++--------- 4 files changed, 32 insertions(+), 17 deletions(-) diff --git a/go/internal/store/agent_config.go b/go/internal/store/agent_config.go index d0e2b638a..9852effb7 100644 --- a/go/internal/store/agent_config.go +++ b/go/internal/store/agent_config.go @@ -1013,7 +1013,8 @@ func parseYAMLMapping(content []byte, joined string) (map[string]any, error) { } // credentialKeys (credential_keys_gen.go) is generated from the SDK's -// isCredential markers; refresh it at a fork bump with `go generate ./...`. +// isCredential markers, read from the installed @oh-my-pi/pi-coding-agent; +// refresh it at an SDK bump with `go generate ./...`. // //go:generate go run gen_credential_keys.go diff --git a/go/internal/store/agent_config_test.go b/go/internal/store/agent_config_test.go index 123badfb1..aaffcc8ff 100644 --- a/go/internal/store/agent_config_test.go +++ b/go/internal/store/agent_config_test.go @@ -585,14 +585,19 @@ func TestValidateConfigBundleRejectsCredentialKeys(t *testing.T) { } // TestCredentialKeysMatchSchema is a change-detector on the generated denylist: -// the seven SDK isCredential paths (settings-schema.ts) are the load-bearing -// door policy, so a fork bump that adds or drops one must be caught. If this -// reds, regenerate credential_keys_gen.go (`go generate ./...`) and re-review. +// the SDK's isCredential paths (@oh-my-pi/pi-coding-agent +// src/config/settings-schema.ts) are the load-bearing door policy, so an SDK +// bump that adds or drops one must be caught. If this reds, regenerate +// credential_keys_gen.go (`go generate ./...`) and re-review the diff — a new +// credential-marked path is expected at a bump and must be ADOPTED here, but a +// path DISAPPEARING means the door stopped rejecting something it used to, which +// is a policy regression to investigate before accepting. func TestCredentialKeysMatchSchema(t *testing.T) { want := []string{ "auth.broker.token", "dev.autoqaPush.token", "hindsight.apiToken", + "images.urls.credentials", "mnemopi.embeddingApiKey", "mnemopi.llmApiKey", "searxng.basicPassword", diff --git a/go/internal/store/credential_keys_gen.go b/go/internal/store/credential_keys_gen.go index a344cfdb6..835827f37 100644 --- a/go/internal/store/credential_keys_gen.go +++ b/go/internal/store/credential_keys_gen.go @@ -1,8 +1,8 @@ // Code generated by gen_credential_keys.go; DO NOT EDIT. // -// Source: forks/oh-my-pi/.../config/settings-schema.ts (isCredential markers: -// `credential: true` at the def level OR `ui.secret === true`). Refresh with -// `go generate ./...` from go/internal/store at a fork bump. +// Source: @oh-my-pi/pi-coding-agent src/config/settings-schema.ts (isCredential +// markers: `credential: true` at the def level OR `ui.secret === true`). Refresh +// with `go generate ./...` from go/internal/store at an SDK bump. package store @@ -12,6 +12,7 @@ var credentialKeys = []string{ "auth.broker.token", "dev.autoqaPush.token", "hindsight.apiToken", + "images.urls.credentials", "mnemopi.embeddingApiKey", "mnemopi.llmApiKey", "searxng.basicPassword", diff --git a/go/internal/store/gen_credential_keys.go b/go/internal/store/gen_credential_keys.go index d1625c75d..8eb14bd62 100644 --- a/go/internal/store/gen_credential_keys.go +++ b/go/internal/store/gen_credential_keys.go @@ -4,24 +4,29 @@ // settings schema. It is the authoritative refresh step for the store door's // credential denylist (RIG-1678 T1, OQ-2 (c)): the door rejects a // settings/config.yml that sets any SDK credential-marked path, and that path -// set MUST track the SDK's own isCredential marker across fork bumps. +// set MUST track the SDK's own isCredential marker across SDK bumps. // -// isCredential (forks/oh-my-pi/packages/coding-agent/src/config/settings-schema.ts, +// isCredential (@oh-my-pi/pi-coding-agent src/config/settings-schema.ts, // `export function isCredential`) marks a path credential when its schema def // carries EITHER `credential: true` at the def level OR `ui.secret === true`. // This generator reproduces that exact rule by structurally parsing the // SETTINGS_SCHEMA object literal — a self-contained scan with no Node/Bun -// runtime and no native-module load, so it stays reproducible at a fork bump +// runtime and no native-module load, so it stays reproducible at an SDK bump // (running isCredential itself would drag the SDK's native `@oh-my-pi/pi-*` // modules and their platform binaries into the build). // -// Refresh at a fork bump: from go/internal/store, run +// The schema is read from the INSTALLED package in node_modules rather than a +// vendored copy, so the denylist tracks the pinned dependency. Note bun's +// isolated linker keeps packages under `node_modules/.bun/node_modules/`, not +// at the plain top level — hence the path below. +// +// Refresh at an SDK bump: from go/internal/store, run // // go generate ./... // // (the //go:generate directive on credentialKeys in agent_config.go invokes // this), then commit the regenerated credential_keys_gen.go. This is the -// fork-bump checklist entry CP-3's prevention property names — any new +// SDK-bump checklist entry CP-3's prevention property names — any new // credential-marked SDK path lands in the denylist here. package main @@ -38,8 +43,11 @@ import ( // schemaRelPath is the settings schema, relative to this generator's own // directory (go/internal/store) — resolved from runtime.Caller so `go generate` -// finds it regardless of the caller's working directory. -const schemaRelPath = "../../../forks/oh-my-pi/packages/coding-agent/src/config/settings-schema.ts" +// finds it regardless of the caller's working directory. It points into the +// INSTALLED `@oh-my-pi/pi-coding-agent`, under bun's isolated-linker virtual +// store (`node_modules/.bun/node_modules/`, not the plain top-level +// `node_modules/`), so a `bun install` must have run before `go generate`. +const schemaRelPath = "../../../node_modules/.bun/node_modules/@oh-my-pi/pi-coding-agent/src/config/settings-schema.ts" func main() { if err := run(); err != nil { @@ -71,9 +79,9 @@ func run() error { var b bytes.Buffer fmt.Fprintln(&b, "// Code generated by gen_credential_keys.go; DO NOT EDIT.") fmt.Fprintln(&b, "//") - fmt.Fprintln(&b, "// Source: forks/oh-my-pi/.../config/settings-schema.ts (isCredential markers:") - fmt.Fprintln(&b, "// `credential: true` at the def level OR `ui.secret === true`). Refresh with") - fmt.Fprintln(&b, "// `go generate ./...` from go/internal/store at a fork bump.") + fmt.Fprintln(&b, "// Source: @oh-my-pi/pi-coding-agent src/config/settings-schema.ts (isCredential") + fmt.Fprintln(&b, "// markers: `credential: true` at the def level OR `ui.secret === true`). Refresh") + fmt.Fprintln(&b, "// with `go generate ./...` from go/internal/store at an SDK bump.") fmt.Fprintln(&b, "") fmt.Fprintln(&b, "package store") fmt.Fprintln(&b, "") From 4afb684fc805b863cde710945afe14ef7a821a9f Mon Sep 17 00:00:00 2001 From: mintaka Date: Sun, 6 Sep 2026 02:23:56 -0400 Subject: [PATCH 2/2] fix(store): gate credential-denylist drift in CI and resolve the schema through the consuming package (RIG-2407) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Review fixes on the generator repoint. One high, two medium, one low. ### The change-detector could no longer detect the thing it exists for (high) Moving the schema into gitignored `node_modules` broke the property that made "regenerate at an SDK bump" enforceable. `TestCredentialKeysMatchSchema` compares `credentialKeys` against a hand-maintained `want` list — both in-repo, so an SDK bump changes the source of truth while touching **neither side**, and the test stays green. Under the vendored subtree the schema was a tracked file, so a bump landed a reviewable diff; that visibility is what the repoint silently removed. Renovate bumps the caret pin unattended, so this is the expected path, not a hypothetical. Added `compass-go:credential-keys-drift` to `go/moon.yml`, modeled on the `sqlc-drift` precedent already there and in `ci` for the same reasons (fully local, no DB, no cross-project delegation): snapshot the generated file, regenerate, fail on any byte diff. Since the schema is gitignored and cannot be a task input, it keys on `bun.lock`, where the pinned version is visible, so a bump reschedules it. Verified red-green rather than assumed: injecting one `credential: true` path into the installed schema makes the gate write 9 paths and exit 1 with the diff naming `zz.injected.probeToken` — while `TestCredentialKeysMatchSchema` stays **green**, which is exactly the blind spot. Restored, the gate passes. `TestCredentialKeysMatchSchema` keeps its real remaining value (catching a hand-edit or bad merge of the DO-NOT-EDIT file); its docstring now says plainly that it does *not* detect a bump and names the gate that does, instead of gating advice on an "if this reds" that cannot happen. ### The schema path resolved through bun's hoist alias (medium) `schemaRelPath` pointed into `node_modules/.bun/node_modules/`, a flat namespace with one entry per package **name**. With two dependents on different versions it can resolve to a version the agent does not run, and it fails silently — the wrong schema parses fine. Repointed to the consuming package's own dependency edge, `packages/compass-agent/node_modules/@oh-my-pi/pi-coding-agent/...`, which is what the frozen design record specifies (`design.md:451-455`, `oq-resolutions.md:29-33`) and which I had deviated from without saying so. The symlink confirms it resolves version-correctly by construction (`-> …/@oh-my-pi+pi-coding-agent@18.0.11+09a228d332b3c507/…`). Regenerating through the new path is **byte-identical**. ### The parser under-collected on ordinary authoring (medium) `extractCredentialKeys` compared marker text with a bare `== "true"`, so shapes the SDK could adopt at any time dropped a credential path *silently*: `credential: true // yes`, `credential: true /* yes */`, and `credential: true as const` all yielded no match. Replaced with an `isLiteralTrue` helper that strips trailing line/block comments and an `as const` assertion before comparing, while still refusing to guess at anything it cannot evaluate (a helper call, a variable). Unit-checked all seven shapes. The `len(keys) == 0` guard only caught a *total* parse failure, which by construction cannot catch a partial miss. Added a plausibility floor on the total top-level key count (`minSchemaKeys = 400`; the pinned schema has ~480), so a parser that has lost its footing on a new shape is a loud error rather than a short denylist. ### Behavioral coverage for the new entry (low) `images.urls.credentials` is the first denylist entry whose leaf is a **record** rather than a scalar string, so it exercises `yamlPathIsSet`'s non-nil-leaf check differently from every existing case, and nothing covered it. Added a door case. Confirmed red-green: with the path removed from the denylist the door **accepts** the credential bundle (`want ErrInvalidArgument, got `) — the exact silent regression it now catches. ### Verification - `go test ./internal/store/... -count=1` — ok - `go generate ./internal/store/...` — byte-identical through the new path, 8 paths - `moon run compass-go:credential-keys-drift` — green on the clean tree, red on an injected schema path - `gofmt -l internal/store/` clean; `go vet ./internal/store/` clean - `grep -rn 'forks/oh-my-pi' go/internal/store/` — no matches Refs RIG-2407 Co-authored-by: Matt Wilkinson --- go/internal/store/agent_config_test.go | 33 ++++++--- go/internal/store/gen_credential_keys.go | 92 +++++++++++++++++++----- go/moon.yml | 40 +++++++++-- 3 files changed, 137 insertions(+), 28 deletions(-) diff --git a/go/internal/store/agent_config_test.go b/go/internal/store/agent_config_test.go index aaffcc8ff..6d2df86cd 100644 --- a/go/internal/store/agent_config_test.go +++ b/go/internal/store/agent_config_test.go @@ -569,6 +569,16 @@ func TestValidateConfigBundleRejectsCredentialKeys(t *testing.T) { member: tarEntry{name: "models.yml", content: "providers:\n 0: junk\n openai:\n 1: junk\n apiKey: sk-secret\n"}, wantSub: "providers.openai.apiKey", }, + { + // The only denylist entry whose leaf is a RECORD rather than a scalar + // string, so it exercises yamlPathIsSet's non-nil-leaf check + // differently from every case above (all of which terminate in a + // string). Pins the door's behavior on the path the SDK 18.x bump + // added. + name: "settings record-valued credential leaf", + member: tarEntry{name: "settings/config.yml", content: "images:\n urls:\n credentials:\n s3: {key: v}\n"}, + wantSub: "images.urls.credentials", + }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { @@ -584,14 +594,21 @@ func TestValidateConfigBundleRejectsCredentialKeys(t *testing.T) { } } -// TestCredentialKeysMatchSchema is a change-detector on the generated denylist: -// the SDK's isCredential paths (@oh-my-pi/pi-coding-agent -// src/config/settings-schema.ts) are the load-bearing door policy, so an SDK -// bump that adds or drops one must be caught. If this reds, regenerate -// credential_keys_gen.go (`go generate ./...`) and re-review the diff — a new -// credential-marked path is expected at a bump and must be ADOPTED here, but a -// path DISAPPEARING means the door stopped rejecting something it used to, which -// is a policy regression to investigate before accepting. +// TestCredentialKeysMatchSchema guards the generated denylist against a +// hand-edit or a bad merge of the DO-NOT-EDIT file: it pins credentialKeys to an +// explicit list, so an in-repo change to either side reds. +// +// It deliberately does NOT detect an SDK bump, and cannot: the schema lives in +// gitignored node_modules, so a bump changes the source of truth while touching +// neither credentialKeys nor `want`, leaving this green. The +// `compass-go:credential-keys-drift` moon task owns that detection by +// regenerating against the installed schema and failing on a byte diff. +// +// When that gate reds, regenerate (`go generate ./...` from go/internal/store) +// and update `want` here in the same commit — a new credential-marked path is +// expected at a bump and must be ADOPTED, but a path DISAPPEARING means the door +// stopped rejecting something it used to, which is a policy regression to +// investigate before accepting. func TestCredentialKeysMatchSchema(t *testing.T) { want := []string{ "auth.broker.token", diff --git a/go/internal/store/gen_credential_keys.go b/go/internal/store/gen_credential_keys.go index 8eb14bd62..f58061d65 100644 --- a/go/internal/store/gen_credential_keys.go +++ b/go/internal/store/gen_credential_keys.go @@ -16,9 +16,9 @@ // modules and their platform binaries into the build). // // The schema is read from the INSTALLED package in node_modules rather than a -// vendored copy, so the denylist tracks the pinned dependency. Note bun's -// isolated linker keeps packages under `node_modules/.bun/node_modules/`, not -// at the plain top level — hence the path below. +// vendored copy, so the denylist tracks the pinned dependency. It resolves +// through the CONSUMING package's own dependency edge (see schemaRelPath), so +// the denylist is generated from the schema compass-agent actually runs. // // Refresh at an SDK bump: from go/internal/store, run // @@ -43,11 +43,22 @@ import ( // schemaRelPath is the settings schema, relative to this generator's own // directory (go/internal/store) — resolved from runtime.Caller so `go generate` -// finds it regardless of the caller's working directory. It points into the -// INSTALLED `@oh-my-pi/pi-coding-agent`, under bun's isolated-linker virtual -// store (`node_modules/.bun/node_modules/`, not the plain top-level -// `node_modules/`), so a `bun install` must have run before `go generate`. -const schemaRelPath = "../../../node_modules/.bun/node_modules/@oh-my-pi/pi-coding-agent/src/config/settings-schema.ts" +// finds it regardless of the caller's working directory. +// +// It deliberately resolves through the CONSUMING workspace package's own +// node_modules edge rather than bun's `node_modules/.bun/node_modules/` hoist +// alias. That alias is a flat namespace with one entry per package NAME, so +// with two dependents on different versions it can resolve to a version the +// agent does not run — and it would fail silently, since the wrong schema +// still parses fine. This path expresses the real invariant: the schema +// belonging to the package that consumes the SDK. +// +// The schema lives in gitignored node_modules, so an SDK bump changes this +// generator's input without touching any tracked file. The +// `compass-go:credential-keys-drift` CI task is what makes such a bump fail +// loudly if the denylist was not regenerated; a `bun install` must have run +// before `go generate`. +const schemaRelPath = "../../../packages/compass-agent/node_modules/@oh-my-pi/pi-coding-agent/src/config/settings-schema.ts" func main() { if err := run(); err != nil { @@ -67,13 +78,22 @@ func run() error { return fmt.Errorf("read settings schema %q: %w", schemaPath, err) } - keys, err := extractCredentialKeys(string(src)) + keys, total, err := extractCredentialKeys(string(src)) if err != nil { return fmt.Errorf("parse settings schema: %w", err) } if len(keys) == 0 { return fmt.Errorf("no credential-marked paths found — schema shape changed, refusing to emit an empty denylist") } + // A plausibility floor on the TOTAL parsed key count. The empty-denylist + // guard above only catches a total parse failure; the hand-rolled parser's + // real risk is UNDER-collection — a def shape it mishandles yields a short + // list, not an error, which would silently drop a path from the door's + // denylist. The pinned schema carries ~480 top-level paths, so a count far + // below that means the parser lost its footing on a new shape. + if total < minSchemaKeys { + return fmt.Errorf("parsed only %d top-level schema paths (expected >= %d) — schema shape changed and the parser is likely under-collecting; refusing to emit a possibly-short denylist", total, minSchemaKeys) + } sort.Strings(keys) var b bytes.Buffer @@ -105,18 +125,25 @@ func run() error { return nil } +// minSchemaKeys is the plausibility floor for the total top-level path count +// (see the check in run). The pinned schema has ~480; this is set well below to +// stay quiet across ordinary schema growth or trimming while still catching a +// parser that has stopped walking the literal properly. +const minSchemaKeys = 400 + // extractCredentialKeys parses the SETTINGS_SCHEMA object literal in the schema -// source and returns every top-level key whose def is credential-marked. It +// source and returns every top-level key whose def is credential-marked, plus +// the total number of top-level keys walked (for run's plausibility floor). It // mirrors isCredential: a def is credential-marked when it has `credential: // true` directly, or a `ui` object with `secret: true`. -func extractCredentialKeys(src string) ([]string, error) { +func extractCredentialKeys(src string) ([]string, int, error) { p := &jsParser{s: src} if err := p.seekSchema(); err != nil { - return nil, err + return nil, 0, err } schema, err := p.parseObject() if err != nil { - return nil, err + return nil, 0, err } var keys []string for key, val := range schema { @@ -124,15 +151,48 @@ func extractCredentialKeys(src string) ([]string, error) { if !ok { continue } - if def["credential"] == "true" { + if isLiteralTrue(def["credential"]) { keys = append(keys, key) continue } - if ui, ok := def["ui"].(map[string]any); ok && ui["secret"] == "true" { + if ui, ok := def["ui"].(map[string]any); ok && isLiteralTrue(ui["secret"]) { keys = append(keys, key) } } - return keys, nil + return keys, len(schema), nil +} + +// isLiteralTrue reports whether a parsed def value is the literal `true`. +// Non-object values arrive as their trimmed raw text, so ordinary authoring +// that decorates the literal must not read as false: a trailing line or block +// comment (`credential: true // yes`) and a `as const` assertion are both +// stripped before comparing. Anything else — a helper call, a variable, a +// computed expression — is deliberately NOT treated as true, since the +// generator cannot evaluate it. +func isLiteralTrue(v any) bool { + s, ok := v.(string) + if !ok { + return false + } + if i := strings.Index(s, "//"); i >= 0 { + s = s[:i] + } + for { + i := strings.Index(s, "/*") + if i < 0 { + break + } + j := strings.Index(s[i+2:], "*/") + if j < 0 { + s = s[:i] + break + } + s = s[:i] + s[i+2+j+2:] + } + s = strings.TrimSpace(s) + s = strings.TrimSuffix(s, "const") + s = strings.TrimSpace(strings.TrimSuffix(strings.TrimSpace(s), "as")) + return s == "true" } // jsParser is a minimal structural parser over a JS/TS source string. It reads diff --git a/go/moon.yml b/go/moon.yml index a35bdc80f..8d67b005e 100644 --- a/go/moon.yml +++ b/go/moon.yml @@ -252,6 +252,37 @@ tasks: runFromWorkspaceRoot: false inputs: *sqlc_sources + credential-keys-drift: + # Fail if the checked-in credential denylist is stale vs the INSTALLED SDK + # settings schema: snapshot the committed file, regenerate, and fail on any + # byte diff. This exists because the generator's input + # (packages/compass-agent/node_modules/@oh-my-pi/pi-coding-agent) is + # gitignored, so an SDK bump moves the source of truth without touching any + # tracked file — the in-repo change-detector test compares credentialKeys + # against a hand-maintained want list, and BOTH sides stay unchanged at a + # bump, so it cannot catch it. Renovate bumps the caret pin + # (`^18.0.11`) unattended, and a missed regeneration means the store door + # silently stops rejecting a newly credential-marked path. Modeled on + # sqlc-drift above and in `ci` for the same reason: fully local, no DB, no + # cross-project delegation. `git diff --no-index` compares raw files (reads + # no index/worktree state, and git is always on PATH), so it behaves + # identically in CI and in a secondary jj workspace. The generator writes in + # place and is idempotent, so a no-drift run leaves the byte-identical file. + # + # Requires a completed `bun install` — the generator fails loudly, naming the + # absolute schema path, when the dependency is not installed. + script: 'tmp=$(mktemp -d); trap ''rm -rf "$tmp"'' EXIT; cp internal/store/credential_keys_gen.go "$tmp/credential_keys_gen.go"; go generate ./internal/store/...; if ! git diff --no-index --quiet "$tmp/credential_keys_gen.go" internal/store/credential_keys_gen.go; then echo "credential-keys drift: internal/store/credential_keys_gen.go is stale vs the installed SDK schema — run \`go generate ./...\` from go/internal/store and commit. A path APPEARING is an SDK bump to adopt; a path DISAPPEARING means the door stopped rejecting a credential it used to, and is a policy regression to investigate before accepting:"; git diff --no-index "$tmp/credential_keys_gen.go" internal/store/credential_keys_gen.go; exit 1; fi' + options: + runFromWorkspaceRoot: false + inputs: + - 'internal/store/gen_credential_keys.go' + - 'internal/store/credential_keys_gen.go' + - 'internal/store/agent_config.go' + # The schema itself is gitignored and cannot be an input, so key on the + # lockfile: the pinned SDK version is visible there, so a bump reschedules + # this task. + - '/bun.lock' + sqlc-vet: # `sqlc vet` with the sqlc/db-prepare rule PREPAREs every generated query # against a live, schema-loaded Postgres — the strongest check sqlc offers @@ -304,15 +335,16 @@ tasks: ci: # module is affected: format, vet, lint (incl. exhaustiveness), -race test, - # build, the supply-chain fence, and the sqlc drift gate. `drift` is NOT a + # build, the supply-chain fence, and the two drift gates. `drift` is NOT a # dep here — it belongs to the schema pipeline (compass-proto) and is # scheduled through the gen tree, matching compass-proto/moon.yml's own - # `ci` composition. `sqlc-drift` IS a dep: it is fully local (no - # cross-project delegation, no DB), so it belongs in ci directly. `sqlc-vet` + # `ci` composition. `sqlc-drift` and `credential-keys-drift` ARE deps: both + # are fully local (no cross-project delegation, no DB), so they belong in ci + # directly. `sqlc-vet` # is deliberately NOT a dep: it needs a live Postgres, which the moon battery # has no business realizing (ci.yml has no service on the moon job); it is # invoked instead by the env-bearing `pgtest` CI job, the same way pgtest/ # microvm/forge/gtk4 are peeled into their own service-carrying peer jobs. - deps: ['fmt', 'vet', 'lint', 'nilaway', 'test', 'build', 'vuln', 'licenses', 'sqlc-drift'] + deps: ['fmt', 'vet', 'lint', 'nilaway', 'test', 'build', 'vuln', 'licenses', 'sqlc-drift', 'credential-keys-drift'] options: cache: false