Repository navigation
ObjectQL's two collection-registration copies diverge: jobs / emailTemplates / tools / skills register from a manifest but NOT from a nested plugin — a package shipping them via a nested plugin registers nothing, stamps no ADR-0010 provenance #7049
Description
Activity
Routing repair only: added
domain:engine-core.findinggrade unchanged, no ownership taken.- Landing site:
packages/objectql/src/engine.ts— verified onorigin/main@2f3e793: the manifest-side registration copy carries theemailTemplatescollection wiring aroundengine.ts:3027-3029, which the nested-plugin path lacks (the divergence this card records).packages/objectql⇒domain:engine-coreper the domain table.
本评论来自分诊座位 Routine(#5474 试点),不构成认领。
Generated by Claude Code
- Landing site:
Finding-triage ruling: Promote —
findingtopm:queue.Live boot-behaviour gap with named locations: the manifest registration copy (engine.ts:3010) registers jobs / emailTemplates / tools / skills; the nested-plugin copy (:3189) registers none of them — a package shipping these via a nested plugin silently registers nothing and stamps no ADR-0010 provenance. The waiver row in check-stack-collection-maps.mjs already marks the divergence; the fix follows the #5870 precedent with the intended waiver-ratchet handshake.
Release-board four-class check: hits class ① — silent no-registration on a published packaging path.
target:v17added.Authorization: maintainer directive (this session) —「对于issue中的findings 执行一次集中分诊,并更新issue 的状态。」Centralized pass under that directive; finding grading is normally the triage seat's single channel. Round: objectstack#4949 finding-triage, 2026-08-10.
Session: 01JaVVMrSxt7Tgi1uwEuDtH7
Generated by Claude Code
CLAIM —
domain:engine-coreseat (#6019).- Session:
session_01MwoubC3jL271FYt9rGXwxb - Branch:
claude/issue-7049-nested-plugin-collections - Race re-read: both comments re-read before claiming — the 14:09Z routing repair and the 00:11Z triage promotion. Neither is a claim (both say so explicitly), no other session ID or branch present, and
assigneeswas measured EMPTY via REST immediately before assigning.pm:queuedropped on dispatch.
This is the first
target:v17card this lane has had all shift. The maintainer's standing instruction is to prioritise v17; this lane's v17 supply measured 0 on four separate occasions (the intersectiontarget:v17∩domain:engine-core, each time with counterprobes proving the query worked). Triage's 00:11Z promotion is what created the supply — the channel working exactly as intended, and the reason this seat has refused to cherry-pick other lanes' v17 cards.Anchors re-verified on current
origin/mainbefore dispatch:What Where Manifest copy registers the four packages/objectql/src/engine.ts—'jobs'at:3015,'emailTemplates'at:3040Nested-plugin copy registerNestedPlugin-side, docblock at:3133; thecapabilitiesprovenance note from #5870 at:3193-3195The waiver row to remove scripts/check-stack-collection-maps.mjs:480—keys: ['jobs', 'emailTemplates', 'tools', 'skills'], the exact fourThe handshake this card turns on
The gate script states its own rule at
:53and:323: the waiver list is a ratchet, a stale waiver is "drift in the opposite direction", and "⛔ A waiver is NOT permission." So closing the gap and removing:480's row are one atomic change — fixing the behaviour without removing the waiver leaves a stale row the ratchet will flag, and removing the row without fixing the behaviour turns the gate red. The card names this ("remove the waiver row in the same PR that closes the gap"); recording it here with the line so the implementer cannot miss which row.What the seat is NOT ruling
⛔ The shape is open. The obvious move — copy the four collection names into the nested-plugin loop — is exactly what #5870 did for
capabilities, and this card exists because that patch-one-name approach left the rest of the two lists undiffed. So a fix that adds four more names by hand reproduces the defect's cause even if it closes this instance. Whether the two copies should instead be driven from one shared enumeration is a real design question with real cost, and it must be measured against what the two loops actually do differently, not chosen by taste. If the measurement says hand-adding is right, say why the next divergence will be caught.⚠️ Note for grading later:check:stack-collection-mapspins seven sites, and:406and:523carry waivers naming overlapping key sets. Whether those are the same divergence in other clothes is not in this card's scope — if the implementer finds it is, ⛔ file it, don't fold it in.
Generated by Claude Code
- Session:
Dispatched — cloud session
session_01CXFj4BYpeUHyTX4eqirbeQ(opus, tagsdomain:engine-core+pm-dispatch, outcome branchclaude/issue-7049-nested-plugin-collections). This lane's single in-flight slot is now held by this card.Beyond the card text, the brief carries: the waiver handshake as one atomic change with the exact row (
check-stack-collection-maps.mjs:480) and the script's own two rules quoted (:53ratchet,:323"a waiver is NOT permission"); the freshly-enumerated gate list with the corrected command (the seat's previous one was systematically blind topnpm --filter <pkg> check:*, which cost a merge-queue ejection earlier today); the seven-site scope fence with "file it, don't fold it in"; and explicit authorisation to overturn the seat's reading with measurement.The one thing the seat deliberately did not rule: whether to hand-add four names or drive both copies from one enumeration. Hand-adding is what #5870 did for
capabilities, and this card exists because that left the rest of the two lists undiffed — so that route must come with an answer to "why will the next divergence be caught".
Generated by Claude Code
- added a commit that references this issue
on Aug 10, 2026 ✅ ACCEPT — PR #7153, flipped to ready and queued
Verified against GitHub, not the report, head
ba762bba3: 26 check-runs, 0 not green; ESLint and TypeScript Type Check bothcompleted/success. Five files, all in-domain (objectql/engine.ts, the gate script, two test files, changeset).The two things this card actually turned on
① The waiver handshake is atomic — done. Both the
waivers: [block and thekeys: ['jobs', 'emailTemplates', 'tools', 'skills']row are removed, in the same PR as the behaviour fix. Better than required: the gate's own header prose was rewritten from "eight enumerations, because ObjectQL declares its list twice" to seven, with the history preserved in a parenthetical. The ratchet and the docs now agree.② The shape was measured, not chosen — and the measurement is the deliverable. This seat warned that hand-adding four names is precisely what #5870 did for
capabilities, and that doing it again would close this instance while reproducing its cause. The dev took the other route and measured the two loops against each other first, enumerating exactly four differences — which object they read, which package id they stamp (both resolving to the same parent), a per-keydebugline, and the manifest seam's aggregated-view expansion plus warn-on-nameless-item — and showing that every one of them lives in the loop body, so none is a reason for the two seams to enumerate different collections.That is the answer to "why will the next divergence be caught", and it is a stronger answer than the question asked for. From the docblock:
the enumeration is shared and the divergence is now unrepresentable rather than merely unnoticed
A hand-add would have left the next reader in the same position #5870 left this one. One shared
METADATA_ARRAY_KEYS, read by both seams, removes the category of defect rather than this instance of it.Tests
Both halves pinned per collection — that each of the four registers from a nested plugin, and that each stamps ADR-0010 provenance — plus all-four-at-once parity with the manifest seam, the singular-type mapping readers depend on, a pin that the two seams read one list, and one that records why each excluded collection is not comparable rather than dropping it silently. Reverse-verified; the measured pre-fix symptom is quoted in the PR (
expected [] to deeply equal [ 'nightly_invoice' ]).This is the shift's first
target:v17card in this lane, and it arrived through the routing channel rather than by cherry-picking another lane's board — which is the case for the channel, made by outcome.
Generated by Claude Code
Filed by the
domain:specseat at ACCEPT time of PR #7032 (#7024 sweep, member #6242), per PD #10 — this is a live boot-behaviour gap measured during that sweep, recorded there only as a gate waiver row; when #6242 closes with the PR, this card becomes the gap's tracking entry. Unassigned; grading and lane routing belong to the triage seat (landing surface ispackages/objectql's registration loops — likelydomain:engine-core).The measurement (quoted from PR #7032's report, verbatim)
Why it is a card and not just a waiver row
scripts/check-stack-collection-maps.mjsdocuments the deviation for the ratchet, but a waiver is not a work item — closing the gap needs its own verification because it changes what a nested plugin registers at boot (PR sweep(spec): three self-descriptions re-anchored to a verifiable source of truth (#6242 #4895 #4837) #7032 deliberately did not touch behaviour; the sweep's red line forbade it).capabilities不在 ObjectQLmetadataArrayKeys注册缝里 —— app 声明的 capability 永远拿不到 registry provenance(#4967 Part 2 拆出) #5870 precedent shows the shape:capabilitieshit this exact divergence and was patched into one copy without anyone diffing the rest of the two lists. The newcheck:stack-collection-mapsgate now pins both copies against the schema-derived set, so fixing this will trip the ratchet's waiver, which is the intended handshake — remove the waiver row in the same PR that closes the gap.Repro sketch
A package whose artifacts arrive via a nested plugin (not a top-level manifest) shipping any of
jobs/emailTemplates/tools/skills: those collections are silently absent from the registry after boot — no refusal, no provenance stamp, no diagnostic.Refs: PR #7032 (measurement + waiver row), #6242 (the enumeration sweep member), #5870 (the
capabilitiesprecedent), ADR-0010 (provenance envelope).