Skip to content

fix(platform-objects): attestFreshDatastore looks its os migrate remedy up, never defaults it - #16180

Merged
zhuangjianguo merged 2 commits into
mainfrom
claude/issue-16067-attest-remedy-mapping
Sep 6, 2026
Merged

fix(platform-objects): attestFreshDatastore looks its os migrate remedy up, never defaults it#16180
zhuangjianguo merged 2 commits into
mainfrom
claude/issue-16067-attest-remedy-mapping

Conversation

@claude

@claude claude Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Fixes #16067

attestFreshDatastore built its operator-facing remedy sentence from a two-way branch:

'Fix the data, then run `os migrate ' +
  (id === FILE_REFERENCES_MIGRATION_ID ? 'files-to-references' : 'value-shapes') +
  ' --apply` to close it on real evidence (ADR-0104).'

CREATION_ATTESTED_MIGRATION_IDS has three members. The else arm handed
os migrate value-shapes --apply to every id that is not the file one — including
adr-0030-notification-event, which that command neither attests nor clears.

The first measurement, because the card's grade rested on it

The card argued this is not a live defect because the engine tallies admitted
violations under the two ADR-0104 ids only. Triage counted 9 references in
packages/objectql/src/engine.ts and said in as many words that it had not
classified which are tally keys. That classification decides the scope, so it was
re-run first. All 9 lines, on origin/main 81b426f77:

engine.ts what it is
53, 54 import of the two id constants
7850 flag-read key — readMigrationFlagMemoized in isFileReferencesMigrationVerified
7869 flag-read key — the same seam in isValueShapesMigrationVerified
8029 the ONLY tally keynoteAdmittedValueShapeViolation
8142 remedy-text branch inside recordObservedDeviation (a consumer of the key)
8245 remedy-text branch inside retractCreationAttestation (a consumer of the key)
8326, 8334 flag-read keys in the kernel:bootstrapped advisory

NOTIFICATION_EVENT_MIGRATION_ID appears zero times in engine.ts. Line 8029 derives
its key from violation.gate, declared 'media' | 'value-shape' in
validation/record-validator.ts:1050 — a closed two-member union — and it is the only
writer of admittedValueShapeViolations. So the shipped engine cannot key that tally with
a third id, and the card's central claim holds.

Population control, because the pathspec trap is live in this tree: git grep -c MIGRATION_ID -- packages/objectql/src finds engine.ts with 9 hits, while the same query
spelled packages/objectql/src/**/*.ts silently returns only validation/record-validator.ts
and drops engine.ts entirely.

What the classification does not license

valueShapeViolationsAdmitted is an optional, duck-typed member of
MigrationFlagEngine (migration-flag.ts:53) returning an open Record<string, ...>.
Any object satisfies it — the doubles in this repo's own tests already do. So the wrong
sentence was one non-ObjectQL producer away from an operator's terminal;
"unreachable" was a property of today's single producer, never of the seam. Grade
unchanged (finding / p3 both stand), and the fix is written so the seam cannot
produce a wrong prescription regardless of who implements it.

The fix

An explicit register, total over the ids a value-shape tally can contradict, and the
loop asks it rather than falling into an arm. An id absent from it is
never-contradictable by this evidence — a value-shape tally says nothing about a fact
that is not about value shapes — and is attested on the birth observation as before.
So a fourth member inherits no remedy; a third arm that happened to be right today
would only have moved the same defect one member along.

A Map, not an object literal: id arrives from a caller-supplied array, and an object
literal answers 'toString' with a function.

os migrate notification-event does not exist — measured, not recalled: no such file
under packages/cli/src/commands/migrate/, with a firing control (value-shapes matches
3 files there). So for that id the correct sentence is no sentence, which is the
route this PR chose over inventing one.

Pins, and the ablation that proves they bite

Two, both aimed at the DEFAULT, which is where the defect lived:

  1. a contradiction fed for a non-ADR-0104 id asserts the id is attested and the warning
    does not name value-shapes;
  2. a case total over CREATION_ATTESTED_MIGRATION_IDS asserts no id is ever handed
    another migration's command — so a fourth member is judged the moment it is added.

Ablation (fix committed first, mutation proven on disk by anchored counts in both
directions, absolute-path trap restore, restore proven by blob equality with the HEAD
blob and an empty git diff HEAD): with the two-way branch put back, exactly the 2 new
cases red and the pre-existing 22 stay green. That is the card's point measured — a pin
over only the two known ids passes on the broken code.

Verification

Union re-run at 5eb248ff2.

  • pnpm --filter '@objectstack/platform-objects^...' build — VERDICT command-exit 0
  • pnpm --filter @objectstack/platform-objects test — 35 files, 532 passed
  • pnpm --filter @objectstack/platform-objects typecheck — VERDICT command-exit 0.
    ⚠️ NOT MEASURED for the test file: that tsconfig excludes **/*.test.ts, and
    tsc --listFiles finds the test file 0 times against a control of 1 for the source.
    A targeted tsc --noEmit --ignoreConfig run with the same strictness over the test
    file reports 0 errors.
  • downstream: @objectstack/objectql adr0104-attestation-evidence +
    adr0104-lax-deviation-marker — 19 passed (after building that closure; the first
    attempt was an unbuilt-dependency Cannot find package and is reported as such, not
    as a red)
  • gate family derived with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack
    (no hand-supplied paths): 56 families, all run, 53 exit 0. The 3 non-zero are
    explicit NOT-MEASURED prerequisites, not findings: check:react-declaration-parity
    ("this gate did NOT run", no MANIFEST), check:dual-build-cjs-loads and
    check:published-readme-exports (exit 3, PREREQUISITE NOT MET, need a full
    pnpm build). Exit codes captured by redirect-then-read, never through a pipe.
  • check:dts-closure green is about this package: it swept 8 built packages and
    packages/platform-objects is one of them (58/58 declaration files across the 8).
  • check:partof-closing-keyword passed its 28 self-test cases only — it reads
    PR_BODY from the environment, so that run was vacuous. Re-run against this posted
    body; result recorded on the card.

Clause ②: no

Limb 1 — published surface. A real ablation: built at head, swapped
migration-flag.ts back to origin/main 81b426f77, rebuilt, diffed all 22
declaration files this package publishes (not the root barrel — that trap is why the
census is total), restored and rebuilt. Rebuild proven, not assumed: 66/66 dist mtimes
moved on each leg. All 22 declarations byte-identical; 12 files differ and every one
is a .js/.mjs bundle or its sourcemap. Controls: the new register appears twice in
each of the 3 built JS entry bundles (instrument live) and in 0 declaration files;
attestFreshDatastore fires in dist/system/index.d.ts (3) and dist/index.d.ts (1),
so the census reached the subpath where the symbol actually lives. This package emits no
content-hashed shared chunk. The remedy string literals appear in 0 declaration files, so
#16076's standing ruling about a literal value inside an emitted data type does not even
arise here.

Limb 2 — accept/reject behaviour. No. For the two gate-bearing ADR-0104 ids the
behaviour is unchanged, and that is measured rather than asserted: under the ablation the
2 parameterised ADR-0104 cases pass on both the fixed and the unfixed code, alongside the
other 20. The only outcome that moves belongs to an id with no value-shape contract,
which NOTIFICATION_EVENT_MIGRATION_ID's own spec docblock declares "Receipt, not gate.
Nothing reads a row under this id as a precondition, and nothing may". No metadata, record
or request is newly accepted or rejected, and on the shipped engine the returned attested
array is identical either side because the tally cannot name that id at all.

Scope

Read-only surfaces respected: packages/spec and docs/adr/** are read, never edited.
#15710 is left alone — that ruling added the third member and stands.

Out-of-scope observations found while measuring are filed separately rather than ridden
here; they are listed on the card.


Generated by Claude Code

…r defaults it (#16067)

`attestFreshDatastore`'s contradiction warning built its `os migrate` sentence
from a two-way branch: the file-references id got `files-to-references` and
EVERY other id got `value-shapes` by default. `CREATION_ATTESTED_MIGRATION_IDS`
has had three members since the ADR-0030 cut-over id joined it, and for that
third id the default is a wrong prescription — `os migrate value-shapes --apply`
neither attests nor clears it, and there is no `os migrate notification-event`
to send an operator to at all (measured: no such sub-command exists under
`packages/cli/src/commands/migrate/`).

Replaced with an explicit id -> remedy register that is TOTAL over the ids a
value-shape tally can contradict, and the loop now asks it instead of falling
into an arm: an id with no value-shape contract is never-contradictable by this
evidence and is attested on the birth observation. A new member therefore
inherits NO remedy — adding a third arm that happened to be right today would
only have moved the same defect onto the fourth member.

A `Map` rather than an object literal: `id` arrives from a caller-supplied
array and an object literal would answer `'toString'` with a function.

Pins the default, which is where the defect lived: a contradiction fed for a
non-ADR-0104 id asserts the warning does not name `value-shapes`, and a case
total over `CREATION_ATTESTED_MIGRATION_IDS` asserts no id is ever handed
another migration's command. A pin over only the two known ids passed on the
broken code.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ARYe3yQTQCUFm5qPYNgKaJ
@github-actions

github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

3 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to listnot a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run.

What this run could not see
  • the SDK route bridge reached 61 of 219 client-bound route-ledger rows — the other 158 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 158: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 56 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 102 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 2 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json d5d8d50db2b698107ee4b36ef7b987e6ed23da0cpackageMentionDocs.

Which tree this was computed on

This run read content/docs from 58c897c48f044a8e72340f21b5e881a607e52c9d — the merge of head 5eb248ff2b6edee89e47ec27e35ade10fa15d93b into base d5d8d50db2b698107ee4b36ef7b987e6ed23da0c, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 58c897c48f044a8e72340f21b5e881a607e52c9d && git checkout 58c897c48f044a8e72340f21b5e881a607e52c9d
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin d5d8d50db2b698107ee4b36ef7b987e6ed23da0c 5eb248ff2b6edee89e47ec27e35ade10fa15d93b && git checkout -B drift-repro d5d8d50db2b698107ee4b36ef7b987e6ed23da0c && git merge --no-ff 5eb248ff2b6edee89e47ec27e35ade10fa15d93b

node scripts/docs-audit/affected-docs.mjs --json d5d8d50db2b698107ee4b36ef7b987e6ed23da0c

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling labels Sep 6, 2026
@zhuangjianguo
zhuangjianguo marked this pull request as ready for review September 6, 2026 05:13
@zhuangjianguo
zhuangjianguo added this pull request to the merge queue Sep 6, 2026
Merged via the queue into main with commit 159dbad Sep 6, 2026
35 checks passed
@zhuangjianguo
zhuangjianguo deleted the claude/issue-16067-attest-remedy-mapping branch September 6, 2026 05:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

2 participants