Repository navigation
Description
Observation-class finding, surfaced while re-measuring reachability for #8103. ⛔ Not assigned, not queued — recording drift, not asking for a sprint.
What is declared vs what is true
packages/platform-objects/src/system/sys-secret.object.ts documents the table correctly in its managedBy note — three privileged producers (#4270): SettingsService, the engine's own secret-field encryption (encryptSecretFields), and the datasource credential binder.
Its two most load-bearing field descriptions still say the table is settings-only:
namespace—'Settings namespace this secret belongs to.'key—'Specifier key within the namespace.'id—'Opaque handle referenced by \sys_setting.value_enc`.'`
Measured on origin/main@3a3a0993, none of the three is true in general:
| Producer | writes namespace |
writes key |
reference lives at |
|---|---|---|---|
SettingsService |
settings namespace | specifier key | sys_setting.value_enc |
engine encryptSecretFields |
object name | field name | a business row's own column |
| datasource binder | caller-supplied (default datasource) |
datasource name | the datasource artefact's credential ref |
Why it is worth a card rather than a shrug
These strings are not internal comments — they are the field help an operator reads in the sys_secret grid, and they are compiled into the shipped translation bundles (packages/platform-objects/src/apps/translations/*.objects.generated.ts). A reader of the grid is told, by the platform, that namespace means "settings namespace".
That is the precise misreading that makes a sys_secret row look attributable to settings when it is not. #8103's re-measurement found that attribution by (namespace, key) is unsound exactly because those two columns mean something different per producer; the field help currently asserts the unsound reading. It is a declared ≠ enforced drift with a safety-relevant direction, which is why it is being written down rather than left.
Shape of the fix
Descriptions that name the tri-producer reality and stop implying a single owner — e.g. namespace/key as "producer-scoped pair; meaning depends on which producer wrote the row (see managedBy)", and id as the handle referenced by whichever producer's holder column. Regenerating the translation bundles is part of it.
⛔ Explicitly not proposing a producer/owner column here — that is a persist-path change with its own consequences and belongs to whoever takes that decision.
Found while working #8103 (reachability re-measurement); filed unassigned for triage.
Generated by Claude Code
Activity
os-support-ai commentedon Aug 26, 2026
Triage (triage seat, hourly round 2026-08-26, session
session_017g12KiZdCMYEGXDMLZifEr): gradedfinding→pm:queue, type Task, lanedomain:engine(fix lands inpackages/platform-objects/src/system/sys-secret.object.tsplus regenerated translation bundles — platform-objects is an engine-lane package). Rationale: the card asks only for truthful field descriptions naming the tri-producer reality already recorded in the same file'smanagedBynote, plus bundle regeneration — a declared-text correction with a named landing spot and no open decision (the card explicitly excludes the producer-column question, which stays out of scope for the dispatch). Task not Bug: the descriptions are the defect, no enforced contract is violated. Dispatch note: safety-relevant direction — the current prose asserts exactly the unsound(namespace, key)attribution reading that #8103's re-measurement rejected; regeneration of*.objects.generated.tsmust go through the repo's tooling, never by hand.Generated by Claude Code
os-warren commentedon Aug 26, 2026
Claimed and dispatched
session_01W6HFzyH98W1YaQXhJUJt6o(seat post [PM seat] domain:server · seat 1 — 🟢 sunhaolin #6367)claude/issue-12550-sys-secret-tri-producer-descriptionsTriage's scope is carried in unchanged, including the exclusion: ⛔ no producer/owner column — that is a persist-path change with its own consequences and belongs to whoever takes that decision. This card is truthful field descriptions plus regenerated bundles, and nothing else.
The safety-relevant direction is stated in the brief rather than left as background: the current prose asserts exactly the unsound
(namespace, key)attribution that #8103's re-measurement rejected, and these strings are field help an operator reads in thesys_secretgrid — not internal comments. The file's ownmanagedBynote already records the tri-producer reality, so the fix is making two descriptions agree with a fact recorded a few lines above them.One interaction the card could not have known about
pnpm i18n:extractnow carries--source-hashesand emits aLOCALE.source-hashes.generated.tscompanion per locale, andcheck:i18nnow compares 11 platform-objects bundles where it compared 8. Two consequences for this dispatch, both in the brief:check:i18ngoes red on a byte comparison.check:i18n-stale-fillfinding is a measurement the dev owes, not a guess — and if it does, that is the mechanism working, not a regression.Serial check: lands in
packages/platform-objects/src/system/sys-secret.object.tsplussrc/apps/translations/. The in-flight #12313 (PR #12595) is inplatform-objects/src/identity/and touches no bundle; #11728 ispackages/cli; #12578 ispackages/objectql. No overlap.Generated by Claude Code
os-warren commentedon Aug 26, 2026
{ "issue": 12550, "status": "done", "branch": "claude/issue-12550-sys-secret-tri-producer-descriptions", "pr": "https://github.com/objectstack-ai/objectstack/pull/12600", "premise_still_valid": true, "producerTableReverified": "YES — re-derived by producer symbol on this branch's base f93df4dbe3 (the card measured 3a3a0993). The table holds unchanged. SettingsService (services/service-settings/src/settings-service.ts, the this.secretStore.insert({id,namespace,key,...}) branch): settings namespace + specifier key, referenced by sys_setting.value_enc as a bare sec_ handle id. Engine encryptSecretFields (packages/objectql/src/engine.ts, secretDriver.create('sys_secret', { namespace: object, key: field, ... })): object name + field name, referenced by a ref on the business row's own column written by makeSecretRef (literal prefix 'secret:' plus the handle id). Datasource credential binder (services/service-datasource/src/datasource-secret-binder.ts): namespace = input.namespace ?? deps.namespace ?? 'datasource' (caller-supplied, default datasource), key = input.key ?? hint.name (datasource name), referenced by the artefact's credentialsRef (literal prefix 'sys_secret:' plus the handle id). No line numbers were inherited from the card.", "staleFillOutcome": "MEASURED, not guessed — and the answer is ZERO findings, for a reason that is itself the point. Leg 1 (English revised, bundles regenerated, translated locales untouched): 'check-i18n-stale-fill: OK (10 bundle set(s) - no new stale fills, 0 baselined)', exit 0, while zh-CN/ja-JP/es-ES were at that moment serving translations of the superseded English. Why the mechanism cannot see it, from its own two predicates: (a) the generated-half predicate is hash(value)===recorded AND hash(currentSource)!==recorded, and no record exists for these leaves - they carried real translations, so at backfill time value!==currentSource left them legacy-trusted, correctly per the ruling's property 1; (b) the cross-locale-agreement half needs at least two locales byte-identical at the leaf, and the three locales hold three different languages. That is exactly the blind spot check-i18n-stale-fill.mjs's own header declares ('a leaf stranded in ONE locale ... carries no evidence at all'), reached from the other side: three locales stale, no two agreeing. So the green gate is not the drift being absent. What I did about it: re-translated the four affected leaves by hand in each of the three locales - the action each locale's generated objects bundle prescribes in its own header ('Re-translate it by hand when its source changes'; merge fills gaps only, so regenerating cannot do it) - then re-ran pnpm i18n:extract so bundles and source-hash companions come from one run. Post-fix the gate is still OK/0, and the three *.source-hashes.generated.ts companions are byte-identical to main (no record is written for a leaf that differs from en - the same fact that made leg 1 report zero). Diff touches 4 .objects.generated.ts files and no companion.", "clause2": "TWO SEPARATE ANSWERS. (1) Published surface: YES, it moves. An object description and a field description are published metadata - served to the sys_secret grid as field help and compiled into the shipped translation bundles of @objectstack/platform-objects. Four leaves (sys_secret.description, fields.id.help, fields.namespace.help, fields.key.help) across four locales. (2) Accept/reject behaviour: NO, nothing moves. No field added, removed, renamed or re-typed; no Zod schema, validation rule, access rule, index, apiMethods or persist path touched; every sys_secret payload that parsed before parses identically.", "clause2_evidence": "git diff --stat vs merge base f93df4dbe: .changeset/sys-secret-tri-producer-field-help.md +42 | en/es-ES/ja-JP/zh-CN .objects.generated.ts 8 lines changed each (4 removed + 4 added) | packages/platform-objects/src/system/sys-secret.object.ts 37 changed; total 6 files, 87 insertions, 24 deletions. Line-kind measurement over that diff: in the object file every changed line is a `description:` string literal or a JSDoc comment line - zero changes to any Field.* option other than description, and zero changes to access, indexes, enable.apiMethods, highlightFields, listViews, or the fields key set. In the bundles the 16 changed leaf lines are exactly 4 `description:` + 12 `help:` values, four removed and four added per file, so no bundle key is added, removed or renamed. Two independent gates confirm the key-set claim without relying on my reading of the diff: check:i18n ('platform-objects in sync (11 bundle(s))') and check:i18n-coverage ('OK (12 config(s), 602 baselined untranslated string(s), none new)').", "regenCommand": "pnpm i18n:extract (= tsx packages/cli/bin/run-dev.js i18n extract packages/platform-objects/scripts/i18n-extract.config.ts --locales=zh-CN,ja-JP,es-ES --fill=default --source-hashes --out=packages/platform-objects/src/apps/translations). It wrote all 11 files including the three LOCALE.source-hashes.generated.ts companions in one run; the final run was the last mutation before commit. Never regenerated by hand.", "gates": { "green": [ "check:i18n — 'check-i18n-bundles: OK (9 package(s) - all bundles in sync, no undeclared authoring keys).' with 'platform-objects in sync (11 bundle(s))' (exit 0)", "check:i18n-stale-fill — 'check-i18n-stale-fill: OK (10 bundle set(s) - no new stale fills, 0 baselined).' (exit 0)", "check:i18n-coverage — 'check-i18n-coverage: OK (12 config(s), 602 baselined untranslated string(s), none new).' (exit 0)", "check:nul-bytes — 'check-nul-bytes: OK (scanned 6950 text file(s) ... no raw ASCII control bytes).' (exit 0)", "check:changeset-gate-self-tests (exit 0)", "check:cross-package-test-inputs — 'OK: 20 package(s) read outside themselves, all declared, and turbo.json hashes every declared glob.' (exit 0)", "check:objectui-changeset (exit 0)", "check:page-declaration-shape — 'OK - 34 page entries across 2215 sources ...' (exit 0)", "check:published-files — '69 publishable package(s) ... declare a files whitelist ...' (exit 0)", "check:slot-lookup — 'ratchet holds: 107 unswept site(s) in 25 file(s), none new' (exit 0)", "check:test-source-alias — 'OK - 72 packages with tests scanned' (exit 0)", "check:type-source-resolution — 'OK - 94 tsc program(s) across 77 packages scanned' (exit 0)", "scripts/check-adr-0087-registration.mjs — 'this PR adds no declared-breaking changeset (1 non-breaking changeset(s) seen).' (exit 0)", "scripts/check-changeset-no-major.mjs — 'This diff introduces no major bump.' (exit 0)", "scripts/check-ci-filter-parity.mjs — 'OK: all 109 declared cross-package glob(s) (84 unique) are covered' (exit 0)", "scripts/check-comment-mask-adoption.mjs — 'OK ... all 20 recorded and every recorded row still reached' (exit 0)", "scripts/check-cross-package-test-inputs.mjs (ci.yml) — 'OK: 20 package(s) read outside themselves, all declared' (exit 0)", "scripts/check-empty-changeset.mjs — 'No empty-frontmatter changeset introduced by this diff (1 declaring changeset(s) added).' (exit 0)", "scripts/check-plugin-teardown-shape.mjs — '63 Plugin implementation(s) ... baseline fully burned down' (exit 0)", "scripts/docs-audit/check-affected-docs.mjs — 'affected-docs self-test: 457 cases pass.' (exit 0)", "scripts/docs-audit/check-drift-comment.mjs — '56 cases pass across 5 fixture diff(s).' (exit 0)", "scripts/pm/release-rehearsal-clone.mjs --self-test — 'self-test passed' (exit 0)", "pnpm --filter @objectstack/platform-objects exec vitest run --maxWorkers=2 — 'Test Files 31 passed (31) / Tests 515 passed (515)' (exit 0)", "pnpm --filter @objectstack/platform-objects typecheck (exit 0)" ], "red": [], "notMeasured": [], "notes": "Union derived with `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack` over the real changeset (6 paths, three-dot vs merge base f93df4dbe): 18 path-matched families + the 2 convention-triggered i18n families. Every gate above ran at HEAD adb117b1c (post-commit), each exit captured BEFORE any pipe via redirect-then-capture; the verdict lines quoted are the gates' own, never a bare $?. Two gates first returned their COULD-NOT-MEASURE branch for unbuilt workspace deps rather than a verdict (check:i18n needed the built CLI; check:i18n-coverage needed the app-showcase dependency closure) - both were re-run green after targeted builds, so notMeasured is empty. WRAPPER CAVEAT, observed here: on one batch os-verify-lock.sh printed 'VERDICT command-exit 0' while an inner command had exited 1 - the wrapper reports the outer script's status. Every verdict in this report comes from the per-command exit line, not from the wrapper." }, "ablations": [ { "name": "bundle freshness — check:i18n catches a source/bundle divergence", "predictedBeforeRunning": "red; exactly 1 package (platform-objects) with exactly 1 of its 11 bundles drifted; the other 8 packages in sync; translated locales and the 3 source-hash companions unaffected; exit non-zero. Recorded to a file before the mutation was written.", "rebuildLeg": "Not required, and that is measured rather than assumed: packages/platform-objects/scripts/i18n-extract.config.ts imports ../src/system/index.js — a relative path into the package SOURCE, loaded via bundle-require — so extraction reads src/, never dist/. Direct evidence in this session: the first pnpm i18n:extract picked up the source edit and rewrote en.objects.generated.ts BEFORE @objectstack/platform-objects had ever been built on this branch.", "mutationProvenOnDisk": "Anchored counts read BEFORE any result: injected pre-fix text grep -cF = 1, removed post-fix text grep -cF = 0, git diff --stat = 1 file / 1 insertion / 2 deletions. The mutating script asserted its anchor and exits non-zero on a miss rather than silently no-op'ing.", "observed": "exit 1. Gate's own lines: 'check-i18n-bundles: 1 bundle problem(s)' and '- platform-objects: 1 bundle(s) drifted from the schema', with the other 8 packages listed 'in sync (4 bundle(s))'. Prediction matched on both direction and count.", "restore": "trap '<git checkout of the mutated path only>' EXIT INT TERM; verified afterwards with an empty git diff AND an empty git status --short." }, { "name": "check:i18n-stale-fill red-ability", "verdict": "deliberately NOT built, argued rather than omitted: pnpm check:i18n-stale-fill runs its own --self-test before every invocation and printed 'check-i18n-stale-fill: self-test OK' on each run here; the self-test already exercises the classifiers going red. A hand-built mutation would restate a check that had already run twice in this session, and it would tell us nothing about THIS change, whose relationship to the gate is the zero-finding measurement reported under staleFillOutcome." } ], "changedAssertions": "none — no existing test or assertion was modified.", "openQuestions": [], "out_of_scope_findings": [ "filed as #12599: CryptoContext.namespace / .key and CryptoHandle.id in packages/spec/src/contracts/crypto-provider.ts carry the identical settings-only prose, one layer below the three producers that all construct a CryptoContext. Filed rather than fixed in this PR: different package, TS doc comments rather than published metadata, and packages/spec brings its own artifact-regeneration gate family (the bounded in-place exemption's 'same gate family, no new verification surface' condition fails). Unassigned, labelled `finding`, not queued. Dedup: one targeted search of open issues before filing returned only #12550 itself and the closed #8026." ], "summary": "Corrected the sys_secret object description and the namespace / key / id field descriptions so they name the tri-producer reality instead of asserting the settings-only reading, and replaced the JSDoc block above namespace (which told the next author the same untruth) with the per-producer table plus the attribution warning. The object-level description is the one item beyond the card's three fields: same defect, same shipped-surface class, one line above them, correct form already pinned by the file's own managedBy note — declared in the PR body rather than slipped in. Bundles regenerated with pnpm i18n:extract (11 files, source-hash companions included); the three translated locales were serving translations of the superseded English, which check:i18n-stale-fill structurally cannot see, so their four leaves were re-translated by hand as that bundle's header prescribes. No producer/owner column was added, prototyped or argued for." }Generated by Claude Code
Generated by Claude Code
os-warren commentedon Aug 26, 2026
os-dev-report
(Supersedes the previous comment, whose HTML-comment marker was eaten by the body sanitizer on write — read-back confirmed the comment began with the fenced JSON and no marker, i.e. invisible to a marker scan. Same content, plus one field that had also lost a bracketed fragment. This one uses the literal-text spelling of the marker on the first line.)
{ "issue": 12550, "status": "done", "branch": "claude/issue-12550-sys-secret-tri-producer-descriptions", "pr": "https://github.com/objectstack-ai/objectstack/pull/12600", "premise_still_valid": true, "producerTableReverified": "YES — re-derived by producer symbol on this branch's base f93df4dbe3 (the card measured 3a3a0993). The table holds unchanged. SettingsService (services/service-settings/src/settings-service.ts, the this.secretStore.insert({id,namespace,key,...}) branch): settings namespace + specifier key, referenced by sys_setting.value_enc as a bare sec_ handle id. Engine encryptSecretFields (packages/objectql/src/engine.ts, secretDriver.create('sys_secret', { namespace: object, key: field, ... })): object name + field name, referenced by a ref on the business row's own column written by makeSecretRef (literal prefix 'secret:' plus the handle id). Datasource credential binder (services/service-datasource/src/datasource-secret-binder.ts): namespace = input.namespace ?? deps.namespace ?? 'datasource' (caller-supplied, default datasource), key = input.key ?? hint.name (datasource name), referenced by the artefact's credentialsRef (literal prefix 'sys_secret:' plus the handle id). No line numbers were inherited from the card.", "staleFillOutcome": "MEASURED, not guessed — and the answer is ZERO findings, for a reason that is itself the point. Leg 1 (English revised, bundles regenerated, translated locales untouched): 'check-i18n-stale-fill: OK (10 bundle set(s) - no new stale fills, 0 baselined)', exit 0, while zh-CN/ja-JP/es-ES were at that moment serving translations of the superseded English. Why the mechanism cannot see it, from its own two predicates: (a) the generated-half predicate is hash(value)===recorded AND hash(currentSource)!==recorded, and no record exists for these leaves - they carried real translations, so at backfill time value!==currentSource left them legacy-trusted, correctly per the ruling's property 1; (b) the cross-locale-agreement half needs at least two locales byte-identical at the leaf, and the three locales hold three different languages. That is exactly the blind spot check-i18n-stale-fill.mjs's own header declares ('a leaf stranded in ONE locale ... carries no evidence at all'), reached from the other side: three locales stale, no two agreeing. So the green gate is not the drift being absent. What I did about it: re-translated the four affected leaves by hand in each of the three locales - the action each locale's generated objects bundle prescribes in its own header ('Re-translate it by hand when its source changes'; merge fills gaps only, so regenerating cannot do it) - then re-ran pnpm i18n:extract so bundles and source-hash companions come from one run. Post-fix the gate is still OK/0, and the three *.source-hashes.generated.ts companions are byte-identical to main (no record is written for a leaf that differs from en - the same fact that made leg 1 report zero). Diff touches 4 .objects.generated.ts files and no companion.", "clause2": "TWO SEPARATE ANSWERS. (1) Published surface: YES, it moves. An object description and a field description are published metadata - served to the sys_secret grid as field help and compiled into the shipped translation bundles of @objectstack/platform-objects. Four leaves (sys_secret.description, fields.id.help, fields.namespace.help, fields.key.help) across four locales. (2) Accept/reject behaviour: NO, nothing moves. No field added, removed, renamed or re-typed; no Zod schema, validation rule, access rule, index, apiMethods or persist path touched; every sys_secret payload that parsed before parses identically.", "clause2_evidence": "git diff --stat vs merge base f93df4dbe: .changeset/sys-secret-tri-producer-field-help.md +42 | en/es-ES/ja-JP/zh-CN .objects.generated.ts 8 lines changed each (4 removed + 4 added) | packages/platform-objects/src/system/sys-secret.object.ts 37 changed; total 6 files, 87 insertions, 24 deletions. Line-kind measurement over that diff: in the object file every changed line is a `description:` string literal or a JSDoc comment line - zero changes to any Field.* option other than description, and zero changes to access, indexes, enable.apiMethods, highlightFields, listViews, or the fields key set. In the bundles the 16 changed leaf lines are exactly 4 `description:` + 12 `help:` values, four removed and four added per file, so no bundle key is added, removed or renamed. Two independent gates confirm the key-set claim without relying on my reading of the diff: check:i18n ('platform-objects in sync (11 bundle(s))') and check:i18n-coverage ('OK (12 config(s), 602 baselined untranslated string(s), none new)').", "regenCommand": "pnpm i18n:extract (= tsx packages/cli/bin/run-dev.js i18n extract packages/platform-objects/scripts/i18n-extract.config.ts --locales=zh-CN,ja-JP,es-ES --fill=default --source-hashes --out=packages/platform-objects/src/apps/translations). It wrote all 11 files including the three per-locale source-hashes.generated.ts companions in one run; the final run was the last mutation before commit. Never regenerated by hand.", "gates": { "green": [ "check:i18n — 'check-i18n-bundles: OK (9 package(s) - all bundles in sync, no undeclared authoring keys).' with 'platform-objects in sync (11 bundle(s))' (exit 0)", "check:i18n-stale-fill — 'check-i18n-stale-fill: OK (10 bundle set(s) - no new stale fills, 0 baselined).' (exit 0)", "check:i18n-coverage — 'check-i18n-coverage: OK (12 config(s), 602 baselined untranslated string(s), none new).' (exit 0)", "check:nul-bytes — 'check-nul-bytes: OK (scanned 6950 text file(s) ... no raw ASCII control bytes).' (exit 0)", "check:changeset-gate-self-tests (exit 0)", "check:cross-package-test-inputs — 'OK: 20 package(s) read outside themselves, all declared, and turbo.json hashes every declared glob.' (exit 0)", "check:objectui-changeset (exit 0)", "check:page-declaration-shape — 'OK - 34 page entries across 2215 sources ...' (exit 0)", "check:published-files — '69 publishable package(s) ... declare a files whitelist ...' (exit 0)", "check:slot-lookup — 'ratchet holds: 107 unswept site(s) in 25 file(s), none new' (exit 0)", "check:test-source-alias — 'OK - 72 packages with tests scanned' (exit 0)", "check:type-source-resolution — 'OK - 94 tsc program(s) across 77 packages scanned' (exit 0)", "scripts/check-adr-0087-registration.mjs — 'this PR adds no declared-breaking changeset (1 non-breaking changeset(s) seen).' (exit 0)", "scripts/check-changeset-no-major.mjs — 'This diff introduces no major bump.' (exit 0)", "scripts/check-ci-filter-parity.mjs — 'OK: all 109 declared cross-package glob(s) (84 unique) are covered' (exit 0)", "scripts/check-comment-mask-adoption.mjs — 'OK ... all 20 recorded and every recorded row still reached' (exit 0)", "scripts/check-cross-package-test-inputs.mjs (ci.yml) — 'OK: 20 package(s) read outside themselves, all declared' (exit 0)", "scripts/check-empty-changeset.mjs — 'No empty-frontmatter changeset introduced by this diff (1 declaring changeset(s) added).' (exit 0)", "scripts/check-plugin-teardown-shape.mjs — '63 Plugin implementation(s) ... baseline fully burned down' (exit 0)", "scripts/docs-audit/check-affected-docs.mjs — 'affected-docs self-test: 457 cases pass.' (exit 0)", "scripts/docs-audit/check-drift-comment.mjs — '56 cases pass across 5 fixture diff(s).' (exit 0)", "scripts/pm/release-rehearsal-clone.mjs --self-test — 'self-test passed' (exit 0)", "pnpm --filter @objectstack/platform-objects exec vitest run --maxWorkers=2 — 'Test Files 31 passed (31) / Tests 515 passed (515)' (exit 0)", "pnpm --filter @objectstack/platform-objects typecheck (exit 0)" ], "red": [], "notMeasured": [], "notes": "Union derived with `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack` over the real changeset (6 paths, three-dot vs merge base f93df4dbe): 18 path-matched families + the 2 convention-triggered i18n families. Every gate above ran at HEAD adb117b1c (post-commit), each exit captured BEFORE any pipe via redirect-then-capture; the verdict lines quoted are the gates' own, never a bare $?. Two gates first returned their COULD-NOT-MEASURE branch for unbuilt workspace deps rather than a verdict (check:i18n needed the built CLI; check:i18n-coverage needed the app-showcase dependency closure) - both were re-run green after targeted builds, so notMeasured is empty. WRAPPER CAVEAT, observed here: on one batch os-verify-lock.sh printed 'VERDICT command-exit 0' while an inner command had exited 1 - the wrapper reports the outer script's status. Every verdict in this report comes from the per-command exit line, not from the wrapper." }, "ablations": [ { "name": "bundle freshness — check:i18n catches a source/bundle divergence", "predictedBeforeRunning": "red; exactly 1 package (platform-objects) with exactly 1 of its 11 bundles drifted; the other 8 packages in sync; translated locales and the 3 source-hash companions unaffected; exit non-zero. Recorded to a file before the mutation was written.", "rebuildLeg": "Not required, and that is measured rather than assumed: packages/platform-objects/scripts/i18n-extract.config.ts imports ../src/system/index.js — a relative path into the package SOURCE, loaded via bundle-require — so extraction reads src/, never dist/. Direct evidence in this session: the first pnpm i18n:extract picked up the source edit and rewrote en.objects.generated.ts BEFORE @objectstack/platform-objects had ever been built on this branch.", "mutationProvenOnDisk": "Anchored counts read BEFORE any result: injected pre-fix text grep -cF = 1, removed post-fix text grep -cF = 0, git diff --stat = 1 file / 1 insertion / 2 deletions. The mutating script asserted its anchor and exits non-zero on a miss rather than silently no-op'ing.", "observed": "exit 1. Gate's own lines: 'check-i18n-bundles: 1 bundle problem(s)' and '- platform-objects: 1 bundle(s) drifted from the schema', with the other 8 packages listed 'in sync (4 bundle(s))'. Prediction matched on both direction and count.", "restore": "A trap on EXIT INT TERM running a git checkout of ONLY the mutated path (never a checkout of '.'); verified afterwards with an empty git diff AND an empty git status --short." }, { "name": "check:i18n-stale-fill red-ability", "verdict": "deliberately NOT built, argued rather than omitted: pnpm check:i18n-stale-fill runs its own --self-test before every invocation and printed 'check-i18n-stale-fill: self-test OK' on each run here; the self-test already exercises the classifiers going red. A hand-built mutation would restate a check that had already run twice in this session, and it would tell us nothing about THIS change, whose relationship to the gate is the zero-finding measurement reported under staleFillOutcome." } ], "changedAssertions": "none — no existing test or assertion was modified.", "openQuestions": [], "out_of_scope_findings": [ "filed as #12599: CryptoContext.namespace / .key and CryptoHandle.id in packages/spec/src/contracts/crypto-provider.ts carry the identical settings-only prose, one layer below the three producers that all construct a CryptoContext. Filed rather than fixed in this PR: different package, TS doc comments rather than published metadata, and packages/spec brings its own artifact-regeneration gate family (the bounded in-place exemption's 'same gate family, no new verification surface' condition fails). Unassigned, labelled `finding`, not queued. Dedup: one targeted search of open issues before filing returned only #12550 itself and the closed #8026." ], "summary": "Corrected the sys_secret object description and the namespace / key / id field descriptions so they name the tri-producer reality instead of asserting the settings-only reading, and replaced the JSDoc block above namespace (which told the next author the same untruth) with the per-producer table plus the attribution warning. The object-level description is the one item beyond the card's three fields: same defect, same shipped-surface class, one line above them, correct form already pinned by the file's own managedBy note — declared in the PR body rather than slipped in. Bundles regenerated with pnpm i18n:extract (11 files, source-hash companions included); the three translated locales were serving translations of the superseded English, which check:i18n-stale-fill structurally cannot see, so their four leaves were re-translated by hand as that bundle's header prescribes. No producer/owner column was added, prototyped or argued for." }Generated by Claude Code
Generated by Claude Code