Skip to content

docs(spec): lead the deletion-gate anchor description with what it must NOT be used for - #16199

Merged
huangyiirene merged 1 commit into
mainfrom
claude/issue-14612-authorable-surface-anchor-warning
Sep 6, 2026
Merged

huangyiirene merged 1 commit into
mainfrom
claude/issue-14612-authorable-surface-anchor-warning

Conversation

@huangyiirene

Copy link
Copy Markdown
Collaborator

Fixes #14612

The anchor already called itself an anchor. Triage's sharpest point holds, and I verified it against the file before writing: the description explains what the file is for (the #4650 deletion gate) and never what it must not be used for, so a reader arriving with "is this key authorable today?" meets "a verbatim copy of the keys in authorable-surface/" first and takes it as the live set. This adds the missing warning, not a warning.

What landed

  1. The description now leads with the negative, in the ruled order: ⛔ NOT the live surface — a pinned anchor for the deletion gate; the live surface is authorable-surface/*.json. It then says what the file must not answer, and only afterwards what it is.
  2. One section in the docs page that covers the authorable surface — docs/spec-generated-artifact-sharding.md, which is the only doc naming both artifacts side by side, in the bullet where a reader actually meets the anchor. It answers "which artifact answers is this key authorable today": the live per-category ratchet, never the anchor.
  3. scripts/docs-audit/affected-docs.mjs is recorded as the reference consumer for the union read, in both places, with its --self-test pins on the lag and the [RETIRED] stripping named. Prose says do not use the anchor alone; the reference says here is how to read both correctly.

keys and baseRev are untouched (the JSON diff is 1 line changed, 1 line added). No consumer's input changes. No rename.

Measured, because the dispatch asked me to falsify it

The file is generated, and the edit therefore belongs at its generator — which is why packages/spec/scripts/build-schemas.ts is in this diff and was not in the claimed file surface. SURFACE_BASE_DESCRIPTION there is the only producer of that string, and readCommittedSurfaceBase() byte-compares the committed file against serializeSurfaceBase(doc.baseRev, doc.keys) — so the JSON alone cannot be edited at all. Both halves move together in one reviewed diff, or check:authorable-surface is fatal.

A re-anchor is NOT required for that, and the constant's own doc comment said it was. The canonical form is computed from the committed baseRev and keys, so rewriting both halves to the same bytes leaves the anchor authentic and the gate green. The comment is corrected in place and now records the measurement instead of the assumption. The clause it deliberately deferred — that Written only by gen:schema under-states --update-base — is left deferred, as that comment asks, for the diff that next re-anchors.

Plain gen:schema does not rewrite this file. Since #5358 only --update-base writes it. Evidence: a full @objectstack/spec build (which runs gen:schema) left git status empty on this branch.

The card's 532-key lag has already rotted, so no number is stated anywhere in the new prose. Re-measured at d5d8d50db, four days after the card: 604 keys live-only after annotation stripping, against the card's 532. The gate prints the current delta itself on every run, and the prose points at that instead. The triage comment's "11 shard files" has moved to 14 for the same reason.

Consumer census re-run rather than trusted, and it holds. git grep -l authorable-surface.base gives packages/spec/scripts/build-schemas.ts, check-generated.ts, lib/sharded-artifacts.ts, scripts/pm/dispatch-gates.mjs, scripts/regen-artifacts.mjs, scripts/docs-audit/affected-docs.mjs plus its README, three test files, .gitattributes, packages/spec/package.json, docs/spec-generated-artifact-sharding.md, the spec CHANGELOG and .claude/agents/os-dev.md. No live-decision consumer beyond the union read. dispatch-gates.mjs carries a standing instruction to re-point one self-test case only if this file is removed — it is not, so that pin is unmoved.

Reverse verification — two ablations, both committed-then-mutated, both restored and proven

Expected direction stated before each run, and both turned red:

  1. One-sided edit. Reverting only the committed JSON description (generator constant left new) → check:authorable-surface exit 1, ❌ authorable-surface.base.json does not match its generated form (#5235). Mutation proven on disk by marker counts (injected 1, removed 0) and by a blob hash differing from HEAD; restore proven by the blob returning to the HEAD hash and git diff HEAD empty.
  2. The claim the new prose makes about the reference consumer. Dropping the live-ratchet half of the union in affected-docs.mjs → --self-test exit 1 on exactly the pin the prose names: ✗ a key added since the anchor commit is still authorable — the live ratchet is read too: data/Object:editMode → expected true, got false. Same on-disk and restore proofs.

Gates

Derived against the real diff with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands (3 paths, merge base d5d8d50db, three-dot): 52 families derived, 50 run green, 2 declared narrowing.

--ran reconciliation named 3 UNRUN; one of those, check:doc-formula-expressions, was cleared by building the two packages its own refusal named (turbo run build --filter=@objectstack/formula --filter=@objectstack/lint) and then re-running it — green.

Declared narrowing, CI-owned: check:dts-closure and check:dual-build-cjs-loads both exit 3 — PREREQUISITE NOT MET, not a finding and not a pass: each reads built output and no workspace package has a dist/ in a fresh worktree. Clearing them needs a whole-repo build, which is the farm's run, not this card's.

Also green: pnpm lint whole-repo, 75s, at 4ad3eba2a (no narrowing needed, so none is claimed); pnpm --filter @objectstack/spec typecheck including check:scripts-typecheck, whose tsconfig.scripts.json names build-schemas.ts explicitly; check:pm-dispatch-gates (1511 cases, 513s); affected-docs.mjs --self-test (585 cases).

Tests. Rule for a gate-script edit is its own suites: every *.test.ts in packages/spec/scripts/ naming build-schemas, plus the git grep hits elsewhere in the package — 19 files, 541 tests, all green. The load-bearing one is scripts/build-schemas-check-mode.test.ts, which seeds its fixtures with the description read off the committed file and runs this generator against them, so it is red for any one-sided edit.

Affected-package narrowing, declared. turbo ls --affected from the branch point lists 75 packages, because @objectstack/spec is the dependency-graph root and everything reaches it — not because this diff moved anything they consume. packages/spec's published files array carries neither scripts/ nor authorable-surface.base.json, and no src/ file changed, so no package's inputs moved. Their suites are CI's.

@objectstack/spec has zero workspace dependencies, so the closure build pnpm --filter '@objectstack/spec^...' build matched no projects — reported here because a zero-match pnpm filter exits 0 and reads like a pass.

Changeset

skip-changeset: this PR publishes nothing. packages/spec's files array is dist, json-schema, liveness, prompts, llms.txt, README.md, src/**/*.zod.ts, CHANGELOG.md, api-surface, spec-changes.json — none of the three changed paths is in it, and the generated output that IS published is byte-unchanged (a full spec build left git status empty).

Generated by Claude Code


Generated by Claude Code

…st NOT be used for

`authorable-surface.base.json` already called itself an anchor. The warning
answered the wrong question: it explains what the file is FOR (the #4650 deletion
gate) and never what it must not be USED for, so a reader arriving with "is this
key authorable today?" reads "a verbatim copy of the keys in authorable-surface/"
and takes it as the live set.

The description now leads with the negative, names the live ratchet, and names
`scripts/docs-audit/affected-docs.mjs` as the reference consumer for the union
read. No count is stated: the gate prints the current delta on every run, and a
hard-coded number in a warning about staleness is the same defect one level up.

The string lives in `build-schemas.ts` — the committed file is byte-compared
against `serializeSurfaceBase()`, so both halves move together or the gate is
fatal. `baseRev` and `keys` are untouched, and no consumer's input changes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T6HeZvT9wdSJD1ZxJb5Eno
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation tooling labels Sep 6, 2026
@github-actions

github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/spec/authorable-surface.base.json) — pages documenting those are invisible to this run
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 130 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 33388f9db91938bd95cb7ba0062652de9e2db9d1 → packageMentionDocs.

@huangyiirene huangyiirene added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 6, 2026 — with Claude
@huangyiirene
huangyiirene marked this pull request as ready for review September 6, 2026 11:15
@huangyiirene
huangyiirene added this pull request to the merge queue Sep 6, 2026
Merged via the queue into main with commit 00e6413 Sep 6, 2026
39 of 40 checks passed
@huangyiirene
huangyiirene deleted the claude/issue-14612-authorable-surface-anchor-warning branch September 6, 2026 11:48
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 17, 2026
… of a number (objectstack-ai#17795)

Clause-②: no

Closes objectstack-ai#16200

## What this changes

Two pieces of prose stated the `authorable-surface.base.json` anchor's
lag behind the live
ratchet as a bare count, with no revision and no date attached, so a
reader took each as a
statement about the tree in front of them:

- `scripts/docs-audit/README.md` — "measured on this tree it lagged the
ratchet by 532 keys"
- `scripts/docs-audit/affected-docs.mjs`, the `AUTHORABLE_KEY_SUFFIX_RE`
docblock — the same
sentence, plus "an exact-match lookup would silently drop all 103 of
them"

Both now name **`check:authorable-surface`** as the thing that prints
the current delta on
every run. A pointer to a live instrument replaces a number that decays.

## Why not simply write today's number in

**A citation of a past measurement** ("measured on 2026-09-02 at
`abc1234`: 532") and **a
live claim** ("it lags by 532") are two different kinds of sentence with
two different
repairs. Both carriers were written as the second and read as the first.
Re-measuring and
writing the fresher figure in would manufacture the second kind all over
again — it buys
a few days and reproduces the defect.

## The irony, which is itself the argument for this shape

PR objectstack-ai#16199 landed a warning about a **stale artifact** and deliberately
stated **no count**,
for exactly this reason: *"No count is stated: the gate prints the
current delta on every
run, and a hard-coded number in a warning about staleness is the same
defect one level up."*
The two live counterexamples sat in the very file that PR names as the
**reference consumer**
for the union read. The convention already existed in this codebase, was
chosen deliberately
by the adjacent card, and these two sentences predate it. That is the
whole case for
pointing at the instrument rather than pinning a figure to a rev: the
correct form was
already here, one directory away, in prose about this exact artifact.

## The argument the sentences support is untouched — and stronger

The claim is that the anchor's lag is real, load-bearing and growing. It
survives verbatim,
stated in the terms that cannot rot: `data/Object:editMode` and every
key of
`security/OrgScopingEntitlement` and `api/ProvenanceWaiver` are absent
from the anchor and
present in `authorable-surface/`, and the class grows with every key
authored after
`baseRev`. Those are the same facts `--self-test` pins behaviourally,
against the live
artifacts, on every run.

## Re-measurement (historical citations, not new live claims)

Measured at `952b9c5e59b7` on **2026-09-12**, by reading the same two
artifacts the tool
reads and stripping the annotation with the tool's own
`AUTHORABLE_KEY_SUFFIX_RE`:

| figure | as written | card, `d5d8d50db`, 2026-09-06 | this PR,
`952b9c5e59b7`, 2026-09-12 |
|:--|--:|--:|--:|
| live-ratchet keys absent from the anchor (annotation stripped) | 532 |
604 | **863** |
| annotated (`[RETIRED]`-class) keys the ratchet carries | 103 | 206 |
**209** |
| shard files under `packages/spec/authorable-surface/` | 11 | 14 |
**14** |

Ten days moved the headline figure by 62%. ⛔ None of these numbers is
written into the
tree by this PR; they are here as the evidence for removing them.

**The instrument was verified, not assumed.** `pnpm --filter
@objectstack/spec
check:authorable-surface` prints, on this tree:

```
ℹ️  authorable-surface.base.json trails the baseline at 952b9c5: it mirrors the older
   53ef057, and they differ by 970 key(s) only that baseline has, 1034 only the anchor has
```

Those two counts reproduce byte-exactly from the raw (unstripped) key
sets — `970` live-only,
`1034` anchor-only — so the gate really does answer the question the
deleted sentences were
answering, and it answers it for whatever tree it runs on.

## The third carrier, and why it is in this diff and not a follow-up

The `--self-test` battery carried a **verbatim copy** of the same `103`
sentence beside the
`[RETIRED]` case (`affected-docs.mjs`, the
`buildContainerSurface.isAuthorable` tombstone
check). Leaving it would have left one carrier in the same file still
asserting a figure this
PR removes from the docblock twelve hundred lines up — a contradiction
created by this change
rather than inherited. Mechanical, identical repair, same file, same
gate family, no new
verification surface.

## 验收备注

### Reverse-read: other bare numbers of the same kind in
`scripts/docs-audit/`

The discriminating test used: *does the sentence read as current state,
and does the quantity
decay with the tree?* A number bound to a named commit, a named card or
a dated ruling is a
citation of a past measurement and is not this defect.

**One finding, and it is outside this card's scope by triage's own
boundary:** the README
states the hand-written corpus as **178 pages** in five places (lines
~188, ~192, ~493, ~627),
unanchored. `check:docs-audit-scope` prints `195 hand-written doc(s)` on
this tree. Every one
of those sentences sits inside a historical measurement narrative bound
to an episode or a
card (objectstack-ai#4162, objectstack-ai#4851, objectstack-ai#6893, the guard-design round), which is the weaker
reading that keeps
them defensible — but "the anchor derivation reads the same 178-page
corpus the old one did"
is written as present tense. Noted, **not filed**: triage ruled
explicitly that a repo-wide
hunt for hard-coded counts "is a different card with its own
population", and the population
card is the PM's to open, not a rider on this one. Successor: the
`domain:devx` PM seat.

**Zeros, reported as readings rather than as silence:**

- **Other `532` / `103` references anywhere in the repo — zero.** `git
grep` for `532 keys`,
`103 of them`, `lagged the ratchet`, `lagged the live ratchet` over the
whole tree returns
nothing after this diff, so no sibling file quotes the figures these
carriers held.
- **Hard-coded shard count (`11`) in `scripts/docs-audit/` — zero.** The
card's third figure
came from objectstack-ai#14612's triage comment, never from a carrier in this
directory; there was
  nothing here to repair.
- **`"measured on this tree"` live-claim phrasing inside
`scripts/docs-audit/` — two other
sites, both sound.** `affected-docs.mjs` line ~1344 ("zero commands
declare `static topic`,
and the one `static id` (`init.ts` to `'init'`) agrees with its path")
was re-verified
against `packages/cli/src/commands/` and is **still true** — zero
`static topic`, and
`init.ts:912` carries `static override id = 'init'`. It is also a claim
about a *zero* whose
failure direction the docblock names, not a decaying magnitude. Line
~6019 already carries
  its rev (`f5a7f9c88`, 7 warm runs) and is the correct form.
- **Anything this diff falsifies elsewhere in the tree — zero.** No pin,
fixture, gate,
workflow or doc reads these sentences; the repo-wide grep above is the
reading.

Two sites outside `scripts/docs-audit/` use the same "measured on this
tree" phrasing
(`scripts/check-bash32-floor.mjs` ~121, `scripts/pm/dispatch-gates.mjs`
~8307; the second
carries a count, "one of the six"). Out of scope here by the same triage
boundary; they belong
to the population card above.

### Verification

All **34** gate families `scripts/pm/dispatch-gates.mjs` derives for
this change set were run
in this worktree and every one exited `0`; reconciled with `--ran`
carrying exit codes:

```
✓ dispatch-gates --ran: 34 derived famil(ies) accounted for — 34 run, 0 NOT-MEASURED
  (a DERIVED zero — all 34 recorded an exit code and none of them is 3).
```

The load-bearing ones for this diff, with the verdict lines they
printed:

- `pnpm check:docs-audit-scope` — `✓ affected-docs self-test: 585 cases
pass.` /
  `✓ check-audit-scope self-test: 32 cases pass.`
- `node scripts/docs-audit/check-affected-docs.mjs` — exit `0` (this is
the discoverable name
for `affected-docs.mjs --self-test`; no `*.test.ts` in the tree names
`affected-docs`, so
  this wrapper is the script's whole suite)
- `pnpm --filter @objectstack/spec run check:authorable-surface` — exit
`0`, and the source of
  the delta line quoted above
- `pnpm check:nul-bytes` — exit `0`; plus a direct control-character
sweep over both edited
  files, clean

Repo-wide `pnpm lint` was **not** run — it is the CI-owned run, and no
narrowing is claimed
here in its place. No changed file carries executable behaviour.

### Changeset: `skip-changeset`

Measured rather than assumed. `scripts/docs-audit/` is repo tooling: the
root manifest is
`private: true`, and no non-private package's `files[]` names a path
that reaches `scripts/`
(checked across every `packages/**/package.json`; positive control —
`packages/spec`'s
`files[]` resolves to real published paths such as `dist`,
`json-schema`, `api-surface`, so
the zero is a measurement and not an empty scan). Nothing published
moves ⇒ the
`skip-changeset` **label** is applied to this PR.

### Clause-②

`no`. The diff changes explanatory prose in repo tooling only. It
touches no spec schema, no
published `exports`, no wire shape and no authorable key — nothing a
contract review has a
surface to review. The card-side carrier the PM placed reads `no`; this
PR-body carrier agrees
and was self-verified with `readClause2Line()`.

### Governance

No governed surface is touched (`docs/adr/**`, `.claude/**`,
`skills/**`, `AGENTS.md`,
`CLAUDE.md` are all absent from the file list). Left as a **draft** and
**not** armed for
auto-merge — arming is the PM's act.

---
_Generated by [Claude
Code](https://claude.ai/code/session_012GKcPZbMoGq7WPzKLfRBTU)_

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate tooling

Projects

None yet

2 participants