Skip to content

Commit 51dcab8

Browse files
claude[bot]claude
andauthored
docs-audit: qualify data-property anchors by their declaring container against the authorable surface (#14607)
* wip(docs-audit): container-qualified data-property anchors (#13713) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WLJQhde67SeTccsmnBVarV * test(docs-audit): pin the three branches of container qualification (#13713) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WLJQhde67SeTccsmnBVarV * docs(docs-audit): document the container qualification (#13713) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WLJQhde67SeTccsmnBVarV * fix(docs-audit): read the live authorable-surface ratchet, not only its pinned anchor (#13713) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WLJQhde67SeTccsmnBVarV --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 4b9e1f3 commit 51dcab8

2 files changed

Lines changed: 437 additions & 22 deletions

File tree

‎scripts/docs-audit/README.md‎

Lines changed: 72 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ Three anchor kinds, each exact:
4848

4949
| anchor | what it is | how it is derived |
5050
|:--|:--|:--|
51-
| `symbol` | a documentable declaration the diff touched | the top-level declaration, or a member of a top-level **container** (class / interface / type / enum / schema object), enclosing each changed line — on **both** sides of the diff, so a removed export still anchors the pages naming it |
51+
| `symbol` | a documentable declaration the diff touched | the top-level declaration, or a member of a top-level **container** (class / interface / type / enum / schema object), enclosing each changed line — on **both** sides of the diff, so a removed export still anchors the pages naming it. A member that is a **data property** is additionally qualified by its declaring container against the authorable surface (see below) |
5252
| `route` | a wire path the change touched | a path literal on a changed line, plus every route whose **registrar handler** references a changed symbol |
5353
| `sdk` | the client method bound to an anchor route | the declared `route` ⟷ `client` rows in the repo's route ledgers |
5454

@@ -104,12 +104,80 @@ maintainer's decision of 2026-08-31 took this (option C) and ruled OUT filtering
104104
rows. A false positive costs a reader a minute; a false negative ships a falsified page.
105105
So no guard, no threshold and no bridge hop reads `from` — `--self-test` pins that the
106106
provenance key set is exactly the anchor set, in both directions, so a future change
107-
cannot start deciding with it without going red. Container-qualified *discrimination*
108-
lives on #13712 (the spec half) and #13713 (the docs-audit half), and needs a TS-name →
109-
spec-name mapping this does not have.
107+
cannot start deciding with it without going red. Container-qualified *discrimination* is
108+
the separate, later step below (option D), and it decides from the declarations directly,
109+
never by parsing this clause back out.
110+
111+
### A data property is qualified by its declaring container (#13713)
112+
113+
Option D of the same ruling, and the half that *does* move rows. It landed once the spec
114+
side (#13712) published the mapping it needs: `packages/spec/declaration-map/*.json` maps a
115+
**TS declaration name** to a **spec type name** (`ObjectSchemaBase` → `data/Object`), and
116+
the authorable surface is keyed as `container:property` (`data/Object:userActions`). All of
117+
it is generated and covered by `check:generated`; this script only ever reads it, and ⛔
118+
never carries a local mapping table of its own — a container the map lacks is a spec-lane
119+
card, not a local fix.
120+
121+
⚠️ The authorable surface is read as the **union** of `packages/spec/authorable-surface/`
122+
(the live per-category ratchet, read as one set) and
123+
`packages/spec/authorable-surface.base.json` (the artifact #12824 names). The second is an
124+
**anchor**, pinned at a fixed `baseRev` for the deletion gate — measured on this tree it
125+
lagged the ratchet by 532 keys, including `data/Object:editMode` and every key of
126+
`security/OrgScopingEntitlement` and `api/ProvenanceWaiver`. Reading the anchor alone would
127+
suppress anchors on genuinely authorable keys, and that class grows with every key added
128+
after `baseRev`. A union can only ever keep an anchor one source vouches for, never drop
129+
one more. `[RETIRED]` is stripped for the same reason: a tombstoned key still rejects with
130+
an upgrade prescription, so it is still surface a page documents.
131+
132+
For a data property `prop` (`name:` / `name?:` / `name =`) whose most specific enclosing
133+
declaration is the container `C`:
134+
135+
| | `C` resolves to | `C:prop` authorable | verdict |
136+
|:--|:--|:--|:--|
137+
| (a) | `cat/T` | yes | **mint** — today's behaviour, now justified. The row also says so: `a field of const object ObjectSchemaBase, an authorable key of data/Object` |
138+
| (b) | `cat/T` | no | **drop** the property anchor and fall through to the container branch that already existed — no new fallback |
139+
| (c) | nothing, or two different types | — | **mint, unchanged** |
140+
141+
⛔ **(c) must never become "drop".** That is option **B** (blanket prefer-container) by
142+
another name, and it is vetoed on two rounds of measurement: its "zero recall loss" was
143+
measured against a ground truth of 10 of 46 pages, most of which were never in this tool's
144+
corpus at all (the real recall is 48.8%, re-derived), and the rows it drops are the most
145+
valuable ones the tool mints. **Fail toward noise, never toward silence.** A method, a
146+
nested interface, a namespaced type — anything whose winning declaration is not a data
147+
property — is outside the rule entirely.
148+
149+
Two consequences worth stating plainly, because both are easy to mistake for bugs:
150+
151+
- **Both ruled true positives survive, and they survive by different routes.**
152+
`userActions` is a key of `ObjectSchemaBase`, which the map carries, so it is kept by
153+
(a) — `data-modeling/objects.mdx` is still listed and the row now names the spec key.
154+
`schemaMode` is declared on `DatasourceDef`, an **objectql-local** interface the
155+
generated map does **not** carry, so it is kept by (c) — the unmapped keep, not the
156+
authorable lookup. `--self-test` pins that absence as a fact rather than papering over
157+
it; the day the map carries `DatasourceDef`, `data/Datasource:schemaMode` is already an
158+
authorable key and the row survives via (a) instead.
159+
- **The realised reduction is far smaller than the −17.4% the projection quoted**, and for
160+
the same reason: the projection hand-classified the 20 dropping containers, and 18 of
161+
them are internal implementation types (`MetaOverlayCacheKey`, `LocalizationCacheEntry`,
162+
`AUTH_MODEL_TO_PROTOCOL`, …) that the generated map does not carry either. Under the
163+
ruled rule those are case (c) and they **keep**. Only containers the map resolves can
164+
drop. Widening the drop set to reach the projected number would be option B; the number
165+
moves when the *map's coverage* grows, which is spec-lane work.
166+
167+
Every drop is published in `containerQualifiedDrops`, naming the property, its container
168+
and the spec key that failed to be authorable — the same discipline as the two guards
169+
below. A deliberate false negative that no field names is one no reviewer can audit.
170+
171+
If either artifact is unreadable (this script runs in contexts where `packages/spec` may be
172+
absent) every container reads as unmapped, the run behaves exactly as it did before this
173+
existed, and one note goes to stderr. `--self-test` pins that degradation too.
110174

111175
### Two guards, and both publish what they removed
112176

177+
(Three narrowings publish now — the container qualification above reports
178+
`containerQualifiedDrops` on the same terms. These two are the ones that run over the
179+
whole anchor set, in every kind, after it is derived.)
180+
113181
The first build of this derivation was, on some PRs, *noisier* than the proxy it replaced
114182
(134 rows where the old tool gave 26). Two guards fixed that, and both run **before** the
115183
route bridge — a name left in the set does not merely add a noisy row, it mints noisy route

0 commit comments

Comments
 (0)