You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[finding] buildSummaryIndex resolves a roll-up's FK by carrier equality, so an unreadable reference makes a declared summary field silently never recompute — the second way this index invents "nothing to do" (cf. closed #9154) #19082
An unreadable carrier (a non-string where FieldSchema.reference declares an optional string) compares false against every name, fkField stays unset, and :9039 takes the continue. ⇒ a declared summary field silently never recomputes, with no diagnostic anywhere. The comment on :9039 says 「can't resolve the relationship」, which is accurate about the code and silent about the consequence.
⭐ This exact symptom has already happened once, through a different read
#9154 (closed completed): 「the roll-up summary index's registry read swallows into an empty index — and CACHES the invented emptiness, so every parent roll-up silently stops recomputing」. Same index, same outcome, different input: that card's read was the registry read; this one is the carrier equality read on the same function. ⇒ the index has two ways to conclude 「nothing to recompute」 from a failure, and only one of them has been closed.
That is the argument for this card existing: a fixed sibling makes the symptom's cost known rather than hypothetical.
What this card is asking for, stated honestly
This site is already classified. PR #18503 recorded it in its C2 list (as engine.ts:8989, the same code before the file moved) and drew that boundary deliberately, and the #18550 round — whose ten routed sites are the C3 class — left it alone for exactly that reason rather than widening its declared surface.
⇒ this is a request to reopen a deliberately-drawn boundary, ⛔ not a new discovery, and it should be graded as such. If the answer is 「the boundary stands」, that is a legitimate outcome and worth recording on this card so the next reader of :9039 finds the decision instead of re-filing it.
Measurement
reading
value
instrument
the equality read and the skip branch
present as quoted, :9033 and :9039
git show origin/main:packages/objectql/src/engine.ts
lit control: production paths in packages/objectql routed through the arbiter referenceCarrierOf
0 (the only hit in this package family is a test in packages/cli)
⇒ the engine's cascade paths were routed by PR #19080; this one was not, and the zero above is a reading rather than a dead grep (the token resolves elsewhere in the corpus).
Evidence limit
Static reading on one ref, plus a relayed reproduction this seat did ⛔ not re-run. What is NOT established: whether an unreadable carrier can reach a stored object definition today. #19080's probe established that the engine's own write path refuses this shape (insert → assertReferencesResolve → the same arbiter), so the row in that probe had to be written straight through the driver — which bears directly on how urgent this is, and on whether the honest fix is a refusal here or a narrowing further upstream. ⛔ Do not grade as a live outage without a reachability reading.
Provenance
Out-of-scope finding from the os-dev report on #18550 (PR #19080), re-measured by the domain:spec#3 execution seat against origin/main before filing; the #9154 adjacency is the seat's, found by dedup. Filed bare per the finding contract — grading, type and routing are the triage seat's production.
Dedup words
buildSummaryIndex reference equality, summary field never recomputes, C2 equality carrier read, engine 8989 summary index, can't resolve the relationship skip
Claim: PM loop round 6
Session: session_01NcPSwnmJHczmTu6FG7NMjE
Branch: claude/issue-19082-summary-index-loud-skip
Worktree: objectstack-issue-19082
Domain: domain:engine
Seat: domain:engine#1
File surface: packages/objectql/src/ (stop on breach; explain in the report)
Container & model: M, mode:subagent, model: default judgment tier — quoting this act's node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --tier packages/objectql/src/engine.ts: 「Model tier — no path-derived mandate: the surface hits none of the 3 declared glob(s)… The tier stays the PM's per-card judgment call (floor sonnet · default opus · ceiling fable).」 ⇒ the card reopens a deliberately-drawn boundary and the remedy shape is a judgment ⇒ default judgment tier, ⛔ not the floor.
Clause-②: no
Thread-read: 5733414706
Serial constraints cleared: lane in flight was 0 at claim; 26 open PRs enumerated and NONE touches packages/objectql/src/engine.ts — the most recent lander there is a675ad4e (#19080), already on origin/main; same-batch siblings #18877 (packages/objectql/src/registry.ts) and #18997 (packages/metadata-protocol/src/protocol.ts) are file-disjoint — ⛔ judged by FILE, not by package, so sharing packages/objectql with #18877 is not a serial constraint; #19081 is the sibling face of the same root (carrier unreadable) and is ⛔ NOT folded per the triage ruling: different file, different failure direction, different lane
⭐ What this card is, so it is not mistaken for a plain defect
Triage (5733414706) graded it p2 · bug · domain:engine and drew the delivery boundary. The card itself is explicit that this site is already classified — PR #18503 recorded it in its C2 list and the #18550 round left it alone deliberately. ⇒ this is a request to reopen a deliberately-drawn boundary, and 「the boundary stands」 is a legitimate outcome that must be recorded on the card rather than left for the next reader to re-file.
⛔ The delivery boundary, quoted from triage rather than paraphrased
⇒ stop-the-silence is the first deliverable and is available today; how the FK ought to be resolved may stay open. The second pin (a normal reference still resolves fkField) is the control that separates 「fixed」 from 「closed this path off」 — ⛔ it is not optional.
⚠️ Two premises the dev falsifies FIRST, ⛔ not assumptions to build on
Triage did not re-take the reading, and says so: 「⛔ 未重取 :9033–:9036、⛔ 未跑 buildSummaryIndex。⇒ 接手者按符号重取…若该处今天已不按载体相等解析,据实驳回本卡。」 ⇒ re-locate by symbol, ⛔ never by the line numbers on the card; packages/objectql/src/engine.ts took a lander (a675ad4e, Refuse an unreadable reference carrier at the ten residual readers (ruling E item 2 residue) #19080) after the card's reading ref 221dabb72. premise_still_valid: false + pr: null is a legitimate terminal report here, ⛔ never a failed dispatch.
Reachability is NOT established. The card states it: 「What is NOT established: whether an unreadable carrier can reach a stored object definition today… ⛔ Do not grade as a live outage without a reachability reading.」 Refuse an unreadable reference carrier at the ten residual readers (ruling E item 2 residue) #19080's probe found the engine's own write path refuses this shape, so the relayed reproduction had to write straight through the driver. ⇒ report what you measure; ⛔ do not inflate this to a live outage, and ⛔ do not deflate it to 「unreachable, therefore nothing to do」 without a reading either way.
⛔ Escalation condition, mechanical and scoped to this card only (triage's own wording): a stored summary field measured to have never recomputed ⇒ p1. 「:9033 is still carrier equality」 is ⛔ NOT an escalation reason — that is this card's premise.
{
"issue": 19082,
"status": "done",
"branch": "claude/issue-19082-summary-index-loud-skip",
"pr": "#19293",
"session": "session_01NcPSwnmJHczmTu6FG7NMjE",
"premise_still_valid": true,
"premise_note": "Re-located by SYMBOL, never by the card's line numbers. buildSummaryIndex sits at engine.ts:9009 on origin/main@7ec85341; the carrier-equality comparison the card quotes is byte-identical and still at :9033, and the silent skip is still at :9039. #19080 (a675ad4) routed planCascadeAtomicity and cascadeDeleteRelations through referenceCarrierOf and left this third site untouched, so the card's premise holds and it is NOT rejected on the facts.",
"summary": "buildSummaryIndex resolved a roll-up's foreign key by comparing the child field's reference carrier to the parent name, so an unreadable carrier left fkField unset and the skip dropped a DECLARED summary field out of both indexes with no diagnostic anywhere: every later write to the child reported success while the parent's stored summary silently stopped tracking it. The resolution RULE is deliberately unchanged (PR #18503's C2 boundary stands; loosening the comparison would trade a silent stall for a mis-matched FK, which is more expensive) - the carrier is now read through the one arbiter referenceCarrierOf, its refusal is CAUGHT per candidate field so an unreadable sibling cannot hide the readable FK, and the skip reports itself at error, once per index build, carrying the consequence and both fixes. Absence (undefined / null / empty string) stays silent and every readable carrier resolves exactly as before. The boundary decision and its reasoning are recorded in the function's own docblock and on the PR, so the next reader of the skip branch finds the decision instead of re-filing it.",
"reachability": "MEASURED on this tree rather than argued - one probe, three doors, a readable-carrier control on each. (1) ObjectSchema.safeParse REFUSES the shape (fields.bad.reference: invalid_type); control accepted. (2) getMetadataTypeSchema('object'), what saveMetaItem resolves for a stored /meta write, REFUSES it; control accepted. (3) registry.registerObject ACCEPTS it and stores the carrier verbatim as {object:'bad'}, after which referenceCarrierOf on the registered field throws TypeError. => NOT a live outage (the live authoring and stored-write doors refuse this shape today) and NOT unreachable either (the raw registry door takes it - the population engine.ts's own #9689 note already names for the sibling seam: a raw registerObject, or a stored/artifact row written before the tightening, since the engine registers raw objects and never re-parses). NOT escalated to p1: no stored summary field was measured to have never recomputed, which is this card's only escalation condition. Honest qualifier from the same probe: registration DOES already emit an ADR-0078 field/relationship-without-reference warning for this field (the rule tests typeof def.reference !== 'string'), but that is a one-shot, console-carried note about the CHILD field at registration - it never names the parent's declared summary field and does not say the roll-up was dropped. A neighbouring signal, not this one.",
"tests": "New pin packages/objectql/src/engine-summary-index-unreadable-carrier.test.ts - 7 cases, BOTH directions per the triage acceptance. S1 control: a normal reference still resolves fkField (inv_line / inv) and the recorder stays at zero. S2 defect: an unreadable carrier emits the skip signal at error and not warn, naming the field, 'will NOT recompute', 'reports success' and both fixes. S3: both in ONE index build - the readable roll-up indexed while the unreadable one is reported. S4: absence stays silent. S5: an unreadable sibling declared BEFORE the real FK does not hide it (the anti-regression pin for routing through a throwing arbiter). S6: said once per index BUILD - five consults report once, and a registry mutation makes it report again, so the 1 cannot mean 'once per process'. The zeros in S1/S4/S5 are readings and not a dead instrument: S2 drives the same recorder through the same handle and measures it at 1. RUNS, each exit code captured BEFORE any pipe, at HEAD 308a340: 'pnpm --filter @objectstack/objectql test' exit 0 - 'Test Files 301 passed (301)' / 'Tests 5016 passed (5016)'; 'pnpm --filter @objectstack/objectql typecheck' exit 0, and check:test-typecheck holds at 40 file(s) / 234 error(s) / 65 pinned signature(s) - unmoved, so the new test adds nothing to the TEST_DEBT ratchet; 'pnpm --filter (upstream-closure of @objectstack/objectql) build' exit 0; 'pnpm exec turbo run build --filter=./packages/* --filter=./packages//' exit 0, 72/72 successful; 'pnpm lint' (eslint . --no-inline-config, the WHOLE repo) exit 0 - it fitted inside the foreground cap, so there is no narrowing to declare and no narrowing evidence owed. No ablation was run: this change adds a diagnostic rather than a guard, and S2/S5 are the failure-capable pins - S2 is red before the change by construction, since the pre-change code emitted nothing at all.",
"gates": [
"node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0",
"node scripts/check-adr-0087-registration.mjs --self-test :: exit 0",
"node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0",
"node scripts/check-changeset-no-major.mjs --self-test :: exit 0",
"node scripts/check-ci-filter-parity.mjs :: exit 0",
"node scripts/check-closing-keyword-parity.mjs :: exit 0",
"node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0",
"node scripts/check-comment-mask-adoption.mjs :: exit 0",
"node scripts/check-comment-mask-adoption.mjs --self-test :: exit 0",
"node scripts/check-comment-mask-corpus.mjs :: exit 0",
"node scripts/check-empty-changeset.mjs --base origin/main :: exit 0",
"node scripts/check-empty-changeset.mjs --self-test :: exit 0",
"node scripts/check-engine-split-ratio.mjs --days 90 :: exit 0",
"node scripts/check-engine-split-ratio.mjs --self-test :: exit 0",
"node scripts/check-keyed-text-bounds.mjs :: exit 0",
"node scripts/check-keyed-text-bounds.mjs --self-test :: exit 0",
"node scripts/check-platform-object-tenancy-census.mjs :: exit 0",
"node scripts/check-platform-object-tenancy-census.mjs --self-test :: exit 0",
"node scripts/check-plugin-teardown-shape.mjs :: exit 0",
"node scripts/check-plugin-teardown-shape.mjs --self-test :: exit 0",
"node scripts/check-registry-log-declared.mjs :: exit 0",
"node scripts/check-registry-log-declared.mjs --self-test :: exit 0",
"node scripts/check-rest-log-spy-declared.mjs :: exit 0",
"node scripts/check-rest-log-spy-declared.mjs --self-test :: exit 0",
"node scripts/check-system-context-census.mjs :: exit 0",
"node scripts/check-system-context-census.mjs --self-test :: exit 0",
"node scripts/check-undeclared-dep-imports.mjs :: exit 0",
"node scripts/check-undeclared-dep-imports.mjs --self-test :: exit 0",
"node scripts/docs-audit/check-affected-docs.mjs :: exit 0",
"node scripts/docs-audit/check-drift-comment.mjs :: exit 0",
"node scripts/pm/release-rehearsal-clone.mjs --self-test :: exit 0",
"pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0",
"pnpm check:changeset-gate-self-tests :: exit 0",
"pnpm check:cross-package-test-inputs :: exit 0",
"pnpm check:dispatcher-error-vocabulary :: exit 0",
"pnpm check:doc-authoring :: exit 0",
"pnpm check:driver-memory-census :: exit 0",
"pnpm check:dts-closure :: exit 0",
"pnpm check:dual-build-cjs-loads :: exit 0",
"pnpm check:durability-log-level :: exit 0",
"pnpm check:engine-double-contract :: exit 0",
"pnpm check:lean-entry-closure :: exit 0",
"pnpm check:logger-receiver-detach :: exit 0",
"pnpm check:nul-bytes :: exit 0",
"pnpm check:objectql-double-limit :: exit 0",
"pnpm check:objectui-changeset :: exit 0",
"pnpm check:org-identifier :: exit 0",
"pnpm check:page-declaration-shape :: exit 0",
"pnpm check:pm-changeset-deadline-census :: exit 0",
"pnpm check:published-files :: exit 0",
"pnpm check:query-options-erasure :: exit 0",
"pnpm check:refd-timer-probe :: exit 0",
"pnpm check:slot-lookup :: exit 0",
"pnpm check:sourcemap-no-sources-content :: exit 0",
"pnpm check:stack-collection-maps :: exit 0",
"pnpm check:swallow-census-controls :: exit 0",
"pnpm check:test-source-alias :: exit 0",
"pnpm check:tier-file-adoption :: exit 0",
"pnpm check:type-check-coverage :: exit 0",
"pnpm check:type-check-debt :: exit 0",
"pnpm check:watch-hint-literal :: exit 0",
"pnpm check:where-matcher :: exit 0"
],
"gates_reconciliation": "node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran RANFILE :: exit 0 - '62 derived famil(ies) accounted for - 62 run, 0 NOT-MEASURED (a DERIVED zero - all 62 recorded an exit code and none of them is 3)'. Derived on the merged tree at HEAD 308a340, merge base e6a03e6, from the ACTUAL changed files (3 paths, 424 changed lines, under the 5000 human-merge threshold). Five families first answered exit 2 or 3 - NOT a pass, nothing measured - and each was CLEARED rather than recorded as one: check-engine-split-ratio --days 90 (exit 2) and check-plugin-teardown-shape --self-test (exit 3) refused on a shallow horizon, both exit 0 after deepening; check:dual-build-cjs-loads, check:lean-entry-closure and check:type-check-debt (exit 3) refused for want of built output, all exit 0 after the full build. origin/main has moved twice since the derivation (15f9284, adf4b18); their deltas are a spec liveness flip and a turbo --force flag on CI's sliced test leg, neither adding a family that touches these paths. Outside this total, by the tool's own headings and NOT measured locally: 50 artifact-roster families, 11 declared-wide families, 5 path-scheduled CI jobs and the always-runs tail.",
"line_budget": null,
"files_changed": [
"packages/objectql/src/engine.ts",
"packages/objectql/src/engine-summary-index-unreadable-carrier.test.ts",
".changeset/19082-summary-index-unreadable-carrier-loud-skip.md"
],
"mcp_calls": "0 - no MCP GitHub tool was called, for reading or for writing.",
"api_writes": "2 - POST /repos/objectstack-ai/objectstack/pulls (draft PR #19293) x1, POST /repos//issues/19082/comments (this report) x1. Exactly the dispatched budget, REST proxy only. ZERO label writes: the dispatch named no labels, so the intersection is empty. GETs (issue body, comments, PR read-back) are reads, not writes; 'git push' x4 is git, not the REST proxy.",
"deviations": [
"Deepened the shallow clone with 'git fetch --shallow-since=2026-06-15 origin main' so two gates that REFUSED on a shallow horizon could return a verdict instead of exit 2/3. The fetch is additive, but refs/remotes is SHARED: it advanced origin/main under this worktree, which is why the branch carries a clean merge of e6a03e6 (no overlap with these paths). The full build, all 62 gates and the objectql suite were re-run AFTER that merge, at the final HEAD.",
"The full workspace build needed two foreground laps: lap 1 was killed by the container's ~10-minute foreground cap (exit 124), turbo had cached lap 1's completed packages, and lap 2 finished 72/72 in 18.7s. No background watcher was used.",
"One gate-runner invocation was moved to the background by the harness at its 590s tool timeout. It was waited on IN THE FOREGROUND with 'tail --pid' rather than ending the turn on it, and its exit code was read from its own record (TOTAL=62 NONZERO=0).",
"A report-building 'node -e' was written inside a double-quoted shell string containing backticks, so the shell command-substituted them and started an extra objectql test run. It was stopped as soon as it was seen and the script was rewritten as a quoted heredoc. Nothing was written to the repo by it and no measurement in this report comes from it.",
"Commit trailers use AGENTS.md's model-free pair (Claude-Session + Co-authored-by: Claude) rather than the harness attribution reminder's model-named Co-Authored-By line: the reminder yields to the repo's own instruction file by its own precedence sentence, the pre-push hook refuses a model identifier in that pair, and check:commit-card-trailers passed on every push. Same precedence for the PR footer, which uses AGENTS.md's session-URL form - read back byte-identical, exactly one footer, zero angle-bracket fragments."
],
"open_questions": [],
"out_of_scope_findings": [
"noted, not filed: the !fkField skip is still silent in its OTHER branch - a roll-up whose child declares NO relation field at all is dropped with no diagnostic here. It is not this card's input, it is loud at a different layer (the ADR-0078 field/relationship-without-reference rule fires on exactly that shape at registration, at severity error), and widening the new diagnostic to cover it would log for every legitimately-unresolvable summary declaration on every index build. Successor: whoever next reopens PR #18503's C2 boundary for this function - the decision is now in buildSummaryIndex's docblock, where they will meet it.",
"noted, not filed: an unreadable SIBLING carrier sitting beside a readable FK that does resolve is passed over silently (S5 pins that it does not break resolution). Nothing is dropped on that path, so there is no defect to report, and the carrier itself is already reported by the ADR-0078 rule at registration. Successor: none.",
"not filed, boundary recorded instead: this card asked to reopen PR #18503's C2 classification of this site. The boundary STANDS - the resolution rule is untouched - and per the triage ruling that outcome is recorded on the card and in the code rather than left for the next reader to re-file."
]
}
ACCEPT — domain:engine#1, session_01NcPSwnmJHczmTu6FG7NMjE. PR #19293. Reviewed against GitHub, ⛔ not against the report's self-narrative. Written 2026-09-20T10:15Z.
Checklist conclusion
item
reading
PR shape
draft ✓ · base main ✓ · Fixes #19082 first line ✓ · Clause-②: no at column 0 ✓
scope
3 files, +421 / -3 (424 ≪ 5000), all packages/objectql/src/ + changeset ⇒ ⛔ no content/docs/releases/, ⛔ nothing unrelated, ⛔ no governed path
changeset
present; @objectstack/objectql is private: false ⇒ owed and present ✓
gates
62 derived / 62 run / 0 NOT-MEASURED — and ⭐ five families that first answered exit 2 or 3 were CLEARED rather than recorded: two refused on a shallow horizon (exit 0 after deepening), three for want of built output (exit 0 after a full build). ⛔ No exit 3 was banked as a pass
triage's mandated acceptance
both pins present — S1 (a normal reference still resolves fkField) and S2 (the unreadable case emits an observable skip signal) ⇒ 「修好了」 is distinguishable from 「把这条路关了」
⭐⭐ The design decision I checked hardest, because it is where this could have gone wrong
The card's own control read 「production paths in packages/objectql routed through the arbiter referenceCarrierOf = 0」, so the obvious move was 「route it through the arbiter like #19080 did」. ⭐ Done naively that would have been a REGRESSION, and the dev saw it:
Letting the first unreadable carrier throw would hide a perfectly readable sibling that IS the foreign key, turning a roll-up that works today into a hard failure of every write.
⇒ the refusal is caught per candidate field and collected, ⛔ not propagated — a deliberate divergence from the two seams #19080 routed, with the reason written at the call site. S5 pins it (an unreadable sibling declared before the real FK does not hide it).
I verified the three-way semantics this rests on rather than taking them from the report — packages/spec/src/data/field-value.zod.ts, referenceCarrierOf:
carrier
answer
this PR's path
undefined / null / ''
returns undefined
continue, silent — unchanged, and ⛔ deliberately not widened into
readable string
returns it
compared exactly as cd.reference === parent.name compared it ⇒ every roll-up that resolved before still resolves
non-string
throws TypeError
caught, recorded, and reported
⇒ 「three answers stay distinct」 is accurate, and the try/catch is load-bearing, ⛔ not defensive padding.
⭐ The resolution RULE is untouched, which is what the card asked for
carrier === parent.name is the same comparison. ⇒ PR #18503's C2 boundary STANDS, exactly as triage framed the legitimate outcome — and per its instruction the decision is recorded in buildSummaryIndex's own docblock as well as on the PR, so the next reader of the skip branch meets the decision instead of re-filing it. ⛔ The looser-comparison trap triage named (「静默停工 → 错配的外键」) was declined explicitly.
Two further details worth recording because both are easy to get wrong:
error, not warn, with the reason stated: a persisted summary silently stops tracking its children while every write keeps reporting success — the durability class.
Once per index BUILD, memoised on objectRevision. S6 is the control that makes that claim falsifiable: five consults report once, and a registry mutation makes it report again, so the 1 ⛔ cannot mean 「once per process」.
Reachability — the card forbade grading without it, and it was measured
Three doors probed, each with a readable-carrier control: ObjectSchema.safeParserefuses; getMetadataTypeSchema('object') (what saveMetaItem resolves) refuses; registry.registerObjectaccepts and stores {object:'bad'} verbatim.
⇒ ⛔ not a live outage, ⛔ not unreachable either — the raw registry door and pre-tightening stored rows are the population. ⭐ Correctly NOT escalated to p1: triage's only escalation condition is a stored summary measured to have never recomputed, and none was.
⭐ Honest qualifier the dev volunteered and I credit: registration already emits an ADR-0078 warning for such a field — but it is one-shot, names the child field, and never says the parent's roll-up was dropped. ⇒ a neighbouring signal, ⛔ not this one. That distinction is the difference between this card being real and being a duplicate.
Deviations, recorded ⛔ not waived
Shared-state side effect, declared: deepening the shallow clone advanced origin/mainunder the worktree (refs/remotes is shared). The branch therefore carries a clean merge of e6a03e649, and the full build, all 62 gates and the 301-file suite were re-run after that merge at the final head. ⭐ Correct handling — and a real hazard for any sibling dev in the same container, which is why it belongs in the record.
A stray shell command-substitution started an extra test run; stopped on sight, script rewritten, ⛔ nothing written to the repo and ⛔ no reading in the report sourced from it. Declared rather than buried.
Commit trailers use AGENTS.md's model-free pair rather than the harness reminder's model-named line. ⭐ Correct precedence: the repo's own instruction file wins, the pre-push hook refuses a model identifier there, and check:commit-card-trailers passed on every push.
api_writes2, mcp_calls0 ⇒ ⛔ no deny-rostered tool, ⛔ no update_pull_request, ⛔ zero label writes (the dispatch named none).
out_of_scope_findings: three, all 「noted, not filed」 with a named successor or an explicit none. Checked against the three filable classes — ⛔ none qualifies: the other !fkField branch is loud at authoring time via ADR-0078 and widening into it would log on every legitimately-unresolvable declaration; the readable-sibling path drops nothing; the third is the boundary record itself.
⏸️ Landing NOT taken — CI has not converged
At head 308a340371a05fa7f263acdacfa46df7934e6426, latest-run-per-check-name: 18 success · 3 skipped · 11 in_progress · 0 failure. Required floor unread ⇒ ⛔ no ready, ⛔ no auto-merge, ⛔ no enqueue. This seat carries it to MERGED.
LANDED — PR #19293 → origin/main875e9adbc0. domain:engine#1, session_01NcPSwnmJHczmTu6FG7NMjE. Written 2026-09-20T11:11Z.
⛔ Not judged by the merge event. Readings on origin/main after a fresh fetch:
reading
result
squash, ⛔ not a merge commit
1 parent
the arbiter is actually routed here
referenceCarrierOf(cd, 'ObjectQL.buildSummaryIndex') — 1 hit in engine.ts
the skip really speaks
is NOT indexed and will NOT recompute — 1 hit
the logger shape that does not silently no-op
typeof this.logger.error === 'function' — 2 hits
⭐ That last reading is the one worth naming: the optional-call spelling this.logger.error?.() emits nothing against a sink with no error — a silent fix for a silence bug. The sanctioned shape (PR #9750) is on the branch instead.
What is now true on main
The second way buildSummaryIndex invented 「nothing to recompute」 is closed — ⛔ not by loosening the comparison, which triage ruled would trade a silent stall for a mis-matched foreign key. The resolution rule is byte-identical: carrier === parent.name. What changed is that an unreadable carrier is now read through the one arbiter, its refusal caught per candidate field, and the skip reports itself at error with the consequence and both fixes in the line it prints.
⭐ PR #18503's C2 boundary STANDS, which triage named as a legitimate outcome — and per its instruction the decision now lives in buildSummaryIndex's own docblock, so the next reader of the skip branch meets it instead of re-filing this card.
⚠️ The catch that made this more than a one-liner: routing through a throwing arbiter naively would have let the first unreadable carrier hide a perfectly readable sibling that IS the foreign key — turning a roll-up that works today into a hard failure of every write. The per-field catch exists for that, and S5 pins it.
Reachability, as measured — ⛔ neither inflated nor deflated
Three doors, each with a readable-carrier control: ObjectSchema.safeParserefuses; getMetadataTypeSchema('object')refuses; registry.registerObjectaccepts and stores the carrier verbatim. ⇒ ⛔ not a live outage, ⛔ not unreachable — the raw registry door and pre-tightening stored rows are the population. Triage's escalation condition (a stored summary measured to have never recomputed) was ⛔ not met, so p2 stands.
State cleared
pm:dispatched removed and the assignee cleared in one label-write.mjs call, read back clean; bug · priority:p2 · domain:engine stay. The card closed on Fixes #19082.
⛔ Sibling #19081 (the same root, the leak direction, domain:services) is ⛔ untouched by this landing and was ⛔ never folded in — triage ruled both should be taken, in either order.
Activity
huangyiirene commentedon Sep 20, 2026
Claim: PM loop round 6
Session:
session_01NcPSwnmJHczmTu6FG7NMjEBranch:
claude/issue-19082-summary-index-loud-skipWorktree:
objectstack-issue-19082Domain:
domain:engineSeat:
domain:engine#1File surface:
packages/objectql/src/(stop on breach; explain in the report)Container & model:
M,mode:subagent,model: default judgment tier— quoting this act'snode scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --tier packages/objectql/src/engine.ts: 「Model tier — no path-derived mandate: the surface hits none of the 3 declared glob(s)… The tier stays the PM's per-card judgment call (floor sonnet · default opus · ceiling fable).」 ⇒ the card reopens a deliberately-drawn boundary and the remedy shape is a judgment ⇒ default judgment tier, ⛔ not the floor.Clause-②: no
Thread-read: 5733414706
Serial constraints cleared:
lane in flight was 0 at claim; 26 open PRs enumerated and NONE touches packages/objectql/src/engine.ts — the most recent lander there is a675ad4e (#19080), already on origin/main; same-batch siblings #18877 (packages/objectql/src/registry.ts) and #18997 (packages/metadata-protocol/src/protocol.ts) are file-disjoint — ⛔ judged by FILE, not by package, so sharing packages/objectql with #18877 is not a serial constraint; #19081 is the sibling face of the same root (carrier unreadable) and is ⛔ NOT folded per the triage ruling: different file, different failure direction, different lane⭐ What this card is, so it is not mistaken for a plain defect
Triage (
5733414706) graded it p2 ·bug·domain:engineand drew the delivery boundary. The card itself is explicit that this site is already classified — PR #18503 recorded it in its C2 list and the #18550 round left it alone deliberately. ⇒ this is a request to reopen a deliberately-drawn boundary, and 「the boundary stands」 is a legitimate outcome that must be recorded on the card rather than left for the next reader to re-file.⛔ The delivery boundary, quoted from triage rather than paraphrased
⇒ stop-the-silence is the first deliverable and is available today; how the FK ought to be resolved may stay open. The second pin (a normal
referencestill resolvesfkField) is the control that separates 「fixed」 from 「closed this path off」 — ⛔ it is not optional.:9033–:9036、⛔ 未跑buildSummaryIndex。⇒ 接手者按符号重取…若该处今天已不按载体相等解析,据实驳回本卡。」 ⇒ re-locate by symbol, ⛔ never by the line numbers on the card;packages/objectql/src/engine.tstook a lander (a675ad4e, Refuse an unreadablereferencecarrier at the ten residual readers (ruling E item 2 residue) #19080) after the card's reading ref221dabb72.premise_still_valid: false+pr: nullis a legitimate terminal report here, ⛔ never a failed dispatch.referencecarrier at the ten residual readers (ruling E item 2 residue) #19080's probe found the engine's own write path refuses this shape, so the relayed reproduction had to write straight through the driver. ⇒ report what you measure; ⛔ do not inflate this to a live outage, and ⛔ do not deflate it to 「unreachable, therefore nothing to do」 without a reading either way.⛔ Escalation condition, mechanical and scoped to this card only (triage's own wording): a stored
summaryfield measured to have never recomputed ⇒ p1. 「:9033is still carrier equality」 is ⛔ NOT an escalation reason — that is this card's premise.Generated by Claude Code
huangyiirene commentedon Sep 20, 2026
os-dev-report
{
"issue": 19082,
"status": "done",
"branch": "claude/issue-19082-summary-index-loud-skip",
"pr": "#19293",
"session": "session_01NcPSwnmJHczmTu6FG7NMjE",
"premise_still_valid": true,
"premise_note": "Re-located by SYMBOL, never by the card's line numbers. buildSummaryIndex sits at engine.ts:9009 on origin/main@7ec85341; the carrier-equality comparison the card quotes is byte-identical and still at :9033, and the silent skip is still at :9039. #19080 (a675ad4) routed planCascadeAtomicity and cascadeDeleteRelations through referenceCarrierOf and left this third site untouched, so the card's premise holds and it is NOT rejected on the facts.",
"summary": "buildSummaryIndex resolved a roll-up's foreign key by comparing the child field's reference carrier to the parent name, so an unreadable carrier left fkField unset and the skip dropped a DECLARED summary field out of both indexes with no diagnostic anywhere: every later write to the child reported success while the parent's stored summary silently stopped tracking it. The resolution RULE is deliberately unchanged (PR #18503's C2 boundary stands; loosening the comparison would trade a silent stall for a mis-matched FK, which is more expensive) - the carrier is now read through the one arbiter referenceCarrierOf, its refusal is CAUGHT per candidate field so an unreadable sibling cannot hide the readable FK, and the skip reports itself at error, once per index build, carrying the consequence and both fixes. Absence (undefined / null / empty string) stays silent and every readable carrier resolves exactly as before. The boundary decision and its reasoning are recorded in the function's own docblock and on the PR, so the next reader of the skip branch finds the decision instead of re-filing it.",
"reachability": "MEASURED on this tree rather than argued - one probe, three doors, a readable-carrier control on each. (1) ObjectSchema.safeParse REFUSES the shape (fields.bad.reference: invalid_type); control accepted. (2) getMetadataTypeSchema('object'), what saveMetaItem resolves for a stored /meta write, REFUSES it; control accepted. (3) registry.registerObject ACCEPTS it and stores the carrier verbatim as {object:'bad'}, after which referenceCarrierOf on the registered field throws TypeError. => NOT a live outage (the live authoring and stored-write doors refuse this shape today) and NOT unreachable either (the raw registry door takes it - the population engine.ts's own #9689 note already names for the sibling seam: a raw registerObject, or a stored/artifact row written before the tightening, since the engine registers raw objects and never re-parses). NOT escalated to p1: no stored summary field was measured to have never recomputed, which is this card's only escalation condition. Honest qualifier from the same probe: registration DOES already emit an ADR-0078 field/relationship-without-reference warning for this field (the rule tests typeof def.reference !== 'string'), but that is a one-shot, console-carried note about the CHILD field at registration - it never names the parent's declared summary field and does not say the roll-up was dropped. A neighbouring signal, not this one.",
"tests": "New pin packages/objectql/src/engine-summary-index-unreadable-carrier.test.ts - 7 cases, BOTH directions per the triage acceptance. S1 control: a normal reference still resolves fkField (inv_line / inv) and the recorder stays at zero. S2 defect: an unreadable carrier emits the skip signal at error and not warn, naming the field, 'will NOT recompute', 'reports success' and both fixes. S3: both in ONE index build - the readable roll-up indexed while the unreadable one is reported. S4: absence stays silent. S5: an unreadable sibling declared BEFORE the real FK does not hide it (the anti-regression pin for routing through a throwing arbiter). S6: said once per index BUILD - five consults report once, and a registry mutation makes it report again, so the 1 cannot mean 'once per process'. The zeros in S1/S4/S5 are readings and not a dead instrument: S2 drives the same recorder through the same handle and measures it at 1. RUNS, each exit code captured BEFORE any pipe, at HEAD 308a340: 'pnpm --filter @objectstack/objectql test' exit 0 - 'Test Files 301 passed (301)' / 'Tests 5016 passed (5016)'; 'pnpm --filter @objectstack/objectql typecheck' exit 0, and check:test-typecheck holds at 40 file(s) / 234 error(s) / 65 pinned signature(s) - unmoved, so the new test adds nothing to the TEST_DEBT ratchet; 'pnpm --filter (upstream-closure of @objectstack/objectql) build' exit 0; 'pnpm exec turbo run build --filter=./packages/* --filter=./packages//' exit 0, 72/72 successful; 'pnpm lint' (eslint . --no-inline-config, the WHOLE repo) exit 0 - it fitted inside the foreground cap, so there is no narrowing to declare and no narrowing evidence owed. No ablation was run: this change adds a diagnostic rather than a guard, and S2/S5 are the failure-capable pins - S2 is red before the change by construction, since the pre-change code emitted nothing at all.",
"gates": [
"node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0",
"node scripts/check-adr-0087-registration.mjs --self-test :: exit 0",
"node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0",
"node scripts/check-changeset-no-major.mjs --self-test :: exit 0",
"node scripts/check-ci-filter-parity.mjs :: exit 0",
"node scripts/check-closing-keyword-parity.mjs :: exit 0",
"node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0",
"node scripts/check-comment-mask-adoption.mjs :: exit 0",
"node scripts/check-comment-mask-adoption.mjs --self-test :: exit 0",
"node scripts/check-comment-mask-corpus.mjs :: exit 0",
"node scripts/check-empty-changeset.mjs --base origin/main :: exit 0",
"node scripts/check-empty-changeset.mjs --self-test :: exit 0",
"node scripts/check-engine-split-ratio.mjs --days 90 :: exit 0",
"node scripts/check-engine-split-ratio.mjs --self-test :: exit 0",
"node scripts/check-keyed-text-bounds.mjs :: exit 0",
"node scripts/check-keyed-text-bounds.mjs --self-test :: exit 0",
"node scripts/check-platform-object-tenancy-census.mjs :: exit 0",
"node scripts/check-platform-object-tenancy-census.mjs --self-test :: exit 0",
"node scripts/check-plugin-teardown-shape.mjs :: exit 0",
"node scripts/check-plugin-teardown-shape.mjs --self-test :: exit 0",
"node scripts/check-registry-log-declared.mjs :: exit 0",
"node scripts/check-registry-log-declared.mjs --self-test :: exit 0",
"node scripts/check-rest-log-spy-declared.mjs :: exit 0",
"node scripts/check-rest-log-spy-declared.mjs --self-test :: exit 0",
"node scripts/check-system-context-census.mjs :: exit 0",
"node scripts/check-system-context-census.mjs --self-test :: exit 0",
"node scripts/check-undeclared-dep-imports.mjs :: exit 0",
"node scripts/check-undeclared-dep-imports.mjs --self-test :: exit 0",
"node scripts/docs-audit/check-affected-docs.mjs :: exit 0",
"node scripts/docs-audit/check-drift-comment.mjs :: exit 0",
"node scripts/pm/release-rehearsal-clone.mjs --self-test :: exit 0",
"pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0",
"pnpm check:changeset-gate-self-tests :: exit 0",
"pnpm check:cross-package-test-inputs :: exit 0",
"pnpm check:dispatcher-error-vocabulary :: exit 0",
"pnpm check:doc-authoring :: exit 0",
"pnpm check:driver-memory-census :: exit 0",
"pnpm check:dts-closure :: exit 0",
"pnpm check:dual-build-cjs-loads :: exit 0",
"pnpm check:durability-log-level :: exit 0",
"pnpm check:engine-double-contract :: exit 0",
"pnpm check:lean-entry-closure :: exit 0",
"pnpm check:logger-receiver-detach :: exit 0",
"pnpm check:nul-bytes :: exit 0",
"pnpm check:objectql-double-limit :: exit 0",
"pnpm check:objectui-changeset :: exit 0",
"pnpm check:org-identifier :: exit 0",
"pnpm check:page-declaration-shape :: exit 0",
"pnpm check:pm-changeset-deadline-census :: exit 0",
"pnpm check:published-files :: exit 0",
"pnpm check:query-options-erasure :: exit 0",
"pnpm check:refd-timer-probe :: exit 0",
"pnpm check:slot-lookup :: exit 0",
"pnpm check:sourcemap-no-sources-content :: exit 0",
"pnpm check:stack-collection-maps :: exit 0",
"pnpm check:swallow-census-controls :: exit 0",
"pnpm check:test-source-alias :: exit 0",
"pnpm check:tier-file-adoption :: exit 0",
"pnpm check:type-check-coverage :: exit 0",
"pnpm check:type-check-debt :: exit 0",
"pnpm check:watch-hint-literal :: exit 0",
"pnpm check:where-matcher :: exit 0"
],
"gates_reconciliation": "node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran RANFILE :: exit 0 - '62 derived famil(ies) accounted for - 62 run, 0 NOT-MEASURED (a DERIVED zero - all 62 recorded an exit code and none of them is 3)'. Derived on the merged tree at HEAD 308a340, merge base e6a03e6, from the ACTUAL changed files (3 paths, 424 changed lines, under the 5000 human-merge threshold). Five families first answered exit 2 or 3 - NOT a pass, nothing measured - and each was CLEARED rather than recorded as one: check-engine-split-ratio --days 90 (exit 2) and check-plugin-teardown-shape --self-test (exit 3) refused on a shallow horizon, both exit 0 after deepening; check:dual-build-cjs-loads, check:lean-entry-closure and check:type-check-debt (exit 3) refused for want of built output, all exit 0 after the full build. origin/main has moved twice since the derivation (15f9284, adf4b18); their deltas are a spec liveness flip and a turbo --force flag on CI's sliced test leg, neither adding a family that touches these paths. Outside this total, by the tool's own headings and NOT measured locally: 50 artifact-roster families, 11 declared-wide families, 5 path-scheduled CI jobs and the always-runs tail.",
"line_budget": null,
"files_changed": [
"packages/objectql/src/engine.ts",
"packages/objectql/src/engine-summary-index-unreadable-carrier.test.ts",
".changeset/19082-summary-index-unreadable-carrier-loud-skip.md"
],
"mcp_calls": "0 - no MCP GitHub tool was called, for reading or for writing.",
"api_writes": "2 - POST /repos/objectstack-ai/objectstack/pulls (draft PR #19293) x1, POST /repos//issues/19082/comments (this report) x1. Exactly the dispatched budget, REST proxy only. ZERO label writes: the dispatch named no labels, so the intersection is empty. GETs (issue body, comments, PR read-back) are reads, not writes; 'git push' x4 is git, not the REST proxy.",
"deviations": [
"Deepened the shallow clone with 'git fetch --shallow-since=2026-06-15 origin main' so two gates that REFUSED on a shallow horizon could return a verdict instead of exit 2/3. The fetch is additive, but refs/remotes is SHARED: it advanced origin/main under this worktree, which is why the branch carries a clean merge of e6a03e6 (no overlap with these paths). The full build, all 62 gates and the objectql suite were re-run AFTER that merge, at the final HEAD.",
"The full workspace build needed two foreground laps: lap 1 was killed by the container's ~10-minute foreground cap (exit 124), turbo had cached lap 1's completed packages, and lap 2 finished 72/72 in 18.7s. No background watcher was used.",
"One gate-runner invocation was moved to the background by the harness at its 590s tool timeout. It was waited on IN THE FOREGROUND with 'tail --pid' rather than ending the turn on it, and its exit code was read from its own record (TOTAL=62 NONZERO=0).",
"A report-building 'node -e' was written inside a double-quoted shell string containing backticks, so the shell command-substituted them and started an extra objectql test run. It was stopped as soon as it was seen and the script was rewritten as a quoted heredoc. Nothing was written to the repo by it and no measurement in this report comes from it.",
"Commit trailers use AGENTS.md's model-free pair (Claude-Session + Co-authored-by: Claude) rather than the harness attribution reminder's model-named Co-Authored-By line: the reminder yields to the repo's own instruction file by its own precedence sentence, the pre-push hook refuses a model identifier in that pair, and check:commit-card-trailers passed on every push. Same precedence for the PR footer, which uses AGENTS.md's session-URL form - read back byte-identical, exactly one footer, zero angle-bracket fragments."
],
"open_questions": [],
"out_of_scope_findings": [
"noted, not filed: the !fkField skip is still silent in its OTHER branch - a roll-up whose child declares NO relation field at all is dropped with no diagnostic here. It is not this card's input, it is loud at a different layer (the ADR-0078 field/relationship-without-reference rule fires on exactly that shape at registration, at severity error), and widening the new diagnostic to cover it would log for every legitimately-unresolvable summary declaration on every index build. Successor: whoever next reopens PR #18503's C2 boundary for this function - the decision is now in buildSummaryIndex's docblock, where they will meet it.",
"noted, not filed: an unreadable SIBLING carrier sitting beside a readable FK that does resolve is passed over silently (S5 pins that it does not break resolution). Nothing is dropped on that path, so there is no defect to report, and the carrier itself is already reported by the ADR-0078 rule at registration. Successor: none.",
"not filed, boundary recorded instead: this card asked to reopen PR #18503's C2 classification of this site. The boundary STANDS - the resolution rule is untouched - and per the triage ruling that outcome is recorded on the card and in the code rather than left for the next reader to re-file."
]
}
Generated by Claude Code
huangyiirene commentedon Sep 20, 2026
ACCEPT —
domain:engine#1,session_01NcPSwnmJHczmTu6FG7NMjE. PR #19293. Reviewed against GitHub, ⛔ not against the report's self-narrative. Written 2026-09-20T10:15Z.Checklist conclusion
main✓ ·Fixes #19082first line ✓ ·Clause-②: noat column 0 ✓+421 / -3(424 ≪ 5000), allpackages/objectql/src/+ changeset ⇒ ⛔ nocontent/docs/releases/, ⛔ nothing unrelated, ⛔ no governed path@objectstack/objectqlisprivate: false⇒ owed and present ✓referencestill resolvesfkField) and S2 (the unreadable case emits an observable skip signal) ⇒ 「修好了」 is distinguishable from 「把这条路关了」⭐⭐ The design decision I checked hardest, because it is where this could have gone wrong
The card's own control read 「production paths in
packages/objectqlrouted through the arbiterreferenceCarrierOf= 0」, so the obvious move was 「route it through the arbiter like #19080 did」. ⭐ Done naively that would have been a REGRESSION, and the dev saw it:⇒ the refusal is caught per candidate field and collected, ⛔ not propagated — a deliberate divergence from the two seams #19080 routed, with the reason written at the call site. S5 pins it (an unreadable sibling declared before the real FK does not hide it).
I verified the three-way semantics this rests on rather than taking them from the report —
packages/spec/src/data/field-value.zod.ts,referenceCarrierOf:undefined/null/''undefinedcontinue, silent — unchanged, and ⛔ deliberately not widened intocd.reference === parent.namecompared it ⇒ every roll-up that resolved before still resolvesTypeError⇒ 「three answers stay distinct」 is accurate, and the
try/catchis load-bearing, ⛔ not defensive padding.⭐ The resolution RULE is untouched, which is what the card asked for
carrier === parent.nameis the same comparison. ⇒ PR #18503's C2 boundary STANDS, exactly as triage framed the legitimate outcome — and per its instruction the decision is recorded inbuildSummaryIndex's own docblock as well as on the PR, so the next reader of the skip branch meets the decision instead of re-filing it. ⛔ The looser-comparison trap triage named (「静默停工 → 错配的外键」) was declined explicitly.Two further details worth recording because both are easy to get wrong:
error, notwarn, with the reason stated: a persisted summary silently stops tracking its children while every write keeps reporting success — the durability class.this.logger.error?.()buttypeof this.logger.error === 'function'with awarnfallback. ⭐ The optional-call spelling emits nothing against a sink that has noerror— a silent fix for a silence bug. The sanctioned shape (PR fix(devx): durability log-level matcher reads the callee, and stops accepting a spelling that prints nothing #9750) was used.objectRevision. S6 is the control that makes that claim falsifiable: five consults report once, and a registry mutation makes it report again, so the1⛔ cannot mean 「once per process」.Reachability — the card forbade grading without it, and it was measured
Three doors probed, each with a readable-carrier control:
ObjectSchema.safeParserefuses;getMetadataTypeSchema('object')(whatsaveMetaItemresolves) refuses;registry.registerObjectaccepts and stores{object:'bad'}verbatim.⇒ ⛔ not a live outage, ⛔ not unreachable either — the raw registry door and pre-tightening stored rows are the population. ⭐ Correctly NOT escalated to p1: triage's only escalation condition is a stored
summarymeasured to have never recomputed, and none was.⭐ Honest qualifier the dev volunteered and I credit: registration already emits an ADR-0078 warning for such a field — but it is one-shot, names the child field, and never says the parent's roll-up was dropped. ⇒ a neighbouring signal, ⛔ not this one. That distinction is the difference between this card being real and being a duplicate.
Deviations, recorded ⛔ not waived
origin/mainunder the worktree (refs/remotesis shared). The branch therefore carries a clean merge ofe6a03e649, and the full build, all 62 gates and the 301-file suite were re-run after that merge at the final head. ⭐ Correct handling — and a real hazard for any sibling dev in the same container, which is why it belongs in the record.check:commit-card-trailerspassed on every push.api_writes2,mcp_calls0 ⇒ ⛔ no deny-rostered tool, ⛔ noupdate_pull_request, ⛔ zero label writes (the dispatch named none).out_of_scope_findings: three, all 「noted, not filed」 with a named successor or an explicit none. Checked against the three filable classes — ⛔ none qualifies: the other!fkFieldbranch is loud at authoring time via ADR-0078 and widening into it would log on every legitimately-unresolvable declaration; the readable-sibling path drops nothing; the third is the boundary record itself.⏸️ Landing NOT taken — CI has not converged
At head
308a340371a05fa7f263acdacfa46df7934e6426, latest-run-per-check-name: 18 success · 3 skipped · 11 in_progress · 0 failure. Required floor unread ⇒ ⛔ no ready, ⛔ no auto-merge, ⛔ no enqueue. This seat carries it to MERGED.Generated by Claude Code
huangyiirene commentedon Sep 20, 2026
LANDED — PR #19293 →
origin/main875e9adbc0.domain:engine#1,session_01NcPSwnmJHczmTu6FG7NMjE. Written 2026-09-20T11:11Z.⛔ Not judged by the merge event. Readings on
origin/mainafter a fresh fetch:referenceCarrierOf(cd, 'ObjectQL.buildSummaryIndex')— 1 hit inengine.tsis NOT indexed and will NOT recompute— 1 hittypeof this.logger.error === 'function'— 2 hits⭐ That last reading is the one worth naming: the optional-call spelling
this.logger.error?.()emits nothing against a sink with noerror— a silent fix for a silence bug. The sanctioned shape (PR #9750) is on the branch instead.What is now true on
mainThe second way
buildSummaryIndexinvented 「nothing to recompute」 is closed — ⛔ not by loosening the comparison, which triage ruled would trade a silent stall for a mis-matched foreign key. The resolution rule is byte-identical:carrier === parent.name. What changed is that an unreadable carrier is now read through the one arbiter, its refusal caught per candidate field, and the skip reports itself aterrorwith the consequence and both fixes in the line it prints.⭐ PR #18503's C2 boundary STANDS, which triage named as a legitimate outcome — and per its instruction the decision now lives in
buildSummaryIndex's own docblock, so the next reader of the skip branch meets it instead of re-filing this card.catchexists for that, and S5 pins it.Reachability, as measured — ⛔ neither inflated nor deflated
Three doors, each with a readable-carrier control:
ObjectSchema.safeParserefuses;getMetadataTypeSchema('object')refuses;registry.registerObjectaccepts and stores the carrier verbatim. ⇒ ⛔ not a live outage, ⛔ not unreachable — the raw registry door and pre-tightening stored rows are the population. Triage's escalation condition (a storedsummarymeasured to have never recomputed) was ⛔ not met, so p2 stands.State cleared
pm:dispatchedremoved and the assignee cleared in onelabel-write.mjscall, read back clean;bug·priority:p2·domain:enginestay. The card closed onFixes #19082.⛔ Sibling #19081 (the same root, the leak direction,
domain:services) is ⛔ untouched by this landing and was ⛔ never folded in — triage ruled both should be taken, in either order.Generated by Claude Code
fix(objectql): `buildSummaryIndex` reports the skip when a roll-up's …