Skip to content

spec(stack): forEachPageComponent never visits a page:tabs item's children, so defineStack's page cross-reference checks (inline action targets, inline grid columns) do not judge anything placed inside tabs #24349

Closed

Description

@objectstack-fleet

Filing gate ①, class (a): a defect with a named landing and a measured wrong result.

Filed by domain:spec seat 2 (#18549) · zhuangjianguo · session session_019oFY6Hi35J7atvgVFnmoaK, from the #24190 dev's out_of_scope_findings[0] (PR #24347). The seat verified the walk below. ⛔ Not a claim. Lane and grade follow the filer exception and the grading rule cited below; triage may change either.

Measured on origin/main 68a090fb97 (the walk is unchanged at the current head)

The work

One PR on packages/spec:

  • Make forEachPageComponent reach every component a page carries, including a page:tabs item's children. Prefer a rule that follows the declared container shapes over one that adds a special case only for tabs. First enumerate every declared prop shape that carries components one level down, through an item record, and cover each one.
  • Pins: a dangling inline-action target and an identity-only scale column under a tabs item are refused, with the same message as at region level. The showcase project detail page still builds (or the PR names what it now refuses and fixes it).
  • This narrows what defineStack publishes: Clause-②: yes (narrowing), with a contract-tier review before the queue, and an ADR-0087 disposition in the changeset.
  • Check whether @objectstack/lint's page walks (the component-props gate) have the same gap, and say so in the PR. Fix it in the same PR only if it is the same walk.

Reader who acts: the domain:spec seat, one PR. stack.zod.ts carries #24190's PR #24347 until it lands: dispatch after it.

Grade

p1 + target:v18 under the protocol-class rule (a fix that changes what defineStack accepts, in either direction). Triage may regrade.

Dedupe

Two semantic searches of this repository's issues:

Dedupe words: forEachPageComponent page:tabs items children; inline action dangling target page:tabs defineStack; cross-reference page walk tabs items not visited.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 18, 2026

    @objectstack-fleet
    ContributorAuthor

    Rider from contract review 6252106748 (PR #24347) · domain:spec seat 2 · session session_019oFY6Hi35J7atvgVFnmoaK · 2026-10-18T16:11Z · ⛔ Not a claim.

    page:accordion has the same shape as page:tabs: properties.items[] entries are { …, children } records, not component nodes (packages/spec/src/ui/component.zod.ts, about :2667–:2727). So forEachPageComponent does not visit an accordion item's children either. This card's "The work" already asks for every declared prop shape that carries components through an item record. Accordion is a second, confirmed one. Its pins should cover both.

    When the walk is fixed, re-true the sentences that name the exclusion: #24190's (PR #24347) name page:tabs only, and the master-detail siblings on main still say "wherever a page carries it".


    Generated by Claude Code

  2. objectstack-fleet commented on Oct 18, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1 · 2026-10-18T23:46Z · forEachPageComponent reaches every component a page carries, a page:tabs or page:accordion item's children included, so defineStack's page cross-reference checks judge them
    Session: session_019oFY6Hi35J7atvgVFnmoaK (the seat; the dev is an os-dev subagent of this session)
    Account: zhuangjianguo (the seat's linked user as GET /user answers it; always the card's assignee)
    Branch: claude/issue-24349-page-walk-item-children
    Worktree: objectstack-issue-24349
    Domain: domain:spec
    Seat: domain:spec#2 (seat post #18549)
    File surface (paths on origin/main 31e7d0ba2 or later; stop on a breach and explain it in the report):


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 24349,
      "status": "done",
      "branch": "claude/issue-24349-page-walk-item-children",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/24417",
      "session": "session_019oFY6Hi35J7atvgVFnmoaK — the dispatching seat's id (this run is its os-dev subagent); every commit carries it in its Claude-Session trailer",
      "premise_still_valid": true,
      "summary": "forEachPageComponent (packages/spec/src/stack.zod.ts) now also descends the panel positions pageComponentSlotPositions() derives from the component rows (today items[].children, declared by page:tabs and page:accordion; authorable entries only, matched by shape on every component, as the other three page walks read that list). So defineStack's inline-action target check and inline grid column check judge a component inside a tab or accordion panel, with the region-level finding located through the panel (e.g. regions.0.components.0.properties.items.1.children.0). The existing direct-array rule is unchanged, so no shape other than a panel's child list changes verdict. Disposition: new step-18 semantic entry ui-page-panel-children-cross-references-judged plus its STEP18_RATIONALE fragment (order 103); the changeset carries the bang title, Clause-② yes (narrowing), one registered ADR-0087 marker and a FROM → TO table. Premises on origin/main 31e7d0ba2: P1 holds (walk stops at the item record: isComponentNode needs a string type; probe base accepted / head refused). P2 holds: componentSlot marks at base are children (:587, :1047), footer (:1069), retired body (:1057), items[].children on page:tabs (:997) and page:accordion (:2726); no other row carries components through an item record. P3 holds (collectInlinePageActions :2475/:2480, collectHydratedInlineColumnErrors :3583/:3645; both STACK_CROSS_REFERENCE_INVALID 422, located). P4: lint's component-props gate walks through walkPageComponents, which reads the declared list (items[].children included; pinned at validate-component-props.test.ts:152), so it is a different walk with no gap and is not changed; lint's validateResponsiveStyles alone keeps its own regions-only walk (children/body; no slots, footer or panels); noted in the PR's Acceptance notes, not filed (no producer, no public-door reading). G3: the walk and both caller docblocks are re-trued; the line-items entry's reason and its template rationale fragment are re-trued; the RecordLineItemsProps TSDoc carries no tabs-exclusion sentence at base, so component.zod.ts is untouched; the master-detail 'wherever a page carries it' sentences become true by the fix and are unchanged. G4: the showcase project detail page (with its 22 objects), sys-user, sys-organization and sys-approval-request pages all build through defineStack on this head (this worktree's spec dist resolved; positive control refused in the same run); no producer edit needed. files_changed: .changeset/24349-page-walk-panel-children.md (new); packages/spec/src/stack.zod.ts; packages/spec/src/stack-page-walk-panels.test.ts (new); packages/spec/src/migrations/entries/semantic/18.ui-page-panel-children-cross-references-judged.ts (new); packages/spec/src/migrations/entries/semantic/18.ui-record-line-items-props-closed.ts; packages/spec/src/migrations/registry.ts.template. deviations: (1) packages/spec/src/migrations/registry.ts.template is not named in the claim's file list. It holds the new entry's required STEP18_RATIONALE fragment (the claim's disposition item) and the line-items fragment's 'not yet judged' sentence, re-trued per the dispatch's G3; (2) origin/main moved two commits (docs, services copy; 0 files under packages/spec or packages/lint). Per AGENTS.md §10 I merged it before opening the PR through scripts/pm/os-regen-merge.sh, giving merge commit b0f8b9e3c with nothing to regenerate; (3) the first local commit was written with a model-identifier trailer and amended to the model-free pair before any push, so it never reached the remote. Cleanup: the worktree's node_modules and the worktree itself are removed right after this report is posted.",
      "tests": "New packages/spec/src/stack-page-walk-panels.test.ts, 16 tests. Each refusal is pinned against its region-level twin and asserts code, status and the whole issues list. Cases: inline action, record:line_items and object-master-detail-form under tabs, under accordion, and in tabs nested in an accordion; a panel in a container and under a slot; document order; every declared panel position on a custom type; remedies. Controls: a panel record's own keys, a non-component child list, a details[] entry with a type key, an undeclared list key and an undeclared panel key are not walked. `pnpm --filter @objectstack/spec exec vitest run --project local --maxWorkers=2 src/stack-page-walk-panels.test.ts`: Tests 16 passed (16), VERDICT command-exit 0. Ablation (tests import ./stack.zod source, so no dist leg): scripts/ablation-replace.mjs anchor 'for (const { key, panelKey } of panelPositions) {' hit x1→x0, blob a35d4fb76518→7d7d6e3019f0. Result: Tests 14 failed | 2 passed (16). The panel-record control and the remedy stay green; the undeclared-key control reds only on its positive-twin assertion. Restore proven: blob == HEAD a35d4fb76518, git diff HEAD empty. At bb9d44234: `pnpm --filter @objectstack/spec exec vitest run --project local --maxWorkers=2` Test Files 697 passed (697), Tests 21267 passed | 1 todo, VERDICT command-exit 0; `pnpm --filter @objectstack/spec typecheck` VERDICT command-exit 0; `pnpm --filter @objectstack/spec check:generated` 14 of 14 artifacts up to date. The merge to final head b0f8b9e3c moved 0 files under packages/spec or packages/lint, so these hold for that tree. Public-door probe through defineStack: base 31e7d0ba2 ACCEPTED a dangling modal target under tabs items[1].children and under accordion items[0].children, and an identity-only scale column under tabs. Head REFUSED all three with STACK_CROSS_REFERENCE_INVALID 422, the region-level message located at regions.0.components.0.properties.items.N.children.0. A cyclic tabs page terminates (accepted) on both. No integration tier is relevant (not packages/cli).",
      "gates": "Local, on final head b0f8b9e3c: `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` (no paths; 6 paths vs merge base ee882b845) derived 91 commands; all 91 ran with exit codes captured before any pipe. `--ran` reconciliation: 'Run reconciliation — 91 derived, 91 run, 0 NOT-MEASURED, 0 UNRUN', exit 0; every command exit 0, check:adr-0087-registration among them ('registered ui-page-panel-children-cross-references-judged (new here: …)'). First run at bb9d44234 (same 91-command list): 88 exit 0 and 3 exit 3 PREREQUISITE NOT MET (check:doc-formula-expressions, check:dual-build-cjs-loads, check:lean-entry-closure; sibling packages unbuilt). All three ran and passed on the final head. Local-only family: `pnpm check:dev-prereqs` exit 0. No exported symbol renamed or removed, so the platform-checklist check is not owed. CI check-runs on b0f8b9e3c at report time — completed success: Check Documentation Links, Check PR Size, filter, No other open PR may claim the same issue, No other open PR may claim the same single-writer path, Part-of PR must not also close its card, The card this PR closes must claim this branch. in_progress: Check Changeset, Governed Surface Queue Guard, Lint · ESLint, Lint · CI, release & engine gates, Lint · PM dispatch-gates, Lint · docs & code guards, Lint · repo & tooling gates, Lint · scoped (3), Type Check · source gates/workspace/consumer gates/debt ledger, Spec property liveness, Auto Label, Flag docs affected by code changes. queued: Build Core, Test Core (1/6)…(6/6), Dogfood Regression Gate. Skipped by their if: Build Docs, Console Pin Gate, Dogfood shards, Dogfood Verify CLI, Temporal Conformance, Packed-tarball smoke. CI convergence is the PM's.",
      "mcp_calls": "0 — no MCP GitHub tool called",
      "api_writes": "2 — both through the fleet-write relay (POST /repos/objectstack-ai/objectstack/dispatches), executed as objectstack-fleet[bot]: (1) pr_create, i.e. POST /repos/objectstack-ai/objectstack/pulls (draft) and POST /repos/objectstack-ai/objectstack/issues/24417/assignees [zhuangjianguo]; (2) comment, i.e. POST /repos/objectstack-ai/objectstack/issues/24349/comments (this report). Plus git push of the branch (not a REST write). No labels written.",
      "open_questions": [],
      "out_of_scope_findings": [
        "class: a · reach: exception: release-text · evidence: the pending .changeset/24190-line-items-hydrated-check.md (unreleased, on origin/main) says 'A block under a `page:tabs` item's `children` is not yet judged.'. Once this PR lands that sentence is false, and it would ship verbatim into @objectstack/spec's CHANGELOG.md. It is outside this claim's file surface, so it is not edited. This PR's changeset says the block is judged now. Route: drop that one sentence in a seat-written edit (or a patch round on this claim, if the seat widens the surface) · dedupe words: 24190 changeset page:tabs not yet judged; line-items changeset tabs reach; changeset sentence superseded panel walk"
      ]
    }
  4. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 24349,
      "status": "done",
      "branch": "claude/issue-24349-page-walk-item-children",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/24417",
      "session": "session_019oFY6Hi35J7atvgVFnmoaK — the dispatching seat's id (this run is its os-dev subagent); every commit carries it in its Claude-Session trailer",
      "premise_still_valid": true,
      "summary": "Patch round 1 (REWORK 6257450280, same claim 6256664531, same branch): the pending, unreleased .changeset/24190-line-items-hydrated-check.md is now true at release. :15 'A block under a `page:tabs` item's `children` is not yet judged.' is gone, and the sentence before it now reads 'in a slot and, since #24349, in a `page:tabs` or `page:accordion` item's `children`'. :28 keeps its dated reading on ec749fbb44 and drops 'so not yet judged'. The diff is lines 15 and 28 only. This PR's own changeset sentence that cited the old text now reads 'That includes a `record:line_items` block in a tab or accordion panel, a position the block's identity-only check did not reach before this change.' The PR body still needs the written confirmation that check-empty-changeset asks for (name the note and what changed under it); it is supplied as pr_body_edits, not PATCHed. Pushed once: b0f8b9e3c..3d635d4cc (e9303fbbc the correction; f5e6ad17d and 3d635d4cc merges of origin/main). files_changed this round: .changeset/24190-line-items-hydrated-check.md; .changeset/24349-page-walk-panel-children.md. PR file list now (7): .changeset/24190-line-items-hydrated-check.md; .changeset/24349-page-walk-panel-children.md; packages/spec/src/stack.zod.ts; packages/spec/src/stack-page-walk-panels.test.ts; packages/spec/src/migrations/entries/semantic/18.ui-page-panel-children-cross-references-judged.ts; packages/spec/src/migrations/entries/semantic/18.ui-record-line-items-props-closed.ts; packages/spec/src/migrations/registry.ts.template. deviations: (1) origin/main moved twice during the round: 97cb5c5a8 (docs/adr only), then, after the container restart, 71fa166e9 (cloud-connection, mcp). Both were merged through scripts/pm/os-regen-merge.sh, with nothing to regenerate; neither touched packages/spec or packages/lint, so the spec test file and typecheck were not re-run (the round's condition was not met); (2) the container restart killed the first gate run (on f5e6ad17d); every validation was re-run from scratch on the final head 3d635d4cc; (3) the gate runner ran `pnpm check:type-check-debt` first (same 91-command set, sorted md5 equal), so its closure build supplies the dist that three gates read; each command ran once; (4) the worktree was recreated at b0f8b9e3c for this round, and its node_modules and the worktree are removed again right after this report is posted. Not mine, left as is: Auto Label set the PR labels documentation, size/m, tests, tooling.",
      "tests": "No code or test file changed this round: e9303fbbc touches only the two changesets, and the two merges touched 0 files under packages/spec or packages/lint. So the round-0 readings at bb9d44234 hold for 3d635d4cc: stack-page-walk-panels.test.ts 16/16; ablation 14 failed | 2 passed with the restore proven; spec local project 697 files, 21267 passed | 1 todo; spec typecheck VERDICT command-exit 0; check:generated 14/14. None of them re-run this round, by the round's own condition.",
      "gates": "Final head 3d635d4cc. Named gates: `node scripts/check-changeset-no-major.mjs --base origin/main` exit 0 ('This diff introduces no `major` bump'); `node scripts/check-adr-0087-registration.mjs --base origin/main` exit 0 ('registered ui-page-panel-children-cross-references-judged (new here: …)'); `node scripts/check-empty-changeset.mjs --base origin/main` exit 1 by design. It says '.changeset/24190-line-items-hydrated-check.md present on the merge base and CHANGED by this PR'; this is the DELIBERATE CORRECTION class, whose remedy is the written confirmation on the PR (pr_body_edits), and the gate 'stays red either way'. Its CI home, the Check Changeset job, is not a required context. Derived: `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` (no paths; 7 paths vs merge base 71fa166e9) gave 91 commands; all 91 ran with exit codes captured before any pipe, 90 exit 0 plus that expected exit 1. `--ran`: 'Run reconciliation — 91 derived, 91 run, 0 NOT-MEASURED, 0 UNRUN', exit 0. Local-only family: `pnpm check:dev-prereqs` exit 0. CI, read once on 3d635d4cc — completed success: Check Documentation Links, Check PR Size, filter, No other open PR may claim the same issue, No other open PR may claim the same single-writer path, Part-of PR must not also close its card, The card this PR closes must claim this branch. in_progress: Check Changeset (expected red), Governed Surface Queue Guard, Build Core, Test Core (1/6)…(6/6), Lint · ESLint / CI, release & engine gates / PM dispatch-gates / docs & code guards / repo & tooling gates / scoped (3), Type Check · source gates / workspace / consumer gates / debt ledger, Spec property liveness, Auto Label, Flag docs affected by code changes, Dogfood Regression Gate. Skipped by their if: Build Docs, Console Pin Gate, Dogfood shards, Dogfood Verify CLI, Temporal Conformance, Packed-tarball smoke. CI convergence is the PM's.",
      "mcp_calls": "0 — no MCP GitHub tool called",
      "api_writes": "1 this round — the fleet-write relay (POST /repos/objectstack-ai/objectstack/dispatches) executing comment, i.e. POST /repos/objectstack-ai/objectstack/issues/24349/comments (this report), as objectstack-fleet[bot]. Plus one git push (b0f8b9e3c..3d635d4cc, not a REST write). The PR body was not PATCHed.",
      "open_questions": [],
      "out_of_scope_findings": [],
      "pr_body_edits": [
        {
          "old_str": "## Acceptance notes",
          "new_str": "## Pending release note corrected (patch round 1)\n\nThis PR also edits `.changeset/24190-line-items-hydrated-check.md`, another PR's release note. It is still pending on `main` and unreleased, so it ships in the same release as this PR's changeset. The seat extended this claim's file surface to that one file (REWORK `6257450280`). This change made two of its sentences false. Both are now true at release:\n\n- `:15` said \"A block under a `page:tabs` item's `children` is not yet judged.\" That sentence is gone. The sentence before it now says the check reaches the block \"in a slot and, since #24349, in a `page:tabs` or `page:accordion` item's `children`\".\n- `:28` said the showcase project detail block sits \"under a `page:tabs` item, so not yet judged\". It keeps its dated reading on `ec749fbb44` and drops \"so not yet judged\".\n\nNothing else in that note changed (lines 15 and 28 only): its frontmatter, `Clause-②` line, ADR-0087 marker, FROM → TO table and figures are as they were. This PR's own changeset cited the old sentence (\"which the entry that brought the block's identity-only check lists as not yet judged\"). It now says \"That includes a `record:line_items` block in a tab or accordion panel, a position the block's identity-only check did not reach before this change.\"\n\n`Check Changeset` stays red on this PR by design. `check-empty-changeset` refuses a PR that changes a changeset it did not add, and names this the DELIBERATE CORRECTION class. Its remedy is this written confirmation on the PR, not the `skip-changeset` label and not restoring the old text. `Check Changeset` is not one of the five required contexts.\n\n## Acceptance notes"
        },
        {
          "old_str": "- The pending `.changeset/24190-line-items-hydrated-check.md` says \"A block under a `page:tabs` item's `children` is not yet judged.\" It is outside this claim's file surface and is not edited. This PR's changeset states that such a block is judged now. Dropping that sentence there is a one-line edit for the seat.\n",
          "new_str": ""
        },
        {
          "old_str": "The merge to `b0f8b9e3c` changed no file under `packages/spec` or `packages/lint`, so these readings hold for that tree.",
          "new_str": "The later commits up to the final head `3d635d4cc` changed no file under `packages/spec` or `packages/lint`: the merges `b0f8b9e3c`, `f5e6ad17d` and `3d635d4cc`, and patch round 1's changeset-only commit `e9303fbbc`. These readings hold for the final tree."
        },
        {
          "old_str": "- **Local, on the final head `b0f8b9e3c`.** `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack`, with no paths, derived 91 commands from 6 changed paths against merge base `ee882b845`. All 91 ran, each with its exit code captured before any pipe. `--ran` reconciliation: `91 derived, 91 run, 0 NOT-MEASURED, 0 UNRUN`, all exit 0.",
          "new_str": "- **Local, on the final head `3d635d4cc` (patch round 1).** `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack`, with no paths, derived the same 91 commands from 7 changed paths against merge base `71fa166e9`. All 91 ran, each with its exit code captured before any pipe, and 90 exit 0. `node scripts/check-empty-changeset.mjs --base origin/main` exits 1 by design, because this PR changes a changeset it did not add (see the section above). `--ran` reconciliation: `91 derived, 91 run, 0 NOT-MEASURED, 0 UNRUN`. `check-changeset-no-major --base origin/main` and `check-adr-0087-registration --base origin/main` exit 0.\n- **Before patch round 1, at `b0f8b9e3c`.** The same 91 commands against merge base `ee882b845`: `91 derived, 91 run, 0 NOT-MEASURED, 0 UNRUN`, all exit 0."
        },
        {
          "old_str": "- **Local-only family.** `pnpm check:dev-prereqs`: exit 0.",
          "new_str": "- **Local-only family.** `pnpm check:dev-prereqs`: exit 0, at `b0f8b9e3c` and again at `3d635d4cc`."
        }
      ]
    }
  5. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · PR #24417 → db1f1fd20f · domain:spec seat 2 · zhuangjianguo · session session_019oFY6Hi35J7atvgVFnmoaK · 2026-10-19T02:49Z

    probe reading
    PR object Merged 2026-10-19T02:49Z out of the merge queue. Merge commit db1f1fd20fba543408b209746e566b48373a4622 on parent e22defb13c.
    origin/main git merge-base --is-ancestor db1f1fd20f origin/main → exit 0
    merged vs reviewed head git diff e22defb13c db1f1fd20f and the reviewed head 3d635d4cc8, over its merge base 71fa166e96, share git patch-id --stable 01b34250. Record chain: seat check 6257915803, REWORK 6257450280, contract review PASS 6258251325, adopted 6258266516.
    queue branch No gh-readonly-queue/main/pr-24417-* ref remains.
    mis-close scan The body's only closing keyword is Fixes #24349. The only issue closed in the merge window is this card.

    What now holds on main:

    • defineStack's page cross-reference checks reach a component inside a page:tabs or page:accordion item's children, at any depth. These are the inline-action target check and the inline grid column check.
    • The walk descends the panel positions pageComponentSlotPositions declares. A component inside a panel gets the region-level verdict and message, located through the panel.
    • The disposition is a new step-18 semantic entry, ui-page-panel-children-cross-references-judged.
    • The changeset is minor on @objectstack/spec, with ! and Clause-②: yes (narrowing).
    • The pending .changeset/24190-line-items-hydrated-check.md now says the same as this release (lines 15 and 28; DELIBERATE CORRECTION, confirmed in 6257915803).

    Downstream:

    State: this card was closed completed by the merge; pm:dispatched is removed in this act. Claim 6256664531 ends here.


    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

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions