Skip to content

docs(service-analytics): re-anchor the dead tracker citations to the commits that decided them - #20729

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-20596-service-analytics-citations
Sep 29, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-20596-service-analytics-citations

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Part of #20596
Clause-②: no

What changed

This is the eighth stage of the domain:services lane of the dead-citation sweep. It covers packages/services/service-analytics/src/** and nothing else. By the seat's census at the claim (5899485578), it is the largest package in the lane that no in-flight work holds. Later stages cover the other packages, so this PR says Part of and the card stays open.

Every comment or docblock site in scope that cited a tracker number answering 404 has been rewritten in ruling C+D's form C (comment 5749154545 on #19123), by the method of stages 1 to 7 (PR #20609 as 422db788a, PR #20626 as b80ab579d, PR #20634 as 4d04b6be3, PR #20658 as 9a4b2bb38, PR #20693 as 0e9ad74fb, PR #20708 as 9b384f63a, PR #20717 as cbaf04c1f). That is 76 sites on 76 lines in 22 files, covering 14 numbers:

  • 42 census sites (every census site this package has);
  • 34 sites in test comments, which the census defers.

The raw scan found no dead site the gate's grammar cannot see (see Acceptance notes), so there is no third class this time.

Each rewritten line now cites the commit in origin/main history that decided what the line describes, and says in its own words what was decided: 13 distinct shas. No number in this package has an ADR or ruling record of its own in the repository (a grep of docs/adr/ for all 14 finds none, and a grep of the rest of docs/ finds none either), so every anchor is a commit, per ruling C's order. No number was dropped.

Only comments changed. Every touched source file keeps its line count (78 lines out, 78 in, over 22 files), so no line citation into these files moves. 2 of those 78 lines hold no dead citation: they are reflow lines, listed under Wordings below. No code token moves (see the guard below).

No citation number is added. Every tracker number on an added line was already on the line it replaces: #10861 (5 lines), #12776 (3), #10413 (2), #16750 (2), and #10759, #11152, #5716 and the decision-batch ordinal #59 once each. Each tracker number among them resolves. Over the whole diff, added minus removed is 0 or negative for every number, and no number is new to the diff. No PR number is the citation on an added line: the two PR #N spellings in scope became their pull request's squash commit, and #16750 stays only as the convenience link beside ed7243d52, on the line it already stood on.

Eight dead sites are left on purpose, all of them test strings (see the list below).

One more file: a patch changeset for @objectstack/service-analytics, because the rewritten docblocks and inline comments ship (see Changeset below).

The AnalyticsResultWithDrill type and its four sidecar members are not touched: its docblocks carry no dead number (#20644, #3214 and #1752 all resolve).

Census: service-analytics, before and after

Instrument (A1). The gate's own node scripts/check-issue-citations.mjs --census --json, read-only and unchanged. The count below is its allocated-but-absent findings under packages/services/service-analytics/. Each run counts as a reading only because its board frontier equals the newest issue number, read by a separate request just before and just after the run.

reading tree board whole-repo allocated-but-absent service-analytics sites lines files numbers
before base cbaf04c1f, run 2026-09-29T21:41:53Z to 21:45:05Z enumerated, 186 pages, frontier #20721 (newest #20721 before and after), 18,548 numbers 1,161 42 42 10 10
after head 967d73531, run 21:55:23Z to 21:58:36Z enumerated, 186 pages, frontier #20723 (newest #20723 before and after), 18,550 numbers 1,119 0 0 0 0

The before count matches the seat's census at the claim and A1 (42 sites): the two comments PR #20712 rewrote in analytics-service.ts did not move it. The whole-repo drop is 42, exactly this diff's census sites. The resolves tally is 32,991 in both runs, and resolves-as-pull-request (1,984) and cross-repo-unjudged (995) did not move either. The after run was taken on 967d73531; the head 82d2b40b2 adds only the changeset. No run was truncated or discarded: both enumerations read 186 pages at the newest frontier.

Supplementary instrument, the whole scope. The census does not read test files or strings, and this stage's scope includes test comments. So a second reading runs the gate's own exported extractCitations (whole-file and comment-prose projections) and namesThisRepository over every .ts file under service-analytics/src (162 files). It takes its verdicts from the before census's own board reading rather than from a second enumeration: a number is dead when that census reported it allocated-but-absent, and alive when that census judged it on this board anywhere (its --list extraction) and did not report it. The 21 numbers the census never saw, because they stand only in test files or strings here, were read one by one on the issues endpoint: 17 answer 200, and #16778, #16860, #16918 and #17125 answer 404.

reading citations dead src comment test comment src string test string
before, cbaf04c1f 3,514 84 42 34 0 8
after, 967d73531 3,438 8 0 0 0 8

Its src-comment column equals the census's 42, which is the control on the second instrument. The 3,410 live citations and the 20 cross-repo citations are the same in both readings, and the drop of 76 citations is exactly the rewritten sites. A third, raw reading (every # followed by 2 to 6 digits, whatever surrounds it) finds 3,598 occurrences and 84 dead before, 3,522 and 8 after; its residue equals the gate's residue site for site, and it sees no dead site beyond the gate.

Per-number table

Sites and files count every dead occurrence in scope at the base (comments and strings, tests included). rewritten / left counts the sites rewritten and the sites left. Each anchor was read in its message and diff, not only its subject, and git blame at the base puts every rewritten line in its anchor commit or in a later commit that descends from it (merge-base --is-ancestor exit 0 for each pair).

number sites / files rewritten / left anchor: what it decided
#11461 20/2 19/1 399ecad58: a cross-object leaf in one measure's own filter (the third producer, lowered onto aggregations[].filter) is refused on both ObjectQL doors with INVALID_FIELD / 400 naming the measure, folded into the one member view, with insertion order keeping every earlier refusal's message. The last line of its message names #11461 as the card it settles. New to the sweep
#17130 17/5 13/4 54b3d1d4a (PR #17336): the row-scope resolution refusals carry READ_SCOPE_COMPILE_FAILED / 500 through one constructor, so queryDataset's catch re-throws them instead of reading their words, every message byte-unchanged; plus the source-derived wording-collision guard. Named in its diff only (18 added lines carry the tag). New to the sweep
#17124 12/8 10/2 86c505286 (PR #17593): explicitDateRangeWindow is the one reading of dateRange's array arm on all four faces, and an array that is not two string bounds is refused with ANALYTICS_DATE_RANGE_UNRECOGNIZED / 400. Named in its diff only (its changeset file is 17124-daterange-array-arm-arity.md). New to the sweep
#12209 10/5 10/0 017130a09 (PR #12318): a custom-SQL measure is refused on the ObjectQL aggregate path with INVALID_FIELD / 400, keyed on the EXPRESSION_METRIC_TYPES partition shared with NativeSQLStrategy. Its message records the two failure modes the lines describe (driver-sql blaming a function key, the in-memory evaluator answering null per bucket). Named in its diff only. New to the sweep
#16778 5/1 4/1 357f4992b: the compile-leg refusal of an aggregate a datetime measure's field type cannot carry, scoped to temporal source fields. The squash commit of the pull request that was #16778; its subject carries the number. New to the sweep
#12940 4/2 4/0 aa16721b6 (PR #13361): this package's consumer-local executeAggregate config mirrors (the plugin options and AnalyticsServiceConfig) narrow aggregations[].method to AggregationFunction, after #12776 narrowed the contract. Named in its diff only. New to the sweep
#17015 4/2 4/0 0da638cd9: the closed dateRange preset vocabulary is lowered once and the rest refused, the [range, range] fallback is removed from the faces it reached, and the shared conformance kit holds them. The squash commit of the pull request that was #17015. New to the sweep
#16860 3/1 3/0 041d9fdc6: the object-level read grant is asked at the analytics door, and its bridge to the security service resolves an explicit three-way (absent admits; throwing or method-less denies at error, finding F3 in its message). The squash commit of the pull request that was #16860. New to the sweep
#12248 2/1 2/0 8425c17cc: the five ruled engine members, getDriverForObject? and resolveEffectiveDatasource among them, adopted onto IDataEngine, and getObject typed. Its subject names it. Stage 5's and the spec stage's anchor
#16685 2/2 2/0 ed7243d52 (PR #16750): boolean / toggle accepted for sum / avg / min / max in the aggregate × field-type table, holding maintainer ruling #11152. Its subject names it. The spec stage's anchor
#17125 2/2 2/0 5d12b16e7: the row-scope bridge tells an absent security service from a broken one, so a broken one refuses the query. The squash commit of the pull request that was #17125 (404 on the pulls endpoint too). New to the sweep
#16918 1/1 1/0 5d12b16e7: the same commit. Its changeset's headline names #16918 as the card it answers, and its diff writes the line (admission-bridge-resolution.test.ts:120)
#6123 1/1 1/0 59d1933f9: err.code lands at error.code, not error.details.code; the commit that wrote this very line. The runtime stage's anchor
#13279 1/1 1/0 6a180e42d: permission-store read failures fail loud, and the same commit renames metadata/src/utils/schema-sync-errors.ts to packages/types/src/driver-error-classification.ts, the move the line describes. The anchor of stages 2, 5 and 6, and of the types, rest and runtime stages

Every cited sha matches exactly one commit (git rev-parse --disambiguate, count 1 for each of the 13), and every one is an ancestor of the base (merge-base --is-ancestor, exit 0 for all 13; control leg: stage 1's landing 422db788a exit 0; the history is complete, --is-shallow-repository false, 15,135 commits). Each of the 14 numbers answers 404 on the issues endpoint, read one by one; #16778, #16860, #17015 and #17125 answer 404 on the pulls endpoint too.

Wordings to check

The 8 sites left

  • Test strings, 8 sites, left as stages 1 to 7 left theirs, all describe / it titles:
    • crossobject-conjunct-refusal.test.ts:589 (#11461);
    • aggregate-nontemporal-measure-refusal.test.ts:243 (#16778);
    • date-range-array-arm-arity.test.ts:213 and :294 (#17124);
    • read-scope-resolution-envelope.test.ts:155, :199 and :226, and refusal-wording-collision.test.ts:336 (#17130).
  • There is no operator string, generated file or quoted ruling carrying a dead number in this package. It has no generated file at all.

Mechanical guard: no code token moves

The guard compares the TypeScript parser's leaf nodes, with comments as trivia and JSDoc nodes never visited, base cbaf04c1f against head. Template literals are therefore read in context. It ran over all 22 touched .ts files.

  • Real run: 26,705 base leaf tokens, 0 files with a token change (exit 0).
  • Comment control in plugin.ts (「refusal buys is in」 to 「refusal earns is in」): 0 files changed, as expected (exit 0).
  • Positive control, a code token added in plugin.ts (field: a.field, given as string): DIFFER (exit 1).
  • Positive control, one digit changed inside a kept test title (date-range-array-arm-arity.test.ts:213): DIFFER (exit 1).

Every mutation went through scripts/ablation-replace.mjs, and each landed (anchor 1 to 0, blob changed). Each restore was proven byte-identical to the HEAD blob (ad3dc9fff4d3, a606ffbb6ead), with git diff HEAD empty and a clean tree afterwards.

Changeset

This change ships bytes, so a patch changeset for @objectstack/service-analytics (.changeset/20596-service-analytics-provenance-anchors.md) is included. Its body is stage 7's, word for word, with the package name changed.

Measured on the built package (A3): files[] is dist, README.md and CHANGELOG.md. After the build (a cache miss for this package, so dist is this head's source), the rewritten comments reach dist: 399ecad58 6 times in each of dist/index.js, index.cjs, index.d.ts and index.d.cts; 86c505286 twice in each JS file and once in each declaration file; 54b3d1d4a once in all four; aa16721b6 once in each JS file and twice in each declaration file; 017130a09 once in each JS file. Positive controls: the unchanged line 「none of the coverage: a compiled measure's own」, in the same docblock as the shipped rewrite at objectql-strategy.ts:744, is found once in each of the four files, and the unchanged line 「back into line. Widening it here again would not be a local matter」 beside the shipped rewrite at analytics-service.ts:559 once in each declaration file. A never-written negative phrase appears nowhere in dist. None of the 14 dead numbers is left anywhere in dist.

Gates (head 82d2b40b2)

  • Citation judging, as CI runs it: pnpm check:issue-citations (self-test) exits 0. node scripts/check-issue-citations.mjs exits 0: the diff-scoped run judged 11 citations across 10 files; 10 resolve and 1 resolves as a pull request (#16750, the convenience link that already stood on its line).
  • Doc authoring: pnpm check:doc-authoring exits 0.
  • Derived gates: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at 82d2b40b2 derived 62 commands: all 56 derived at dispatch, plus check:engine-double-contract, check:objectql-double-limit, check:query-options-erasure, check:type-check-coverage, check:type-check-debt and check:where-matcher. Each ran with its exit code captured before any pipe, and all 62 exit 0. --ran, fed each command with its exit code, reports 62 run, 0 NOT MEASURED (a derived zero), 0 unrun, and exits 0. A full turbo run build of ./packages/* and ./packages/*/* ran first under the shared verify lock (71 of 71 tasks, exit 0), so no gate hit an unbuilt workspace.
  • Roster families the derivation lists outside its commands (their rosters sit in directories this diff touches): node scripts/check-changeset-fixed.mjs, pnpm check:authz-resolver, pnpm check:error-code-casing and pnpm check:filter-alias-parity, each exit 0.
  • Tests and typecheck, under the verify lock:
    • pnpm --filter @objectstack/service-analytics test: 137 files pass and 3,216 tests pass. That is every test file in the package, the 12 touched ones included.
    • pnpm --filter @objectstack/service-analytics typecheck exits 0 (tsc --noEmit on tsconfig.json). --listFiles: the program holds all 162 files under src/, the 137 test files and all 22 touched files included.
  • Lint, as a proven narrowing: eslint --no-inline-config --format json over the 22 touched .ts files gives 22 files, 0 errors and 0 warnings. All 22 are in eslint's own population (isPathIgnored is false for each; a dist file, as the control, is ignored). eslint.config.mjs never enables type-aware linting (no parserOptions.project, as its own lines 327-328 state), so a comment edit here cannot move the verdict on any untouched file. The repo-wide pnpm lint is CI's run.
  • Control bytes: pnpm check:nul-bytes exits 0, and a raw scan of the 23 changed files for control bytes finds none.

Acceptance notes

  • The gate-invisible spellings, grepped as the claim asked. CITATION_RE refuses a hyphen after the digits and a / before the # (check-issue-citations closeout (extractor spellings): CITATION_RE refuses a hyphen after the digits, so a dead #N-word citation (#13398-class) is invisible to the diff gate and to the census #20636), and NON_CITATION_HEADS excuses a number after the word 「option」. In this package:
  • 「This card」 phrases are left. 113 lines in 39 files of this package speak of 「this card」, 「that card」 or 「the card」. They carry no number, neither instrument sees them, and most sit in blocks whose numbers still resolve. Stage 7 rewrote two such lines as lost referents; here none is changed, because the phrase runs through the whole package and rewriting a subset would be arbitrary.
  • Prose that names queryDataset's catch, not changed. Nine comment lines say queryDataset's catch. Since 10c36cc43 that catch sits in the private answerDataset, whose docblock calls it the body of queryDataset, so the lines still hold at the level of the public method. This is not a dead citation, so it is outside this stage.
  • The census instrument did not truncate in this stage. Both enumerations read 186 pages at the newest frontier.
  • Anchors the next stages can reuse, each checked here: #11461 → 399ecad58; #17130 → 54b3d1d4a; #17124 → 86c505286; #12209 → 017130a09; #16778 → 357f4992b; #12940 → aa16721b6; #17015 → 0da638cd9; #16860 → 041d9fdc6; #17125 and #16918 → 5d12b16e7.
  • Base. The branch is on main at cbaf04c1f. main has since moved four commits (3711e0b76, 61455de27, 6afccda5a, 671d4c164). They touch packages/spec, packages/metadata/package.json, pnpm-lock.yaml, docs and changesets, and no file under service-analytics or in this diff, so no merge was taken; the merge queue rebuilds on the merged generation. One of them, 671d4c164, declares the four drill-through sidecars on AnalyticsResult in the spec. This diff leaves the local AnalyticsResultWithDrill untouched, as the claim requires.

Generated by Claude Code

…commits that decided them

Every comment or docblock site under packages/services/service-analytics/src
that cited a tracker number answering 404 now cites the commit in this
repository's history that decided what the line describes (ruling C+D,
form C): 76 sites on 76 lines in 22 files, 14 numbers, 13 anchor commits.
Two further lines are reflowed; every file keeps its line count. Comments
only: no code token moves, and the 8 dead numbers inside test titles and
test strings are left as they are.

Claude-Session: https://claude.ai/code/session_01XY5uCwTjZj7884yYtyur4H
Co-authored-by: Claude <noreply@anthropic.com>
…ring

The rewritten docblocks and inline comments reach dist/ (measured on the
built package), so the change ships bytes and takes a patch changeset.

Claude-Session: https://claude.ai/code/session_01XY5uCwTjZj7884yYtyur4H
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tests tooling labels Sep 29, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/service-analytics, touching 11 documentable anchor(s). ⚠️ 4 changed file(s) yielded no anchor (packages/services/service-analytics/src/date-range-array-arm.ts, packages/services/service-analytics/src/measure-result-type.ts, packages/services/service-analytics/src/read-scope-refusal.ts, …), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/plugins/packages.mdx (via AnalyticsServicePlugin (symbol, a top-level class))

⛔ 3 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v14.mdx (via generateSql (symbol, a method of class NativeSQLStrategy))
  • content/docs/releases/v17/17-0.mdx (via ObjectQLStrategy (symbol, a top-level class))
  • content/docs/releases/v17/17-5.mdx (via AnalyticsService (symbol, a top-level class), generateSql (symbol, a method of class NativeSQLStrategy))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 4 changed file(s) yielded no anchor (packages/services/service-analytics/src/date-range-array-arm.ts, packages/services/service-analytics/src/measure-result-type.ts, packages/services/service-analytics/src/read-scope-refusal.ts, …) — pages documenting those are invisible to this run
  • the SDK route bridge reached 54 of 206 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 10 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 671d4c164f11882e23f2196708e02b610ad7f254 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 4093826c903c928374236a5c36e0ebbea3e0a21d — the merge of head 82d2b40b21bc9ac0dc6d3c3af3d6dd04537c53ed into base 671d4c164f11882e23f2196708e02b610ad7f254, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 4093826c903c928374236a5c36e0ebbea3e0a21d && git checkout 4093826c903c928374236a5c36e0ebbea3e0a21d
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 671d4c164f11882e23f2196708e02b610ad7f254 82d2b40b21bc9ac0dc6d3c3af3d6dd04537c53ed && git checkout -B drift-repro 671d4c164f11882e23f2196708e02b610ad7f254 && git merge --no-ff 82d2b40b21bc9ac0dc6d3c3af3d6dd04537c53ed

node scripts/docs-audit/affected-docs.mjs --json 671d4c164f11882e23f2196708e02b610ad7f254

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

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 671d4c164f11882e23f2196708e02b610ad7f254 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 82d2b40b21bc9ac0dc6d3c3af3d6dd04537c53ed
Local-runs: none

① Derived judgments

Read against main at the merge-base cbaf04c1f (stage 7's landing). The branch was fetched into an owned ref and read with git show / git diff; the board was read on the issues endpoint one number at a time; the head's check-runs were read over the API; the last of those reads was taken at 2026-09-29T22:43Z. The PR's recorded base 671d4c164 is four commits past the merge-base and origin/main (b291fcdae) six (3711e0b76, 61455de27, 6afccda5a, 671d4c164, 63bfe6964, b291fcdae); on this PR's paths those six add five unrelated .changeset/*.md files and touch nothing under packages/services/service-analytics, nothing in scripts/check-issue-citations.mjs and not .changeset/config.json, so the three-dot diff against origin/main and the merge-base diff are the same 23 files, +88/−78 — 22 source files under packages/services/service-analytics/src/** (10 modules, 12 test files) and one changeset. The head 82d2b40b2 adds only the changeset on top of 967d73531, which holds every source line.

  • Accept-set: no change — right. No Zod schema, REST handler, query-parameter set, refusal text, log text or runtime string moves. 78 source lines out, 78 in; 77 of the 78 removed/added pairs are lines that open with a comment marker after whitespace (//, *, /**), and the one remaining pair is aggregate-nontemporal-measure-refusal.test.ts:179, expect(temporal).toBe(6); with its trailing comment, whose code bytes before the // are identical. Each of the 22 touched source files has additions equal to deletions, so no line citation into these files moves. The 8 test titles that still carry a dead number are untouched string tokens. The dev's parser leaf-token guard (0 files with a token change; both positive controls DIFFER) says the same and is not repeated here.
  • Public surface: no change — right. No export added, removed or renamed; no packages/spec file touched, so no generated artifact is owed. AnalyticsResultWithDrill appears on 0 lines of the diff, as the claim (5899485578) required.
  • Published bytes: changed — right, and it decides ②. @objectstack/service-analytics (17.5.0, not private, files = dist, README.md, CHANGELOG.md) emits declarations, and rewritten docblocks sit on declarations src/index.ts exports: the method bullet of AnalyticsServiceConfig (analytics-service.ts:559) and of AnalyticsServicePluginOptions (plugin.ts:173), and ObjectQLStrategy's member-origin docblocks (objectql-strategy.ts:729 to :866). So dist/index.d.ts changes. The dev's A3 build reading (all four dist entries; 399ecad58 six times, aa16721b6 twice, 86c505286 and 54b3d1d4a once in each declaration file; positive and negative controls; none of the 14 numbers left in dist) says the same; this record does not repeat the build.
  • The 14 numbers are dead — right. Each of #11461 #17130 #17124 #12209 #16778 #12940 #17015 #16860 #12248 #16685 #17125 #16918 #6123 #13279 answers 404 on the issues endpoint, read one by one for this record. A git grep for all 14 over docs/ at the head finds none, so ruling C's first rung (an ADR or ruling record) is empty and a commit is the right anchor for every one.
  • The 13 anchors — each right. Each abbreviated sha resolves to exactly one commit (rev-parse --disambiguate, count 1 for all 13) and is an ancestor of the base (merge-base --is-ancestor, exit 0 for all 13; --is-shallow-repository false). Each names the number it replaces — nine in the message: 399ecad58 ("Fixes A cross-object per-measure filter reaches engine.aggregate unrefused on the ObjectQL path — the third producer #10861's member view does not cover #11461"), 357f4992b ((#16778)), 0da638cd9 ((#17015)), 041d9fdc6 ((#16860)), 8425c17cc ((#12248)), ed7243d52 ((#16685)), 5d12b16e7 ((#17125)), 59d1933f9 ((#6123)), 6a180e42d (#13279, four times); four in the diff only: 54b3d1d4a (#17130, 18 lines), 86c505286 (#17124, 10 lines, and its changeset is 17124-daterange-array-arm-arity.md), 017130a09 (#12209, 6), aa16721b6 (#12940, 5); and #16918 stands in 5d12b16e7's own changeset headline. The decision each rewritten line states is the commit's: 54b3d1d4a's message names READ_SCOPE_COMPILE_FAILED and the wording-collision guard; 86c505286's names ANALYTICS_DATE_RANGE_UNRECOGNIZED; 017130a09's names INVALID_FIELD and the EXPRESSION_METRIC_TYPES partition; aa16721b6's names AggregationFunction; 041d9fdc6's carries finding F3 (the admission bridge collapsed three resolutions into one; a throwing or method-less service now denies at error, an absent one still admits); 0da638cd9's is the closed preset vocabulary; 357f4992b's is the compile leg scoped to temporal source fields; ed7243d52's names maintainer ruling #11152 and option A; 8425c17cc's diff declares getDriverForObject? on IDataEngine; 59d1933f9's says error.code; 6a180e42d's diffstat carries the rename to packages/types/src/driver-error-classification.ts that analytics-service.ts:238 now describes. Four anchors are the squash commits of pull requests that were the number (357f4992b, 0da638cd9, 041d9fdc6, 5d12b16e7), the form the landed stages accepted for that case.
  • Citation accounting — right. Over the diff, line pair by line pair: the 78 removed lines carry 76 dead occurrences, and the per-number counts equal the body's rewritten column (#11461 19, #17130 13, #17124 10, #12209 10, #17015 4, #16778 4, #12940 4, #16860 3, #17125 2, #16685 2, #12248 2, #6123 1, #16918 1, #13279 1); the 2 removed lines carrying none are the reflow lines measure-result-type.ts:116 and aggregate-datetime-measure-refusal.test.ts:66, each keeping the #16750 it carried. Added lines carry exactly the live numbers the removed lines carried, at the same counts — #10861 ×5, #12776 ×3, #10413 ×2, #16750 ×2, #10759, #11152, #5716 and the batch ordinal #59 — so no number is new to the diff and none grew; each of the seven resolves (#16750 as a pull request), read here. No PR #N stands on an added line: the two PR #17125's became Commit 5d12b16e7's. 13 distinct shas stand on added lines.
  • The 8 sites left — right, and the list is exact. A grep of the 14 numbers over service-analytics/src at the head returns exactly 8 lines, every one a describe or it title, the same 8 the body lists (crossobject-conjunct-refusal.test.ts:589, aggregate-nontemporal-measure-refusal.test.ts:243, date-range-array-arm-arity.test.ts:213 and :294, read-scope-resolution-envelope.test.ts:155, :199, :226, refusal-wording-collision.test.ts:336). No operator string, generated file or quoted ruling in this package carries a dead number.
  • The gate-invisible spellings — right. At the head under service-analytics/src: 8 #N-word lines (#10413-phase ×2, pre-#10413-phase, #13570-pinned ×2, #13570-guarded, #13640-guarded, #5298-guarded), 29 #A/#B lines carrying 26 distinct numbers, and 0 option #N. Every one of those 30 numbers resolves, read here — 21 as issues and 9 as pull requests (#2138 #2149 #3601 #4315 #4870 #5243 #5329 #13570 #16101). Nothing there needed rewriting.
  • The wordings — each right. [#N] → [commit sha] in the bracket position; [#10861 / #11461] and [#10861, #11461] keep the live number beside the sha. The boolean rows (measure-result-type.ts:115, aggregate-datetime-measure-refusal.test.ts:65) turn 「[Decision] Two maintainer rulings collide on boolean aggregates — batch #59's "every other pair refused" would refuse avg(flag), which ruling #11152 pins on six backends as having no per-aggregate exception #16685 ruled A, landed as feat(spec): accept boolean / toggle for sum / avg / min / max in the aggregate × field-type table (#16685) #16750」 into 「commit ed7243d (feat(spec): accept boolean / toggle for sum / avg / min / max in the aggregate × field-type table (#16685) #16750) added those rows」 — ed7243d52's message is the record of option A and of ruling #11152, so nothing is lost and #16750 stays where it stood. 「[finding] a bare-Error refusal reaching queryDataset is classified by WORDING — a security refusal whose text happens to contain "not registered" becomes a 200 with an empty chart #17130 exists to remove it」 → 「commit 54b3d1d was made to remove it」; 「the exact move [finding] a bare-Error refusal reaching queryDataset is classified by WORDING — a security refusal whose text happens to contain "not registered" becomes a 200 with an empty chart #17130 forbids」 → 「ruled out」, which its message states; 「fix(analytics): lower the closed dateRange preset vocabulary once, and refuse the rest (#16322) #17015's kit」 → 「commit 0da638c's kit」, the conformance kit that commit built; 「[finding] a permission-store read failure resolves as an AUTHENTICATED caller holding ZERO capabilities — the package door answers 403 FORBIDDEN, byte-identical to a genuine capability denial #13279 moved it there」 → 「commit 6a180e4 moved it there」, the rename its diffstat shows; the present-tense verbs after a number moved to the past tense with the sha.
  • Form — consistent with the landed stages 1 to 7 (422db788a, b80ab579d, 4d04b6be3, 9a4b2bb38, 0e9ad74fb, 9b384f63a, cbaf04c1f): the word commit plus the abbreviated sha where the number stood, the decision carried in the sentence. The four reused anchors (8425c17cc, ed7243d52, 59d1933f9, 6a180e42d) are the ones earlier re-anchoring stages gave the same numbers.
  • Check-runs on the head, the gate verdicts (34 check-runs at the last read, all completed): 31 success, 3 skipped (Build Docs, Console Pin Gate, Packed-tarball smoke (opt-in): paths-filtered or opt-in, not verdicts against), 0 in_progress, 0 failure. Every one of the seven required contexts is success: Lint & Repo Gates (which carries check:issue-citations and check:doc-authoring, the two gates this diff answers to), TypeScript Type Check, Test Core (all six shards success before the aggregate), Dogfood Regression Gate, Build Core, Temporal Conformance (live PG + MySQL), Governed Surface Queue Guard. Check Changeset, Check PR Size, Part-of PR must not also close its card, The card this PR closes must claim this branch and both single-writer checks are success too. Two earlier reads during this review caught Lint & Repo Gates, Type Check · workspace and three Test Core shards still in_progress and the two aggregates not yet created; nothing was awaited, the read was simply repeated at the end. Nothing was built, run or re-run locally.

② Semver level

  • .changeset/20596-service-analytics-provenance-anchors.md declares '@objectstack/service-analytics': patch — matches what the diff publishes. The package is released and its dist carries the rewritten docblocks (① above), so bytes ship; skip-changeset would be wrong (it is for a diff that publishes nothing from any released package), and the PR carries no such label. Not minor: no accept set widens and no surface is added. The body is truthful (comments only; no type, schema, export, log or refusal text, or runtime behaviour change), carries no tracker number and no model identifier, is stage 7's landed body word for word with the package name swapped (compared against 20596-plugin-approvals-provenance-anchors.md at cbaf04c1f), and the filename carries the card number.
  • Clause-②: no — right. It is line 2 of the PR body under Part of #20596, and the claim (5899485578) declares the same. The diff widens no accept set, so no arm is owed and no minor is owed. Nothing breaks, so no ADR-0087 marker is owed; Check Changeset on the head is success.
  • Not a governed-surface diff (no path under docs/adr/**, .claude/**, skills/**, AGENTS.md, CLAUDE.md); 166 changed lines, under the 5,000-line human-merge threshold; head repo equals base repo; Governed Surface Queue Guard on the head is success. A draft with Part of on line 1 and no closing keyword anywhere in the body, so the card stays open for the remaining stages. Both head commits end with the model-free trailer pair, and the PR body's footer is the session-URL form.

③ Boundary flags

The dev report (5900307597) has open_questions: []. Its nine deviations and two out-of-scope findings, each answered:

  1. 34 test-comment sites beyond the census's 42 — answered, in scope. The claim's surface is comment and docblock prose under service-analytics/src/**; test comments are that, and stages 1 to 7 rewrote theirs. The head grep above confirms the residue is titles only.
  2. Two reflow lines with no dead site — answered, right. measure-result-type.ts:116 and aggregate-datetime-measure-refusal.test.ts:66 are the second halves of the two boolean-row rewrites; each keeps its #16750, and both files keep their line counts.
  3. No lost-referent rewrite; 「this card」 left package-wide — answered, right. A case-insensitive grep for 「this card」 / 「that card」 / 「the card」 over service-analytics/src at the head reads 134 lines in 42 files (the report's 113 in 39 is a narrower spelling; the point stands either way). The phrase carries no number, so neither the gate nor the census sees it, and a subset rewrite inside a stage of this card would be arbitrary. Wording drift, not a defect or an authoring trap, so the Acceptance note is the right filing. Not blocking.
  4. Supplementary and raw instruments judged by stage 6's method, with no board-dump script — answered, immaterial. The census is the instrument of record: its before count equals the seat's A1 (42), its after count is 0, the whole-repo drop is exactly 42, and both enumerations read the frontier at the newest number.
  5. The harness attribution reminder versus AGENTS.md's trailer pair — answered, right. Both head commits end with Claude-Session: and Co-authored-by: Claude; no model identifier appears in either message, the PR body or the changeset; the PR body's footer is the session-URL form the PR-body surface keeps.
  6. No pre-PR merge of main; four (now six) commits past the base — answered, right. Verified above: none of the six touches a path in this diff, the citation gate or .changeset/config.json, so the three-dot diff equals the merge-base diff and the queue's rebuild has nothing to reconcile by hand. 671d4c164's spec sidecars leave the local AnalyticsResultWithDrill untouched here (0 diff lines).
  7. Labels — answered. documentation, size/m, tests, tooling are the labeler's; no skip-changeset, which is right.
  8. The report comment posted from the shared checkout after the worktree was removed — answered, immaterial to the head. A process note; the head is what this record reads.
  9. #N-word count 8 against the claim's 7 — answered, right. The eighth is pre-#10413-phase-2 (execution-context-bridge.test.ts:223), excluded by a hyphen before the #; #10413 resolves.
  10. Out-of-scope 1, nine comment lines say 「queryDataset's catch」 — answered, carrier stands. Verified at the head: queryDataset (analytics-service.ts:1770) delegates to the private answerDataset (:1786) since 10c36cc43 (fix(service-analytics): every dataset answer names its base object #20712), so the lines hold at the public-method level. Not a dead citation and not a defect; outside this stage; the Acceptance note names the next editor of analytics-service.ts (spec(contracts): the dataset answer's drill sidecars (dimensionFields, drillRawRows, drillRawTotals, drillRanges) are emitted by service-analytics and read by objectui, but AnalyticsResult declares none of them #20700's follow-up). Nothing for the dev.
  11. Out-of-scope 2, 「this card」 on package-wide lines — answered (3 above).

Nothing is escalated. Two readings for the seat, neither a flag on this PR: the Docs Drift Check (5900270846) names content/docs/plugins/packages.mdx through AnalyticsServicePlugin, and a comment-only diff moves no documented behaviour, so no re-verification is owed and the three release-owned pages it lists stay read-only; and every check on the head was success at the last read, so the landing waits on nothing this record can see.

Implemented-by: claude/issue-20596-service-analytics-citations
Reviewed-by: session_01XY5uCwTjZj7884yYtyur4H

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review September 29, 2026 22:47
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 29, 2026
Merged via the queue into main with commit d282087 Sep 29, 2026
36 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-20596-service-analytics-citations branch September 29, 2026 23:05
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/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants