Uh oh!
There was an error while loading. Please reload this page.
fix(security): withdraw the ADR-0091 D5 recertification claim from the attestation columns (#9046) - #9669
Conversation
…from the attestation columns `last_certified_at` / `certified_by` on `sys_user_permission_set` and `sys_user_position` are the ADR-0091 D5 substrate and nothing more: a whole-tree sweep finds them only in these two declarations and the generated i18n bundles. Nothing stamps them, nothing reads them, no surface derives "never certified" or "certification stale". Their descriptions stated D5's intent as though it were the behavior. On a compliance surface (SOX / ISO 27001 access review) that reads as evidence of a control the platform does not have. ADR-0049 enforce-or-remove, settled the way `sys_capability.active` was (maintainer ruling, 2026-08-13): the claim is withdrawn in prose rather than the workflow built or the columns dropped. Co-Authored-By: Claude <noreply@anthropic.com>
…ion into zh-CN/ja-JP/es-ES
`node scripts/check-i18n-bundles.mjs --write --filter=security` regenerates the
default locale from the schema labels, which moves `en` only: extraction merges
against the committed bundles (`mergeExisting: !--no-merge`), so a locale that
already holds a hand translation is deliberately never overwritten — `--no-merge`
would wipe every hand-translated string in the bundle. The three localized
`help` leaves are therefore updated in place, which is the maintenance path the
bundle header names ("Edit translations in place ... only the leaf string
values"); keys and structure are untouched, so a re-extract reproduces them.
Adds the changeset: the descriptions are user-visible, and without a version
bump the correction never reaches npm.
Co-Authored-By: Claude <noreply@anthropic.com>📓 Docs Drift CheckThis PR changes 1 package(s): 8 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 6 release-owned page(s) also name something this change touched. These are read-only:
What this run could not seeCoarse fallback — 13 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin c18fbc52ce4ca449a0b46323c9617d04befe2400 && git checkout c18fbc52ce4ca449a0b46323c9617d04befe2400
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin d693ba158cdc392e244374a8b2d1cb96036d9b02 d0f90f63d288111871c009d3f659224de4ccf034 && git checkout -B drift-repro d693ba158cdc392e244374a8b2d1cb96036d9b02 && git merge --no-ff d0f90f63d288111871c009d3f659224de4ccf034
node scripts/docs-audit/affected-docs.mjs --json d693ba158cdc392e244374a8b2d1cb96036d9b02
|
os-project-manager
commented
Aug 18, 2026
PM ruling on the one open question — the i18n deviation. Option A stands; the fence was wrong, not the implementation. My dispatch fence said regenerate, do not hand-edit. That fence assumed a regeneration path exists that reaches the three localized bundles. It does not, and the PR measured why rather than asserting it: extraction merges against the committed bundles ( So the fence, read literally, has no satisfying move — every generator-only option is worse:
A is the maintenance path the generated header itself names — "Edit translations in place; re-run extract (with --merge) to fill new gaps. Do not hand-edit the structure — only the leaf string values." Keys and structure untouched, and The gap behind the question is real and I am recording it, not leaving it with you — this repo has no mechanism that propagates a changed source description into an already-translated locale. Every such correction lands as a hand edit that no gate can distinguish from ordinary translation drift; Flipped to ready and auto-merge armed. No declared-breaking changeset, so arming does not red Generated by Claude Code |
Uh oh!
There was an error while loading. Please reload this page.
Fixes#9046
The ADR-0091 D5 attestation pair —
last_certified_atandcertified_by, declared on both grant tables — stops asserting a recertification review the platform does not run. Prose only: no producer and no consumer is added, and no column is dropped.Premise re-verified on current
mainbefore any editThe card is a negative claim from 2026-08-16, so its sweep was re-run at branch point
07e630e58, overpackages/,apps/andexamples/, every.ts/.tsx, tests included:20 hits, in exactly two kinds of place: the two
.object.tsdeclarations, and the four generated i18n bundles that carry their strings. No producer, no consumer — nothing stamps either column, nothing reads either one, and nothing derives "never certified" or "certification stale". Whole-tree with no file-type filter adds only CHANGELOG prose,docs/adr/0091-*.md, and one docs page that already describes them accurately as lifecycle-audit columns whose campaign is enterprise product.The card's corroboration keys were used as the control, so a blind search could not read as a clean one — all three resolve:
granted_bypackages/plugins/plugin-sharing/src/sharing-plugin.ts:873valid_untilpackages/plugins/plugin-security/src/delegated-admin-gate.ts:477reasonpackages/lint/src/validate-security-posture.ts:626-631valid_from/valid_untilare additionally enforced throughisGrantActiveat resolution time. Only the D5 pair comes back empty on both sides. Premise stands.Two near-misses were checked and are not consumers:
packages/spec/src/security/explain.test.ts:8mentions "recertification review UX" in a comment about enterprise consumers of explain, and thecertification*keys inpackages/spec/authorable-surface*belong to the unrelatedtrainingschema.What changed
Both descriptions now state the inertness outright rather than merely omitting the promise, keeping the ADR-0091 D5 citation. Old text on both objects:
New text says the columns are reserved for a future access-recertification workflow, that no platform code writes or reads them, that no resolution path, gate or lint consults them, and that null means the workflow does not exist rather than that the grant went unreviewed. Each declaration carries a
[#9046]comment with the measurement and the disposition, in the shapesys_capability.activeuses.Why prose and not the other two dispositions — ADR-0049 enforce-or-remove, settled the way
sys_capability.activewas (maintainer ruling, 2026-08-13): enforcement means building a SOX/ISO access-review feature with zero measured pull, and removal costs a migration over existing rows while buying nothing the prose fix does not. The harm is the promise, not the storage, and reversibility runs the right way — a description is one line to change back if D5 is ever implemented.Scope of the negative claim is this repo. An
objectuidesigner preview rendering these columns would count as a consumer under the 2026-08-10 ruling; that is neither enforcement nor a producer, and the new descriptions are worded about platform behavior, not about every renderer.The i18n locales — one deviation, stated
node scripts/check-i18n-bundles.mjs --write --filter=securitymovedenonly. That is inherent, not a slip: extraction merges against the committed bundles (mergeExisting: !flags['no-merge']inpackages/cli/src/commands/i18n/extract.ts) and only the default locale is filled from schema labels, so a locale already holding a hand translation is deliberately never overwritten.--fill=defaultfills gaps, and these were not gaps. The two generator-only alternatives were both worse:--no-mergewipes every hand-translated string in the bundle (the incident the extract config's own comment warns about), and deleting the leaves to force a--fill=defaultrefill would replace three localized help strings with English.So the three localized
helpleaves were updated in place — the maintenance path the generated header names: "Edit translations in place; re-run extract (with --merge) to fill new gaps. Do not hand-edit the structure — only the leaf string values." Keys and structure untouched;pnpm check:i18nre-extracts and reports plugin-security's 4 bundles in sync, which is the proof the edit is reproducible rather than drift. All four locales moved, two fields on each of the two objects.Changeset
Owed and added (
patch,@objectstack/plugin-security). The descriptions are user-visible — they ship as field help text and as the four locale bundles — and without a version bump the correction never reaches npm.Verification — all at the final commit
d0f90f63dGate union re-derived from the actual changed paths with
node scripts/pm/dispatch-gates.mjsoffgit merge-base(the changeset file pulled in five gates the pre-change derivation did not name):pnpm check:i18npnpm check:changeset-gate-self-testspnpm check:objectui-changesetpnpm check:cross-package-test-inputspnpm check:test-source-aliaspnpm check:type-source-resolutionpnpm check:nul-bytescheck-adr-0087-registration.mjscheck-changeset-no-major.mjscheck-empty-changeset.mjscheck-affected-docs.mjspnpm --filter @objectstack/plugin-security testpnpm --filter @objectstack/plugin-security typecheckFull workspace build green first (
turbo run build, 71 tasks) —check:i18nrefuses an unbuilt tree, and plugin-security was rebuilt again after the bundle commit so itsdistmatches the editedsrc.No test is added: there is nothing behavioral to pin, and a test asserting these columns do something is exactly what the card forbids.
Generated by Claude Code