fix(lint): teach describe() the live-elsewhere verdict instead of throwing - #14632

Merged
os-musk merged 4 commits into
mainfrom
claude/issue-14057-lint-liveness-live-elsewhere
Sep 2, 2026
Merged

fix(lint): teach describe() the live-elsewhere verdict instead of throwing#14632
os-musk merged 4 commits into
mainfrom
claude/issue-14057-lint-liveness-live-elsewhere

Conversation

@os-musk

@os-muskos-musk commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator

Fixes#14057

describe() in packages/lint/src/lint-liveness-properties.ts maps ledger verdicts to author-facing findings, and throws — deliberately, per its own boundary comment — on a status it has not been taught. #13483 then shipped the ledger's fifth status, live-elsewhere (dead HERE by measurement, genuinely enforced in a sibling repo), and migrated manifest.runtime onto it. describe() was never taught it. The day any live-elsewhere row opts into authorWarn: true, os lint raises a shipped-ledger-integrity error instead of the advisory warning the author should have received.

It must never be the dead branch

This is the load-bearing constraint on the repair, and it is why the fix is a new branch rather than a widened one. Per the #11384 lesson, the verdicts imply opposite author actions, so telling an author to "Remove it" about a key whose enforcement is real and remote is exactly the wrong sentence: for manifest.runtime the enforcer is the cloud marketplace publish gate, where an unverified publisher requesting the node tier is hard-rejected, so deleting the key would tear out a live gate's input. live-elsewhere therefore gets its own rule id, its own message, and a default hint that keeps the property and points at the ledger row's evidence for the enforcer.

What changed

  • A fourth branch in describe(), placed against the dead branch it must never be, with a comment saying why the adjacency is deliberate: the two verdicts are one measurement apart and opposite in what they ask of the author.
  • LIVENESS_LIVE_ELSEWHERE_PROPERTY = 'liveness-live-elsewhere-property', exported beside its three siblings and re-exported from src/index.ts. Advisory-only, like them; nothing downstream keys off these ids today (measured: the only non-CHANGELOG consumers of any liveness rule id are src/index.ts, this package's tests, and one CLI test; there is no rule catalogue and no docs page listing them).
  • The sentinel throw stays for genuinely unknown statuses, with its enumeration of the known ones updated — and a test pins the enumeration equal to the branches, since a stale list is how the next status gets misread.
  • Two stale statements in the file's own comments corrected: it described the vocabulary as four members and quoted check-liveness.mts's header as Statuses: live | experimental | planned | dead. That header now reads live | experimental | planned | dead | live-elsewhere.

Reachability, measured

Still a fuse, not a fire. An entry reaches describe() only after shouldWarn() says yes (authorWarn: true, or status === 'experimental'), and the shipped manifest.runtime row carries no authorWarn. A negative control on the real ledger pins that, with an anti-vacuity guard: the coverage walk must SEE a live-elsewhere row (the one row carrying that status lives in manifest.json, so seeing the status proves the file was read) while authorWarnedProperties('manifest') must not contain runtime. If that row ever opts in, the pin goes red and the reviewer updates the pin — the new branch is what makes that flip safe.

Coverage, derived rather than hand-listed

The triage asked for a check that describe() covers every verdict, because patching one status leaves the next to repeat this card. Hand-listing the vocabulary in a test would go stale for exactly the reason describe() did — nobody edits the list they did not know existed — so the pin walks the shipped ledger rows (shippedLedgerStatuses(), a package-internal seam in the same posture as the existing #10262 seam: exported from the module only, re-exported by neither src/index.ts nor the package's exports map) and requires every distinct status it finds to be answered by describe() with a rule id of its own, plus asserts no two verdicts share a rule id. The vocabulary is prose in a comment, not an importable enum, so rows are the only machine-readable statement of it in this package's reach; a status with zero rows cannot reach describe() anyway, and the moment one appears, the pin names it.

live is the one member that deliberately gets no branch, and the source header already ruled it out in a sentence this PR keeps and now pins: an entry marked authorWarn: true on a live row "would be a ledger authoring mistake, not a user error". A mistake in our own shipped data is what the sentinel is for, so the pin asserts live stays loud rather than handled — matching the case the suite already carried.

Measured vocabulary: five members at packages/spec/scripts/liveness/check-liveness.mts:36 and in packages/spec/liveness/README.md. The inconclusive mentioned in triage is not a ledger status — zero hits across packages/spec/; it belongs to the skill-side vocabulary of the sibling card, which is out of scope here.

Changeset

'@objectstack/lint': minor, in .changeset/lint-liveness-live-elsewhere-rule-id.md. LIVENESS_LIVE_ELSEWHERE_PROPERTY is a new export on packages/lint/src/index.ts, the public entry, and a new public-entry export is minor under the precedent this lane applied today — #14606 took @objectstack/types: minor for three new exports. Nothing narrows, so no BREAKING banner and no ADR-0087 marker is owed; the behaviour change is an internal integrity error becoming the advisory finding it should always have been.

An earlier revision of this PR got two things wrong here and both are fixed. It declared patch, arguing from the planned verdict's own changeset; and it wrote its note to .changeset/liveness-live-elsewhere-verdict.md, a filename derived from the verdict — which is #13483's existing file on origin/main, declaring the pending @objectstack/spec: patch bump and describing the fifth verdict's gate-executable criteria and the 180-day re-attestation discipline. Writing there truncated it. That file is restored byte for byte (blob 5321f1b09, git diff origin/main on the path empty, and the re-derived three-dot change set no longer mentions it at all), and this package's note lives under its own non-colliding name.

Verification, at 168c1d7fa for the code, re-confirmed at 6d75c6462

The head moved only by the two changeset files above; the re-derived gate family is byte-identical between the two heads, so the union below carries over.

  • pnpm --filter @objectstack/lint typecheck and the full package suite: 93 files, 2821 passed, 5 skipped. The new tests are genuinely in the typecheck population — tsconfig.test.json --listFiles lists lint-liveness-properties.test.ts.
  • Reverse verification (ablation). Reverting only the new branch, tests untouched, turns three of the new cases red with the sentinel firing on live-elsewhere — the card's defect, reproduced: 3 failed | 65 passed. Restored: blob equality with HEAD, git diff HEAD empty, git status --porcelain empty, 68 passed. The mutation was confirmed on disk before the run (removed text 0 hits, injected marker 1 hit, blob hash changed), and the subject is a package-relative source import, so no dist indirection is in the path.
  • Gate union re-derived on this tree with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands (34 commands): 30 green. pnpm lint (whole repo), pnpm check:nul-bytes and pnpm check:error-status-conformance also green. On the new head, check-empty-changeset ("1 declaring changeset(s) added"), check-changeset-no-major and check-adr-0087-registration were re-run: all exit 0.
  • Four gates answered exit 3 — NOT MEASURED in their own words, not green and not red: check-test-completeness, pm/check-half-states, check:dual-build-cjs-loads ("PREREQUISITE NOT MET ... this is NOT a pass: nothing was measured") and check:type-check-debt. The latter two read built output for every package; CI builds that closure before running them.
  • check:doc-authoring caught a real defect in the first draft: the new tracker id sat inside the sentinel's runtime string, which reaches authors and generated surfaces that cannot resolve it. Moved to the adjacent // comment; the gate is green.

Scope

packages/spec/liveness/** and its scripts were read, never changed. The skills-lane sibling card is untouched.

🤖 Generated with Claude Code

https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68

…owing
`describe()` in `lint-liveness-properties.ts` mapped three ledger verdicts to
author-facing findings and threw, by design, on anything else. #13483 shipped
the ledger's fifth status — `live-elsewhere` (dead here by measurement,
genuinely enforced in a sibling repo; first row `manifest.runtime`, enforced at
the cloud marketplace publish gate) — without teaching this function about it,
so the day such a row opts into `authorWarn: true` the author gets a crash
instead of an advisory warning.
`live-elsewhere` now has its own branch and its own rule id
(`liveness-live-elsewhere-property`), a message that says the property is
enforced in a sibling repo rather than here, and a default hint that keeps the
property and points at the ledger row's evidence. It must never fall into the
`dead` branch: per #11384 the verdicts imply opposite author actions, and
"Remove it" is exactly the wrong sentence about a key whose enforcement is real
and remote — deleting it tears out a live gate's input.
The sentinel throw stays for genuinely unknown statuses, its enumeration
updated. The suite gains a coverage pin derived from the shipped ledger rows —
not a hand-list — so a sixth status fails by name instead of repeating this
card.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68
`check:doc-authoring` (sibling-package prose-id leg) fails a NEW `#NNNN` inside
a runtime string: an author, an operator or a generated surface reads that
message and cannot resolve a tracker id. The reference moves to the adjacent
`//` comment, where the reader who can resolve it is already looking.
Maintainer ruling 2026-08-12, quoted by the gate:
处理 issue 时犯的错应该总结成经验,保留 issue id没有意义
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68
@github-actions

github-actionsBot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

2 anchor(s) derived from 1 changed package(s); no hand-written page names any of them. ⚠️1 changed file(s) yielded no anchor (packages/lint/src/index.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/lint/src/index.ts) — pages documenting those are invisible to this run
  • 1 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 47 of 219 client-bound route-ledger rows — the other 172 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 172: 14 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 — 5 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 937ec142d5bb6e3f112dae2530842b1292be1cd4packageMentionDocs.

Which tree this was computed on

This run read content/docs from 913f571e75fbda67f34777d7051aeaa695dca7df — the merge of head 6d75c646275cbb88c980bbd3c4d06799fe68b17f into base 937ec142d5bb6e3f112dae2530842b1292be1cd4, 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 913f571e75fbda67f34777d7051aeaa695dca7df && git checkout 913f571e75fbda67f34777d7051aeaa695dca7df
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 937ec142d5bb6e3f112dae2530842b1292be1cd4 6d75c646275cbb88c980bbd3c4d06799fe68b17f && git checkout -B drift-repro 937ec142d5bb6e3f112dae2530842b1292be1cd4 && git merge --no-ff 6d75c646275cbb88c980bbd3c4d06799fe68b17f
node scripts/docs-audit/affected-docs.mjs --json 937ec142d5bb6e3f112dae2530842b1292be1cd4

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

…wn at minor
Two defects in the previous commit's changeset, neither in the implementation.
RESTORED. `.changeset/liveness-live-elsewhere-verdict.md` already existed on
`origin/main` — it is #13483's, declaring `@objectstack/spec: patch` and
describing the fifth verdict, its gate-executable criteria, the 180-day
re-attestation discipline and the `manifest.runtime` migration. Writing this
package's note to that path truncated it, which would have dropped the pending
`@objectstack/spec` bump and taken all of that out of the next release notes.
`packages/spec/CHANGELOG.md` has zero hits for `live-elsewhere`, so it is
unconsumed and still owed, not a stale leftover. The file is restored byte for
byte from `origin/main` (blob 5321f1b) and this package's note moves to
`.changeset/lint-liveness-live-elsewhere-rule-id.md`.
LEVEL. `minor`, not `patch`: `LIVENESS_LIVE_ELSEWHERE_PROPERTY` is a new export
on `packages/lint/src/index.ts`, the public entry, and a new public-entry export
is `minor` under the precedent this lane applied today (#14606 took
`@objectstack/types: minor` for three new exports). Nothing narrows, so no
BREAKING banner and no ADR-0087 marker are owed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68
@os-muskClaude

Copy link
Copy Markdown
CollaboratorAuthor

Landing provenance (engine execution seat, session session_0112hMx9hjJ9BgB28X97DS68): flipped ready at 16:17Z and armed auto-merge (squash) on head 6d75c6462, the patch-round head.


Generated by Claude Code

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

lint-liveness-properties.ts describe() has not been taught the live-elsewhere verdict — throws by design the day such a row opts into authorWarn

2 participants

@os-musk@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix(lint): teach describe() the live-elsewhere verdict instead of throwing - #14632

Merged
os-musk merged 4 commits into
mainfrom
claude/issue-14057-lint-liveness-live-elsewhere
Sep 2, 2026
Merged

fix(lint): teach describe() the live-elsewhere verdict instead of throwing#14632
os-musk merged 4 commits into
mainfrom
claude/issue-14057-lint-liveness-live-elsewhere

Conversation

@os-musk

@os-muskos-musk commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator

Fixes#14057

describe() in packages/lint/src/lint-liveness-properties.ts maps ledger verdicts to author-facing findings, and throws — deliberately, per its own boundary comment — on a status it has not been taught. #13483 then shipped the ledger's fifth status, live-elsewhere (dead HERE by measurement, genuinely enforced in a sibling repo), and migrated manifest.runtime onto it. describe() was never taught it. The day any live-elsewhere row opts into authorWarn: true, os lint raises a shipped-ledger-integrity error instead of the advisory warning the author should have received.

It must never be the dead branch

This is the load-bearing constraint on the repair, and it is why the fix is a new branch rather than a widened one. Per the #11384 lesson, the verdicts imply opposite author actions, so telling an author to "Remove it" about a key whose enforcement is real and remote is exactly the wrong sentence: for manifest.runtime the enforcer is the cloud marketplace publish gate, where an unverified publisher requesting the node tier is hard-rejected, so deleting the key would tear out a live gate's input. live-elsewhere therefore gets its own rule id, its own message, and a default hint that keeps the property and points at the ledger row's evidence for the enforcer.

What changed

  • A fourth branch in describe(), placed against the dead branch it must never be, with a comment saying why the adjacency is deliberate: the two verdicts are one measurement apart and opposite in what they ask of the author.
  • LIVENESS_LIVE_ELSEWHERE_PROPERTY = 'liveness-live-elsewhere-property', exported beside its three siblings and re-exported from src/index.ts. Advisory-only, like them; nothing downstream keys off these ids today (measured: the only non-CHANGELOG consumers of any liveness rule id are src/index.ts, this package's tests, and one CLI test; there is no rule catalogue and no docs page listing them).
  • The sentinel throw stays for genuinely unknown statuses, with its enumeration of the known ones updated — and a test pins the enumeration equal to the branches, since a stale list is how the next status gets misread.
  • Two stale statements in the file's own comments corrected: it described the vocabulary as four members and quoted check-liveness.mts's header as Statuses: live | experimental | planned | dead. That header now reads live | experimental | planned | dead | live-elsewhere.

Reachability, measured

Still a fuse, not a fire. An entry reaches describe() only after shouldWarn() says yes (authorWarn: true, or status === 'experimental'), and the shipped manifest.runtime row carries no authorWarn. A negative control on the real ledger pins that, with an anti-vacuity guard: the coverage walk must SEE a live-elsewhere row (the one row carrying that status lives in manifest.json, so seeing the status proves the file was read) while authorWarnedProperties('manifest') must not contain runtime. If that row ever opts in, the pin goes red and the reviewer updates the pin — the new branch is what makes that flip safe.

Coverage, derived rather than hand-listed

The triage asked for a check that describe() covers every verdict, because patching one status leaves the next to repeat this card. Hand-listing the vocabulary in a test would go stale for exactly the reason describe() did — nobody edits the list they did not know existed — so the pin walks the shipped ledger rows (shippedLedgerStatuses(), a package-internal seam in the same posture as the existing #10262 seam: exported from the module only, re-exported by neither src/index.ts nor the package's exports map) and requires every distinct status it finds to be answered by describe() with a rule id of its own, plus asserts no two verdicts share a rule id. The vocabulary is prose in a comment, not an importable enum, so rows are the only machine-readable statement of it in this package's reach; a status with zero rows cannot reach describe() anyway, and the moment one appears, the pin names it.

live is the one member that deliberately gets no branch, and the source header already ruled it out in a sentence this PR keeps and now pins: an entry marked authorWarn: true on a live row "would be a ledger authoring mistake, not a user error". A mistake in our own shipped data is what the sentinel is for, so the pin asserts live stays loud rather than handled — matching the case the suite already carried.

Measured vocabulary: five members at packages/spec/scripts/liveness/check-liveness.mts:36 and in packages/spec/liveness/README.md. The inconclusive mentioned in triage is not a ledger status — zero hits across packages/spec/; it belongs to the skill-side vocabulary of the sibling card, which is out of scope here.

Changeset

'@objectstack/lint': minor, in .changeset/lint-liveness-live-elsewhere-rule-id.md. LIVENESS_LIVE_ELSEWHERE_PROPERTY is a new export on packages/lint/src/index.ts, the public entry, and a new public-entry export is minor under the precedent this lane applied today — #14606 took @objectstack/types: minor for three new exports. Nothing narrows, so no BREAKING banner and no ADR-0087 marker is owed; the behaviour change is an internal integrity error becoming the advisory finding it should always have been.

An earlier revision of this PR got two things wrong here and both are fixed. It declared patch, arguing from the planned verdict's own changeset; and it wrote its note to .changeset/liveness-live-elsewhere-verdict.md, a filename derived from the verdict — which is #13483's existing file on origin/main, declaring the pending @objectstack/spec: patch bump and describing the fifth verdict's gate-executable criteria and the 180-day re-attestation discipline. Writing there truncated it. That file is restored byte for byte (blob 5321f1b09, git diff origin/main on the path empty, and the re-derived three-dot change set no longer mentions it at all), and this package's note lives under its own non-colliding name.

Verification, at 168c1d7fa for the code, re-confirmed at 6d75c6462

The head moved only by the two changeset files above; the re-derived gate family is byte-identical between the two heads, so the union below carries over.

  • pnpm --filter @objectstack/lint typecheck and the full package suite: 93 files, 2821 passed, 5 skipped. The new tests are genuinely in the typecheck population — tsconfig.test.json --listFiles lists lint-liveness-properties.test.ts.
  • Reverse verification (ablation). Reverting only the new branch, tests untouched, turns three of the new cases red with the sentinel firing on live-elsewhere — the card's defect, reproduced: 3 failed | 65 passed. Restored: blob equality with HEAD, git diff HEAD empty, git status --porcelain empty, 68 passed. The mutation was confirmed on disk before the run (removed text 0 hits, injected marker 1 hit, blob hash changed), and the subject is a package-relative source import, so no dist indirection is in the path.
  • Gate union re-derived on this tree with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands (34 commands): 30 green. pnpm lint (whole repo), pnpm check:nul-bytes and pnpm check:error-status-conformance also green. On the new head, check-empty-changeset ("1 declaring changeset(s) added"), check-changeset-no-major and check-adr-0087-registration were re-run: all exit 0.
  • Four gates answered exit 3 — NOT MEASURED in their own words, not green and not red: check-test-completeness, pm/check-half-states, check:dual-build-cjs-loads ("PREREQUISITE NOT MET ... this is NOT a pass: nothing was measured") and check:type-check-debt. The latter two read built output for every package; CI builds that closure before running them.
  • check:doc-authoring caught a real defect in the first draft: the new tracker id sat inside the sentinel's runtime string, which reaches authors and generated surfaces that cannot resolve it. Moved to the adjacent // comment; the gate is green.

Scope

packages/spec/liveness/** and its scripts were read, never changed. The skills-lane sibling card is untouched.

🤖 Generated with Claude Code

https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68

…owing
`describe()` in `lint-liveness-properties.ts` mapped three ledger verdicts to
author-facing findings and threw, by design, on anything else. #13483 shipped
the ledger's fifth status — `live-elsewhere` (dead here by measurement,
genuinely enforced in a sibling repo; first row `manifest.runtime`, enforced at
the cloud marketplace publish gate) — without teaching this function about it,
so the day such a row opts into `authorWarn: true` the author gets a crash
instead of an advisory warning.
`live-elsewhere` now has its own branch and its own rule id
(`liveness-live-elsewhere-property`), a message that says the property is
enforced in a sibling repo rather than here, and a default hint that keeps the
property and points at the ledger row's evidence. It must never fall into the
`dead` branch: per #11384 the verdicts imply opposite author actions, and
"Remove it" is exactly the wrong sentence about a key whose enforcement is real
and remote — deleting it tears out a live gate's input.
The sentinel throw stays for genuinely unknown statuses, its enumeration
updated. The suite gains a coverage pin derived from the shipped ledger rows —
not a hand-list — so a sixth status fails by name instead of repeating this
card.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68
`check:doc-authoring` (sibling-package prose-id leg) fails a NEW `#NNNN` inside
a runtime string: an author, an operator or a generated surface reads that
message and cannot resolve a tracker id. The reference moves to the adjacent
`//` comment, where the reader who can resolve it is already looking.
Maintainer ruling 2026-08-12, quoted by the gate:
处理 issue 时犯的错应该总结成经验,保留 issue id没有意义
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68
@github-actions

github-actionsBot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

2 anchor(s) derived from 1 changed package(s); no hand-written page names any of them. ⚠️1 changed file(s) yielded no anchor (packages/lint/src/index.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/lint/src/index.ts) — pages documenting those are invisible to this run
  • 1 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 47 of 219 client-bound route-ledger rows — the other 172 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 172: 14 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 — 5 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 937ec142d5bb6e3f112dae2530842b1292be1cd4packageMentionDocs.

Which tree this was computed on

This run read content/docs from 913f571e75fbda67f34777d7051aeaa695dca7df — the merge of head 6d75c646275cbb88c980bbd3c4d06799fe68b17f into base 937ec142d5bb6e3f112dae2530842b1292be1cd4, 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 913f571e75fbda67f34777d7051aeaa695dca7df && git checkout 913f571e75fbda67f34777d7051aeaa695dca7df
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 937ec142d5bb6e3f112dae2530842b1292be1cd4 6d75c646275cbb88c980bbd3c4d06799fe68b17f && git checkout -B drift-repro 937ec142d5bb6e3f112dae2530842b1292be1cd4 && git merge --no-ff 6d75c646275cbb88c980bbd3c4d06799fe68b17f
node scripts/docs-audit/affected-docs.mjs --json 937ec142d5bb6e3f112dae2530842b1292be1cd4

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

…wn at minor
Two defects in the previous commit's changeset, neither in the implementation.
RESTORED. `.changeset/liveness-live-elsewhere-verdict.md` already existed on
`origin/main` — it is #13483's, declaring `@objectstack/spec: patch` and
describing the fifth verdict, its gate-executable criteria, the 180-day
re-attestation discipline and the `manifest.runtime` migration. Writing this
package's note to that path truncated it, which would have dropped the pending
`@objectstack/spec` bump and taken all of that out of the next release notes.
`packages/spec/CHANGELOG.md` has zero hits for `live-elsewhere`, so it is
unconsumed and still owed, not a stale leftover. The file is restored byte for
byte from `origin/main` (blob 5321f1b) and this package's note moves to
`.changeset/lint-liveness-live-elsewhere-rule-id.md`.
LEVEL. `minor`, not `patch`: `LIVENESS_LIVE_ELSEWHERE_PROPERTY` is a new export
on `packages/lint/src/index.ts`, the public entry, and a new public-entry export
is `minor` under the precedent this lane applied today (#14606 took
`@objectstack/types: minor` for three new exports). Nothing narrows, so no
BREAKING banner and no ADR-0087 marker are owed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68
@os-muskClaude

Copy link
Copy Markdown
CollaboratorAuthor

Landing provenance (engine execution seat, session session_0112hMx9hjJ9BgB28X97DS68): flipped ready at 16:17Z and armed auto-merge (squash) on head 6d75c6462, the patch-round head.


Generated by Claude Code

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

lint-liveness-properties.ts describe() has not been taught the live-elsewhere verdict — throws by design the day such a row opts into authorWarn

2 participants

@os-musk@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(lint): teach describe() the live-elsewhere verdict instead of throwing - #14632

Merged
os-musk merged 4 commits into
mainfrom
claude/issue-14057-lint-liveness-live-elsewhere
Sep 2, 2026
Merged

fix(lint): teach describe() the live-elsewhere verdict instead of throwing#14632
os-musk merged 4 commits into
mainfrom
claude/issue-14057-lint-liveness-live-elsewhere

Conversation

@os-musk

@os-muskos-musk commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator

Fixes#14057

describe() in packages/lint/src/lint-liveness-properties.ts maps ledger verdicts to author-facing findings, and throws — deliberately, per its own boundary comment — on a status it has not been taught. #13483 then shipped the ledger's fifth status, live-elsewhere (dead HERE by measurement, genuinely enforced in a sibling repo), and migrated manifest.runtime onto it. describe() was never taught it. The day any live-elsewhere row opts into authorWarn: true, os lint raises a shipped-ledger-integrity error instead of the advisory warning the author should have received.

It must never be the dead branch

This is the load-bearing constraint on the repair, and it is why the fix is a new branch rather than a widened one. Per the #11384 lesson, the verdicts imply opposite author actions, so telling an author to "Remove it" about a key whose enforcement is real and remote is exactly the wrong sentence: for manifest.runtime the enforcer is the cloud marketplace publish gate, where an unverified publisher requesting the node tier is hard-rejected, so deleting the key would tear out a live gate's input. live-elsewhere therefore gets its own rule id, its own message, and a default hint that keeps the property and points at the ledger row's evidence for the enforcer.

What changed

  • A fourth branch in describe(), placed against the dead branch it must never be, with a comment saying why the adjacency is deliberate: the two verdicts are one measurement apart and opposite in what they ask of the author.
  • LIVENESS_LIVE_ELSEWHERE_PROPERTY = 'liveness-live-elsewhere-property', exported beside its three siblings and re-exported from src/index.ts. Advisory-only, like them; nothing downstream keys off these ids today (measured: the only non-CHANGELOG consumers of any liveness rule id are src/index.ts, this package's tests, and one CLI test; there is no rule catalogue and no docs page listing them).
  • The sentinel throw stays for genuinely unknown statuses, with its enumeration of the known ones updated — and a test pins the enumeration equal to the branches, since a stale list is how the next status gets misread.
  • Two stale statements in the file's own comments corrected: it described the vocabulary as four members and quoted check-liveness.mts's header as Statuses: live | experimental | planned | dead. That header now reads live | experimental | planned | dead | live-elsewhere.

Reachability, measured

Still a fuse, not a fire. An entry reaches describe() only after shouldWarn() says yes (authorWarn: true, or status === 'experimental'), and the shipped manifest.runtime row carries no authorWarn. A negative control on the real ledger pins that, with an anti-vacuity guard: the coverage walk must SEE a live-elsewhere row (the one row carrying that status lives in manifest.json, so seeing the status proves the file was read) while authorWarnedProperties('manifest') must not contain runtime. If that row ever opts in, the pin goes red and the reviewer updates the pin — the new branch is what makes that flip safe.

Coverage, derived rather than hand-listed

The triage asked for a check that describe() covers every verdict, because patching one status leaves the next to repeat this card. Hand-listing the vocabulary in a test would go stale for exactly the reason describe() did — nobody edits the list they did not know existed — so the pin walks the shipped ledger rows (shippedLedgerStatuses(), a package-internal seam in the same posture as the existing #10262 seam: exported from the module only, re-exported by neither src/index.ts nor the package's exports map) and requires every distinct status it finds to be answered by describe() with a rule id of its own, plus asserts no two verdicts share a rule id. The vocabulary is prose in a comment, not an importable enum, so rows are the only machine-readable statement of it in this package's reach; a status with zero rows cannot reach describe() anyway, and the moment one appears, the pin names it.

live is the one member that deliberately gets no branch, and the source header already ruled it out in a sentence this PR keeps and now pins: an entry marked authorWarn: true on a live row "would be a ledger authoring mistake, not a user error". A mistake in our own shipped data is what the sentinel is for, so the pin asserts live stays loud rather than handled — matching the case the suite already carried.

Measured vocabulary: five members at packages/spec/scripts/liveness/check-liveness.mts:36 and in packages/spec/liveness/README.md. The inconclusive mentioned in triage is not a ledger status — zero hits across packages/spec/; it belongs to the skill-side vocabulary of the sibling card, which is out of scope here.

Changeset

'@objectstack/lint': minor, in .changeset/lint-liveness-live-elsewhere-rule-id.md. LIVENESS_LIVE_ELSEWHERE_PROPERTY is a new export on packages/lint/src/index.ts, the public entry, and a new public-entry export is minor under the precedent this lane applied today — #14606 took @objectstack/types: minor for three new exports. Nothing narrows, so no BREAKING banner and no ADR-0087 marker is owed; the behaviour change is an internal integrity error becoming the advisory finding it should always have been.

An earlier revision of this PR got two things wrong here and both are fixed. It declared patch, arguing from the planned verdict's own changeset; and it wrote its note to .changeset/liveness-live-elsewhere-verdict.md, a filename derived from the verdict — which is #13483's existing file on origin/main, declaring the pending @objectstack/spec: patch bump and describing the fifth verdict's gate-executable criteria and the 180-day re-attestation discipline. Writing there truncated it. That file is restored byte for byte (blob 5321f1b09, git diff origin/main on the path empty, and the re-derived three-dot change set no longer mentions it at all), and this package's note lives under its own non-colliding name.

Verification, at 168c1d7fa for the code, re-confirmed at 6d75c6462

The head moved only by the two changeset files above; the re-derived gate family is byte-identical between the two heads, so the union below carries over.

  • pnpm --filter @objectstack/lint typecheck and the full package suite: 93 files, 2821 passed, 5 skipped. The new tests are genuinely in the typecheck population — tsconfig.test.json --listFiles lists lint-liveness-properties.test.ts.
  • Reverse verification (ablation). Reverting only the new branch, tests untouched, turns three of the new cases red with the sentinel firing on live-elsewhere — the card's defect, reproduced: 3 failed | 65 passed. Restored: blob equality with HEAD, git diff HEAD empty, git status --porcelain empty, 68 passed. The mutation was confirmed on disk before the run (removed text 0 hits, injected marker 1 hit, blob hash changed), and the subject is a package-relative source import, so no dist indirection is in the path.
  • Gate union re-derived on this tree with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands (34 commands): 30 green. pnpm lint (whole repo), pnpm check:nul-bytes and pnpm check:error-status-conformance also green. On the new head, check-empty-changeset ("1 declaring changeset(s) added"), check-changeset-no-major and check-adr-0087-registration were re-run: all exit 0.
  • Four gates answered exit 3 — NOT MEASURED in their own words, not green and not red: check-test-completeness, pm/check-half-states, check:dual-build-cjs-loads ("PREREQUISITE NOT MET ... this is NOT a pass: nothing was measured") and check:type-check-debt. The latter two read built output for every package; CI builds that closure before running them.
  • check:doc-authoring caught a real defect in the first draft: the new tracker id sat inside the sentinel's runtime string, which reaches authors and generated surfaces that cannot resolve it. Moved to the adjacent // comment; the gate is green.

Scope

packages/spec/liveness/** and its scripts were read, never changed. The skills-lane sibling card is untouched.

🤖 Generated with Claude Code

https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68

…owing
`describe()` in `lint-liveness-properties.ts` mapped three ledger verdicts to
author-facing findings and threw, by design, on anything else. #13483 shipped
the ledger's fifth status — `live-elsewhere` (dead here by measurement,
genuinely enforced in a sibling repo; first row `manifest.runtime`, enforced at
the cloud marketplace publish gate) — without teaching this function about it,
so the day such a row opts into `authorWarn: true` the author gets a crash
instead of an advisory warning.
`live-elsewhere` now has its own branch and its own rule id
(`liveness-live-elsewhere-property`), a message that says the property is
enforced in a sibling repo rather than here, and a default hint that keeps the
property and points at the ledger row's evidence. It must never fall into the
`dead` branch: per #11384 the verdicts imply opposite author actions, and
"Remove it" is exactly the wrong sentence about a key whose enforcement is real
and remote — deleting it tears out a live gate's input.
The sentinel throw stays for genuinely unknown statuses, its enumeration
updated. The suite gains a coverage pin derived from the shipped ledger rows —
not a hand-list — so a sixth status fails by name instead of repeating this
card.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68
`check:doc-authoring` (sibling-package prose-id leg) fails a NEW `#NNNN` inside
a runtime string: an author, an operator or a generated surface reads that
message and cannot resolve a tracker id. The reference moves to the adjacent
`//` comment, where the reader who can resolve it is already looking.
Maintainer ruling 2026-08-12, quoted by the gate:
处理 issue 时犯的错应该总结成经验,保留 issue id没有意义
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68
@github-actions

github-actionsBot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

2 anchor(s) derived from 1 changed package(s); no hand-written page names any of them. ⚠️1 changed file(s) yielded no anchor (packages/lint/src/index.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/lint/src/index.ts) — pages documenting those are invisible to this run
  • 1 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 47 of 219 client-bound route-ledger rows — the other 172 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 172: 14 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 — 5 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 937ec142d5bb6e3f112dae2530842b1292be1cd4packageMentionDocs.

Which tree this was computed on

This run read content/docs from 913f571e75fbda67f34777d7051aeaa695dca7df — the merge of head 6d75c646275cbb88c980bbd3c4d06799fe68b17f into base 937ec142d5bb6e3f112dae2530842b1292be1cd4, 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 913f571e75fbda67f34777d7051aeaa695dca7df && git checkout 913f571e75fbda67f34777d7051aeaa695dca7df
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 937ec142d5bb6e3f112dae2530842b1292be1cd4 6d75c646275cbb88c980bbd3c4d06799fe68b17f && git checkout -B drift-repro 937ec142d5bb6e3f112dae2530842b1292be1cd4 && git merge --no-ff 6d75c646275cbb88c980bbd3c4d06799fe68b17f
node scripts/docs-audit/affected-docs.mjs --json 937ec142d5bb6e3f112dae2530842b1292be1cd4

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

…wn at minor
Two defects in the previous commit's changeset, neither in the implementation.
RESTORED. `.changeset/liveness-live-elsewhere-verdict.md` already existed on
`origin/main` — it is #13483's, declaring `@objectstack/spec: patch` and
describing the fifth verdict, its gate-executable criteria, the 180-day
re-attestation discipline and the `manifest.runtime` migration. Writing this
package's note to that path truncated it, which would have dropped the pending
`@objectstack/spec` bump and taken all of that out of the next release notes.
`packages/spec/CHANGELOG.md` has zero hits for `live-elsewhere`, so it is
unconsumed and still owed, not a stale leftover. The file is restored byte for
byte from `origin/main` (blob 5321f1b) and this package's note moves to
`.changeset/lint-liveness-live-elsewhere-rule-id.md`.
LEVEL. `minor`, not `patch`: `LIVENESS_LIVE_ELSEWHERE_PROPERTY` is a new export
on `packages/lint/src/index.ts`, the public entry, and a new public-entry export
is `minor` under the precedent this lane applied today (#14606 took
`@objectstack/types: minor` for three new exports). Nothing narrows, so no
BREAKING banner and no ADR-0087 marker are owed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68
@os-muskClaude

Copy link
Copy Markdown
CollaboratorAuthor

Landing provenance (engine execution seat, session session_0112hMx9hjJ9BgB28X97DS68): flipped ready at 16:17Z and armed auto-merge (squash) on head 6d75c6462, the patch-round head.


Generated by Claude Code

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

lint-liveness-properties.ts describe() has not been taught the live-elsewhere verdict — throws by design the day such a row opts into authorWarn

2 participants

@os-musk@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(lint): teach describe() the live-elsewhere verdict instead of throwing - #14632

Merged
os-musk merged 4 commits into
mainfrom
claude/issue-14057-lint-liveness-live-elsewhere
Sep 2, 2026
Merged

fix(lint): teach describe() the live-elsewhere verdict instead of throwing#14632
os-musk merged 4 commits into
mainfrom
claude/issue-14057-lint-liveness-live-elsewhere

Conversation

@os-musk

@os-muskos-musk commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator

Fixes#14057

describe() in packages/lint/src/lint-liveness-properties.ts maps ledger verdicts to author-facing findings, and throws — deliberately, per its own boundary comment — on a status it has not been taught. #13483 then shipped the ledger's fifth status, live-elsewhere (dead HERE by measurement, genuinely enforced in a sibling repo), and migrated manifest.runtime onto it. describe() was never taught it. The day any live-elsewhere row opts into authorWarn: true, os lint raises a shipped-ledger-integrity error instead of the advisory warning the author should have received.

It must never be the dead branch

This is the load-bearing constraint on the repair, and it is why the fix is a new branch rather than a widened one. Per the #11384 lesson, the verdicts imply opposite author actions, so telling an author to "Remove it" about a key whose enforcement is real and remote is exactly the wrong sentence: for manifest.runtime the enforcer is the cloud marketplace publish gate, where an unverified publisher requesting the node tier is hard-rejected, so deleting the key would tear out a live gate's input. live-elsewhere therefore gets its own rule id, its own message, and a default hint that keeps the property and points at the ledger row's evidence for the enforcer.

What changed

  • A fourth branch in describe(), placed against the dead branch it must never be, with a comment saying why the adjacency is deliberate: the two verdicts are one measurement apart and opposite in what they ask of the author.
  • LIVENESS_LIVE_ELSEWHERE_PROPERTY = 'liveness-live-elsewhere-property', exported beside its three siblings and re-exported from src/index.ts. Advisory-only, like them; nothing downstream keys off these ids today (measured: the only non-CHANGELOG consumers of any liveness rule id are src/index.ts, this package's tests, and one CLI test; there is no rule catalogue and no docs page listing them).
  • The sentinel throw stays for genuinely unknown statuses, with its enumeration of the known ones updated — and a test pins the enumeration equal to the branches, since a stale list is how the next status gets misread.
  • Two stale statements in the file's own comments corrected: it described the vocabulary as four members and quoted check-liveness.mts's header as Statuses: live | experimental | planned | dead. That header now reads live | experimental | planned | dead | live-elsewhere.

Reachability, measured

Still a fuse, not a fire. An entry reaches describe() only after shouldWarn() says yes (authorWarn: true, or status === 'experimental'), and the shipped manifest.runtime row carries no authorWarn. A negative control on the real ledger pins that, with an anti-vacuity guard: the coverage walk must SEE a live-elsewhere row (the one row carrying that status lives in manifest.json, so seeing the status proves the file was read) while authorWarnedProperties('manifest') must not contain runtime. If that row ever opts in, the pin goes red and the reviewer updates the pin — the new branch is what makes that flip safe.

Coverage, derived rather than hand-listed

The triage asked for a check that describe() covers every verdict, because patching one status leaves the next to repeat this card. Hand-listing the vocabulary in a test would go stale for exactly the reason describe() did — nobody edits the list they did not know existed — so the pin walks the shipped ledger rows (shippedLedgerStatuses(), a package-internal seam in the same posture as the existing #10262 seam: exported from the module only, re-exported by neither src/index.ts nor the package's exports map) and requires every distinct status it finds to be answered by describe() with a rule id of its own, plus asserts no two verdicts share a rule id. The vocabulary is prose in a comment, not an importable enum, so rows are the only machine-readable statement of it in this package's reach; a status with zero rows cannot reach describe() anyway, and the moment one appears, the pin names it.

live is the one member that deliberately gets no branch, and the source header already ruled it out in a sentence this PR keeps and now pins: an entry marked authorWarn: true on a live row "would be a ledger authoring mistake, not a user error". A mistake in our own shipped data is what the sentinel is for, so the pin asserts live stays loud rather than handled — matching the case the suite already carried.

Measured vocabulary: five members at packages/spec/scripts/liveness/check-liveness.mts:36 and in packages/spec/liveness/README.md. The inconclusive mentioned in triage is not a ledger status — zero hits across packages/spec/; it belongs to the skill-side vocabulary of the sibling card, which is out of scope here.

Changeset

'@objectstack/lint': minor, in .changeset/lint-liveness-live-elsewhere-rule-id.md. LIVENESS_LIVE_ELSEWHERE_PROPERTY is a new export on packages/lint/src/index.ts, the public entry, and a new public-entry export is minor under the precedent this lane applied today — #14606 took @objectstack/types: minor for three new exports. Nothing narrows, so no BREAKING banner and no ADR-0087 marker is owed; the behaviour change is an internal integrity error becoming the advisory finding it should always have been.

An earlier revision of this PR got two things wrong here and both are fixed. It declared patch, arguing from the planned verdict's own changeset; and it wrote its note to .changeset/liveness-live-elsewhere-verdict.md, a filename derived from the verdict — which is #13483's existing file on origin/main, declaring the pending @objectstack/spec: patch bump and describing the fifth verdict's gate-executable criteria and the 180-day re-attestation discipline. Writing there truncated it. That file is restored byte for byte (blob 5321f1b09, git diff origin/main on the path empty, and the re-derived three-dot change set no longer mentions it at all), and this package's note lives under its own non-colliding name.

Verification, at 168c1d7fa for the code, re-confirmed at 6d75c6462

The head moved only by the two changeset files above; the re-derived gate family is byte-identical between the two heads, so the union below carries over.

  • pnpm --filter @objectstack/lint typecheck and the full package suite: 93 files, 2821 passed, 5 skipped. The new tests are genuinely in the typecheck population — tsconfig.test.json --listFiles lists lint-liveness-properties.test.ts.
  • Reverse verification (ablation). Reverting only the new branch, tests untouched, turns three of the new cases red with the sentinel firing on live-elsewhere — the card's defect, reproduced: 3 failed | 65 passed. Restored: blob equality with HEAD, git diff HEAD empty, git status --porcelain empty, 68 passed. The mutation was confirmed on disk before the run (removed text 0 hits, injected marker 1 hit, blob hash changed), and the subject is a package-relative source import, so no dist indirection is in the path.
  • Gate union re-derived on this tree with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands (34 commands): 30 green. pnpm lint (whole repo), pnpm check:nul-bytes and pnpm check:error-status-conformance also green. On the new head, check-empty-changeset ("1 declaring changeset(s) added"), check-changeset-no-major and check-adr-0087-registration were re-run: all exit 0.
  • Four gates answered exit 3 — NOT MEASURED in their own words, not green and not red: check-test-completeness, pm/check-half-states, check:dual-build-cjs-loads ("PREREQUISITE NOT MET ... this is NOT a pass: nothing was measured") and check:type-check-debt. The latter two read built output for every package; CI builds that closure before running them.
  • check:doc-authoring caught a real defect in the first draft: the new tracker id sat inside the sentinel's runtime string, which reaches authors and generated surfaces that cannot resolve it. Moved to the adjacent // comment; the gate is green.

Scope

packages/spec/liveness/** and its scripts were read, never changed. The skills-lane sibling card is untouched.

🤖 Generated with Claude Code

https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68

…owing
`describe()` in `lint-liveness-properties.ts` mapped three ledger verdicts to
author-facing findings and threw, by design, on anything else. #13483 shipped
the ledger's fifth status — `live-elsewhere` (dead here by measurement,
genuinely enforced in a sibling repo; first row `manifest.runtime`, enforced at
the cloud marketplace publish gate) — without teaching this function about it,
so the day such a row opts into `authorWarn: true` the author gets a crash
instead of an advisory warning.
`live-elsewhere` now has its own branch and its own rule id
(`liveness-live-elsewhere-property`), a message that says the property is
enforced in a sibling repo rather than here, and a default hint that keeps the
property and points at the ledger row's evidence. It must never fall into the
`dead` branch: per #11384 the verdicts imply opposite author actions, and
"Remove it" is exactly the wrong sentence about a key whose enforcement is real
and remote — deleting it tears out a live gate's input.
The sentinel throw stays for genuinely unknown statuses, its enumeration
updated. The suite gains a coverage pin derived from the shipped ledger rows —
not a hand-list — so a sixth status fails by name instead of repeating this
card.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68
`check:doc-authoring` (sibling-package prose-id leg) fails a NEW `#NNNN` inside
a runtime string: an author, an operator or a generated surface reads that
message and cannot resolve a tracker id. The reference moves to the adjacent
`//` comment, where the reader who can resolve it is already looking.
Maintainer ruling 2026-08-12, quoted by the gate:
处理 issue 时犯的错应该总结成经验,保留 issue id没有意义
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68
@github-actions

github-actionsBot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

2 anchor(s) derived from 1 changed package(s); no hand-written page names any of them. ⚠️1 changed file(s) yielded no anchor (packages/lint/src/index.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/lint/src/index.ts) — pages documenting those are invisible to this run
  • 1 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 47 of 219 client-bound route-ledger rows — the other 172 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 172: 14 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 — 5 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 937ec142d5bb6e3f112dae2530842b1292be1cd4packageMentionDocs.

Which tree this was computed on

This run read content/docs from 913f571e75fbda67f34777d7051aeaa695dca7df — the merge of head 6d75c646275cbb88c980bbd3c4d06799fe68b17f into base 937ec142d5bb6e3f112dae2530842b1292be1cd4, 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 913f571e75fbda67f34777d7051aeaa695dca7df && git checkout 913f571e75fbda67f34777d7051aeaa695dca7df
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 937ec142d5bb6e3f112dae2530842b1292be1cd4 6d75c646275cbb88c980bbd3c4d06799fe68b17f && git checkout -B drift-repro 937ec142d5bb6e3f112dae2530842b1292be1cd4 && git merge --no-ff 6d75c646275cbb88c980bbd3c4d06799fe68b17f
node scripts/docs-audit/affected-docs.mjs --json 937ec142d5bb6e3f112dae2530842b1292be1cd4

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

…wn at minor
Two defects in the previous commit's changeset, neither in the implementation.
RESTORED. `.changeset/liveness-live-elsewhere-verdict.md` already existed on
`origin/main` — it is #13483's, declaring `@objectstack/spec: patch` and
describing the fifth verdict, its gate-executable criteria, the 180-day
re-attestation discipline and the `manifest.runtime` migration. Writing this
package's note to that path truncated it, which would have dropped the pending
`@objectstack/spec` bump and taken all of that out of the next release notes.
`packages/spec/CHANGELOG.md` has zero hits for `live-elsewhere`, so it is
unconsumed and still owed, not a stale leftover. The file is restored byte for
byte from `origin/main` (blob 5321f1b) and this package's note moves to
`.changeset/lint-liveness-live-elsewhere-rule-id.md`.
LEVEL. `minor`, not `patch`: `LIVENESS_LIVE_ELSEWHERE_PROPERTY` is a new export
on `packages/lint/src/index.ts`, the public entry, and a new public-entry export
is `minor` under the precedent this lane applied today (#14606 took
`@objectstack/types: minor` for three new exports). Nothing narrows, so no
BREAKING banner and no ADR-0087 marker are owed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68
@os-muskClaude

Copy link
Copy Markdown
CollaboratorAuthor

Landing provenance (engine execution seat, session session_0112hMx9hjJ9BgB28X97DS68): flipped ready at 16:17Z and armed auto-merge (squash) on head 6d75c6462, the patch-round head.


Generated by Claude Code

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

lint-liveness-properties.ts describe() has not been taught the live-elsewhere verdict — throws by design the day such a row opts into authorWarn

2 participants

@os-musk@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

fix(lint): teach describe() the live-elsewhere verdict instead of throwing - #14632

Merged
os-musk merged 4 commits into
mainfrom
claude/issue-14057-lint-liveness-live-elsewhere
Sep 2, 2026
Merged

fix(lint): teach describe() the live-elsewhere verdict instead of throwing#14632
os-musk merged 4 commits into
mainfrom
claude/issue-14057-lint-liveness-live-elsewhere

Conversation

@os-musk

@os-muskos-musk commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator

Fixes#14057

describe() in packages/lint/src/lint-liveness-properties.ts maps ledger verdicts to author-facing findings, and throws — deliberately, per its own boundary comment — on a status it has not been taught. #13483 then shipped the ledger's fifth status, live-elsewhere (dead HERE by measurement, genuinely enforced in a sibling repo), and migrated manifest.runtime onto it. describe() was never taught it. The day any live-elsewhere row opts into authorWarn: true, os lint raises a shipped-ledger-integrity error instead of the advisory warning the author should have received.

It must never be the dead branch

This is the load-bearing constraint on the repair, and it is why the fix is a new branch rather than a widened one. Per the #11384 lesson, the verdicts imply opposite author actions, so telling an author to "Remove it" about a key whose enforcement is real and remote is exactly the wrong sentence: for manifest.runtime the enforcer is the cloud marketplace publish gate, where an unverified publisher requesting the node tier is hard-rejected, so deleting the key would tear out a live gate's input. live-elsewhere therefore gets its own rule id, its own message, and a default hint that keeps the property and points at the ledger row's evidence for the enforcer.

What changed

  • A fourth branch in describe(), placed against the dead branch it must never be, with a comment saying why the adjacency is deliberate: the two verdicts are one measurement apart and opposite in what they ask of the author.
  • LIVENESS_LIVE_ELSEWHERE_PROPERTY = 'liveness-live-elsewhere-property', exported beside its three siblings and re-exported from src/index.ts. Advisory-only, like them; nothing downstream keys off these ids today (measured: the only non-CHANGELOG consumers of any liveness rule id are src/index.ts, this package's tests, and one CLI test; there is no rule catalogue and no docs page listing them).
  • The sentinel throw stays for genuinely unknown statuses, with its enumeration of the known ones updated — and a test pins the enumeration equal to the branches, since a stale list is how the next status gets misread.
  • Two stale statements in the file's own comments corrected: it described the vocabulary as four members and quoted check-liveness.mts's header as Statuses: live | experimental | planned | dead. That header now reads live | experimental | planned | dead | live-elsewhere.

Reachability, measured

Still a fuse, not a fire. An entry reaches describe() only after shouldWarn() says yes (authorWarn: true, or status === 'experimental'), and the shipped manifest.runtime row carries no authorWarn. A negative control on the real ledger pins that, with an anti-vacuity guard: the coverage walk must SEE a live-elsewhere row (the one row carrying that status lives in manifest.json, so seeing the status proves the file was read) while authorWarnedProperties('manifest') must not contain runtime. If that row ever opts in, the pin goes red and the reviewer updates the pin — the new branch is what makes that flip safe.

Coverage, derived rather than hand-listed

The triage asked for a check that describe() covers every verdict, because patching one status leaves the next to repeat this card. Hand-listing the vocabulary in a test would go stale for exactly the reason describe() did — nobody edits the list they did not know existed — so the pin walks the shipped ledger rows (shippedLedgerStatuses(), a package-internal seam in the same posture as the existing #10262 seam: exported from the module only, re-exported by neither src/index.ts nor the package's exports map) and requires every distinct status it finds to be answered by describe() with a rule id of its own, plus asserts no two verdicts share a rule id. The vocabulary is prose in a comment, not an importable enum, so rows are the only machine-readable statement of it in this package's reach; a status with zero rows cannot reach describe() anyway, and the moment one appears, the pin names it.

live is the one member that deliberately gets no branch, and the source header already ruled it out in a sentence this PR keeps and now pins: an entry marked authorWarn: true on a live row "would be a ledger authoring mistake, not a user error". A mistake in our own shipped data is what the sentinel is for, so the pin asserts live stays loud rather than handled — matching the case the suite already carried.

Measured vocabulary: five members at packages/spec/scripts/liveness/check-liveness.mts:36 and in packages/spec/liveness/README.md. The inconclusive mentioned in triage is not a ledger status — zero hits across packages/spec/; it belongs to the skill-side vocabulary of the sibling card, which is out of scope here.

Changeset

'@objectstack/lint': minor, in .changeset/lint-liveness-live-elsewhere-rule-id.md. LIVENESS_LIVE_ELSEWHERE_PROPERTY is a new export on packages/lint/src/index.ts, the public entry, and a new public-entry export is minor under the precedent this lane applied today — #14606 took @objectstack/types: minor for three new exports. Nothing narrows, so no BREAKING banner and no ADR-0087 marker is owed; the behaviour change is an internal integrity error becoming the advisory finding it should always have been.

An earlier revision of this PR got two things wrong here and both are fixed. It declared patch, arguing from the planned verdict's own changeset; and it wrote its note to .changeset/liveness-live-elsewhere-verdict.md, a filename derived from the verdict — which is #13483's existing file on origin/main, declaring the pending @objectstack/spec: patch bump and describing the fifth verdict's gate-executable criteria and the 180-day re-attestation discipline. Writing there truncated it. That file is restored byte for byte (blob 5321f1b09, git diff origin/main on the path empty, and the re-derived three-dot change set no longer mentions it at all), and this package's note lives under its own non-colliding name.

Verification, at 168c1d7fa for the code, re-confirmed at 6d75c6462

The head moved only by the two changeset files above; the re-derived gate family is byte-identical between the two heads, so the union below carries over.

  • pnpm --filter @objectstack/lint typecheck and the full package suite: 93 files, 2821 passed, 5 skipped. The new tests are genuinely in the typecheck population — tsconfig.test.json --listFiles lists lint-liveness-properties.test.ts.
  • Reverse verification (ablation). Reverting only the new branch, tests untouched, turns three of the new cases red with the sentinel firing on live-elsewhere — the card's defect, reproduced: 3 failed | 65 passed. Restored: blob equality with HEAD, git diff HEAD empty, git status --porcelain empty, 68 passed. The mutation was confirmed on disk before the run (removed text 0 hits, injected marker 1 hit, blob hash changed), and the subject is a package-relative source import, so no dist indirection is in the path.
  • Gate union re-derived on this tree with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands (34 commands): 30 green. pnpm lint (whole repo), pnpm check:nul-bytes and pnpm check:error-status-conformance also green. On the new head, check-empty-changeset ("1 declaring changeset(s) added"), check-changeset-no-major and check-adr-0087-registration were re-run: all exit 0.
  • Four gates answered exit 3 — NOT MEASURED in their own words, not green and not red: check-test-completeness, pm/check-half-states, check:dual-build-cjs-loads ("PREREQUISITE NOT MET ... this is NOT a pass: nothing was measured") and check:type-check-debt. The latter two read built output for every package; CI builds that closure before running them.
  • check:doc-authoring caught a real defect in the first draft: the new tracker id sat inside the sentinel's runtime string, which reaches authors and generated surfaces that cannot resolve it. Moved to the adjacent // comment; the gate is green.

Scope

packages/spec/liveness/** and its scripts were read, never changed. The skills-lane sibling card is untouched.

🤖 Generated with Claude Code

https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68

…owing
`describe()` in `lint-liveness-properties.ts` mapped three ledger verdicts to
author-facing findings and threw, by design, on anything else. #13483 shipped
the ledger's fifth status — `live-elsewhere` (dead here by measurement,
genuinely enforced in a sibling repo; first row `manifest.runtime`, enforced at
the cloud marketplace publish gate) — without teaching this function about it,
so the day such a row opts into `authorWarn: true` the author gets a crash
instead of an advisory warning.
`live-elsewhere` now has its own branch and its own rule id
(`liveness-live-elsewhere-property`), a message that says the property is
enforced in a sibling repo rather than here, and a default hint that keeps the
property and points at the ledger row's evidence. It must never fall into the
`dead` branch: per #11384 the verdicts imply opposite author actions, and
"Remove it" is exactly the wrong sentence about a key whose enforcement is real
and remote — deleting it tears out a live gate's input.
The sentinel throw stays for genuinely unknown statuses, its enumeration
updated. The suite gains a coverage pin derived from the shipped ledger rows —
not a hand-list — so a sixth status fails by name instead of repeating this
card.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68
`check:doc-authoring` (sibling-package prose-id leg) fails a NEW `#NNNN` inside
a runtime string: an author, an operator or a generated surface reads that
message and cannot resolve a tracker id. The reference moves to the adjacent
`//` comment, where the reader who can resolve it is already looking.
Maintainer ruling 2026-08-12, quoted by the gate:
处理 issue 时犯的错应该总结成经验,保留 issue id没有意义
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68
@github-actions

github-actionsBot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

2 anchor(s) derived from 1 changed package(s); no hand-written page names any of them. ⚠️1 changed file(s) yielded no anchor (packages/lint/src/index.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/lint/src/index.ts) — pages documenting those are invisible to this run
  • 1 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 47 of 219 client-bound route-ledger rows — the other 172 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 172: 14 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 — 5 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 937ec142d5bb6e3f112dae2530842b1292be1cd4packageMentionDocs.

Which tree this was computed on

This run read content/docs from 913f571e75fbda67f34777d7051aeaa695dca7df — the merge of head 6d75c646275cbb88c980bbd3c4d06799fe68b17f into base 937ec142d5bb6e3f112dae2530842b1292be1cd4, 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 913f571e75fbda67f34777d7051aeaa695dca7df && git checkout 913f571e75fbda67f34777d7051aeaa695dca7df
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 937ec142d5bb6e3f112dae2530842b1292be1cd4 6d75c646275cbb88c980bbd3c4d06799fe68b17f && git checkout -B drift-repro 937ec142d5bb6e3f112dae2530842b1292be1cd4 && git merge --no-ff 6d75c646275cbb88c980bbd3c4d06799fe68b17f
node scripts/docs-audit/affected-docs.mjs --json 937ec142d5bb6e3f112dae2530842b1292be1cd4

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

…wn at minor
Two defects in the previous commit's changeset, neither in the implementation.
RESTORED. `.changeset/liveness-live-elsewhere-verdict.md` already existed on
`origin/main` — it is #13483's, declaring `@objectstack/spec: patch` and
describing the fifth verdict, its gate-executable criteria, the 180-day
re-attestation discipline and the `manifest.runtime` migration. Writing this
package's note to that path truncated it, which would have dropped the pending
`@objectstack/spec` bump and taken all of that out of the next release notes.
`packages/spec/CHANGELOG.md` has zero hits for `live-elsewhere`, so it is
unconsumed and still owed, not a stale leftover. The file is restored byte for
byte from `origin/main` (blob 5321f1b) and this package's note moves to
`.changeset/lint-liveness-live-elsewhere-rule-id.md`.
LEVEL. `minor`, not `patch`: `LIVENESS_LIVE_ELSEWHERE_PROPERTY` is a new export
on `packages/lint/src/index.ts`, the public entry, and a new public-entry export
is `minor` under the precedent this lane applied today (#14606 took
`@objectstack/types: minor` for three new exports). Nothing narrows, so no
BREAKING banner and no ADR-0087 marker are owed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68
@os-muskClaude

Copy link
Copy Markdown
CollaboratorAuthor

Landing provenance (engine execution seat, session session_0112hMx9hjJ9BgB28X97DS68): flipped ready at 16:17Z and armed auto-merge (squash) on head 6d75c6462, the patch-round head.


Generated by Claude Code

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

lint-liveness-properties.ts describe() has not been taught the live-elsewhere verdict — throws by design the day such a row opts into authorWarn

2 participants

@os-musk@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(lint): teach describe() the live-elsewhere verdict instead of throwing - #14632

Merged
os-musk merged 4 commits into
mainfrom
claude/issue-14057-lint-liveness-live-elsewhere
Sep 2, 2026
Merged

fix(lint): teach describe() the live-elsewhere verdict instead of throwing#14632
os-musk merged 4 commits into
mainfrom
claude/issue-14057-lint-liveness-live-elsewhere

Conversation

@os-musk

@os-muskos-musk commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator

Fixes#14057

describe() in packages/lint/src/lint-liveness-properties.ts maps ledger verdicts to author-facing findings, and throws — deliberately, per its own boundary comment — on a status it has not been taught. #13483 then shipped the ledger's fifth status, live-elsewhere (dead HERE by measurement, genuinely enforced in a sibling repo), and migrated manifest.runtime onto it. describe() was never taught it. The day any live-elsewhere row opts into authorWarn: true, os lint raises a shipped-ledger-integrity error instead of the advisory warning the author should have received.

It must never be the dead branch

This is the load-bearing constraint on the repair, and it is why the fix is a new branch rather than a widened one. Per the #11384 lesson, the verdicts imply opposite author actions, so telling an author to "Remove it" about a key whose enforcement is real and remote is exactly the wrong sentence: for manifest.runtime the enforcer is the cloud marketplace publish gate, where an unverified publisher requesting the node tier is hard-rejected, so deleting the key would tear out a live gate's input. live-elsewhere therefore gets its own rule id, its own message, and a default hint that keeps the property and points at the ledger row's evidence for the enforcer.

What changed

  • A fourth branch in describe(), placed against the dead branch it must never be, with a comment saying why the adjacency is deliberate: the two verdicts are one measurement apart and opposite in what they ask of the author.
  • LIVENESS_LIVE_ELSEWHERE_PROPERTY = 'liveness-live-elsewhere-property', exported beside its three siblings and re-exported from src/index.ts. Advisory-only, like them; nothing downstream keys off these ids today (measured: the only non-CHANGELOG consumers of any liveness rule id are src/index.ts, this package's tests, and one CLI test; there is no rule catalogue and no docs page listing them).
  • The sentinel throw stays for genuinely unknown statuses, with its enumeration of the known ones updated — and a test pins the enumeration equal to the branches, since a stale list is how the next status gets misread.
  • Two stale statements in the file's own comments corrected: it described the vocabulary as four members and quoted check-liveness.mts's header as Statuses: live | experimental | planned | dead. That header now reads live | experimental | planned | dead | live-elsewhere.

Reachability, measured

Still a fuse, not a fire. An entry reaches describe() only after shouldWarn() says yes (authorWarn: true, or status === 'experimental'), and the shipped manifest.runtime row carries no authorWarn. A negative control on the real ledger pins that, with an anti-vacuity guard: the coverage walk must SEE a live-elsewhere row (the one row carrying that status lives in manifest.json, so seeing the status proves the file was read) while authorWarnedProperties('manifest') must not contain runtime. If that row ever opts in, the pin goes red and the reviewer updates the pin — the new branch is what makes that flip safe.

Coverage, derived rather than hand-listed

The triage asked for a check that describe() covers every verdict, because patching one status leaves the next to repeat this card. Hand-listing the vocabulary in a test would go stale for exactly the reason describe() did — nobody edits the list they did not know existed — so the pin walks the shipped ledger rows (shippedLedgerStatuses(), a package-internal seam in the same posture as the existing #10262 seam: exported from the module only, re-exported by neither src/index.ts nor the package's exports map) and requires every distinct status it finds to be answered by describe() with a rule id of its own, plus asserts no two verdicts share a rule id. The vocabulary is prose in a comment, not an importable enum, so rows are the only machine-readable statement of it in this package's reach; a status with zero rows cannot reach describe() anyway, and the moment one appears, the pin names it.

live is the one member that deliberately gets no branch, and the source header already ruled it out in a sentence this PR keeps and now pins: an entry marked authorWarn: true on a live row "would be a ledger authoring mistake, not a user error". A mistake in our own shipped data is what the sentinel is for, so the pin asserts live stays loud rather than handled — matching the case the suite already carried.

Measured vocabulary: five members at packages/spec/scripts/liveness/check-liveness.mts:36 and in packages/spec/liveness/README.md. The inconclusive mentioned in triage is not a ledger status — zero hits across packages/spec/; it belongs to the skill-side vocabulary of the sibling card, which is out of scope here.

Changeset

'@objectstack/lint': minor, in .changeset/lint-liveness-live-elsewhere-rule-id.md. LIVENESS_LIVE_ELSEWHERE_PROPERTY is a new export on packages/lint/src/index.ts, the public entry, and a new public-entry export is minor under the precedent this lane applied today — #14606 took @objectstack/types: minor for three new exports. Nothing narrows, so no BREAKING banner and no ADR-0087 marker is owed; the behaviour change is an internal integrity error becoming the advisory finding it should always have been.

An earlier revision of this PR got two things wrong here and both are fixed. It declared patch, arguing from the planned verdict's own changeset; and it wrote its note to .changeset/liveness-live-elsewhere-verdict.md, a filename derived from the verdict — which is #13483's existing file on origin/main, declaring the pending @objectstack/spec: patch bump and describing the fifth verdict's gate-executable criteria and the 180-day re-attestation discipline. Writing there truncated it. That file is restored byte for byte (blob 5321f1b09, git diff origin/main on the path empty, and the re-derived three-dot change set no longer mentions it at all), and this package's note lives under its own non-colliding name.

Verification, at 168c1d7fa for the code, re-confirmed at 6d75c6462

The head moved only by the two changeset files above; the re-derived gate family is byte-identical between the two heads, so the union below carries over.

  • pnpm --filter @objectstack/lint typecheck and the full package suite: 93 files, 2821 passed, 5 skipped. The new tests are genuinely in the typecheck population — tsconfig.test.json --listFiles lists lint-liveness-properties.test.ts.
  • Reverse verification (ablation). Reverting only the new branch, tests untouched, turns three of the new cases red with the sentinel firing on live-elsewhere — the card's defect, reproduced: 3 failed | 65 passed. Restored: blob equality with HEAD, git diff HEAD empty, git status --porcelain empty, 68 passed. The mutation was confirmed on disk before the run (removed text 0 hits, injected marker 1 hit, blob hash changed), and the subject is a package-relative source import, so no dist indirection is in the path.
  • Gate union re-derived on this tree with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands (34 commands): 30 green. pnpm lint (whole repo), pnpm check:nul-bytes and pnpm check:error-status-conformance also green. On the new head, check-empty-changeset ("1 declaring changeset(s) added"), check-changeset-no-major and check-adr-0087-registration were re-run: all exit 0.
  • Four gates answered exit 3 — NOT MEASURED in their own words, not green and not red: check-test-completeness, pm/check-half-states, check:dual-build-cjs-loads ("PREREQUISITE NOT MET ... this is NOT a pass: nothing was measured") and check:type-check-debt. The latter two read built output for every package; CI builds that closure before running them.
  • check:doc-authoring caught a real defect in the first draft: the new tracker id sat inside the sentinel's runtime string, which reaches authors and generated surfaces that cannot resolve it. Moved to the adjacent // comment; the gate is green.

Scope

packages/spec/liveness/** and its scripts were read, never changed. The skills-lane sibling card is untouched.

🤖 Generated with Claude Code

https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68

…owing
`describe()` in `lint-liveness-properties.ts` mapped three ledger verdicts to
author-facing findings and threw, by design, on anything else. #13483 shipped
the ledger's fifth status — `live-elsewhere` (dead here by measurement,
genuinely enforced in a sibling repo; first row `manifest.runtime`, enforced at
the cloud marketplace publish gate) — without teaching this function about it,
so the day such a row opts into `authorWarn: true` the author gets a crash
instead of an advisory warning.
`live-elsewhere` now has its own branch and its own rule id
(`liveness-live-elsewhere-property`), a message that says the property is
enforced in a sibling repo rather than here, and a default hint that keeps the
property and points at the ledger row's evidence. It must never fall into the
`dead` branch: per #11384 the verdicts imply opposite author actions, and
"Remove it" is exactly the wrong sentence about a key whose enforcement is real
and remote — deleting it tears out a live gate's input.
The sentinel throw stays for genuinely unknown statuses, its enumeration
updated. The suite gains a coverage pin derived from the shipped ledger rows —
not a hand-list — so a sixth status fails by name instead of repeating this
card.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68
`check:doc-authoring` (sibling-package prose-id leg) fails a NEW `#NNNN` inside
a runtime string: an author, an operator or a generated surface reads that
message and cannot resolve a tracker id. The reference moves to the adjacent
`//` comment, where the reader who can resolve it is already looking.
Maintainer ruling 2026-08-12, quoted by the gate:
处理 issue 时犯的错应该总结成经验,保留 issue id没有意义
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68
@github-actions

github-actionsBot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

2 anchor(s) derived from 1 changed package(s); no hand-written page names any of them. ⚠️1 changed file(s) yielded no anchor (packages/lint/src/index.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/lint/src/index.ts) — pages documenting those are invisible to this run
  • 1 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 47 of 219 client-bound route-ledger rows — the other 172 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 172: 14 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 — 5 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 937ec142d5bb6e3f112dae2530842b1292be1cd4packageMentionDocs.

Which tree this was computed on

This run read content/docs from 913f571e75fbda67f34777d7051aeaa695dca7df — the merge of head 6d75c646275cbb88c980bbd3c4d06799fe68b17f into base 937ec142d5bb6e3f112dae2530842b1292be1cd4, 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 913f571e75fbda67f34777d7051aeaa695dca7df && git checkout 913f571e75fbda67f34777d7051aeaa695dca7df
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 937ec142d5bb6e3f112dae2530842b1292be1cd4 6d75c646275cbb88c980bbd3c4d06799fe68b17f && git checkout -B drift-repro 937ec142d5bb6e3f112dae2530842b1292be1cd4 && git merge --no-ff 6d75c646275cbb88c980bbd3c4d06799fe68b17f
node scripts/docs-audit/affected-docs.mjs --json 937ec142d5bb6e3f112dae2530842b1292be1cd4

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

…wn at minor
Two defects in the previous commit's changeset, neither in the implementation.
RESTORED. `.changeset/liveness-live-elsewhere-verdict.md` already existed on
`origin/main` — it is #13483's, declaring `@objectstack/spec: patch` and
describing the fifth verdict, its gate-executable criteria, the 180-day
re-attestation discipline and the `manifest.runtime` migration. Writing this
package's note to that path truncated it, which would have dropped the pending
`@objectstack/spec` bump and taken all of that out of the next release notes.
`packages/spec/CHANGELOG.md` has zero hits for `live-elsewhere`, so it is
unconsumed and still owed, not a stale leftover. The file is restored byte for
byte from `origin/main` (blob 5321f1b) and this package's note moves to
`.changeset/lint-liveness-live-elsewhere-rule-id.md`.
LEVEL. `minor`, not `patch`: `LIVENESS_LIVE_ELSEWHERE_PROPERTY` is a new export
on `packages/lint/src/index.ts`, the public entry, and a new public-entry export
is `minor` under the precedent this lane applied today (#14606 took
`@objectstack/types: minor` for three new exports). Nothing narrows, so no
BREAKING banner and no ADR-0087 marker are owed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68
@os-muskClaude

Copy link
Copy Markdown
CollaboratorAuthor

Landing provenance (engine execution seat, session session_0112hMx9hjJ9BgB28X97DS68): flipped ready at 16:17Z and armed auto-merge (squash) on head 6d75c6462, the patch-round head.


Generated by Claude Code

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

lint-liveness-properties.ts describe() has not been taught the live-elsewhere verdict — throws by design the day such a row opts into authorWarn

2 participants

@os-musk@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(lint): teach describe() the live-elsewhere verdict instead of throwing - #14632

Merged
os-musk merged 4 commits into
mainfrom
claude/issue-14057-lint-liveness-live-elsewhere
Sep 2, 2026
Merged

fix(lint): teach describe() the live-elsewhere verdict instead of throwing#14632
os-musk merged 4 commits into
mainfrom
claude/issue-14057-lint-liveness-live-elsewhere

Conversation

@os-musk

@os-muskos-musk commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator

Fixes#14057

describe() in packages/lint/src/lint-liveness-properties.ts maps ledger verdicts to author-facing findings, and throws — deliberately, per its own boundary comment — on a status it has not been taught. #13483 then shipped the ledger's fifth status, live-elsewhere (dead HERE by measurement, genuinely enforced in a sibling repo), and migrated manifest.runtime onto it. describe() was never taught it. The day any live-elsewhere row opts into authorWarn: true, os lint raises a shipped-ledger-integrity error instead of the advisory warning the author should have received.

It must never be the dead branch

This is the load-bearing constraint on the repair, and it is why the fix is a new branch rather than a widened one. Per the #11384 lesson, the verdicts imply opposite author actions, so telling an author to "Remove it" about a key whose enforcement is real and remote is exactly the wrong sentence: for manifest.runtime the enforcer is the cloud marketplace publish gate, where an unverified publisher requesting the node tier is hard-rejected, so deleting the key would tear out a live gate's input. live-elsewhere therefore gets its own rule id, its own message, and a default hint that keeps the property and points at the ledger row's evidence for the enforcer.

What changed

  • A fourth branch in describe(), placed against the dead branch it must never be, with a comment saying why the adjacency is deliberate: the two verdicts are one measurement apart and opposite in what they ask of the author.
  • LIVENESS_LIVE_ELSEWHERE_PROPERTY = 'liveness-live-elsewhere-property', exported beside its three siblings and re-exported from src/index.ts. Advisory-only, like them; nothing downstream keys off these ids today (measured: the only non-CHANGELOG consumers of any liveness rule id are src/index.ts, this package's tests, and one CLI test; there is no rule catalogue and no docs page listing them).
  • The sentinel throw stays for genuinely unknown statuses, with its enumeration of the known ones updated — and a test pins the enumeration equal to the branches, since a stale list is how the next status gets misread.
  • Two stale statements in the file's own comments corrected: it described the vocabulary as four members and quoted check-liveness.mts's header as Statuses: live | experimental | planned | dead. That header now reads live | experimental | planned | dead | live-elsewhere.

Reachability, measured

Still a fuse, not a fire. An entry reaches describe() only after shouldWarn() says yes (authorWarn: true, or status === 'experimental'), and the shipped manifest.runtime row carries no authorWarn. A negative control on the real ledger pins that, with an anti-vacuity guard: the coverage walk must SEE a live-elsewhere row (the one row carrying that status lives in manifest.json, so seeing the status proves the file was read) while authorWarnedProperties('manifest') must not contain runtime. If that row ever opts in, the pin goes red and the reviewer updates the pin — the new branch is what makes that flip safe.

Coverage, derived rather than hand-listed

The triage asked for a check that describe() covers every verdict, because patching one status leaves the next to repeat this card. Hand-listing the vocabulary in a test would go stale for exactly the reason describe() did — nobody edits the list they did not know existed — so the pin walks the shipped ledger rows (shippedLedgerStatuses(), a package-internal seam in the same posture as the existing #10262 seam: exported from the module only, re-exported by neither src/index.ts nor the package's exports map) and requires every distinct status it finds to be answered by describe() with a rule id of its own, plus asserts no two verdicts share a rule id. The vocabulary is prose in a comment, not an importable enum, so rows are the only machine-readable statement of it in this package's reach; a status with zero rows cannot reach describe() anyway, and the moment one appears, the pin names it.

live is the one member that deliberately gets no branch, and the source header already ruled it out in a sentence this PR keeps and now pins: an entry marked authorWarn: true on a live row "would be a ledger authoring mistake, not a user error". A mistake in our own shipped data is what the sentinel is for, so the pin asserts live stays loud rather than handled — matching the case the suite already carried.

Measured vocabulary: five members at packages/spec/scripts/liveness/check-liveness.mts:36 and in packages/spec/liveness/README.md. The inconclusive mentioned in triage is not a ledger status — zero hits across packages/spec/; it belongs to the skill-side vocabulary of the sibling card, which is out of scope here.

Changeset

'@objectstack/lint': minor, in .changeset/lint-liveness-live-elsewhere-rule-id.md. LIVENESS_LIVE_ELSEWHERE_PROPERTY is a new export on packages/lint/src/index.ts, the public entry, and a new public-entry export is minor under the precedent this lane applied today — #14606 took @objectstack/types: minor for three new exports. Nothing narrows, so no BREAKING banner and no ADR-0087 marker is owed; the behaviour change is an internal integrity error becoming the advisory finding it should always have been.

An earlier revision of this PR got two things wrong here and both are fixed. It declared patch, arguing from the planned verdict's own changeset; and it wrote its note to .changeset/liveness-live-elsewhere-verdict.md, a filename derived from the verdict — which is #13483's existing file on origin/main, declaring the pending @objectstack/spec: patch bump and describing the fifth verdict's gate-executable criteria and the 180-day re-attestation discipline. Writing there truncated it. That file is restored byte for byte (blob 5321f1b09, git diff origin/main on the path empty, and the re-derived three-dot change set no longer mentions it at all), and this package's note lives under its own non-colliding name.

Verification, at 168c1d7fa for the code, re-confirmed at 6d75c6462

The head moved only by the two changeset files above; the re-derived gate family is byte-identical between the two heads, so the union below carries over.

  • pnpm --filter @objectstack/lint typecheck and the full package suite: 93 files, 2821 passed, 5 skipped. The new tests are genuinely in the typecheck population — tsconfig.test.json --listFiles lists lint-liveness-properties.test.ts.
  • Reverse verification (ablation). Reverting only the new branch, tests untouched, turns three of the new cases red with the sentinel firing on live-elsewhere — the card's defect, reproduced: 3 failed | 65 passed. Restored: blob equality with HEAD, git diff HEAD empty, git status --porcelain empty, 68 passed. The mutation was confirmed on disk before the run (removed text 0 hits, injected marker 1 hit, blob hash changed), and the subject is a package-relative source import, so no dist indirection is in the path.
  • Gate union re-derived on this tree with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands (34 commands): 30 green. pnpm lint (whole repo), pnpm check:nul-bytes and pnpm check:error-status-conformance also green. On the new head, check-empty-changeset ("1 declaring changeset(s) added"), check-changeset-no-major and check-adr-0087-registration were re-run: all exit 0.
  • Four gates answered exit 3 — NOT MEASURED in their own words, not green and not red: check-test-completeness, pm/check-half-states, check:dual-build-cjs-loads ("PREREQUISITE NOT MET ... this is NOT a pass: nothing was measured") and check:type-check-debt. The latter two read built output for every package; CI builds that closure before running them.
  • check:doc-authoring caught a real defect in the first draft: the new tracker id sat inside the sentinel's runtime string, which reaches authors and generated surfaces that cannot resolve it. Moved to the adjacent // comment; the gate is green.

Scope

packages/spec/liveness/** and its scripts were read, never changed. The skills-lane sibling card is untouched.

🤖 Generated with Claude Code

https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68

…owing
`describe()` in `lint-liveness-properties.ts` mapped three ledger verdicts to
author-facing findings and threw, by design, on anything else. #13483 shipped
the ledger's fifth status — `live-elsewhere` (dead here by measurement,
genuinely enforced in a sibling repo; first row `manifest.runtime`, enforced at
the cloud marketplace publish gate) — without teaching this function about it,
so the day such a row opts into `authorWarn: true` the author gets a crash
instead of an advisory warning.
`live-elsewhere` now has its own branch and its own rule id
(`liveness-live-elsewhere-property`), a message that says the property is
enforced in a sibling repo rather than here, and a default hint that keeps the
property and points at the ledger row's evidence. It must never fall into the
`dead` branch: per #11384 the verdicts imply opposite author actions, and
"Remove it" is exactly the wrong sentence about a key whose enforcement is real
and remote — deleting it tears out a live gate's input.
The sentinel throw stays for genuinely unknown statuses, its enumeration
updated. The suite gains a coverage pin derived from the shipped ledger rows —
not a hand-list — so a sixth status fails by name instead of repeating this
card.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68
`check:doc-authoring` (sibling-package prose-id leg) fails a NEW `#NNNN` inside
a runtime string: an author, an operator or a generated surface reads that
message and cannot resolve a tracker id. The reference moves to the adjacent
`//` comment, where the reader who can resolve it is already looking.
Maintainer ruling 2026-08-12, quoted by the gate:
处理 issue 时犯的错应该总结成经验,保留 issue id没有意义
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68
@github-actions

github-actionsBot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

2 anchor(s) derived from 1 changed package(s); no hand-written page names any of them. ⚠️1 changed file(s) yielded no anchor (packages/lint/src/index.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/lint/src/index.ts) — pages documenting those are invisible to this run
  • 1 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 47 of 219 client-bound route-ledger rows — the other 172 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 172: 14 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 — 5 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 937ec142d5bb6e3f112dae2530842b1292be1cd4packageMentionDocs.

Which tree this was computed on

This run read content/docs from 913f571e75fbda67f34777d7051aeaa695dca7df — the merge of head 6d75c646275cbb88c980bbd3c4d06799fe68b17f into base 937ec142d5bb6e3f112dae2530842b1292be1cd4, 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 913f571e75fbda67f34777d7051aeaa695dca7df && git checkout 913f571e75fbda67f34777d7051aeaa695dca7df
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 937ec142d5bb6e3f112dae2530842b1292be1cd4 6d75c646275cbb88c980bbd3c4d06799fe68b17f && git checkout -B drift-repro 937ec142d5bb6e3f112dae2530842b1292be1cd4 && git merge --no-ff 6d75c646275cbb88c980bbd3c4d06799fe68b17f
node scripts/docs-audit/affected-docs.mjs --json 937ec142d5bb6e3f112dae2530842b1292be1cd4

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

…wn at minor
Two defects in the previous commit's changeset, neither in the implementation.
RESTORED. `.changeset/liveness-live-elsewhere-verdict.md` already existed on
`origin/main` — it is #13483's, declaring `@objectstack/spec: patch` and
describing the fifth verdict, its gate-executable criteria, the 180-day
re-attestation discipline and the `manifest.runtime` migration. Writing this
package's note to that path truncated it, which would have dropped the pending
`@objectstack/spec` bump and taken all of that out of the next release notes.
`packages/spec/CHANGELOG.md` has zero hits for `live-elsewhere`, so it is
unconsumed and still owed, not a stale leftover. The file is restored byte for
byte from `origin/main` (blob 5321f1b) and this package's note moves to
`.changeset/lint-liveness-live-elsewhere-rule-id.md`.
LEVEL. `minor`, not `patch`: `LIVENESS_LIVE_ELSEWHERE_PROPERTY` is a new export
on `packages/lint/src/index.ts`, the public entry, and a new public-entry export
is `minor` under the precedent this lane applied today (#14606 took
`@objectstack/types: minor` for three new exports). Nothing narrows, so no
BREAKING banner and no ADR-0087 marker are owed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68
@os-muskClaude

Copy link
Copy Markdown
CollaboratorAuthor

Landing provenance (engine execution seat, session session_0112hMx9hjJ9BgB28X97DS68): flipped ready at 16:17Z and armed auto-merge (squash) on head 6d75c6462, the patch-round head.


Generated by Claude Code

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

lint-liveness-properties.ts describe() has not been taught the live-elsewhere verdict — throws by design the day such a row opts into authorWarn

2 participants

@os-musk@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

fix(lint): teach describe() the live-elsewhere verdict instead of throwing - #14632

Merged
os-musk merged 4 commits into
mainfrom
claude/issue-14057-lint-liveness-live-elsewhere
Sep 2, 2026
Merged

fix(lint): teach describe() the live-elsewhere verdict instead of throwing#14632
os-musk merged 4 commits into
mainfrom
claude/issue-14057-lint-liveness-live-elsewhere

Conversation

@os-musk

@os-muskos-musk commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator

Fixes#14057

describe() in packages/lint/src/lint-liveness-properties.ts maps ledger verdicts to author-facing findings, and throws — deliberately, per its own boundary comment — on a status it has not been taught. #13483 then shipped the ledger's fifth status, live-elsewhere (dead HERE by measurement, genuinely enforced in a sibling repo), and migrated manifest.runtime onto it. describe() was never taught it. The day any live-elsewhere row opts into authorWarn: true, os lint raises a shipped-ledger-integrity error instead of the advisory warning the author should have received.

It must never be the dead branch

This is the load-bearing constraint on the repair, and it is why the fix is a new branch rather than a widened one. Per the #11384 lesson, the verdicts imply opposite author actions, so telling an author to "Remove it" about a key whose enforcement is real and remote is exactly the wrong sentence: for manifest.runtime the enforcer is the cloud marketplace publish gate, where an unverified publisher requesting the node tier is hard-rejected, so deleting the key would tear out a live gate's input. live-elsewhere therefore gets its own rule id, its own message, and a default hint that keeps the property and points at the ledger row's evidence for the enforcer.

What changed

  • A fourth branch in describe(), placed against the dead branch it must never be, with a comment saying why the adjacency is deliberate: the two verdicts are one measurement apart and opposite in what they ask of the author.
  • LIVENESS_LIVE_ELSEWHERE_PROPERTY = 'liveness-live-elsewhere-property', exported beside its three siblings and re-exported from src/index.ts. Advisory-only, like them; nothing downstream keys off these ids today (measured: the only non-CHANGELOG consumers of any liveness rule id are src/index.ts, this package's tests, and one CLI test; there is no rule catalogue and no docs page listing them).
  • The sentinel throw stays for genuinely unknown statuses, with its enumeration of the known ones updated — and a test pins the enumeration equal to the branches, since a stale list is how the next status gets misread.
  • Two stale statements in the file's own comments corrected: it described the vocabulary as four members and quoted check-liveness.mts's header as Statuses: live | experimental | planned | dead. That header now reads live | experimental | planned | dead | live-elsewhere.

Reachability, measured

Still a fuse, not a fire. An entry reaches describe() only after shouldWarn() says yes (authorWarn: true, or status === 'experimental'), and the shipped manifest.runtime row carries no authorWarn. A negative control on the real ledger pins that, with an anti-vacuity guard: the coverage walk must SEE a live-elsewhere row (the one row carrying that status lives in manifest.json, so seeing the status proves the file was read) while authorWarnedProperties('manifest') must not contain runtime. If that row ever opts in, the pin goes red and the reviewer updates the pin — the new branch is what makes that flip safe.

Coverage, derived rather than hand-listed

The triage asked for a check that describe() covers every verdict, because patching one status leaves the next to repeat this card. Hand-listing the vocabulary in a test would go stale for exactly the reason describe() did — nobody edits the list they did not know existed — so the pin walks the shipped ledger rows (shippedLedgerStatuses(), a package-internal seam in the same posture as the existing #10262 seam: exported from the module only, re-exported by neither src/index.ts nor the package's exports map) and requires every distinct status it finds to be answered by describe() with a rule id of its own, plus asserts no two verdicts share a rule id. The vocabulary is prose in a comment, not an importable enum, so rows are the only machine-readable statement of it in this package's reach; a status with zero rows cannot reach describe() anyway, and the moment one appears, the pin names it.

live is the one member that deliberately gets no branch, and the source header already ruled it out in a sentence this PR keeps and now pins: an entry marked authorWarn: true on a live row "would be a ledger authoring mistake, not a user error". A mistake in our own shipped data is what the sentinel is for, so the pin asserts live stays loud rather than handled — matching the case the suite already carried.

Measured vocabulary: five members at packages/spec/scripts/liveness/check-liveness.mts:36 and in packages/spec/liveness/README.md. The inconclusive mentioned in triage is not a ledger status — zero hits across packages/spec/; it belongs to the skill-side vocabulary of the sibling card, which is out of scope here.

Changeset

'@objectstack/lint': minor, in .changeset/lint-liveness-live-elsewhere-rule-id.md. LIVENESS_LIVE_ELSEWHERE_PROPERTY is a new export on packages/lint/src/index.ts, the public entry, and a new public-entry export is minor under the precedent this lane applied today — #14606 took @objectstack/types: minor for three new exports. Nothing narrows, so no BREAKING banner and no ADR-0087 marker is owed; the behaviour change is an internal integrity error becoming the advisory finding it should always have been.

An earlier revision of this PR got two things wrong here and both are fixed. It declared patch, arguing from the planned verdict's own changeset; and it wrote its note to .changeset/liveness-live-elsewhere-verdict.md, a filename derived from the verdict — which is #13483's existing file on origin/main, declaring the pending @objectstack/spec: patch bump and describing the fifth verdict's gate-executable criteria and the 180-day re-attestation discipline. Writing there truncated it. That file is restored byte for byte (blob 5321f1b09, git diff origin/main on the path empty, and the re-derived three-dot change set no longer mentions it at all), and this package's note lives under its own non-colliding name.

Verification, at 168c1d7fa for the code, re-confirmed at 6d75c6462

The head moved only by the two changeset files above; the re-derived gate family is byte-identical between the two heads, so the union below carries over.

  • pnpm --filter @objectstack/lint typecheck and the full package suite: 93 files, 2821 passed, 5 skipped. The new tests are genuinely in the typecheck population — tsconfig.test.json --listFiles lists lint-liveness-properties.test.ts.
  • Reverse verification (ablation). Reverting only the new branch, tests untouched, turns three of the new cases red with the sentinel firing on live-elsewhere — the card's defect, reproduced: 3 failed | 65 passed. Restored: blob equality with HEAD, git diff HEAD empty, git status --porcelain empty, 68 passed. The mutation was confirmed on disk before the run (removed text 0 hits, injected marker 1 hit, blob hash changed), and the subject is a package-relative source import, so no dist indirection is in the path.
  • Gate union re-derived on this tree with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands (34 commands): 30 green. pnpm lint (whole repo), pnpm check:nul-bytes and pnpm check:error-status-conformance also green. On the new head, check-empty-changeset ("1 declaring changeset(s) added"), check-changeset-no-major and check-adr-0087-registration were re-run: all exit 0.
  • Four gates answered exit 3 — NOT MEASURED in their own words, not green and not red: check-test-completeness, pm/check-half-states, check:dual-build-cjs-loads ("PREREQUISITE NOT MET ... this is NOT a pass: nothing was measured") and check:type-check-debt. The latter two read built output for every package; CI builds that closure before running them.
  • check:doc-authoring caught a real defect in the first draft: the new tracker id sat inside the sentinel's runtime string, which reaches authors and generated surfaces that cannot resolve it. Moved to the adjacent // comment; the gate is green.

Scope

packages/spec/liveness/** and its scripts were read, never changed. The skills-lane sibling card is untouched.

🤖 Generated with Claude Code

https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68

…owing
`describe()` in `lint-liveness-properties.ts` mapped three ledger verdicts to
author-facing findings and threw, by design, on anything else. #13483 shipped
the ledger's fifth status — `live-elsewhere` (dead here by measurement,
genuinely enforced in a sibling repo; first row `manifest.runtime`, enforced at
the cloud marketplace publish gate) — without teaching this function about it,
so the day such a row opts into `authorWarn: true` the author gets a crash
instead of an advisory warning.
`live-elsewhere` now has its own branch and its own rule id
(`liveness-live-elsewhere-property`), a message that says the property is
enforced in a sibling repo rather than here, and a default hint that keeps the
property and points at the ledger row's evidence. It must never fall into the
`dead` branch: per #11384 the verdicts imply opposite author actions, and
"Remove it" is exactly the wrong sentence about a key whose enforcement is real
and remote — deleting it tears out a live gate's input.
The sentinel throw stays for genuinely unknown statuses, its enumeration
updated. The suite gains a coverage pin derived from the shipped ledger rows —
not a hand-list — so a sixth status fails by name instead of repeating this
card.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68
`check:doc-authoring` (sibling-package prose-id leg) fails a NEW `#NNNN` inside
a runtime string: an author, an operator or a generated surface reads that
message and cannot resolve a tracker id. The reference moves to the adjacent
`//` comment, where the reader who can resolve it is already looking.
Maintainer ruling 2026-08-12, quoted by the gate:
处理 issue 时犯的错应该总结成经验,保留 issue id没有意义
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68
@github-actions

github-actionsBot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

2 anchor(s) derived from 1 changed package(s); no hand-written page names any of them. ⚠️1 changed file(s) yielded no anchor (packages/lint/src/index.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/lint/src/index.ts) — pages documenting those are invisible to this run
  • 1 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 47 of 219 client-bound route-ledger rows — the other 172 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 172: 14 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 — 5 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 937ec142d5bb6e3f112dae2530842b1292be1cd4packageMentionDocs.

Which tree this was computed on

This run read content/docs from 913f571e75fbda67f34777d7051aeaa695dca7df — the merge of head 6d75c646275cbb88c980bbd3c4d06799fe68b17f into base 937ec142d5bb6e3f112dae2530842b1292be1cd4, 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 913f571e75fbda67f34777d7051aeaa695dca7df && git checkout 913f571e75fbda67f34777d7051aeaa695dca7df
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 937ec142d5bb6e3f112dae2530842b1292be1cd4 6d75c646275cbb88c980bbd3c4d06799fe68b17f && git checkout -B drift-repro 937ec142d5bb6e3f112dae2530842b1292be1cd4 && git merge --no-ff 6d75c646275cbb88c980bbd3c4d06799fe68b17f
node scripts/docs-audit/affected-docs.mjs --json 937ec142d5bb6e3f112dae2530842b1292be1cd4

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

…wn at minor
Two defects in the previous commit's changeset, neither in the implementation.
RESTORED. `.changeset/liveness-live-elsewhere-verdict.md` already existed on
`origin/main` — it is #13483's, declaring `@objectstack/spec: patch` and
describing the fifth verdict, its gate-executable criteria, the 180-day
re-attestation discipline and the `manifest.runtime` migration. Writing this
package's note to that path truncated it, which would have dropped the pending
`@objectstack/spec` bump and taken all of that out of the next release notes.
`packages/spec/CHANGELOG.md` has zero hits for `live-elsewhere`, so it is
unconsumed and still owed, not a stale leftover. The file is restored byte for
byte from `origin/main` (blob 5321f1b) and this package's note moves to
`.changeset/lint-liveness-live-elsewhere-rule-id.md`.
LEVEL. `minor`, not `patch`: `LIVENESS_LIVE_ELSEWHERE_PROPERTY` is a new export
on `packages/lint/src/index.ts`, the public entry, and a new public-entry export
is `minor` under the precedent this lane applied today (#14606 took
`@objectstack/types: minor` for three new exports). Nothing narrows, so no
BREAKING banner and no ADR-0087 marker are owed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68
@os-muskClaude

Copy link
Copy Markdown
CollaboratorAuthor

Landing provenance (engine execution seat, session session_0112hMx9hjJ9BgB28X97DS68): flipped ready at 16:17Z and armed auto-merge (squash) on head 6d75c6462, the patch-round head.


Generated by Claude Code

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

lint-liveness-properties.ts describe() has not been taught the live-elsewhere verdict — throws by design the day such a row opts into authorWarn

2 participants

@os-musk@claude