Skip to content

spec(object-extension): the extension refusal tells an author to declare a top-level hook or a top-level view bound to the target object, but defineStack() refuses both for an object outside the stack (the hook refusal by ADR-0130 §1.5's design) #24471

Closed

Description

@objectstack-fleet

Blocked-by: #24400

Filing gate ①, class (b): wrong text at a public door, with the wrong answer measured.

Filed by domain:spec seat 2 (#18549) · zhuangjianguo · session session_019oFY6Hi35J7atvgVFnmoaK, from the #24400 dev's out_of_scope_findings[0] and [1] (report 6259877793, dispatch item G3). One card for the family: the two sibling sentences of the one PR #24458 re-trues. ⛔ Not a claim. Lane and grade follow the filer exception; triage may change either.

Measured on origin/main 31ce45c071

The work

One PR on packages/spec, plus the docs mirror rows.

  • hooks: re-true the sentence to the route that works. ADR-0130 §1.5 decides that the refusal is deliberate, so ⛔ do not widen the hooks arm in this card. Name what an author does instead (the hook lives with the package that owns the object), or name no route if none exists.

  • listViews: no recorded decision was found. Two honest answers:

    The dispatching seat picks one with four-axis reasons. If the axes split, a decision goes to the box first.

  • Pins: each sentence's claim is pinned at defineStack, with a same-stack control.

Reader who acts: the domain:spec seat, in one PR, after PR #24458 lands. It edits the same ObjectExtensionSchema guidance and the same docs table.

Grade

p2 when both answers are text. If listViews widens the views arm, the fix changes what defineStack accepts, so it becomes protocol class (p1 + target:v18). Triage may regrade.

Dedupe

Dedupe words: extension guidance hooks top-level hook another package object refused; ObjectExtension listViews guidance view data.object cross-package.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    ContributorAuthor

    Unblocked: #24400 landed as fb0d85b7fb · pm:blocked → pm:queue · domain:spec seat 2 · zhuangjianguo · session session_019oFY6Hi35J7atvgVFnmoaK · 2026-10-19T06:38Z

    ⛔ Not a claim.


    Generated by Claude Code

  2. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1 · 2026-10-19T06:43Z · the extension refusal's hooks and listViews sentences are re-trued to what defineStack() does with a top-level hook or view bound to an object outside the stack (refused at build; ADR-0130 §1.5 for hooks), with no accept-set change
    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-24471-extension-guidance-siblings
    Worktree: objectstack-issue-24471
    Domain: domain:spec
    Seat: domain:spec#2 (seat post #18549)
    File surface (paths on origin/main e11ae4b6b0 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

    Dispatched · domain:spec seat 2 · session session_019oFY6Hi35J7atvgVFnmoaK · 2026-10-19T06:45Z · for claim 6261361740.

    mode:subagent: the dev is an os-dev subagent of this session, on branch claude/issue-24471-extension-guidance-siblings, at the default tier.

    The ruling in the brief is premise-bound. Both sentences are re-trued to what defineStack() does, with no accept-set change. The hooks sentence follows ADR-0130 §1.5. Neither sentence names the object-less early return as a route. P1 says no producer needs a wider route. If P1 is false, the dev stops and reports needs_decision (widen the arm, or re-true the text), with the four axes for each option.

    It reports here as an os-dev-report comment and opens a draft PR. The seat subscribes to that PR the moment it exists.


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 24471,
      "status": "done",
      "branch": "claude/issue-24471-extension-guidance-siblings",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/24496",
      "session": "session_019oFY6Hi35J7atvgVFnmoaK (subagent, so this is the parent seat's id; the transcript's Claude-Session line names it)",
      "premise_still_valid": true,
      "summary": "Re-trued ObjectExtensionSchema's guidance.hooks and guidance.listViews (packages/spec/src/data/object.zod.ts:4005 and :4010 on head a55182033) to what defineStack() does, with no accept-set change. stack.zod.ts is untouched: the hooks arm :3830 and the views arm :3846, behind the :3812 early return. hooks now reads: 'the merge does not carry them. A hook follows the ownership of its object: declare it as a top-level hook in the stack that defines the object. `defineStack()` refuses a top-level hook bound to an object the stack does not define, an extended one included.' That is the one route consistent with ADR-0130 §1.5, measured as the same-stack control. listViews now reads: 'the merge does not carry them. Declare the views as a top-level `view` in the stack that defines the target object. `defineStack()` refuses a top-level `view` whose `list` or `form` data source is an object the stack does not define, an extended one included.' The actions sentence is unchanged. The two rows at content/docs/data-modeling/object-extensions.mdx:86-:87 mirror the new sentences. P1 holds: 3 top-level hooks producers and 5 views producers, every binding same-stack; 3 objectExtensions entries, none carrying hooks or listViews; no platform package declares either collection. P2 holds as the card states. The views arm reads list.data and form.data only, so a container's own `object` (and its listViews entries) is accepted even when it is a typo; hence the narrow wording and open_questions[0]. P3 holds: ADR-0130 :155-:157 and :446 read as quoted, and neither addendum touches hooks. files_changed: packages/spec/src/data/object.zod.ts; content/docs/data-modeling/object-extensions.mdx; packages/spec/src/stack-extension-hook-view-foreign-object.test.ts (new); .changeset/24471-extension-guidance-hooks-list-views.md (new, patch @objectstack/spec, Clause-②: no). deviations: (a) the commits carry the AGENTS.md model-free trailer pair, and the PR body the AGENTS.md session-URL footer, rather than the harness reminder's model-named trailer and footer; AGENTS.md takes precedence. (b) to measure three doc gates that refused PREREQUISITE NOT MET, I built @objectstack/formula, @objectstack/sdui-parser and @objectstack/lint, which is outside the empty ① closure. This widens the local measurement; it does not narrow it. (c) test:repo was not run locally: the diff edits none of its tests and the derivation does not name it; it is CI's (Test Core). (d) origin/main moved during the work (#24451 landed in object.zod.ts, disjoint). It was merged before the PR opened, through scripts/pm/os-regen-merge.sh; no regeneration was owed, and the gates and suites were re-run on the merge head.",
      "tests": "Head a55182033 (the merge of origin/main ae6bd1a69). New test packages/spec/src/stack-extension-hook-view-foreign-object.test.ts, 7 cases: a hook on an extended sys_organization is refused (STACK_CROSS_REFERENCE_INVALID, 422, located message); the same-stack hook is accepted; a view whose list or form reads the extended target is refused (2 cases); the same-stack view is accepted; each guidance sentence names the owning stack and the extended-target refusal (2 cases). Targeted run `pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2` on the new file plus stack-bound-action-foreign-object.test.ts and stack-artifact-crossref.test.ts: 'Test Files 3 passed (3) / Tests 43 passed (43)', VERDICT command-exit 0. Full `vitest run --project local --maxWorkers=2` for @objectstack/spec: 'Test Files 700 passed (700) / Tests 21284 passed | 1 todo (21285)'. `pnpm --filter @objectstack/spec typecheck`: 'check:test-typecheck: OK — 52 file(s) / 242 error(s) / 132 pinned signature(s)'. The two were joined with &&, VERDICT command-exit 0. `pnpm --filter @objectstack/spec build && check:generated`: 'All 14 generated artifacts are up to date'; the guidance strings feed no generated file. Ablations, one per changed sentence, through scripts/ablation-replace.mjs: anchor 1->0, blob 0f61b6835ad5 -> e235b3742389 (hooks) and -> f91837a5e346 (listViews). Hooks sentence restored to the old 'declare a top-level hook bound to the target object instead': 'Tests 1 failed | 6 passed (7)', exactly the hooks guidance case. listViews sentence cut to '...target object instead.': 'Tests 1 failed | 6 passed (7)', exactly the listViews guidance case. Both restored: 'blob == HEAD (0f61b6835ad5) and git diff HEAD is empty'. No dist is involved, because the test imports src. The 5 refusal and control cases stay green under both ablations, because the diff does not change the behaviour they pin. P2 probe (tsx against src, the same before and after the merge): hook on sys_organization refused '✗ Hook 'g3_hook' references object 'sys_organization' which is not defined in objects.'; list view refused '✗ View[0].list references object ...'; form refused; both same-stack controls accepted; container {object:'sys_organization'|'nope_typo', listViews} accepted; object-less stack with a hook on sys_organization accepted.",
      "gates": "Local: `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` (no paths) on head a55182033 derives 108 commands, the same list as on f9854b68d before the merge. Each was run with its exit code captured before any pipe. `--ran` reconciles them: 'Run reconciliation — 108 derived, 108 run, 0 NOT-MEASURED, 0 UNRUN', and all 108 exited 0 (RECON_EXIT=0). On the pre-merge head f9854b68d it was 108 derived, 105 run, 3 NOT-MEASURED: check:skill-examples, check:dual-build-cjs-loads and check:lean-entry-closure, PREREQUISITE NOT MET; all three exit 0 on the final head. Outside the derivation: `pnpm check:dev-prereqs` exit 0 (local-only family; spec was built); `pnpm --filter @objectstack/spec check:generated` exit 0 after a rebuild; a control-byte grep over the changed files found 0 hits. CI, the PR #24496 check-runs on a55182033f at report time (38 in all). success (15): Auto Label, Check Changeset, Check Documentation Links, Check PR Size, Dogfood Regression Gate, Flag docs affected by code changes, Governed Surface Queue Guard, Lint · PM dispatch-gates, Lint · scoped · tooling self-tests, 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, Spec property liveness, The card this PR closes must claim this branch, filter. skipped (5): Console Pin Gate, the Dogfood Regression Gate shards, Dogfood Verify CLI, Packed-tarball smoke (opt-in), Temporal Conformance (live PG + MySQL). in_progress (18): Build Core, Build Docs, Lint · CI, release & engine gates, Lint · ESLint, Lint · docs & code guards, Lint · repo & tooling gates, Lint · scoped · query-options erasure, Lint · scoped · slot lookup & corpus, Test Core (1/6) through (6/6), Type Check · consumer gates, Type Check · debt ledger, Type Check · source gates, Type Check · workspace. Not converged, and CI convergence is the PM's.",
      "mcp_calls": "0 — no MCP GitHub tool was called",
      "api_writes": "2 relay strokes through scripts/pm/fleet-write/dispatch.mjs, as objectstack-fleet[bot], each one POST /repos/objectstack-ai/objectstack/dispatches: (1) pr_create, which executed POST /repos/objectstack-ai/objectstack/pulls (draft, #24496) and POST /repos/objectstack-ai/objectstack/issues/24496/assignees (zhuangjianguo); relay read-back: 9764 bytes sent, 9764 stored, identical; (2) the os-dev-report comment, POST /repos/objectstack-ai/objectstack/issues/24471/comments. Plus 3 `git push` of the branch (not REST). All reads were unauthenticated REST GETs.",
      "open_questions": [
        {
          "question": "P2's difference: defineStack()'s views arm (stack.zod.ts:3838-:3860) judges a container's list.data and form.data only. A container's own `object` (the binding getViewsByObject() reads) and its listViews/formViews entries' data are not judged. views:[{object:'sys_organization', listViews:{...}}] is accepted in a stack with objects, and so is the same with object:'nope_typo'. os validate judges the container `object` (lint object-reference-unknown: an error on an unprefixed miss, a pass for a known platform name). Whether the runtime serves a code-shipped container bound to another package's object as that object's list views is NOT MEASURED. This PR's listViews sentence therefore states only the list/form refusal and the same-stack route; it neither offers nor refuses the container binding. Should anything more be done?",
          "options": [
            "A. Keep this PR's text and do nothing further. Business need: no producer writes such a container (P1), so there is no measured pull. Long-term fit: the text stays true under any later decision about the arm. AI-error axis: the text advertises no unmeasured route, and os validate already catches the container-object typo class (though defineStack stays silent, and listViews entries' data.object is judged nowhere I found). Startup focus: zero new surface, zero new gate. Cost: none.",
            "B. Measure the container route at runtime: boot with a code-shipped {object:'sys_organization', listViews}, then read GET /api/v1/meta/view?object=sys_organization and the Console list-view switcher. If it is served, name it in the sentence (text only); if not, file a (c) trap card. Business need: none measured, so it is speculative. Long-term fit: it puts a cross-package views route on record without a decision about cross-package container slots (the containers-of-one-object displacement family). AI-error axis: a proven route helps authors. Startup focus: a runtime probe plus a pin, about half a day.",
            "C. Make the views arm judge the container's own `object` and its listViews/formViews entries' data the way it judges list/form. Business need: breaks no producer (P1), but nothing pulls for it. Long-term fit: one rule for every binding, loud at build (contract-first), but it contradicts lint's deliberate pass for a platform name on container `object` and needs a ruling on whether a cross-package container is legitimate. AI-error axis: the strongest structural guard. Startup focus: an accept-set narrowing (protocol class, Clause-② yes (narrowing)), a ruling and a breaking changeset."
          ],
          "recommendation": "A. There is no producer pull on any axis. The text is true under every later outcome. B and C each need a decision (a cross-package view route, or its refusal) that the measured population does not ask for. C is the right shape only if a ruling first decides that a cross-package container is illegitimate."
        }
      ],
      "out_of_scope_findings": [
        "carrier: 承接者:无 · noted, not filed, and in the PR's Acceptance notes. The object-less early return (stack.zod.ts:3812) skips both arms, so in an object-less stack defineStack() ACCEPTS a top-level hook (and a list/form view) bound to another package's object. Measured: an object-less stack with objectExtensions sys_organization and hooks:[{object:'sys_organization'}] is accepted. The new sentences are unconditional, so in that corner they over-state the refusal; they err toward prescribing no route. Per the ruling the sentences do not present it as a route. The leniency is deliberate and pinned for the artifact-scoped classes (stack-artifact-crossref.test.ts:306; stack.zod.ts:6307, 'references may be served by a plugin that is not in this composition'). No producer uses it for hooks or views (P1). Dedupe words: object-less stack early return hook foreign object accepted; validateCrossReferences objectNames.size === 0 hooks views; defineStack no objects cross-reference skipped.",
        "carrier: the domain:spec seat, through open_questions[0] · noted, not filed. The views arm does not judge a container's own `object` or its listViews/formViews entries' data.object; the measurements and options are in open_questions[0]. No reach to a wrong runtime answer was measured, so it is not a filable class (a). Dedupe words: views arm container object not judged defineStack; listViews data.object cross-reference unchecked; view container object foreign package accepted."
      ]
    }

    Generated by Claude Code

  5. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · PR #24496 → 8eac321996 · domain:spec seat 2 · zhuangjianguo · session session_019oFY6Hi35J7atvgVFnmoaK · 2026-10-19T09:25Z

    probe reading
    PR object The seat landed it after contract review PASS 6262965489, adopted in 6263370014 (ready and auto-merge). It was added to the merge queue at 2026-10-19T08:52Z and merged out of it at 2026-10-19T09:23Z, as merge commit 8eac3219963d5da43f7a3223eb274d4697bff9b6 on parent ca426cb65d.
    origin/main git merge-base --is-ancestor 8eac321996 origin/main → exit 0
    merged vs reviewed head git diff ca426cb65d 8eac321996 and the reviewed head a55182033f, measured against its merge base ae6bd1a696, share git patch-id --stable a8480f38. 4 files, +129 / −6.
    queue branch no gh-readonly-queue/main/pr-24496-* ref remains
    mis-close scan The body's only closing keyword is Fixes #24471. Two other issues closed in the same queue batch: #24448 (PR #24495) and #24375 (PR #24500). Both PRs are other lanes' merges, not this one.

    What now holds on main:

    • ObjectExtensionSchema's guidance.hooks says a hook follows the ownership of its object. The author declares it as a top-level hook in the stack that defines the object, and defineStack() refuses one bound to an object the stack does not define, an extended one included (ADR-0130 §1.5).
    • guidance.listViews says to declare the views as a top-level view in the stack that defines the target object. defineStack() refuses a view whose list or form data source is an object the stack does not define.
    • The two rows in content/docs/data-modeling/object-extensions.mdx mirror both sentences.
    • The accept set is unchanged: patch on @objectstack/spec, Clause-②: no.

    Noted, not filed (carried in the PR's Acceptance notes):

    • the object-less early return in validateCrossReferences;
    • the views arm not judging a container's own object.

    Neither reaches a measured wrong answer at a door. A booted read of a code-shipped foreign container is the next honest measurement, if the route is wanted on record.

    State: the merge closed this card completed. pm:dispatched is removed in this act. Claim 6261361740 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