docs-audit: state where every emitted anchor came from - #13738

Merged
os-project-manager merged 1 commit into
mainfrom
claude/issue-12824-anchor-provenance
Aug 31, 2026
Merged

docs-audit: state where every emitted anchor came from#13738
os-project-manager merged 1 commit into
mainfrom
claude/issue-12824-anchor-provenance

Conversation

@os-project-manager

Copy link
Copy Markdown
Collaborator

Fixes#12824

Implements option C of the maintainer's ruling of 2026-08-31 (verbatim 「同意」), and only C: every row the docs-drift advisory emits now states where its anchor came from — the member and the declaration enclosing it — so a reader can judge the row instead of guessing.

before: organizationId (symbol)
after: organizationId (symbol, a field of interface MetaOverlayCacheKey)
after: userActions (symbol, a field of const object ObjectSchemaBase)

Those last two are the same syntactic formname: inside an object or interface — and that is the whole finding. One is a field of an internal cache struct that lands 10 pages documenting an unrelated organizationId; the other is the canonical authorable key whose row is the best this tool produces. The card's three disproven discriminators (syntactic form, declaring package, property name against the authorable registry) all fail to separate them. The declaring container separates them, and no row printed it before.

Scope — what this deliberately is not

Proof that the anchor set is unchanged — measured, not asserted

Both arms of affected-docs.mjs (origin/main and this branch) were run against the same tree at the same commit over 100 consecutive main commits touching packages/, comparing the full --json output:

commits replayed100
rows, base arm550
rows, this branch550
anchors emitted722
anchors carrying provenance722 (100%)
mismatches0

Compared per commit: the anchor set as kind + token; the docs list; and the entire JSON document once the two additive fields (anchors[].from, and the clause appended inside each detail[].via string) are removed. All three byte-identical on every commit. Harness and per-commit records are in the dev report on #12824.

The same property is pinned in --self-test rather than left to this one measurement: the provenance key set is asserted to be exactly the anchor set, in both directions — never a superset (a name nothing minted) and never a subset (an anchor with no clause). A future change cannot start deciding with from without going red.

The ruling's own example, end to end

Driven through the real pipeline by committing a one-line widening at each declaration site and running both arms at HEAD^:

editbase rowthis branchrows
organizationId?: string on MetaOverlayCacheKeyorganizationId (symbol)organizationId (symbol, a field of interface MetaOverlayCacheKey)10 → 10
userActions on ObjectSchemaBaseuserActions (symbol)userActions (symbol, a field of const object ObjectSchemaBase)5 → 5

content/docs/data-modeling/objects.mdx — the true positive option B drops — is present in both arms, unchanged.

Every anchor kind names its origin

symbol is the card's subject, but the card also records the bridge amplification it feeds (publishItem (sdk) and /:type/:name/publish (route) on a diff touching no state machine), and that is only judgeable when the row names the hop it rode. From a real run on this tree:

sdk meta.getView the route ledger binds it to GET /api/v1/ui/view/:object/:type
route /view/:object/:type bridged from symbol enforceEnvironmentOwnership — its registrar handler names it
route /api/v1/ui/view/:object/:type a path literal in RestServer
symbol computeExecCtx a method of class RestServer

Measured cost

The advisory comment gets longer, which the ruling accepted implicitly (C "does not reduce the row count"). Quantified on the worst run in the 100-commit population: the rendered row block grows 1265 → 2312 bytes (x1.8) at the 15-row display cap. That worst case is command anchors, whose clause was first drafted as the CLI command id ID, read off FILE — a restatement of the token — and is now just read off FILE, which took the worst single row from 2201 to 1549 chars. Median row is 76 chars, p90 222.

⛔ The display-cut problem itself is not addressed here and survives, exactly as the card says of option C.

Verification

  • node scripts/docs-audit/affected-docs.mjs --self-test503 cases pass (487 before; +16 pins for the provenance derivation, the member-form classifier including its degraded answer, and the key-set invariant).
  • node scripts/docs-audit/check-affected-docs.mjs — exit 0 (self-test + --bridge-coverage; discovered population unchanged).
  • node scripts/docs-audit/check-drift-comment.mjs56 cases pass across 5 fixture diffs. This runs the workflow's real comment script against real mapper output, so it is also the check that the added clause does not break the renderer.
  • pnpm lint (repo-wide, eslint . --no-inline-config) — exit 0.
  • Derived gate family (node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack), all exit 0: check:nul-bytes, check:docs-audit-scope, check:watch-hint-literal, check:agent-test-spelling, check:entry-guard, check:parse-guard, check:cli-command-ids, check:pnpm-filter-targets, check:bash32-floor, check:pm-governed-merges, check:cross-package-test-inputs, check-self-test-wired, check-ci-filter-parity, check-shard-attestation.
  • check-test-completeness.mjsNOT MEASURED (PREREQUISITE NOT MET, exit 3): it grades a saved turbo run test log and the family names it with no argument. Not a red.
  • Reverse verification, on the committed tree, mutation and restore both proven on disk: inverting memberFormOn's verdict reds 8 pins; withholding the provenance write reds the 4 key-set invariant pins. Restored blob 56b1f118… matches HEAD and git diff HEAD is empty in both cases.

All of the above ran at 8087897a7, the branch head.

No changeset: this edits a CI-internal tooling script and its README and publishes nothing from any package — the skip-changeset case the workflow prescribes.


Generated by Claude Code

Each row of the docs-drift advisory now names the declaration that minted its
anchor, so a reader can judge the row instead of guessing:
organizationId (symbol, a field of interface MetaOverlayCacheKey)
userActions (symbol, a field of const object ObjectSchemaBase)
Those two are the same syntactic form — `name:` inside an object or interface —
and that is the finding this implements: one is the noisiest anchor the tool
mints, the other the most valuable, and no syntactic or per-package test
separates them. The declaring container does, and no row printed it before.
Publication only. No guard, no threshold and no bridge hop reads the new field;
the emitted anchor set is byte-identical before and after, with only the
rendering differing. `--self-test` pins the provenance key set as exactly the
anchor set in both directions, so a later change cannot start deciding with it
without going red.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
@github-actionsgithub-actionsBot added size/m documentation Improvements or additions to documentation labels Aug 31, 2026
@os-project-manageros-project-manager added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 31, 2026 — with Claude
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs.

What this run could not see
  • 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 — 0 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 787d757405db4f3ebbc6ca811948af4438a3fe7apackageMentionDocs.

@os-project-manager
os-project-manager marked this pull request as ready for review August 31, 2026 09:15
@os-project-manager
os-project-manager added this pull request to the merge queueAug 31, 2026
Merged via the queue into main with commit b3d8a75Aug 31, 2026
35 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-12824-anchor-provenance branch August 31, 2026 09:40
os-project-manager pushed a commit that referenced this pull request Aug 31, 2026
… predicate pair
`isCodeShaped` has called `OS_MODE` an identifier since the shape guard was written
(it is pinned as `SCREAMING_SNAKE` in the self-test's shape cases), while
`literalAnchorsFromLines` accepted only three lowercase-initial shapes and so declined
to mint any anchor from it. One predicate in the pair called the token an identifier,
the other silently called it prose, and nothing reported the split.
Measured both ways over the 60 most recent `packages/**` commits, attributing every
added row to the declaration that minted it (the provenance published by #13738):
rows 374 -> 383 (+2.4%), zero rows lost, `overbroadAnchors` unchanged at 8, and only
2 of 60 runs moved. On `b6d3d76b5` the advisory went from 0 to 2 of the 3 docs pages
that commit edited itself.
Also pins the agreement as an invariant, and pins the disagreements that remain as
deliberate: delegating the literal test to `isCodeShaped` was measured at +9.1% and
admits quoted sentence fragments.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 1, 2026
… predicate pair (objectstack-ai#13782)
`isCodeShaped` has called `OS_MODE` an identifier since the shape guard was written
(it is pinned as `SCREAMING_SNAKE` in the self-test's shape cases), while
`literalAnchorsFromLines` accepted only three lowercase-initial shapes and so declined
to mint any anchor from it. One predicate in the pair called the token an identifier,
the other silently called it prose, and nothing reported the split.
Measured both ways over the 60 most recent `packages/**` commits, attributing every
added row to the declaration that minted it (the provenance published by objectstack-ai#13738):
rows 374 -> 383 (+2.4%), zero rows lost, `overbroadAnchors` unchanged at 8, and only
2 of 60 runs moved. On `b6d3d76b5` the advisory went from 0 to 2 of the 3 docs pages
that commit edited itself.
Also pins the agreement as an invariant, and pins the disagreements that remain as
deliberate: delegating the literal test to `isCodeShaped` was measured at +9.1% and
admits quoted sentence fragments.
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
Co-authored-by: Claude <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

1 participant

@os-project-manager
, '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

docs-audit: state where every emitted anchor came from - #13738

Merged
os-project-manager merged 1 commit into
mainfrom
claude/issue-12824-anchor-provenance
Aug 31, 2026
Merged

docs-audit: state where every emitted anchor came from#13738
os-project-manager merged 1 commit into
mainfrom
claude/issue-12824-anchor-provenance

Conversation

@os-project-manager

Copy link
Copy Markdown
Collaborator

Fixes#12824

Implements option C of the maintainer's ruling of 2026-08-31 (verbatim 「同意」), and only C: every row the docs-drift advisory emits now states where its anchor came from — the member and the declaration enclosing it — so a reader can judge the row instead of guessing.

before: organizationId (symbol)
after: organizationId (symbol, a field of interface MetaOverlayCacheKey)
after: userActions (symbol, a field of const object ObjectSchemaBase)

Those last two are the same syntactic formname: inside an object or interface — and that is the whole finding. One is a field of an internal cache struct that lands 10 pages documenting an unrelated organizationId; the other is the canonical authorable key whose row is the best this tool produces. The card's three disproven discriminators (syntactic form, declaring package, property name against the authorable registry) all fail to separate them. The declaring container separates them, and no row printed it before.

Scope — what this deliberately is not

Proof that the anchor set is unchanged — measured, not asserted

Both arms of affected-docs.mjs (origin/main and this branch) were run against the same tree at the same commit over 100 consecutive main commits touching packages/, comparing the full --json output:

commits replayed100
rows, base arm550
rows, this branch550
anchors emitted722
anchors carrying provenance722 (100%)
mismatches0

Compared per commit: the anchor set as kind + token; the docs list; and the entire JSON document once the two additive fields (anchors[].from, and the clause appended inside each detail[].via string) are removed. All three byte-identical on every commit. Harness and per-commit records are in the dev report on #12824.

The same property is pinned in --self-test rather than left to this one measurement: the provenance key set is asserted to be exactly the anchor set, in both directions — never a superset (a name nothing minted) and never a subset (an anchor with no clause). A future change cannot start deciding with from without going red.

The ruling's own example, end to end

Driven through the real pipeline by committing a one-line widening at each declaration site and running both arms at HEAD^:

editbase rowthis branchrows
organizationId?: string on MetaOverlayCacheKeyorganizationId (symbol)organizationId (symbol, a field of interface MetaOverlayCacheKey)10 → 10
userActions on ObjectSchemaBaseuserActions (symbol)userActions (symbol, a field of const object ObjectSchemaBase)5 → 5

content/docs/data-modeling/objects.mdx — the true positive option B drops — is present in both arms, unchanged.

Every anchor kind names its origin

symbol is the card's subject, but the card also records the bridge amplification it feeds (publishItem (sdk) and /:type/:name/publish (route) on a diff touching no state machine), and that is only judgeable when the row names the hop it rode. From a real run on this tree:

sdk meta.getView the route ledger binds it to GET /api/v1/ui/view/:object/:type
route /view/:object/:type bridged from symbol enforceEnvironmentOwnership — its registrar handler names it
route /api/v1/ui/view/:object/:type a path literal in RestServer
symbol computeExecCtx a method of class RestServer

Measured cost

The advisory comment gets longer, which the ruling accepted implicitly (C "does not reduce the row count"). Quantified on the worst run in the 100-commit population: the rendered row block grows 1265 → 2312 bytes (x1.8) at the 15-row display cap. That worst case is command anchors, whose clause was first drafted as the CLI command id ID, read off FILE — a restatement of the token — and is now just read off FILE, which took the worst single row from 2201 to 1549 chars. Median row is 76 chars, p90 222.

⛔ The display-cut problem itself is not addressed here and survives, exactly as the card says of option C.

Verification

  • node scripts/docs-audit/affected-docs.mjs --self-test503 cases pass (487 before; +16 pins for the provenance derivation, the member-form classifier including its degraded answer, and the key-set invariant).
  • node scripts/docs-audit/check-affected-docs.mjs — exit 0 (self-test + --bridge-coverage; discovered population unchanged).
  • node scripts/docs-audit/check-drift-comment.mjs56 cases pass across 5 fixture diffs. This runs the workflow's real comment script against real mapper output, so it is also the check that the added clause does not break the renderer.
  • pnpm lint (repo-wide, eslint . --no-inline-config) — exit 0.
  • Derived gate family (node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack), all exit 0: check:nul-bytes, check:docs-audit-scope, check:watch-hint-literal, check:agent-test-spelling, check:entry-guard, check:parse-guard, check:cli-command-ids, check:pnpm-filter-targets, check:bash32-floor, check:pm-governed-merges, check:cross-package-test-inputs, check-self-test-wired, check-ci-filter-parity, check-shard-attestation.
  • check-test-completeness.mjsNOT MEASURED (PREREQUISITE NOT MET, exit 3): it grades a saved turbo run test log and the family names it with no argument. Not a red.
  • Reverse verification, on the committed tree, mutation and restore both proven on disk: inverting memberFormOn's verdict reds 8 pins; withholding the provenance write reds the 4 key-set invariant pins. Restored blob 56b1f118… matches HEAD and git diff HEAD is empty in both cases.

All of the above ran at 8087897a7, the branch head.

No changeset: this edits a CI-internal tooling script and its README and publishes nothing from any package — the skip-changeset case the workflow prescribes.


Generated by Claude Code

Each row of the docs-drift advisory now names the declaration that minted its
anchor, so a reader can judge the row instead of guessing:
organizationId (symbol, a field of interface MetaOverlayCacheKey)
userActions (symbol, a field of const object ObjectSchemaBase)
Those two are the same syntactic form — `name:` inside an object or interface —
and that is the finding this implements: one is the noisiest anchor the tool
mints, the other the most valuable, and no syntactic or per-package test
separates them. The declaring container does, and no row printed it before.
Publication only. No guard, no threshold and no bridge hop reads the new field;
the emitted anchor set is byte-identical before and after, with only the
rendering differing. `--self-test` pins the provenance key set as exactly the
anchor set in both directions, so a later change cannot start deciding with it
without going red.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
@github-actionsgithub-actionsBot added size/m documentation Improvements or additions to documentation labels Aug 31, 2026
@os-project-manageros-project-manager added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 31, 2026 — with Claude
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs.

What this run could not see
  • 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 — 0 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 787d757405db4f3ebbc6ca811948af4438a3fe7apackageMentionDocs.

@os-project-manager
os-project-manager marked this pull request as ready for review August 31, 2026 09:15
@os-project-manager
os-project-manager added this pull request to the merge queueAug 31, 2026
Merged via the queue into main with commit b3d8a75Aug 31, 2026
35 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-12824-anchor-provenance branch August 31, 2026 09:40
os-project-manager pushed a commit that referenced this pull request Aug 31, 2026
… predicate pair
`isCodeShaped` has called `OS_MODE` an identifier since the shape guard was written
(it is pinned as `SCREAMING_SNAKE` in the self-test's shape cases), while
`literalAnchorsFromLines` accepted only three lowercase-initial shapes and so declined
to mint any anchor from it. One predicate in the pair called the token an identifier,
the other silently called it prose, and nothing reported the split.
Measured both ways over the 60 most recent `packages/**` commits, attributing every
added row to the declaration that minted it (the provenance published by #13738):
rows 374 -> 383 (+2.4%), zero rows lost, `overbroadAnchors` unchanged at 8, and only
2 of 60 runs moved. On `b6d3d76b5` the advisory went from 0 to 2 of the 3 docs pages
that commit edited itself.
Also pins the agreement as an invariant, and pins the disagreements that remain as
deliberate: delegating the literal test to `isCodeShaped` was measured at +9.1% and
admits quoted sentence fragments.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 1, 2026
… predicate pair (objectstack-ai#13782)
`isCodeShaped` has called `OS_MODE` an identifier since the shape guard was written
(it is pinned as `SCREAMING_SNAKE` in the self-test's shape cases), while
`literalAnchorsFromLines` accepted only three lowercase-initial shapes and so declined
to mint any anchor from it. One predicate in the pair called the token an identifier,
the other silently called it prose, and nothing reported the split.
Measured both ways over the 60 most recent `packages/**` commits, attributing every
added row to the declaration that minted it (the provenance published by objectstack-ai#13738):
rows 374 -> 383 (+2.4%), zero rows lost, `overbroadAnchors` unchanged at 8, and only
2 of 60 runs moved. On `b6d3d76b5` the advisory went from 0 to 2 of the 3 docs pages
that commit edited itself.
Also pins the agreement as an invariant, and pins the disagreements that remain as
deliberate: delegating the literal test to `isCodeShaped` was measured at +9.1% and
admits quoted sentence fragments.
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
Co-authored-by: Claude <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

1 participant

@os-project-manager
, '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

docs-audit: state where every emitted anchor came from - #13738

Merged
os-project-manager merged 1 commit into
mainfrom
claude/issue-12824-anchor-provenance
Aug 31, 2026
Merged

docs-audit: state where every emitted anchor came from#13738
os-project-manager merged 1 commit into
mainfrom
claude/issue-12824-anchor-provenance

Conversation

@os-project-manager

Copy link
Copy Markdown
Collaborator

Fixes#12824

Implements option C of the maintainer's ruling of 2026-08-31 (verbatim 「同意」), and only C: every row the docs-drift advisory emits now states where its anchor came from — the member and the declaration enclosing it — so a reader can judge the row instead of guessing.

before: organizationId (symbol)
after: organizationId (symbol, a field of interface MetaOverlayCacheKey)
after: userActions (symbol, a field of const object ObjectSchemaBase)

Those last two are the same syntactic formname: inside an object or interface — and that is the whole finding. One is a field of an internal cache struct that lands 10 pages documenting an unrelated organizationId; the other is the canonical authorable key whose row is the best this tool produces. The card's three disproven discriminators (syntactic form, declaring package, property name against the authorable registry) all fail to separate them. The declaring container separates them, and no row printed it before.

Scope — what this deliberately is not

Proof that the anchor set is unchanged — measured, not asserted

Both arms of affected-docs.mjs (origin/main and this branch) were run against the same tree at the same commit over 100 consecutive main commits touching packages/, comparing the full --json output:

commits replayed100
rows, base arm550
rows, this branch550
anchors emitted722
anchors carrying provenance722 (100%)
mismatches0

Compared per commit: the anchor set as kind + token; the docs list; and the entire JSON document once the two additive fields (anchors[].from, and the clause appended inside each detail[].via string) are removed. All three byte-identical on every commit. Harness and per-commit records are in the dev report on #12824.

The same property is pinned in --self-test rather than left to this one measurement: the provenance key set is asserted to be exactly the anchor set, in both directions — never a superset (a name nothing minted) and never a subset (an anchor with no clause). A future change cannot start deciding with from without going red.

The ruling's own example, end to end

Driven through the real pipeline by committing a one-line widening at each declaration site and running both arms at HEAD^:

editbase rowthis branchrows
organizationId?: string on MetaOverlayCacheKeyorganizationId (symbol)organizationId (symbol, a field of interface MetaOverlayCacheKey)10 → 10
userActions on ObjectSchemaBaseuserActions (symbol)userActions (symbol, a field of const object ObjectSchemaBase)5 → 5

content/docs/data-modeling/objects.mdx — the true positive option B drops — is present in both arms, unchanged.

Every anchor kind names its origin

symbol is the card's subject, but the card also records the bridge amplification it feeds (publishItem (sdk) and /:type/:name/publish (route) on a diff touching no state machine), and that is only judgeable when the row names the hop it rode. From a real run on this tree:

sdk meta.getView the route ledger binds it to GET /api/v1/ui/view/:object/:type
route /view/:object/:type bridged from symbol enforceEnvironmentOwnership — its registrar handler names it
route /api/v1/ui/view/:object/:type a path literal in RestServer
symbol computeExecCtx a method of class RestServer

Measured cost

The advisory comment gets longer, which the ruling accepted implicitly (C "does not reduce the row count"). Quantified on the worst run in the 100-commit population: the rendered row block grows 1265 → 2312 bytes (x1.8) at the 15-row display cap. That worst case is command anchors, whose clause was first drafted as the CLI command id ID, read off FILE — a restatement of the token — and is now just read off FILE, which took the worst single row from 2201 to 1549 chars. Median row is 76 chars, p90 222.

⛔ The display-cut problem itself is not addressed here and survives, exactly as the card says of option C.

Verification

  • node scripts/docs-audit/affected-docs.mjs --self-test503 cases pass (487 before; +16 pins for the provenance derivation, the member-form classifier including its degraded answer, and the key-set invariant).
  • node scripts/docs-audit/check-affected-docs.mjs — exit 0 (self-test + --bridge-coverage; discovered population unchanged).
  • node scripts/docs-audit/check-drift-comment.mjs56 cases pass across 5 fixture diffs. This runs the workflow's real comment script against real mapper output, so it is also the check that the added clause does not break the renderer.
  • pnpm lint (repo-wide, eslint . --no-inline-config) — exit 0.
  • Derived gate family (node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack), all exit 0: check:nul-bytes, check:docs-audit-scope, check:watch-hint-literal, check:agent-test-spelling, check:entry-guard, check:parse-guard, check:cli-command-ids, check:pnpm-filter-targets, check:bash32-floor, check:pm-governed-merges, check:cross-package-test-inputs, check-self-test-wired, check-ci-filter-parity, check-shard-attestation.
  • check-test-completeness.mjsNOT MEASURED (PREREQUISITE NOT MET, exit 3): it grades a saved turbo run test log and the family names it with no argument. Not a red.
  • Reverse verification, on the committed tree, mutation and restore both proven on disk: inverting memberFormOn's verdict reds 8 pins; withholding the provenance write reds the 4 key-set invariant pins. Restored blob 56b1f118… matches HEAD and git diff HEAD is empty in both cases.

All of the above ran at 8087897a7, the branch head.

No changeset: this edits a CI-internal tooling script and its README and publishes nothing from any package — the skip-changeset case the workflow prescribes.


Generated by Claude Code

Each row of the docs-drift advisory now names the declaration that minted its
anchor, so a reader can judge the row instead of guessing:
organizationId (symbol, a field of interface MetaOverlayCacheKey)
userActions (symbol, a field of const object ObjectSchemaBase)
Those two are the same syntactic form — `name:` inside an object or interface —
and that is the finding this implements: one is the noisiest anchor the tool
mints, the other the most valuable, and no syntactic or per-package test
separates them. The declaring container does, and no row printed it before.
Publication only. No guard, no threshold and no bridge hop reads the new field;
the emitted anchor set is byte-identical before and after, with only the
rendering differing. `--self-test` pins the provenance key set as exactly the
anchor set in both directions, so a later change cannot start deciding with it
without going red.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
@github-actionsgithub-actionsBot added size/m documentation Improvements or additions to documentation labels Aug 31, 2026
@os-project-manageros-project-manager added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 31, 2026 — with Claude
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs.

What this run could not see
  • 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 — 0 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 787d757405db4f3ebbc6ca811948af4438a3fe7apackageMentionDocs.

@os-project-manager
os-project-manager marked this pull request as ready for review August 31, 2026 09:15
@os-project-manager
os-project-manager added this pull request to the merge queueAug 31, 2026
Merged via the queue into main with commit b3d8a75Aug 31, 2026
35 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-12824-anchor-provenance branch August 31, 2026 09:40
os-project-manager pushed a commit that referenced this pull request Aug 31, 2026
… predicate pair
`isCodeShaped` has called `OS_MODE` an identifier since the shape guard was written
(it is pinned as `SCREAMING_SNAKE` in the self-test's shape cases), while
`literalAnchorsFromLines` accepted only three lowercase-initial shapes and so declined
to mint any anchor from it. One predicate in the pair called the token an identifier,
the other silently called it prose, and nothing reported the split.
Measured both ways over the 60 most recent `packages/**` commits, attributing every
added row to the declaration that minted it (the provenance published by #13738):
rows 374 -> 383 (+2.4%), zero rows lost, `overbroadAnchors` unchanged at 8, and only
2 of 60 runs moved. On `b6d3d76b5` the advisory went from 0 to 2 of the 3 docs pages
that commit edited itself.
Also pins the agreement as an invariant, and pins the disagreements that remain as
deliberate: delegating the literal test to `isCodeShaped` was measured at +9.1% and
admits quoted sentence fragments.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 1, 2026
… predicate pair (objectstack-ai#13782)
`isCodeShaped` has called `OS_MODE` an identifier since the shape guard was written
(it is pinned as `SCREAMING_SNAKE` in the self-test's shape cases), while
`literalAnchorsFromLines` accepted only three lowercase-initial shapes and so declined
to mint any anchor from it. One predicate in the pair called the token an identifier,
the other silently called it prose, and nothing reported the split.
Measured both ways over the 60 most recent `packages/**` commits, attributing every
added row to the declaration that minted it (the provenance published by objectstack-ai#13738):
rows 374 -> 383 (+2.4%), zero rows lost, `overbroadAnchors` unchanged at 8, and only
2 of 60 runs moved. On `b6d3d76b5` the advisory went from 0 to 2 of the 3 docs pages
that commit edited itself.
Also pins the agreement as an invariant, and pins the disagreements that remain as
deliberate: delegating the literal test to `isCodeShaped` was measured at +9.1% and
admits quoted sentence fragments.
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
Co-authored-by: Claude <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

1 participant

@os-project-manager
, '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

docs-audit: state where every emitted anchor came from - #13738

Merged
os-project-manager merged 1 commit into
mainfrom
claude/issue-12824-anchor-provenance
Aug 31, 2026
Merged

docs-audit: state where every emitted anchor came from#13738
os-project-manager merged 1 commit into
mainfrom
claude/issue-12824-anchor-provenance

Conversation

@os-project-manager

Copy link
Copy Markdown
Collaborator

Fixes#12824

Implements option C of the maintainer's ruling of 2026-08-31 (verbatim 「同意」), and only C: every row the docs-drift advisory emits now states where its anchor came from — the member and the declaration enclosing it — so a reader can judge the row instead of guessing.

before: organizationId (symbol)
after: organizationId (symbol, a field of interface MetaOverlayCacheKey)
after: userActions (symbol, a field of const object ObjectSchemaBase)

Those last two are the same syntactic formname: inside an object or interface — and that is the whole finding. One is a field of an internal cache struct that lands 10 pages documenting an unrelated organizationId; the other is the canonical authorable key whose row is the best this tool produces. The card's three disproven discriminators (syntactic form, declaring package, property name against the authorable registry) all fail to separate them. The declaring container separates them, and no row printed it before.

Scope — what this deliberately is not

Proof that the anchor set is unchanged — measured, not asserted

Both arms of affected-docs.mjs (origin/main and this branch) were run against the same tree at the same commit over 100 consecutive main commits touching packages/, comparing the full --json output:

commits replayed100
rows, base arm550
rows, this branch550
anchors emitted722
anchors carrying provenance722 (100%)
mismatches0

Compared per commit: the anchor set as kind + token; the docs list; and the entire JSON document once the two additive fields (anchors[].from, and the clause appended inside each detail[].via string) are removed. All three byte-identical on every commit. Harness and per-commit records are in the dev report on #12824.

The same property is pinned in --self-test rather than left to this one measurement: the provenance key set is asserted to be exactly the anchor set, in both directions — never a superset (a name nothing minted) and never a subset (an anchor with no clause). A future change cannot start deciding with from without going red.

The ruling's own example, end to end

Driven through the real pipeline by committing a one-line widening at each declaration site and running both arms at HEAD^:

editbase rowthis branchrows
organizationId?: string on MetaOverlayCacheKeyorganizationId (symbol)organizationId (symbol, a field of interface MetaOverlayCacheKey)10 → 10
userActions on ObjectSchemaBaseuserActions (symbol)userActions (symbol, a field of const object ObjectSchemaBase)5 → 5

content/docs/data-modeling/objects.mdx — the true positive option B drops — is present in both arms, unchanged.

Every anchor kind names its origin

symbol is the card's subject, but the card also records the bridge amplification it feeds (publishItem (sdk) and /:type/:name/publish (route) on a diff touching no state machine), and that is only judgeable when the row names the hop it rode. From a real run on this tree:

sdk meta.getView the route ledger binds it to GET /api/v1/ui/view/:object/:type
route /view/:object/:type bridged from symbol enforceEnvironmentOwnership — its registrar handler names it
route /api/v1/ui/view/:object/:type a path literal in RestServer
symbol computeExecCtx a method of class RestServer

Measured cost

The advisory comment gets longer, which the ruling accepted implicitly (C "does not reduce the row count"). Quantified on the worst run in the 100-commit population: the rendered row block grows 1265 → 2312 bytes (x1.8) at the 15-row display cap. That worst case is command anchors, whose clause was first drafted as the CLI command id ID, read off FILE — a restatement of the token — and is now just read off FILE, which took the worst single row from 2201 to 1549 chars. Median row is 76 chars, p90 222.

⛔ The display-cut problem itself is not addressed here and survives, exactly as the card says of option C.

Verification

  • node scripts/docs-audit/affected-docs.mjs --self-test503 cases pass (487 before; +16 pins for the provenance derivation, the member-form classifier including its degraded answer, and the key-set invariant).
  • node scripts/docs-audit/check-affected-docs.mjs — exit 0 (self-test + --bridge-coverage; discovered population unchanged).
  • node scripts/docs-audit/check-drift-comment.mjs56 cases pass across 5 fixture diffs. This runs the workflow's real comment script against real mapper output, so it is also the check that the added clause does not break the renderer.
  • pnpm lint (repo-wide, eslint . --no-inline-config) — exit 0.
  • Derived gate family (node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack), all exit 0: check:nul-bytes, check:docs-audit-scope, check:watch-hint-literal, check:agent-test-spelling, check:entry-guard, check:parse-guard, check:cli-command-ids, check:pnpm-filter-targets, check:bash32-floor, check:pm-governed-merges, check:cross-package-test-inputs, check-self-test-wired, check-ci-filter-parity, check-shard-attestation.
  • check-test-completeness.mjsNOT MEASURED (PREREQUISITE NOT MET, exit 3): it grades a saved turbo run test log and the family names it with no argument. Not a red.
  • Reverse verification, on the committed tree, mutation and restore both proven on disk: inverting memberFormOn's verdict reds 8 pins; withholding the provenance write reds the 4 key-set invariant pins. Restored blob 56b1f118… matches HEAD and git diff HEAD is empty in both cases.

All of the above ran at 8087897a7, the branch head.

No changeset: this edits a CI-internal tooling script and its README and publishes nothing from any package — the skip-changeset case the workflow prescribes.


Generated by Claude Code

Each row of the docs-drift advisory now names the declaration that minted its
anchor, so a reader can judge the row instead of guessing:
organizationId (symbol, a field of interface MetaOverlayCacheKey)
userActions (symbol, a field of const object ObjectSchemaBase)
Those two are the same syntactic form — `name:` inside an object or interface —
and that is the finding this implements: one is the noisiest anchor the tool
mints, the other the most valuable, and no syntactic or per-package test
separates them. The declaring container does, and no row printed it before.
Publication only. No guard, no threshold and no bridge hop reads the new field;
the emitted anchor set is byte-identical before and after, with only the
rendering differing. `--self-test` pins the provenance key set as exactly the
anchor set in both directions, so a later change cannot start deciding with it
without going red.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
@github-actionsgithub-actionsBot added size/m documentation Improvements or additions to documentation labels Aug 31, 2026
@os-project-manageros-project-manager added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 31, 2026 — with Claude
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs.

What this run could not see
  • 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 — 0 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 787d757405db4f3ebbc6ca811948af4438a3fe7apackageMentionDocs.

@os-project-manager
os-project-manager marked this pull request as ready for review August 31, 2026 09:15
@os-project-manager
os-project-manager added this pull request to the merge queueAug 31, 2026
Merged via the queue into main with commit b3d8a75Aug 31, 2026
35 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-12824-anchor-provenance branch August 31, 2026 09:40
os-project-manager pushed a commit that referenced this pull request Aug 31, 2026
… predicate pair
`isCodeShaped` has called `OS_MODE` an identifier since the shape guard was written
(it is pinned as `SCREAMING_SNAKE` in the self-test's shape cases), while
`literalAnchorsFromLines` accepted only three lowercase-initial shapes and so declined
to mint any anchor from it. One predicate in the pair called the token an identifier,
the other silently called it prose, and nothing reported the split.
Measured both ways over the 60 most recent `packages/**` commits, attributing every
added row to the declaration that minted it (the provenance published by #13738):
rows 374 -> 383 (+2.4%), zero rows lost, `overbroadAnchors` unchanged at 8, and only
2 of 60 runs moved. On `b6d3d76b5` the advisory went from 0 to 2 of the 3 docs pages
that commit edited itself.
Also pins the agreement as an invariant, and pins the disagreements that remain as
deliberate: delegating the literal test to `isCodeShaped` was measured at +9.1% and
admits quoted sentence fragments.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 1, 2026
… predicate pair (objectstack-ai#13782)
`isCodeShaped` has called `OS_MODE` an identifier since the shape guard was written
(it is pinned as `SCREAMING_SNAKE` in the self-test's shape cases), while
`literalAnchorsFromLines` accepted only three lowercase-initial shapes and so declined
to mint any anchor from it. One predicate in the pair called the token an identifier,
the other silently called it prose, and nothing reported the split.
Measured both ways over the 60 most recent `packages/**` commits, attributing every
added row to the declaration that minted it (the provenance published by objectstack-ai#13738):
rows 374 -> 383 (+2.4%), zero rows lost, `overbroadAnchors` unchanged at 8, and only
2 of 60 runs moved. On `b6d3d76b5` the advisory went from 0 to 2 of the 3 docs pages
that commit edited itself.
Also pins the agreement as an invariant, and pins the disagreements that remain as
deliberate: delegating the literal test to `isCodeShaped` was measured at +9.1% and
admits quoted sentence fragments.
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
Co-authored-by: Claude <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

1 participant

@os-project-manager
, '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

docs-audit: state where every emitted anchor came from - #13738

Merged
os-project-manager merged 1 commit into
mainfrom
claude/issue-12824-anchor-provenance
Aug 31, 2026
Merged

docs-audit: state where every emitted anchor came from#13738
os-project-manager merged 1 commit into
mainfrom
claude/issue-12824-anchor-provenance

Conversation

@os-project-manager

Copy link
Copy Markdown
Collaborator

Fixes#12824

Implements option C of the maintainer's ruling of 2026-08-31 (verbatim 「同意」), and only C: every row the docs-drift advisory emits now states where its anchor came from — the member and the declaration enclosing it — so a reader can judge the row instead of guessing.

before: organizationId (symbol)
after: organizationId (symbol, a field of interface MetaOverlayCacheKey)
after: userActions (symbol, a field of const object ObjectSchemaBase)

Those last two are the same syntactic formname: inside an object or interface — and that is the whole finding. One is a field of an internal cache struct that lands 10 pages documenting an unrelated organizationId; the other is the canonical authorable key whose row is the best this tool produces. The card's three disproven discriminators (syntactic form, declaring package, property name against the authorable registry) all fail to separate them. The declaring container separates them, and no row printed it before.

Scope — what this deliberately is not

Proof that the anchor set is unchanged — measured, not asserted

Both arms of affected-docs.mjs (origin/main and this branch) were run against the same tree at the same commit over 100 consecutive main commits touching packages/, comparing the full --json output:

commits replayed100
rows, base arm550
rows, this branch550
anchors emitted722
anchors carrying provenance722 (100%)
mismatches0

Compared per commit: the anchor set as kind + token; the docs list; and the entire JSON document once the two additive fields (anchors[].from, and the clause appended inside each detail[].via string) are removed. All three byte-identical on every commit. Harness and per-commit records are in the dev report on #12824.

The same property is pinned in --self-test rather than left to this one measurement: the provenance key set is asserted to be exactly the anchor set, in both directions — never a superset (a name nothing minted) and never a subset (an anchor with no clause). A future change cannot start deciding with from without going red.

The ruling's own example, end to end

Driven through the real pipeline by committing a one-line widening at each declaration site and running both arms at HEAD^:

editbase rowthis branchrows
organizationId?: string on MetaOverlayCacheKeyorganizationId (symbol)organizationId (symbol, a field of interface MetaOverlayCacheKey)10 → 10
userActions on ObjectSchemaBaseuserActions (symbol)userActions (symbol, a field of const object ObjectSchemaBase)5 → 5

content/docs/data-modeling/objects.mdx — the true positive option B drops — is present in both arms, unchanged.

Every anchor kind names its origin

symbol is the card's subject, but the card also records the bridge amplification it feeds (publishItem (sdk) and /:type/:name/publish (route) on a diff touching no state machine), and that is only judgeable when the row names the hop it rode. From a real run on this tree:

sdk meta.getView the route ledger binds it to GET /api/v1/ui/view/:object/:type
route /view/:object/:type bridged from symbol enforceEnvironmentOwnership — its registrar handler names it
route /api/v1/ui/view/:object/:type a path literal in RestServer
symbol computeExecCtx a method of class RestServer

Measured cost

The advisory comment gets longer, which the ruling accepted implicitly (C "does not reduce the row count"). Quantified on the worst run in the 100-commit population: the rendered row block grows 1265 → 2312 bytes (x1.8) at the 15-row display cap. That worst case is command anchors, whose clause was first drafted as the CLI command id ID, read off FILE — a restatement of the token — and is now just read off FILE, which took the worst single row from 2201 to 1549 chars. Median row is 76 chars, p90 222.

⛔ The display-cut problem itself is not addressed here and survives, exactly as the card says of option C.

Verification

  • node scripts/docs-audit/affected-docs.mjs --self-test503 cases pass (487 before; +16 pins for the provenance derivation, the member-form classifier including its degraded answer, and the key-set invariant).
  • node scripts/docs-audit/check-affected-docs.mjs — exit 0 (self-test + --bridge-coverage; discovered population unchanged).
  • node scripts/docs-audit/check-drift-comment.mjs56 cases pass across 5 fixture diffs. This runs the workflow's real comment script against real mapper output, so it is also the check that the added clause does not break the renderer.
  • pnpm lint (repo-wide, eslint . --no-inline-config) — exit 0.
  • Derived gate family (node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack), all exit 0: check:nul-bytes, check:docs-audit-scope, check:watch-hint-literal, check:agent-test-spelling, check:entry-guard, check:parse-guard, check:cli-command-ids, check:pnpm-filter-targets, check:bash32-floor, check:pm-governed-merges, check:cross-package-test-inputs, check-self-test-wired, check-ci-filter-parity, check-shard-attestation.
  • check-test-completeness.mjsNOT MEASURED (PREREQUISITE NOT MET, exit 3): it grades a saved turbo run test log and the family names it with no argument. Not a red.
  • Reverse verification, on the committed tree, mutation and restore both proven on disk: inverting memberFormOn's verdict reds 8 pins; withholding the provenance write reds the 4 key-set invariant pins. Restored blob 56b1f118… matches HEAD and git diff HEAD is empty in both cases.

All of the above ran at 8087897a7, the branch head.

No changeset: this edits a CI-internal tooling script and its README and publishes nothing from any package — the skip-changeset case the workflow prescribes.


Generated by Claude Code

Each row of the docs-drift advisory now names the declaration that minted its
anchor, so a reader can judge the row instead of guessing:
organizationId (symbol, a field of interface MetaOverlayCacheKey)
userActions (symbol, a field of const object ObjectSchemaBase)
Those two are the same syntactic form — `name:` inside an object or interface —
and that is the finding this implements: one is the noisiest anchor the tool
mints, the other the most valuable, and no syntactic or per-package test
separates them. The declaring container does, and no row printed it before.
Publication only. No guard, no threshold and no bridge hop reads the new field;
the emitted anchor set is byte-identical before and after, with only the
rendering differing. `--self-test` pins the provenance key set as exactly the
anchor set in both directions, so a later change cannot start deciding with it
without going red.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
@github-actionsgithub-actionsBot added size/m documentation Improvements or additions to documentation labels Aug 31, 2026
@os-project-manageros-project-manager added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 31, 2026 — with Claude
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs.

What this run could not see
  • 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 — 0 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 787d757405db4f3ebbc6ca811948af4438a3fe7apackageMentionDocs.

@os-project-manager
os-project-manager marked this pull request as ready for review August 31, 2026 09:15
@os-project-manager
os-project-manager added this pull request to the merge queueAug 31, 2026
Merged via the queue into main with commit b3d8a75Aug 31, 2026
35 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-12824-anchor-provenance branch August 31, 2026 09:40
os-project-manager pushed a commit that referenced this pull request Aug 31, 2026
… predicate pair
`isCodeShaped` has called `OS_MODE` an identifier since the shape guard was written
(it is pinned as `SCREAMING_SNAKE` in the self-test's shape cases), while
`literalAnchorsFromLines` accepted only three lowercase-initial shapes and so declined
to mint any anchor from it. One predicate in the pair called the token an identifier,
the other silently called it prose, and nothing reported the split.
Measured both ways over the 60 most recent `packages/**` commits, attributing every
added row to the declaration that minted it (the provenance published by #13738):
rows 374 -> 383 (+2.4%), zero rows lost, `overbroadAnchors` unchanged at 8, and only
2 of 60 runs moved. On `b6d3d76b5` the advisory went from 0 to 2 of the 3 docs pages
that commit edited itself.
Also pins the agreement as an invariant, and pins the disagreements that remain as
deliberate: delegating the literal test to `isCodeShaped` was measured at +9.1% and
admits quoted sentence fragments.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 1, 2026
… predicate pair (objectstack-ai#13782)
`isCodeShaped` has called `OS_MODE` an identifier since the shape guard was written
(it is pinned as `SCREAMING_SNAKE` in the self-test's shape cases), while
`literalAnchorsFromLines` accepted only three lowercase-initial shapes and so declined
to mint any anchor from it. One predicate in the pair called the token an identifier,
the other silently called it prose, and nothing reported the split.
Measured both ways over the 60 most recent `packages/**` commits, attributing every
added row to the declaration that minted it (the provenance published by objectstack-ai#13738):
rows 374 -> 383 (+2.4%), zero rows lost, `overbroadAnchors` unchanged at 8, and only
2 of 60 runs moved. On `b6d3d76b5` the advisory went from 0 to 2 of the 3 docs pages
that commit edited itself.
Also pins the agreement as an invariant, and pins the disagreements that remain as
deliberate: delegating the literal test to `isCodeShaped` was measured at +9.1% and
admits quoted sentence fragments.
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
Co-authored-by: Claude <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

1 participant

@os-project-manager
, '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

docs-audit: state where every emitted anchor came from - #13738

Merged
os-project-manager merged 1 commit into
mainfrom
claude/issue-12824-anchor-provenance
Aug 31, 2026
Merged

docs-audit: state where every emitted anchor came from#13738
os-project-manager merged 1 commit into
mainfrom
claude/issue-12824-anchor-provenance

Conversation

@os-project-manager

Copy link
Copy Markdown
Collaborator

Fixes#12824

Implements option C of the maintainer's ruling of 2026-08-31 (verbatim 「同意」), and only C: every row the docs-drift advisory emits now states where its anchor came from — the member and the declaration enclosing it — so a reader can judge the row instead of guessing.

before: organizationId (symbol)
after: organizationId (symbol, a field of interface MetaOverlayCacheKey)
after: userActions (symbol, a field of const object ObjectSchemaBase)

Those last two are the same syntactic formname: inside an object or interface — and that is the whole finding. One is a field of an internal cache struct that lands 10 pages documenting an unrelated organizationId; the other is the canonical authorable key whose row is the best this tool produces. The card's three disproven discriminators (syntactic form, declaring package, property name against the authorable registry) all fail to separate them. The declaring container separates them, and no row printed it before.

Scope — what this deliberately is not

Proof that the anchor set is unchanged — measured, not asserted

Both arms of affected-docs.mjs (origin/main and this branch) were run against the same tree at the same commit over 100 consecutive main commits touching packages/, comparing the full --json output:

commits replayed100
rows, base arm550
rows, this branch550
anchors emitted722
anchors carrying provenance722 (100%)
mismatches0

Compared per commit: the anchor set as kind + token; the docs list; and the entire JSON document once the two additive fields (anchors[].from, and the clause appended inside each detail[].via string) are removed. All three byte-identical on every commit. Harness and per-commit records are in the dev report on #12824.

The same property is pinned in --self-test rather than left to this one measurement: the provenance key set is asserted to be exactly the anchor set, in both directions — never a superset (a name nothing minted) and never a subset (an anchor with no clause). A future change cannot start deciding with from without going red.

The ruling's own example, end to end

Driven through the real pipeline by committing a one-line widening at each declaration site and running both arms at HEAD^:

editbase rowthis branchrows
organizationId?: string on MetaOverlayCacheKeyorganizationId (symbol)organizationId (symbol, a field of interface MetaOverlayCacheKey)10 → 10
userActions on ObjectSchemaBaseuserActions (symbol)userActions (symbol, a field of const object ObjectSchemaBase)5 → 5

content/docs/data-modeling/objects.mdx — the true positive option B drops — is present in both arms, unchanged.

Every anchor kind names its origin

symbol is the card's subject, but the card also records the bridge amplification it feeds (publishItem (sdk) and /:type/:name/publish (route) on a diff touching no state machine), and that is only judgeable when the row names the hop it rode. From a real run on this tree:

sdk meta.getView the route ledger binds it to GET /api/v1/ui/view/:object/:type
route /view/:object/:type bridged from symbol enforceEnvironmentOwnership — its registrar handler names it
route /api/v1/ui/view/:object/:type a path literal in RestServer
symbol computeExecCtx a method of class RestServer

Measured cost

The advisory comment gets longer, which the ruling accepted implicitly (C "does not reduce the row count"). Quantified on the worst run in the 100-commit population: the rendered row block grows 1265 → 2312 bytes (x1.8) at the 15-row display cap. That worst case is command anchors, whose clause was first drafted as the CLI command id ID, read off FILE — a restatement of the token — and is now just read off FILE, which took the worst single row from 2201 to 1549 chars. Median row is 76 chars, p90 222.

⛔ The display-cut problem itself is not addressed here and survives, exactly as the card says of option C.

Verification

  • node scripts/docs-audit/affected-docs.mjs --self-test503 cases pass (487 before; +16 pins for the provenance derivation, the member-form classifier including its degraded answer, and the key-set invariant).
  • node scripts/docs-audit/check-affected-docs.mjs — exit 0 (self-test + --bridge-coverage; discovered population unchanged).
  • node scripts/docs-audit/check-drift-comment.mjs56 cases pass across 5 fixture diffs. This runs the workflow's real comment script against real mapper output, so it is also the check that the added clause does not break the renderer.
  • pnpm lint (repo-wide, eslint . --no-inline-config) — exit 0.
  • Derived gate family (node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack), all exit 0: check:nul-bytes, check:docs-audit-scope, check:watch-hint-literal, check:agent-test-spelling, check:entry-guard, check:parse-guard, check:cli-command-ids, check:pnpm-filter-targets, check:bash32-floor, check:pm-governed-merges, check:cross-package-test-inputs, check-self-test-wired, check-ci-filter-parity, check-shard-attestation.
  • check-test-completeness.mjsNOT MEASURED (PREREQUISITE NOT MET, exit 3): it grades a saved turbo run test log and the family names it with no argument. Not a red.
  • Reverse verification, on the committed tree, mutation and restore both proven on disk: inverting memberFormOn's verdict reds 8 pins; withholding the provenance write reds the 4 key-set invariant pins. Restored blob 56b1f118… matches HEAD and git diff HEAD is empty in both cases.

All of the above ran at 8087897a7, the branch head.

No changeset: this edits a CI-internal tooling script and its README and publishes nothing from any package — the skip-changeset case the workflow prescribes.


Generated by Claude Code

Each row of the docs-drift advisory now names the declaration that minted its
anchor, so a reader can judge the row instead of guessing:
organizationId (symbol, a field of interface MetaOverlayCacheKey)
userActions (symbol, a field of const object ObjectSchemaBase)
Those two are the same syntactic form — `name:` inside an object or interface —
and that is the finding this implements: one is the noisiest anchor the tool
mints, the other the most valuable, and no syntactic or per-package test
separates them. The declaring container does, and no row printed it before.
Publication only. No guard, no threshold and no bridge hop reads the new field;
the emitted anchor set is byte-identical before and after, with only the
rendering differing. `--self-test` pins the provenance key set as exactly the
anchor set in both directions, so a later change cannot start deciding with it
without going red.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
@github-actionsgithub-actionsBot added size/m documentation Improvements or additions to documentation labels Aug 31, 2026
@os-project-manageros-project-manager added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 31, 2026 — with Claude
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs.

What this run could not see
  • 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 — 0 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 787d757405db4f3ebbc6ca811948af4438a3fe7apackageMentionDocs.

@os-project-manager
os-project-manager marked this pull request as ready for review August 31, 2026 09:15
@os-project-manager
os-project-manager added this pull request to the merge queueAug 31, 2026
Merged via the queue into main with commit b3d8a75Aug 31, 2026
35 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-12824-anchor-provenance branch August 31, 2026 09:40
os-project-manager pushed a commit that referenced this pull request Aug 31, 2026
… predicate pair
`isCodeShaped` has called `OS_MODE` an identifier since the shape guard was written
(it is pinned as `SCREAMING_SNAKE` in the self-test's shape cases), while
`literalAnchorsFromLines` accepted only three lowercase-initial shapes and so declined
to mint any anchor from it. One predicate in the pair called the token an identifier,
the other silently called it prose, and nothing reported the split.
Measured both ways over the 60 most recent `packages/**` commits, attributing every
added row to the declaration that minted it (the provenance published by #13738):
rows 374 -> 383 (+2.4%), zero rows lost, `overbroadAnchors` unchanged at 8, and only
2 of 60 runs moved. On `b6d3d76b5` the advisory went from 0 to 2 of the 3 docs pages
that commit edited itself.
Also pins the agreement as an invariant, and pins the disagreements that remain as
deliberate: delegating the literal test to `isCodeShaped` was measured at +9.1% and
admits quoted sentence fragments.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 1, 2026
… predicate pair (objectstack-ai#13782)
`isCodeShaped` has called `OS_MODE` an identifier since the shape guard was written
(it is pinned as `SCREAMING_SNAKE` in the self-test's shape cases), while
`literalAnchorsFromLines` accepted only three lowercase-initial shapes and so declined
to mint any anchor from it. One predicate in the pair called the token an identifier,
the other silently called it prose, and nothing reported the split.
Measured both ways over the 60 most recent `packages/**` commits, attributing every
added row to the declaration that minted it (the provenance published by objectstack-ai#13738):
rows 374 -> 383 (+2.4%), zero rows lost, `overbroadAnchors` unchanged at 8, and only
2 of 60 runs moved. On `b6d3d76b5` the advisory went from 0 to 2 of the 3 docs pages
that commit edited itself.
Also pins the agreement as an invariant, and pins the disagreements that remain as
deliberate: delegating the literal test to `isCodeShaped` was measured at +9.1% and
admits quoted sentence fragments.
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
Co-authored-by: Claude <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

1 participant

@os-project-manager
, '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

docs-audit: state where every emitted anchor came from - #13738

Merged
os-project-manager merged 1 commit into
mainfrom
claude/issue-12824-anchor-provenance
Aug 31, 2026
Merged

docs-audit: state where every emitted anchor came from#13738
os-project-manager merged 1 commit into
mainfrom
claude/issue-12824-anchor-provenance

Conversation

@os-project-manager

Copy link
Copy Markdown
Collaborator

Fixes#12824

Implements option C of the maintainer's ruling of 2026-08-31 (verbatim 「同意」), and only C: every row the docs-drift advisory emits now states where its anchor came from — the member and the declaration enclosing it — so a reader can judge the row instead of guessing.

before: organizationId (symbol)
after: organizationId (symbol, a field of interface MetaOverlayCacheKey)
after: userActions (symbol, a field of const object ObjectSchemaBase)

Those last two are the same syntactic formname: inside an object or interface — and that is the whole finding. One is a field of an internal cache struct that lands 10 pages documenting an unrelated organizationId; the other is the canonical authorable key whose row is the best this tool produces. The card's three disproven discriminators (syntactic form, declaring package, property name against the authorable registry) all fail to separate them. The declaring container separates them, and no row printed it before.

Scope — what this deliberately is not

Proof that the anchor set is unchanged — measured, not asserted

Both arms of affected-docs.mjs (origin/main and this branch) were run against the same tree at the same commit over 100 consecutive main commits touching packages/, comparing the full --json output:

commits replayed100
rows, base arm550
rows, this branch550
anchors emitted722
anchors carrying provenance722 (100%)
mismatches0

Compared per commit: the anchor set as kind + token; the docs list; and the entire JSON document once the two additive fields (anchors[].from, and the clause appended inside each detail[].via string) are removed. All three byte-identical on every commit. Harness and per-commit records are in the dev report on #12824.

The same property is pinned in --self-test rather than left to this one measurement: the provenance key set is asserted to be exactly the anchor set, in both directions — never a superset (a name nothing minted) and never a subset (an anchor with no clause). A future change cannot start deciding with from without going red.

The ruling's own example, end to end

Driven through the real pipeline by committing a one-line widening at each declaration site and running both arms at HEAD^:

editbase rowthis branchrows
organizationId?: string on MetaOverlayCacheKeyorganizationId (symbol)organizationId (symbol, a field of interface MetaOverlayCacheKey)10 → 10
userActions on ObjectSchemaBaseuserActions (symbol)userActions (symbol, a field of const object ObjectSchemaBase)5 → 5

content/docs/data-modeling/objects.mdx — the true positive option B drops — is present in both arms, unchanged.

Every anchor kind names its origin

symbol is the card's subject, but the card also records the bridge amplification it feeds (publishItem (sdk) and /:type/:name/publish (route) on a diff touching no state machine), and that is only judgeable when the row names the hop it rode. From a real run on this tree:

sdk meta.getView the route ledger binds it to GET /api/v1/ui/view/:object/:type
route /view/:object/:type bridged from symbol enforceEnvironmentOwnership — its registrar handler names it
route /api/v1/ui/view/:object/:type a path literal in RestServer
symbol computeExecCtx a method of class RestServer

Measured cost

The advisory comment gets longer, which the ruling accepted implicitly (C "does not reduce the row count"). Quantified on the worst run in the 100-commit population: the rendered row block grows 1265 → 2312 bytes (x1.8) at the 15-row display cap. That worst case is command anchors, whose clause was first drafted as the CLI command id ID, read off FILE — a restatement of the token — and is now just read off FILE, which took the worst single row from 2201 to 1549 chars. Median row is 76 chars, p90 222.

⛔ The display-cut problem itself is not addressed here and survives, exactly as the card says of option C.

Verification

  • node scripts/docs-audit/affected-docs.mjs --self-test503 cases pass (487 before; +16 pins for the provenance derivation, the member-form classifier including its degraded answer, and the key-set invariant).
  • node scripts/docs-audit/check-affected-docs.mjs — exit 0 (self-test + --bridge-coverage; discovered population unchanged).
  • node scripts/docs-audit/check-drift-comment.mjs56 cases pass across 5 fixture diffs. This runs the workflow's real comment script against real mapper output, so it is also the check that the added clause does not break the renderer.
  • pnpm lint (repo-wide, eslint . --no-inline-config) — exit 0.
  • Derived gate family (node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack), all exit 0: check:nul-bytes, check:docs-audit-scope, check:watch-hint-literal, check:agent-test-spelling, check:entry-guard, check:parse-guard, check:cli-command-ids, check:pnpm-filter-targets, check:bash32-floor, check:pm-governed-merges, check:cross-package-test-inputs, check-self-test-wired, check-ci-filter-parity, check-shard-attestation.
  • check-test-completeness.mjsNOT MEASURED (PREREQUISITE NOT MET, exit 3): it grades a saved turbo run test log and the family names it with no argument. Not a red.
  • Reverse verification, on the committed tree, mutation and restore both proven on disk: inverting memberFormOn's verdict reds 8 pins; withholding the provenance write reds the 4 key-set invariant pins. Restored blob 56b1f118… matches HEAD and git diff HEAD is empty in both cases.

All of the above ran at 8087897a7, the branch head.

No changeset: this edits a CI-internal tooling script and its README and publishes nothing from any package — the skip-changeset case the workflow prescribes.


Generated by Claude Code

Each row of the docs-drift advisory now names the declaration that minted its
anchor, so a reader can judge the row instead of guessing:
organizationId (symbol, a field of interface MetaOverlayCacheKey)
userActions (symbol, a field of const object ObjectSchemaBase)
Those two are the same syntactic form — `name:` inside an object or interface —
and that is the finding this implements: one is the noisiest anchor the tool
mints, the other the most valuable, and no syntactic or per-package test
separates them. The declaring container does, and no row printed it before.
Publication only. No guard, no threshold and no bridge hop reads the new field;
the emitted anchor set is byte-identical before and after, with only the
rendering differing. `--self-test` pins the provenance key set as exactly the
anchor set in both directions, so a later change cannot start deciding with it
without going red.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
@github-actionsgithub-actionsBot added size/m documentation Improvements or additions to documentation labels Aug 31, 2026
@os-project-manageros-project-manager added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 31, 2026 — with Claude
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs.

What this run could not see
  • 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 — 0 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 787d757405db4f3ebbc6ca811948af4438a3fe7apackageMentionDocs.

@os-project-manager
os-project-manager marked this pull request as ready for review August 31, 2026 09:15
@os-project-manager
os-project-manager added this pull request to the merge queueAug 31, 2026
Merged via the queue into main with commit b3d8a75Aug 31, 2026
35 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-12824-anchor-provenance branch August 31, 2026 09:40
os-project-manager pushed a commit that referenced this pull request Aug 31, 2026
… predicate pair
`isCodeShaped` has called `OS_MODE` an identifier since the shape guard was written
(it is pinned as `SCREAMING_SNAKE` in the self-test's shape cases), while
`literalAnchorsFromLines` accepted only three lowercase-initial shapes and so declined
to mint any anchor from it. One predicate in the pair called the token an identifier,
the other silently called it prose, and nothing reported the split.
Measured both ways over the 60 most recent `packages/**` commits, attributing every
added row to the declaration that minted it (the provenance published by #13738):
rows 374 -> 383 (+2.4%), zero rows lost, `overbroadAnchors` unchanged at 8, and only
2 of 60 runs moved. On `b6d3d76b5` the advisory went from 0 to 2 of the 3 docs pages
that commit edited itself.
Also pins the agreement as an invariant, and pins the disagreements that remain as
deliberate: delegating the literal test to `isCodeShaped` was measured at +9.1% and
admits quoted sentence fragments.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 1, 2026
… predicate pair (objectstack-ai#13782)
`isCodeShaped` has called `OS_MODE` an identifier since the shape guard was written
(it is pinned as `SCREAMING_SNAKE` in the self-test's shape cases), while
`literalAnchorsFromLines` accepted only three lowercase-initial shapes and so declined
to mint any anchor from it. One predicate in the pair called the token an identifier,
the other silently called it prose, and nothing reported the split.
Measured both ways over the 60 most recent `packages/**` commits, attributing every
added row to the declaration that minted it (the provenance published by objectstack-ai#13738):
rows 374 -> 383 (+2.4%), zero rows lost, `overbroadAnchors` unchanged at 8, and only
2 of 60 runs moved. On `b6d3d76b5` the advisory went from 0 to 2 of the 3 docs pages
that commit edited itself.
Also pins the agreement as an invariant, and pins the disagreements that remain as
deliberate: delegating the literal test to `isCodeShaped` was measured at +9.1% and
admits quoted sentence fragments.
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
Co-authored-by: Claude <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

1 participant

@os-project-manager
, '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

docs-audit: state where every emitted anchor came from - #13738

Merged
os-project-manager merged 1 commit into
mainfrom
claude/issue-12824-anchor-provenance
Aug 31, 2026
Merged

docs-audit: state where every emitted anchor came from#13738
os-project-manager merged 1 commit into
mainfrom
claude/issue-12824-anchor-provenance

Conversation

@os-project-manager

Copy link
Copy Markdown
Collaborator

Fixes#12824

Implements option C of the maintainer's ruling of 2026-08-31 (verbatim 「同意」), and only C: every row the docs-drift advisory emits now states where its anchor came from — the member and the declaration enclosing it — so a reader can judge the row instead of guessing.

before: organizationId (symbol)
after: organizationId (symbol, a field of interface MetaOverlayCacheKey)
after: userActions (symbol, a field of const object ObjectSchemaBase)

Those last two are the same syntactic formname: inside an object or interface — and that is the whole finding. One is a field of an internal cache struct that lands 10 pages documenting an unrelated organizationId; the other is the canonical authorable key whose row is the best this tool produces. The card's three disproven discriminators (syntactic form, declaring package, property name against the authorable registry) all fail to separate them. The declaring container separates them, and no row printed it before.

Scope — what this deliberately is not

Proof that the anchor set is unchanged — measured, not asserted

Both arms of affected-docs.mjs (origin/main and this branch) were run against the same tree at the same commit over 100 consecutive main commits touching packages/, comparing the full --json output:

commits replayed100
rows, base arm550
rows, this branch550
anchors emitted722
anchors carrying provenance722 (100%)
mismatches0

Compared per commit: the anchor set as kind + token; the docs list; and the entire JSON document once the two additive fields (anchors[].from, and the clause appended inside each detail[].via string) are removed. All three byte-identical on every commit. Harness and per-commit records are in the dev report on #12824.

The same property is pinned in --self-test rather than left to this one measurement: the provenance key set is asserted to be exactly the anchor set, in both directions — never a superset (a name nothing minted) and never a subset (an anchor with no clause). A future change cannot start deciding with from without going red.

The ruling's own example, end to end

Driven through the real pipeline by committing a one-line widening at each declaration site and running both arms at HEAD^:

editbase rowthis branchrows
organizationId?: string on MetaOverlayCacheKeyorganizationId (symbol)organizationId (symbol, a field of interface MetaOverlayCacheKey)10 → 10
userActions on ObjectSchemaBaseuserActions (symbol)userActions (symbol, a field of const object ObjectSchemaBase)5 → 5

content/docs/data-modeling/objects.mdx — the true positive option B drops — is present in both arms, unchanged.

Every anchor kind names its origin

symbol is the card's subject, but the card also records the bridge amplification it feeds (publishItem (sdk) and /:type/:name/publish (route) on a diff touching no state machine), and that is only judgeable when the row names the hop it rode. From a real run on this tree:

sdk meta.getView the route ledger binds it to GET /api/v1/ui/view/:object/:type
route /view/:object/:type bridged from symbol enforceEnvironmentOwnership — its registrar handler names it
route /api/v1/ui/view/:object/:type a path literal in RestServer
symbol computeExecCtx a method of class RestServer

Measured cost

The advisory comment gets longer, which the ruling accepted implicitly (C "does not reduce the row count"). Quantified on the worst run in the 100-commit population: the rendered row block grows 1265 → 2312 bytes (x1.8) at the 15-row display cap. That worst case is command anchors, whose clause was first drafted as the CLI command id ID, read off FILE — a restatement of the token — and is now just read off FILE, which took the worst single row from 2201 to 1549 chars. Median row is 76 chars, p90 222.

⛔ The display-cut problem itself is not addressed here and survives, exactly as the card says of option C.

Verification

  • node scripts/docs-audit/affected-docs.mjs --self-test503 cases pass (487 before; +16 pins for the provenance derivation, the member-form classifier including its degraded answer, and the key-set invariant).
  • node scripts/docs-audit/check-affected-docs.mjs — exit 0 (self-test + --bridge-coverage; discovered population unchanged).
  • node scripts/docs-audit/check-drift-comment.mjs56 cases pass across 5 fixture diffs. This runs the workflow's real comment script against real mapper output, so it is also the check that the added clause does not break the renderer.
  • pnpm lint (repo-wide, eslint . --no-inline-config) — exit 0.
  • Derived gate family (node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack), all exit 0: check:nul-bytes, check:docs-audit-scope, check:watch-hint-literal, check:agent-test-spelling, check:entry-guard, check:parse-guard, check:cli-command-ids, check:pnpm-filter-targets, check:bash32-floor, check:pm-governed-merges, check:cross-package-test-inputs, check-self-test-wired, check-ci-filter-parity, check-shard-attestation.
  • check-test-completeness.mjsNOT MEASURED (PREREQUISITE NOT MET, exit 3): it grades a saved turbo run test log and the family names it with no argument. Not a red.
  • Reverse verification, on the committed tree, mutation and restore both proven on disk: inverting memberFormOn's verdict reds 8 pins; withholding the provenance write reds the 4 key-set invariant pins. Restored blob 56b1f118… matches HEAD and git diff HEAD is empty in both cases.

All of the above ran at 8087897a7, the branch head.

No changeset: this edits a CI-internal tooling script and its README and publishes nothing from any package — the skip-changeset case the workflow prescribes.


Generated by Claude Code

Each row of the docs-drift advisory now names the declaration that minted its
anchor, so a reader can judge the row instead of guessing:
organizationId (symbol, a field of interface MetaOverlayCacheKey)
userActions (symbol, a field of const object ObjectSchemaBase)
Those two are the same syntactic form — `name:` inside an object or interface —
and that is the finding this implements: one is the noisiest anchor the tool
mints, the other the most valuable, and no syntactic or per-package test
separates them. The declaring container does, and no row printed it before.
Publication only. No guard, no threshold and no bridge hop reads the new field;
the emitted anchor set is byte-identical before and after, with only the
rendering differing. `--self-test` pins the provenance key set as exactly the
anchor set in both directions, so a later change cannot start deciding with it
without going red.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
@github-actionsgithub-actionsBot added size/m documentation Improvements or additions to documentation labels Aug 31, 2026
@os-project-manageros-project-manager added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 31, 2026 — with Claude
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs.

What this run could not see
  • 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 — 0 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 787d757405db4f3ebbc6ca811948af4438a3fe7apackageMentionDocs.

@os-project-manager
os-project-manager marked this pull request as ready for review August 31, 2026 09:15
@os-project-manager
os-project-manager added this pull request to the merge queueAug 31, 2026
Merged via the queue into main with commit b3d8a75Aug 31, 2026
35 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-12824-anchor-provenance branch August 31, 2026 09:40
os-project-manager pushed a commit that referenced this pull request Aug 31, 2026
… predicate pair
`isCodeShaped` has called `OS_MODE` an identifier since the shape guard was written
(it is pinned as `SCREAMING_SNAKE` in the self-test's shape cases), while
`literalAnchorsFromLines` accepted only three lowercase-initial shapes and so declined
to mint any anchor from it. One predicate in the pair called the token an identifier,
the other silently called it prose, and nothing reported the split.
Measured both ways over the 60 most recent `packages/**` commits, attributing every
added row to the declaration that minted it (the provenance published by #13738):
rows 374 -> 383 (+2.4%), zero rows lost, `overbroadAnchors` unchanged at 8, and only
2 of 60 runs moved. On `b6d3d76b5` the advisory went from 0 to 2 of the 3 docs pages
that commit edited itself.
Also pins the agreement as an invariant, and pins the disagreements that remain as
deliberate: delegating the literal test to `isCodeShaped` was measured at +9.1% and
admits quoted sentence fragments.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 1, 2026
… predicate pair (objectstack-ai#13782)
`isCodeShaped` has called `OS_MODE` an identifier since the shape guard was written
(it is pinned as `SCREAMING_SNAKE` in the self-test's shape cases), while
`literalAnchorsFromLines` accepted only three lowercase-initial shapes and so declined
to mint any anchor from it. One predicate in the pair called the token an identifier,
the other silently called it prose, and nothing reported the split.
Measured both ways over the 60 most recent `packages/**` commits, attributing every
added row to the declaration that minted it (the provenance published by objectstack-ai#13738):
rows 374 -> 383 (+2.4%), zero rows lost, `overbroadAnchors` unchanged at 8, and only
2 of 60 runs moved. On `b6d3d76b5` the advisory went from 0 to 2 of the 3 docs pages
that commit edited itself.
Also pins the agreement as an invariant, and pins the disagreements that remain as
deliberate: delegating the literal test to `isCodeShaped` was measured at +9.1% and
admits quoted sentence fragments.
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
Co-authored-by: Claude <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

1 participant

@os-project-manager