Repository navigation
Description
Filed out of the review of #13979 (card #13940), by the domain:devx execution PM seat. #13940's grep excluded content/docs/** by construction, so the docs population was never in that card's scope. This card carries it — with the measurement that the obvious fix does not work.
The gate does not see docs
scripts/check-vendor-version-stamps.mjs (shipped in #13979) scans:
export const ROOTS = ['packages', 'scripts', 'apps', 'examples'];content/docs is not there. Running that PR's own detector over the 405 files under content/docs yields 15 sites — 4 historical, 11 unattributed, 0 live-stale.
⭐ The obvious fix is measurably insufficient — this is the point of the card
Adding 'content' to ROOTS would not catch the one site that matters:
content/docs/permissions/authentication.mdx:853— "better-auth declaresaddMemberas a server-only API with no HTTP path of its own — measured on 1.7.1, …". This is a customer-facing attestation naming a version that is no longer installed (the family pins at^1.7.2since deps(auth): move the better-auth family 1.7.1 to 1.7.2 in step, and return @better-auth/scim to the family range #13938). The detector lands it as unattributed, because the package name sits roughly 90 characters earlier in the sentence — pastCLAIM_GAP = 60. It is counted, never judged.content/docs/permissions/authentication.mdx:1217— "the stable1.7.0/1.7.1releases renamed it back toaccountId" — is not seen at all. It is also correctly frozen and must stay exactly as written; it is listed here only to show the two buckets sitting side by side in one file, which is why a coarse detector over this root is dangerous rather than merely weak.
⇒ Wiring content into ROOTS produces the appearance of coverage without the substance — the declared-not-enforced shape this repo treats as worse than no check at all. The work here is a detector that can actually attribute a version to a package across prose distance, not a root-list edit.
Scope
- In scope:
content/docs/**except the exclusion below, plus whatever attribution work the detector needs to make a claim there trustworthy. - ⛔ Excluded by construction:
content/docs/releases/**. Release pages are written centrally at release time and are never edited by a code PR (CLAUDE.md). 3 of the 4 historical docs sites live there — so excluding them is most of the historical bucket, by design.
Provenance
The ruling this card implements, from the #13979 ACCEPT (Q2):
A, as recommended, and the measurement above is why: C is not merely weaker, it is measurably insufficient, and B understates a live customer-facing attestation. Filed, excluding
content/docs/releases/**by construction.
Left unassigned and ungraded — the triage seat grades.
Generated by Claude Code
Activity
claude commentedon Aug 31, 2026
Claim —
domain:devxexecution PM seat (#6023), sessionsession_01Pk26oZ12t5N1hwGW1m1MgC. Branchclaude/issue-13981-vendor-stamp-docs-coverage.⛔⛔ Zone 1 rules out the obvious fix: a delivery that adds
'content'to the gate'sROOTSis rejected, because it is measurably insufficient —authentication.mdx:853still lands unattributed pastCLAIM_GAP = 60, and:1217is not seen at all. The real work is attribution across prose distance.⭐ The two-bucket contrast is the acceptance test, not background:⚠️ A detector that reports
:853must be caught (a customer-facing attestation naming a version no longer installed),:1217must be left alone (correctly frozen history).:1217is worse than no detector — "fixing" it turns a true sentence into a false one.⭐ Two Zone 2 hypotheses are set up so that falsifying me makes the delivery smaller: that raising
CLAIM_GAPis not the fix, and that the existing phrasing test may already separate the two buckets once attribution reaches them — in which case the classifier is untouched and only attribution changes.Hot-file status:
scripts/check-vendor-version-stamps.mjsis free — PR #13979 merged at 22:27:18Z, so triage's hot-file serialization note is satisfied.Generated by Claude Code
Attribute a version to its package across prose distance, and put con…
claude commentedon Sep 1, 2026
os-dev-report
{ "issue": 13981, "status": "done", "branch": "claude/issue-13981-vendor-stamp-docs-coverage", "pr": "https://github.com/objectstack-ai/objectstack/pull/14017", "premise_still_valid": true, "summary": "Rebuilt the attribution half of scripts/check-vendor-version-stamps.mjs and added content/docs as a root with content/docs/releases excluded by construction. Attribution is now sentence scope (a name reaches to the end of its sentence: blank line, bullet, heading, JSX tag, '.' or ';' outside a closing parenthetical) plus nearest-claimant-wins (any package in the tree may claim; vocabulary read from pnpm-lock.yaml, never hand-listed). The classifier gained one new red shape, 'unanchored-measurement': a standing claim resting on a reading taken against a version that no longer installs, with no date or issue anchor. That rule is POSITIONAL (the verb must govern the number), because a sentence-wide substring test reds init.ts:108 where the verb governs a configuration, not the version. ACCEPTANCE TEST PASSES BOTH WAYS: authentication.mdx:853 is caught as unanchored-measurement; :1217 is not even a site, and is additionally pinned to stay green with attribution FORCED on. The gate found 6 stamps the old attribution could not see; all 6 are repaired by scoping or anchoring the sentence (remedy b), never by restamping.", "tests": "Gate families derived with `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack` (no hand-built path list): 46 families, `comm -23` empty, ALL RUN AT FINAL COMMIT 17c431ed9. 43 green (each read from the gate's own verdict line, exit codes captured before any pipe). 3 NOT MEASURED, all PREREQUISITE NOT MET rather than red: check:skill-examples ('packages/client-react/dist holds no .d.ts declarations' - a 38-package build closure), check:dual-build-cjs-loads ('PREREQUISITE NOT MET - this gate reads built output, and some package has no dist/', exit 3), check:type-check-debt ('check-type-check-coverage: PREREQUISITE NOT MET', exit 3). I DID build the 4-package closure (spec/formula/lint/sdui-parser) under the shared verify lock to un-block three others, which then went green: check:doc-formula-expressions, check:doc-security-posture, spec check:docs ('230 generated files in sync'). check:entry-guard green. SELF-TEST: 33 assertions before, 64 after ('check-vendor-version-stamps --self-test: 64 checks pass'). GATE ITSELF: 'check:vendor-version-stamps: OK - 5988 file(s) scanned' (was 5593 code files; +396 docs files, releases excluded). ABLATIONS (3), each with the mutation proven on disk by a grep count and each restored with `git checkout HEAD -- ABSOLUTE_PATH` to a byte-identical blob (git hash-object equal to the HEAD blob, worktree 0 entries; trap on EXIT/INT/TERM with an absolute REPO_ROOT path). No build step is involved - the gate is a plain .mjs run directly, so there is no dist/ leg to rebuild. (1) disable the measurement rule => 'an unanchored measurement naming a stale version is CAUGHT' fails, exit 1. (2) sentence scope => legacy character window => 6 pins fail, led by 'a prose sentence attributes across a wrapped line'. (3) add a coarse 'renamed' live marker => all 3 'frozen history is never reported, even attributed' pins fail. NOTE ON (3): the FIRST version of that pin did NOT fail this ablation - it passed because the site was unattributed, i.e. for a reason unrelated to its phrasing. The pin was rewritten to force attribution on; the reading above is from the rewritten pin. LINT: `npx eslint . --no-inline-config --format json` repo-wide - 5837 files, 0 errors, 0 warnings, so no narrowing to justify. check:nul-bytes green ('scanned 7666 text file(s)... no raw ASCII control bytes'); `grep -naP` over the diff's 7 files finds none. `pnpm --filter @objectstack/cli exec vitest run --maxWorkers=2 test/init.test.ts` (through os-verify-lock.sh): 'Test Files 1 passed (1) / Tests 55 passed (55)'. Every packages/** edit is comment-only, proved mechanically: `git diff -U0 BASE HEAD -- packages/` has zero changed lines outside a comment. GOVERNED SURFACES on the final diff (docs/adr/**, .claude/**, skills/**, AGENTS.md, CLAUDE.md): none touched. CHANGESET: skip-changeset applied ADDITIVELY via REST POST .../issues/14017/labels (HTTP 200) and READ BACK (labels now: ['skip-changeset']) - the PR releases nothing, which is route 2 of the changeset gate's own message.", "mcp_calls": "1 - a single targeted search_issues, used only because REST /search/issues answered 403 for this seat (repo-scoped REST reads and writes all worked: issue read, comments read, label POST, issue create, PR create). Channel switch declared.", "zone_2_measurements": { "A_widening_CLAIM_GAP": "CONFIRMED, and with a mechanism rather than a count. --attribution-sweep (new, mirrors --window-sweep) prints both mechanisms with a CONTESTED column = attributions binding a version whose nearest claimant is another package. At final head: gap 60 -> 133 attributed / 4 contested; gap 90 -> 149 / 9; gap 120 -> 156 / 10; gap 200 -> 166 / 12 / 1 failing; gap 400 -> 173 / 12 / 2 failing; SENTENCE SCOPE -> 141 attributed / 0 contested / 0 failing. On the PRE-REPAIR corpus, gap 120 newly attributed 23 sites and among them bound better-call 1.3.7, better-auth/utils 0.4.2, minimatch 10.2.3 and an internal '0.0.0-polyfill' sentinel to a watched family member none of them is about. The decisive cost is not noise: at gap 200 the legacy mechanism REDS auth-manager.ts:3125 ('That bridge dates from 1.6.20...'), a permanently true historical sentence, by reaching past a boundary and picking up 'installed' from the next sentence - the ':1217 direction', a red whose only repair turns a true sentence into a false one.", "B_phrasing_already_separates": "FALSIFIED, in the direction that makes the delivery BIGGER. Given the attribution it lacked, the shipped classifier returns the SAME verdict for both buckets: 853 -> {verdict:'historical', live:false, anchored:false, drifted:true} and 1217 -> {verdict:'historical', live:false, anchored:false, drifted:true}. ':853' says 'measured on', which is none of LIVE_READING_MARKERS, so attribution alone leaves the must-catch site green. The classifier had to gain the measurement shape; 'attribution alone, classifier untouched' was not available.", "C_census_moved": "NOT MOVED. Re-derived on origin/main with the shipped detector: 15 sites over 405 files under content/docs - 11 unattributed, 4 historical, 0 live-stale. Identical to the card. ONE CORRECTION: all FOUR historical sites live under content/docs/releases (v15.mdx:987, v17.mdx:824, :852, :3896), not three of four. With the exclusion applied the in-scope docs population is EXACTLY ONE SITE - :853 - so this root's whole enforced surface today is the site it was added to judge.", "D_blast_radius": "250 sites over the whole swept population (249 code + 1 docs). 23 rows change against the shipped detector. 6 become failures, and every one is a TRUE instance of the class: 3 from attribution alone (organization-add-member.ts:10, auth-plugin.ts:2559, cli/test/init.test.ts:392 - all live-stale), 2 more from the new classifier shape (admin-has-permission-endpoint.ts:109, auth-manager.ts:2105), plus authentication.mdx:853. The other 17 change attribution without changing a verdict. 5 are FIXES of existing misattributions: the pnpm override keys in init.ts:171-174 and init.test.ts:399 were binding better-auth/utils's version to the LEFT side of the '>', and auth-manager.ts:2220 was reading the ObjectStack release 15.1.0 as a better-auth version. 2 misattributions REMAIN, both harmless (verdict historical, never enforced) and reported not absorbed: init.ts:334 reads 1.4.0 (better-call's) as better-auth's, and template-consistency.test.ts:523 reads 0.5.0 (better-auth/utils's) as better-auth/sso's across four words of prose. A stronger span rule that removes both WAS implemented and then REVERTED: it removed 5 misattributions that were all already historical and cost 1 genuine live-stale catch (init.test.ts:392). The failing set is what the gate decides, so that trade is backwards." }, "repairs_made": [ "content/docs/permissions/authentication.mdx:853 - 'measured on 1.7.1' -> 'measured on the then-installed 1.7.1' (+ paragraph re-wrap). THE ONE SCOPE CALL - see open_questions.", "packages/plugins/plugin-auth/src/organization-add-member.ts:10 - 'the installed 1.7.1' -> 'the then-installed 1.7.1' (comment)", "packages/plugins/plugin-auth/src/auth-plugin.ts:2559 - same scoping (comment)", "packages/plugins/plugin-auth/src/admin-has-permission-endpoint.ts:109 - 'Measured on better-auth 1.7.1' -> 'Measured on the then-installed better-auth 1.7.1' (comment)", "packages/plugins/plugin-auth/src/auth-manager.ts:2105 - 'Measured against better-auth 1.7.1' -> 'Measured against the then-installed better-auth 1.7.1' (comment)", "packages/cli/test/init.test.ts:392 - anchored the sentence with '#3653', the issue its own test title already names (comment)" ], "open_questions": [ { "question": "The dispatch says touching a content/docs FILE is a scope question to report BEFORE doing it, and there was no way to ask mid-run. I made the edit. Keep it?", "options": [ "A - keep the three-word scoping edit to authentication.mdx:853 ('measured on the then-installed 1.7.1'). The gate is green, main stays green, and the sentence is accurate: 1.7.1 WAS installed when the measurement was taken. No measurement is manufactured and no claim changes - this is the gate's own remedy (b).", "B - revert the docs edit and land the gate red on that one site, with the repair left to a docs-owning card. Honest, but it puts main in a red state and every other PR pays for it.", "C - revert the docs edit and make the docs root advisory (report-only). Rejected on its face by Zone 1: that is 'the appearance of coverage without the substance'." ], "recommendation": "A. B blocks everyone on a three-word edit; C is the shape the card exists to forbid. If you disagree with A, the revert is one hunk in one file and the gate then reds exactly one site, which is a clean, well-labelled state to hand to a docs owner." } ], "out_of_scope_findings": [ "filed as #14020: a live-reading better-auth stamp at packages/plugins/plugin-auth/src/admin-sso-bridge-gate.test.ts:271 ('measured vendor posture (installed 1.7.1)') stays UNATTRIBUTED because its package name is one paragraph away, past a bare '//' line. Deliberately not repaired here: the card's own measurement shows the obvious widening reds a true historical sentence (auth-manager.ts:3125). Filed unassigned and unlabelled for triage, with three candidate directions and the sweep to reproduce.", "NOT filed, reported here instead: content/blog (3 files) is a customer-facing prose surface still outside every root. Measured 0 stamp sites today, so it is a gap with no instance - your call whether that earns a card or a one-line ROOTS addition on a later pass." ] }Generated by Claude Code
Attribute a version to its package across prose distance, and put con…