Skip to content

chore(spec): re-classify the field.conditionalRequired liveness entry dead (#13043) — the last line citation, counter to 0 - #13069

Merged
os-trump merged 1 commit into
mainfrom
claude/issue-13043-conditional-required-dead
Aug 29, 2026
Merged

chore(spec): re-classify the field.conditionalRequired liveness entry dead (#13043) — the last line citation, counter to 0#13069
os-trump merged 1 commit into
mainfrom
claude/issue-13043-conditional-required-dead

Conversation

@os-trump

@os-trump os-trump commented Aug 29, 2026

Copy link
Copy Markdown
Collaborator

Fixes #13043

Fixes #13003

The second one is the worklist's ruled closure, not a rider. The line-citation counter reaches 0 here, which is the event ruling 5458356183 named as the moment both non-vacuity floors are deleted, and the endgame wiring recorded in ACCEPT 5459233103: "#13043 stays HELD in the serial queue … its landing is the counter-to-0 event that deletes the floors per ruling 5458356183".

Two related cards, cross-referenced and deliberately kept away from any closing keyword: #13042 is the recorded remainder and remains open; #13041 (the planned/experimental evidence-scan gap) is the held follow-on, blocked behind these same two test files, and remains open.

Verified at 21b610d73. Authored by Claude Code, session https://claude.ai/code/session_01LpRNHxWZgSUgVnFT9mQQo4 — recorded here in prose as well as in the footer, because a body edit rewrites the footer's session form back to the bare one, and because on this PR the first edit dropped the footer line outright (measured, twice; see the note at the bottom).

Notation, forced by a measured GitHub sanitizer behaviour. BANG1 and BANG2 below stand for the first and second regex capture read through a TypeScript non-null assertion — in the source, the identifier m, then an exclamation mark, then the bracketed index. Written literally, GitHub's body sanitizer deletes the exclamation mark before the bracket, silently, in inline code spans and inside fenced blocks. Measured on this very PR: the first revision of this body was read back twice — through the API and through the rendered page — and both came back with the operator gone from all four quoted assertions, while the files on disk are unchanged. Quoting an assertion wrongly is precisely the "looks precise, is not" failure this ledger governs, so the operator is spelled out rather than left corrupted. The assertions in the repository are unaffected.

The flip

packages/spec/liveness/field.json, prop conditionalRequired.

Before (2 keys + status):

After (status / verifiedAt / note, the measured house style):

  • status: "dead", verifiedAt: "2026-08-29"
  • no evidence key — measured on this tree, not recalled: of 79 dead entries across the ledgers, 73 carry no evidence; the two dominant shapes are {status, verifiedAt, note} (37) and {status, evidenceScope, verifiedAt, note} (36). authorWarn appears on 0 of 79, so it is not part of this shape either.
  • the row STAYSretiredKey keeps the key in the walked shape (the rls.priority precedent the chore(spec): re-classify the action.execute liveness entry dead (#13036) #13045 body cites); deleting it would report UNCLASSIFIED.
  • pointers moved into the note in path#symbol grammar: the tombstone packages/spec/src/data/field.zod.ts#conditionalRequired, the consumer-side refusal packages/objectql/src/validation/rule-validator.ts#ConditionalFieldDef, and the case that pins the absence, does NOT read the retired conditionalRequired alias (PD #12 — no dialect fallback) in packages/objectql/src/validation/rule-validator.test.ts.
  • the #3754 / #3713 lowering history is preserved in substance, because it is the argument for requiredWhen being the single slot.
  • the close-out machinery that remains is named as machinery, not as a consumer: the ADR-0087 conversion field-conditionalRequired-to-requiredWhen, its RETIRED_KEYS_BY_MAJOR entry and D3 chain step, and authoring-key-lint.ts's explicit "no entry for conditionalRequired".

Premise re-measured on this base (all four legs held)

Base cae2169cf = origin/main with #13045 merged.

  1. The cited .transform does not exist — stronger than the card claimed. packages/spec/src/data/field.zod.ts has zero .transform( calls. Its only two occurrences of that token are comments recording that .overwrite() was chosen instead, per the measured finding: FormViewSchema.groups is declared as "alias to sections" but nothing folds it — every consumer reads only sections #6926 precedent. The key is retiredKey(...) at line 1380, and git log -S dates that tombstone to acbf364a0, 2026-07-28, "retire the last three deprecated authorable aliases ([P3] Retire the three deprecated aliases — via the ADR-0087 D2 conversion layer, not by deleting the keys #3855) (feat(spec)!: retire the last three deprecated authorable aliases (#3855) #3883)" — the same commit that tombstoned action.execute.
  2. The rule-validator fallback is gone. rule-validator.ts names the alias exactly once, in a comment on ConditionalFieldDef declaring requiredWhen "The ONLY predicate slot" and stating "PD Add comprehensive test suite for Zod schema validation #12: no consumer-side dialect fallback returns here". The absence is pinned by the named test, beside a positive case asserting the rule DOES fire once applyConversionsToStoredItem has lowered the alias.
  3. The ledger entry was live citing field.zod.ts:639 — confirmed byte-for-byte. Line 639 today is the tail of FieldMaskingRuleSchema, in range in a 2,132-line file that names the key five times, so existence, line bound and key-mention were all green on it.
  4. The counter read exactly 1 at base, and the 1 was this entry. Gate output at cae2169cf: line citations: 1 pointer(s) written path:NNN, 1 inside the cited file. Attribution measured by re-running the gate's own checkEvidence extractor over every ledger: sole result field/conditionalRequired -> packages/spec/src/data/field.zod.ts:639.

The counter, 1 to 0

base cae2169cf : line citations: 1 pointer(s) written `path:NNN`, 1 inside the cited file.
head 21b610d73 : line citations: 0 pointer(s) written `path:NNN`, 0 inside the cited file.

check:liveness is green at head. Its verdict line, quoted:

✓ every governed-type property at the walk's one-level granularity is classified, … every path:NNN citation names a line that file actually has, every path#symbol anchor names a symbol its file contains …
✓ packages/spec/liveness/state-counts.md is current — the same 31 row(s), no count column left in the README.

state-counts.md was regenerated by pnpm --filter @objectstack/spec gen:liveness-counts, never by hand (the file carries merge=os-regen for exactly that reason). Current line, and the totals row:

| `field` | 88 | 0 | 1 | 2 | 91 |          (was: 89 | 0 | 0 | 2 | 91)
| **total** | **819** | **5** | **80** | **10** | **914** |   (was: 820 | 5 | 79 | 10 | 914)

Deleted-assertion inventory

Executed exactly as the two guard comments instruct — the instruction lives in the code, so this PR runs it rather than re-deriving it.

1. packages/spec/scripts/liveness/evidence.test.ts — the guard comment, quoted:

⛔ WHEN THIS POPULATION LEGITIMATELY REACHES ZERO — the last line citation retired — DELETE this assertion AND this comment IN THE SAME PR that retires it, together with the outOfRange assertion above, which has nothing left to check. That is the conscious decision at zero the > 0 floor exists to force. Never let it pass silently on an empty population.

Deleted: expect(citations).toBeGreaterThan(0); (the floor), its 22-line guard comment, and the named companion expect(outOfRange).toEqual([]);.

Those two were all the assertions the case "every local path:NNN citation names a line that file has" had, so the case went with them: a body that walks every ledger and asserts nothing is the vacuous shape #5623 exists to refuse. A note stands where it was, recording what was deleted, why, what still covers the bound (the arithmetic stays fully unit-tested in the checkCitationLines describe above it), and to restore a contract test if a path:NNN citation is ever written again.

2. packages/spec/scripts/liveness/check-liveness.test.ts — the guard comment, quoted:

⛔ WHEN THIS POPULATION LEGITIMATELY REACHES ZERO — the last line citation retired — DELETE this assertion AND this comment IN THE SAME PR that retires it, along with the equality check below, which then compares two zeroes and asserts nothing. A conscious decision at zero, never a silent one; that is the whole reason the floor sits at > 0 rather than gone.

Deleted: expect(Number(BANG1)).toBeGreaterThan(0); (the floor), its 19-line guard comment, and the named companion expect(BANG2).toBe(BANG1);.

Here the it survives, and that asymmetry is the point: its remaining assertions still measure something at a zero population — the gate must keep EMITTING the line citations: line in the shape the regex documents (expect(m, line).not.toBeNull()), on a green run (expect(status, output).toBe(0)), without PAST EOF. Drop that line, rename it, or collapse it to one number and this case reds. The title's "equal on a green run" clause named the deleted equality check, so it is corrected to "in the documented two-number shape".

Two small consequentials of the deletion, both named here rather than left to rot: the checkCitationLines describe preamble said "a citation whose real line count matters is asserted against the real ledgers in the contract test at the bottom of this file" — corrected, since that test is gone; and the surviving case's own lead comment pointed at "the floor's own note at the assertion below" — rewritten.

Minimality

packages/spec/liveness/README.md                 |  2 +-
packages/spec/liveness/field.json                |  6 +-
packages/spec/liveness/state-counts.md           |  4 +-
packages/spec/scripts/liveness/check-liveness.test.ts | 40 ++++++-------
packages/spec/scripts/liveness/evidence.test.ts       | 68 ++++++----------------
.changeset/field-conditional-required-ledger-dead.md  | new

Six files. In the two test files nothing outside the named assertions, their guard comments and the two prose corrections above moved — the check-liveness.test.ts hunk is a single contiguous block at lines 183-211, the evidence.test.ts hunk a single contiguous block where the deleted case stood plus the preamble sentence. No change to check-liveness.mts or to any scan behaviour; #13041 stays held behind this PR by design.

Surviving floors, grep-verified (not intent)

The two siblings guard populations that are not draining, and they keep their constants:

packages/spec/scripts/liveness/evidence.test.ts:346      expect(local).toBeGreaterThan(100);
packages/spec/scripts/liveness/check-liveness.test.ts:497 expect(Number(BANG1)).toBeGreaterThan(100);

Their population at head, from the gate: evidence paths: 465 repo-local path(s) declared by 'live' entries, 465 resolved against this checkout — 466 at base, the single drop being the entry this flip retires.

The two symbol-anchor floors from the anchor-grammar card also survive untouched: evidence.test.ts:393 expect(anchors).toBeGreaterThanOrEqual(2) and check-liveness.test.ts:306 the same, over a population of 499 anchors, all naming a symbol their file contains.

Tests

pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2 scripts/liveness/ (heavy runs serialised through scripts/pm/os-verify-lock.sh):

after  (21b610d73):  Test Files  10 passed (10)      Tests  253 passed (253)
before (cae2169cf test files, implementation committed):
                     Test Files  2 failed | 8 passed (10)
                     Tests  2 failed | 252 passed (254)

254 → 253: exactly one case removed, the evidence.test.ts contract test.

Reverse verification — predicted direction: RED, and only at the floors

The implementation was committed first, then the two pre-deletion test files were restored from base over it, the run measured, and the files restored to HEAD. Both legs proved on disk, not by exit code:

  • mutation landed: the injected floor text found exactly once in each file (grep -c = 1, 1) and the post-deletion marker text absent from both (grep -c = 0, 0); the run aborts on any other reading.
  • restore: git diff HEAD empty for both paths, and each file's git hash-object compared equal to its HEAD blob (ef2daaa44…, 92d3daecd…) — an empty or mismatched hash is a hard failure, not "nothing to compare".
  • the script carries trap restore EXIT INT TERM with absolute paths resolved from git rev-parse --show-toplevel.

Result — exactly the two deleted floors, and nothing else:

FAIL check-liveness.test.ts > prints the citation count … equal on a green run
     AssertionError: expected 0 to be greater than 0
FAIL evidence.test.ts > shipped ledgers > every local `path:NNN` citation names a line that file has
     AssertionError: expected 0 to be greater than 0

This is the ruling's own design claim, now measured: the > 0 floor "reds at precisely the moment the endgame decision is due". And the second half is measured too — the two named companions did NOT fail: outOfRange and the equality check were both vacuously true at a zero population, which is exactly why the guard comments named them as deletions rather than leaving them behind.

No ablation is owed for the flip itself: it introduces no new anchor mechanism, and the change is ledger data plus test deletions.

Gates

Family derived from the real change set, not from a recalled list: node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack — provenance line confirms "gate list derived from the tree of 'objectstack-ai/objectstack' at commit cae2169", change set "6 path(s)", matching the six above. 30 path-matched families plus 6 convention-triggered ones (the test-file kind). All re-run at 21b610d73 on a clean tree, exit codes captured before any pipe:

  • green: check:liveness, check:empty-state, check:strictness-ledger, check:variant-docs, check:nul-bytes, check:cross-package-test-inputs (both the pnpm and ci.yml spellings), check:merge-driver, check:objectql-double-limit, check:objectui-changeset, check:page-declaration-shape, check:pm-governed-merges, check:published-files, check:slot-lookup, check:test-source-alias, check:type-source-resolution, check:changeset-gate-self-tests, check-adr-0087-registration, check-changeset-no-major, check-ci-filter-parity, check-comment-mask-adoption, check-empty-changeset, check-keyed-text-bounds, check-plugin-teardown-shape, check-undeclared-dep-imports, check-dev-prereqs (67/67, on the built closure), release-rehearsal-clone --self-test, docs-audit/check-affected-docs, docs-audit/check-drift-comment.
  • convention-triggered, all green: check:query-options-erasure, check:type-check-coverage, check:engine-double-contract, check:where-matcher, check:cross-package-test-inputs, and check:type-check-debt — the last built the workspace closure first, exactly as lint.yml does (turbo run build --filter=./packages/* --filter=./packages/*/*, 70/70 tasks), so it is a real measurement and not a refusal: "31 ledger entr(ies) re-measured in 239.3s, 1570 raw tsc error(s) total, none above its recorded number. surplus: none".
  • pnpm --filter @objectstack/spec typecheck green across all three legs, with --listFiles coverage proof rather than a green read as coverage: the scripts program (tsconfig.scripts.json) names both edited files; the base program names neither (0 and 0 of 1002 files). check:test-typecheck: "OK — @objectstack/spec's test layer compiles".
  • repo-wide eslint . --no-inline-config was run in full rather than narrowed: 5392 files, 0 errors, 0 warnings (68s under the verify lock). No narrowing argument is needed.

Census position

This is the final landing of the #13003 worklist. Line citations over live entries, by landing: 300 at the card's filing → 175 after batch 2 → 82 after batch 3 → 2 after batch 4 (#13044) → 1 after #13045 (action.execute, #13036) → 0 here. Both of the last two were the same shape and neither was a citation repair: a live verdict resting on a protocol-17 .transform that no longer exists, on a key that has been a retiredKey tombstone since 2026-07-28. Batch 4 correctly reported them instead of re-anchoring them, which is what left exactly these two for their own cards.

Honest residual

  • Two inert survivors reference the key and are neither consumers nor rot. packages/lint/src/validate-expressions.ts still walks conditionalRequired in its field-rule loop; its own docblock says the branch is inert on the parsed compile path because the strict FieldSchema rejects the key by name, and it gets a slot-agnostic consequence clause "rather than a fabricated fourth measurement". packages/drivers/driver-sql/src/builtin-column-collision.ts lists it in a key-classification map as presentation, which classifies a name, not a value anyone reads. Both are recorded in the ledger note so the next reader does not resurrect a live verdict from them. Neither is touched here — that would be a scope change, and neither is a defect.
  • The field row's README Notes cell said "healthy", which this flip falsifies (the type's dead column moves 0 to 1). Corrected in the spec(liveness): 9 rows of the README state table have count columns that disagree with the gate's --json report #7377 shape used for action one round ago, rather than rewritten.
  • node scripts/pm/check-half-states.mjs (the live sweep) is NOT MEASURED here — exit 3, its own words: "PREREQUISITE NOT MET — the token in the environment is not a valid GitHub credential … Nothing was swept … It is not a clean board and it is not a dirty one — it is no reading at all." A container fact, not a fact about this change; its self-test half (pnpm check:pm-half-states) is green, and CI runs the sweep with a real credential.
  • CI has not been waited on — per the standing dispatch contract the report goes out at draft-PR time and CI convergence is the PM's leg.
  • Two body-write observations, recorded because both are silent and both were caught only by reading the body back. (1) The non-null-assertion strip described in the Notation note above. (2) The attribution footer: created with the session-URL form, it survived creation; the first edit removed the whole trailing --- plus footer line rather than downgrading it to the bare form, so the session URL is carried in prose above as the durable copy. If the footer below is absent when you read this, that is observation (2) repeating, not a missing signature.

… dead (#13043)

The entry was ledgered `live` on a FieldSchema `.transform` lowering
conditionalRequired -> requiredWhen and dropping the alias. No such transform
exists: field.zod.ts has ZERO `.transform` calls, and its two occurrences of the
token are comments recording that `.overwrite()` was chosen instead (#6926). The
note's second leg - that objectql rule-validator.ts keeps a
`requiredWhen ?? conditionalRequired` fallback on purpose - was retired by #3903,
which replays the ADR-0087 conversion chain at rehydration so a stored pre-17 row
arrives already lowered; its absence is pinned by that module's own test.

Status flips to `dead` with the ROW STAYING - the rls.priority precedent, a
retiredKey tombstone keeps the key in the walked shape. The falsified `evidence`
goes, the #3754 / #3713 lowering history is preserved in the note.

This was the LAST `path:NNN` citation in any ledger. The gate's line-citation
counter goes 1 -> 0, which is the end of the #13003 anchor-adoption worklist and
the trigger for the delete-at-zero instruction the two non-vacuity floors carried
in their own guard comments: both floors, both guard comments and both named
companions (`outOfRange`; the equality check) are deleted here. The two sibling
floors on non-draining populations (`local` paths, `evidence paths`) stay at
`> 100`, verified by grep.

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

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

⚠️ 3 changed file(s) yielded no anchor (packages/spec/liveness/README.md, packages/spec/liveness/field.json, packages/spec/liveness/state-counts.md), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files. Nothing else in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 1 changed package(s)).

What this run could not see
  • 3 changed file(s) yielded no anchor (packages/spec/liveness/README.md, packages/spec/liveness/field.json, packages/spec/liveness/state-counts.md) — pages documenting those are invisible to this run
  • 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 — 126 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 6c6157af05b25b28be0b3a7e92ce03ef85a98653packageMentionDocs.

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

1 participant