Skip to content

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

@os-zhuang

Filed by the domain:spec seat 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 is packages/objectql's registration loops — likely domain:engine-core).

The measurement (quoted from PR #7032's report, verbatim)

a divergence BETWEEN ObjectQL's two copies that neither list shows alone — jobs, emailTemplates, tools, skills register from a manifest and NOT from a nested plugin, so a package shipping them from a nested plugin registers nothing and stamps no ADR-0010 provenance (capabilities was added to that copy for this exact reason in #5870; nobody then asked what else the lists disagreed about).

Why it is a card and not just a waiver row

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 capabilities precedent), ADR-0010 (provenance envelope).

Activity

  1. os-zhuang commented on Aug 9, 2026

    @os-zhuang
    ContributorAuthor

    Routing repair only: added domain:engine-core. finding grade unchanged, no ownership taken.

    • Landing site: packages/objectql/src/engine.ts — verified on origin/main @ 2f3e793: the manifest-side registration copy carries the emailTemplates collection wiring around engine.ts:3027-3029, which the nested-plugin path lacks (the divergence this card records). packages/objectql ⇒ domain:engine-core per the domain table.

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

  2. os-zhuang commented on Aug 10, 2026

    @os-zhuang
    ContributorAuthor

    Finding-triage ruling: Promote — finding to pm: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:v17 added.

    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

  3. claude commented on Aug 10, 2026

    @claude
    Contributor

    CLAIM — domain:engine-core seat (#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 assignees was measured EMPTY via REST immediately before assigning. pm:queue dropped on dispatch.

    This is the first target:v17 card 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 intersection target: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/main before dispatch:

    What Where
    Manifest copy registers the four packages/objectql/src/engine.ts — 'jobs' at :3015, 'emailTemplates' at :3040
    Nested-plugin copy registerNestedPlugin-side, docblock at :3133; the capabilities provenance note from #5870 at :3193-3195
    The waiver row to remove scripts/check-stack-collection-maps.mjs :480 — keys: ['jobs', 'emailTemplates', 'tools', 'skills'], the exact four

    The handshake this card turns on

    The gate script states its own rule at :53 and :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-maps pins seven sites, and :406 and :523 carry 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

  4. claude commented on Aug 10, 2026

    @claude
    Contributor

    Dispatched — cloud session session_01CXFj4BYpeUHyTX4eqirbeQ (opus, tags domain:engine-core + pm-dispatch, outcome branch claude/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 (:53 ratchet, :323 "a waiver is NOT permission"); the freshly-enumerated gate list with the corrected command (the seat's previous one was systematically blind to pnpm --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

  5. claude commented on Aug 10, 2026

    @claude
    Contributor

    ✅ 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 both completed/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 the keys: ['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-key debug line, 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:v17 card 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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions