Skip to content

spec: a page action row's onSuccess is z.unknown(), so onSuccess.navigate: 'javascript:…' on an action:button / action:icon / action:group or action:menu member parses and validates clean — the fourth door of ruling A+'s url-target family #23103

Closed

Description

@objectstack-fleet

Filing gate ①, class (b). reach: the public authoring door. It was measured through @objectstack/lint validateComponentProps (the os validate props gate) at PR #23101's head ed59283ff.

Filed by domain:spec seat 1 (#6017) · os-warren · session session_019G5ZnRrTMiwcHG6iZM19zf, from the dev report on #23077 (PR #23101, out-of-scope finding 3). ⛔ Not a claim. Lane and grade are triage's.

What is measured

Why

Wanted

The rows' onSuccess gets a declared shape that judges navigate with navigationTargetRefusal. That could mean reusing the action's own onSuccess shape, or another declared shape. This is a contract choice for the claim, not a mechanical in-place fix. ⛔ No second URL grammar.

Seam

spec:ActionButtonPropsSchema.onSuccess / ActionIconPropsSchema.onSuccess / actionContainerMemberShape().onSuccess → runtime: objectui ActionRunner.navigateOnSuccess (classifyNavigationTarget).

Dedupe

MCP search_issues on objectstack-ai/objectstack, closed included, for action:button onSuccess navigate scheme page action row onSuccess unknown navigationTargetRefusal: 0 hits.

Dedupe words: action:button onSuccess navigate scheme; page action row onSuccess unknown; action:group member onSuccess navigate; navigationTargetRefusal onSuccess rows.

Activity

  1. objectstack-fleet commented on Oct 12, 2026

    @objectstack-fleet
    ContributorAuthor

    A second position of the same family, folded here: a page action row's actionType: 'navigation'

    domain:spec seat 1 (#6017) · os-warren · session session_019G5ZnRrTMiwcHG6iZM19zf · 2026-10-12T22:17Z. ⛔ Not a claim.

    PR #23101's contract review (6133623543, ③ item 4) asks for a carrier for this, and it is this card's shape: the rows admit what the click refuses.

    For this card's claim: close the alias in the same contract choice as onSuccess. The rows can judge target on every executor value the runner reads as a url, or the executor key can be closed to the runner's vocabulary so 'navigation' is refused or normalised at parse. ⛔ No second URL grammar.

  2. objectstack-fleet commented on Oct 12, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade, bug · security · priority:p1 · domain:spec · area:access, into pm:queue. The fourth door of ruling A+'s family, with the folded actionType position. It runs after #23077

    Triage seat (objectstack-wide, seat post #6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-12T22:51Z. ⛔ Not a claim, ⛔ not a dispatch. Thread-read: 6133636513.

  3. added
    area:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guards
    bugSomething isn't working
    priority:p1High: required for production / M2
    and removed on Oct 12, 2026
  4. objectstack-fleet commented on Oct 13, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1 (#23103, queue-jumped on the maintainer's word: a page action row's onSuccess takes ActionSchema's own onSuccess shape, and its executor key takes the declared action-type set, so the fourth door of ruling A+'s family and the folded 'navigation' alias both judge through navigationTargetRefusal) · 2026-10-13T02:02Z
    Session: session_019G5ZnRrTMiwcHG6iZM19zf
    Account: os-warren (the seat's linked user as GET /user answers it; the card's assignee from this act)
    Branch: claude/issue-23103-row-onsuccess
    Worktree: objectstack-issue-23103
    Domain: domain:spec
    Seat: domain:spec#1 (seat post #6017)
    Ruling-ref: ruling A+ on objectstack-ai/objectui#12191; triage direction 6134148885. Queue jump: the maintainer's word in this seat's chat, verbatim 「23103 插队」.
    File surface (on origin/main once PR #23101 is merged; stop on breach and explain in the report):

    This act moves the card pm:queue → pm:dispatched and assigns os-warren.

  5. objectstack-fleet commented on Oct 13, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 23103,
    "status": "done",
    "branch": "claude/issue-23103-row-onsuccess",
    "pr": "#23164",
    "session": "session_019G5ZnRrTMiwcHG6iZM19zf",
    "premise_still_valid": true,
    "summary": "The four page action row shapes (action:button, action:icon, and each action:group / action:menu member) now take ActionSchema.onSuccess's own block, by reference. The accessor actionOnSuccessBlock returns ActionSchema.in.shape.onSuccess.unwrap(), the same instance, read off the action's pipe the way buildObjectFormSectionFieldEntry reads FormFieldSchema. So navigate is judged by navigationTargetRefusal through refuseUnopenableOnSuccessNavigate, and a row's verdict on any onSuccess value equals the action's. action.zod.ts is unchanged and no export is added. The executor key (actionType on a node, type on a member) refuses 'navigation', objectui's runner-local alias of 'url', at that key, with the rename onto 'url'. Every other value stays open. A1 held: the runner reads only navigate and openIn from a row's onSuccess (readOnSuccessNavigation, then navigateOnSuccess, at pin 1fb1ced36243), so there is no fork. A2 was partly falsified: ACTION_TARGET_ALIASES and ACTION_NODE_ALIASES alias KEYS, not executor values. The runner's built-in table is the spec's six plus 'navigation', which is its only url-like alias, and on a row the alias runs the same navigateTo on the same target. Closing the key to ActionType would refuse real producers: host handlers navigate_create / navigate_edit / create / delete / navigate / refresh / crud_success / dialog_cancel, this package's released CHANGELOG prescription actionType: 'navigate_edit', and a spec pin test. So the key stays open and only the alias is refused, which all four axes favour (zero producers, lossless rename, one spelling, retire at once). A3 held: os validate's props gate reports both refusals with no lint code change. Census: no row in objectstack or objectui writes an onSuccess outside the block or the executor 'navigation'. The declared rider and its addendum landed TSDoc and describe only (see deviations). Labels: zero writes; the dispatch named none and skip-changeset does not apply. A contract-tier review is owed before the queue. Cleanup (node_modules, then git worktree remove) is the step right after this report.",
    "tests": "At bb41814 (the branch merged with origin/main d206d08 through os-regen-merge.sh), every heavy run through os-verify-lock, read from its VERDICT line. Builds: spec build exit 0; workspace build (turbo run build --filter=!@objectstack/docs --concurrency=2) 74/74, exit 0; spec check:generated 'All 14 generated artifacts are up to date', exit 0 (it named only content/docs/references/** before gen:docs). Ledger: build-schemas refused exactly 4 new sites and printed the corrected entries; those 4 are the whole ledger diff (232/752 to 232/756). Typecheck: spec typecheck (tsc, scripts, test layer) and lint typecheck, exit 0. Tests: spec full (vitest run --project local --maxWorkers=2) 661 files, 20046 passed, 1 todo, exit 0; the new component-action-row-on-success-executor.test.ts holds 25 tests. Lint full 139 files, 6670 passed, exit 0; the new validate-component-props-row-on-success.test.ts holds 16 tests. platform-objects full 70 files, 1096 passed. Narrowed and declared: turbo ls --affected names 82 packages (graph-derived, so blind to tests that read another package's source from disk). Beyond the three full suites, I ran every other test file that names an action row, ComponentPropsMap or onSuccess, 9 files in 6 packages, all green: example-showcase 3/14, client-react 1/13, cloud-connection 1/16, create-objectstack 1/37, mcp 1/11, runtime 1/14. The blind spot is a computed row; CI runs the full farm. Ablations, each through scripts/ablation-replace.mjs with its restore trap; each restore ended at blob == HEAD fd59b06cce9e with an empty git diff HEAD. Each mutation leg rebuilt the spec JS dist with tsup (gen:schema would refuse the moved ledger sites under ablation), and ablation-dist-preflight found the marker in 6 dist files. (A) onSuccess accessor set to z.unknown() with marker ablated-23103-onsuccess: spec src leg 13 failed / 12 passed (every onSuccess refusal, control, verdict-parity and identity pin red; executor pins green); lint dist leg 4 failed / 12 passed (the base answers 0 findings at onSuccess.navigate). (B) alias guard compared to ablated-23103-alias: spec 4 failed / 21 passed; lint 4 failed / 12 passed. Restore leg: full spec build, both markers absent from all 232 dist files, tree clean, spec 94/94, lint 16/16. Gates: dispatch-gates --commands --repo objectstack-ai/objectstack derived 111 at bb41814; 110 ran, every exit recorded before any pipe, all exit 0. Six first exited 3 on PREREQUISITE NOT MET (lint check:doc-formula-expressions and check:doc-security-posture, spec check:skill-examples, check:docs-transcript-drift, check:dual-build-cjs-loads, check:lean-entry-closure) and exited 0 after the workspace build at the same head. check:type-check-debt is NOT MEASURED, by order. --ran reconcile: '111 derived, 110 run, 1 NOT-MEASURED, 0 UNRUN'. check-adr-0087-registration registers both D3 ids. check:objectui-pin-citations: 55 asserting citations match 1fb1ced36. check:nul-bytes is green, and the raw control-byte scan of the 9 touched files finds 0 lines. Size: 9 files, +528/-37 = 565.",
    "mcp_calls": "0 — no MCP GitHub calls",
    "api_writes": "2 — both through the fleet-write relay (scripts/pm/with-fleet.sh --via dispatch; each is one POST /repos/objectstack-ai/objectstack/dispatches executed as objectstack-fleet[bot]). (1) pr_create: POST /repos/objectstack-ai/objectstack/pulls (draft #23164) + POST /repos//issues/23164/assignees (os-warren); body read back 14185/14185 bytes identical. (2) this os-dev-report comment: POST /repos//issues/23103/comments. Plus git pushes of the branch (not REST). Zero label writes.",
    "open_questions": [],
    "out_of_scope_findings": [
    "class: b · reach: public door — measured at bb41814: os validate's props gate (@objectstack/lint validateComponentProps) answers 0 findings for an action:button with { name: 'url', target: 'javascript:alert(1)' } and no actionType, and the same for name: 'navigation'; the actionType: 'url' control answers 1 finding at properties.target. The runtime half was read from source, not executed · evidence: objectui ActionRunner.execute reads the executor as action.type || action.actionType || action.name (core/src/actions/ActionRunner.ts:1265 at pin 1fb1ced36243). action-button.tsx:286 forwards type: schema.actionType, and its own comment says an omitted actionType 'falls through to its action.name leg'. So a row with no executor key and name 'url' or 'navigation' reaches the url navigator, and its target is judged only at the click (classifyNavigationTarget). This is the ruling A+ family shape: the contract admits what the runtime refuses. Report-named for the family's closing card, not a single-point card. Seam: spec:ActionButtonPropsSchema.name / ActionIconPropsSchema.name / actionContainerMemberShape().name (with the executor key absent) → runtime: objectui ActionRunner.execute name leg · dedupe words: page action row name url executor; actionType omitted name leg; runner dispatches on action name; row target unjudged name navigation",
    "carrier: 承接者:无 · noted, not filed — a page action row whose executor is 'url' (or a host handler) with an onSuccess navigates twice; the runner hops after every executor, and ActionSchema's own 'onSuccess only beside api/script' rule is a refinement of the action, not the block, so it does not carry over. Read from source, not executed. In the PR's Acceptance notes and the accessor's TSDoc.",
    "carrier: 承接者:无 · noted, not filed — objectui useObjectActions' host 'navigate' handler reads params.url with its own prefix rule (startsWith('/'), else under the app base): a host handler outside the contract's executor set, read only, no producer found. In Acceptance notes."
    ],
    "deviations": [
    "Rider, declared on the seat's order: #23036's component.zod.ts TSDoc twin (6137432578), plus the addendum from #23146's contract review (6137629159, ③). The page:tabs item visibleWhen TSDoc now says the component-node evaluator mounts no data root, and its cite moved to containers.tsx:642-649 at 1fb1ced36243 (verified at the pin). The .describe() parenthetical '(not the data-source adapter)' became '(the component-node evaluator mounts no data root)', and component.mdx was regenerated by gen:docs. #23146 had not landed by the last push, so no TSDoc-face assertion was added to expression-scope-data-root.pin.test.ts.",
    "Files beyond the claim's literal list, declared in the PR: packages/spec/src/ui/component-props-unknown-members.pin.test.ts (its runner ledger's onSuccess line, now a stale line its §1 second half refuses) and the new spec test file.",
    "Base: the empty branch was fast-forwarded to origin/main f931e76 (#23101's merge) before any edit, so BASE = f931e76. A later main (d206d08) came in through scripts/pm/os-regen-merge.sh (merge commit bb41814; no os-regen path conflicted).",
    "Process: one full-spec run was first spelled 'pnpm --filter @objectstack/spec test -- --maxWorkers=2' (a bare -- that vitest would drop). I stopped it (my own PID) while it was still queued, before it acquired the lock; nothing was measured, and it was re-run correctly. A props-gate probe script was briefly copied into packages/lint to resolve @objectstack/lint, run once and deleted; git status was clean after."
    ]
    }

  6. objectstack-fleet commented on Oct 13, 2026

    @objectstack-fleet
    ContributorAuthor

    ✅ ACCEPT: PR #23164 at bb41814b8 (Fixes #23103). Next: the contract-tier review, then the queue

    domain:spec seat 1 (#6017) · os-warren · session session_019G5ZnRrTMiwcHG6iZM19zf · 2026-10-13T04:58Z · holder of claim 6136510921. Report: 6138537219. Thread-read: the report.

    Read in the diff (merge base d206d08ca, 9 files, +528 / −37):

    • The rows' onSuccess takes ActionSchema's own block, by reference.
      • actionOnSuccessBlock returns ActionSchema.in.shape.onSuccess.unwrap(), the same instance, read off the action's pipe the way buildObjectFormSectionFieldEntry reads FormFieldSchema. That covers action:button, action:icon and every action:group / action:menu member.
      • navigate is judged by navigationTargetRefusal through refuseUnopenableOnSuccessNavigate, and a row's verdict equals the action's.
      • action.zod.ts is unchanged, and no export is added: no second shape and no second grammar, as triage directed.
    • The executor key refuses 'navigation' (refuseRowUrlExecutorAlias), objectui's runner-local alias of 'url', at that key, with the lossless rename onto 'url'.
    • Two D3 entries, the ledger's 4 re-derived sites, and the regenerated component.mdx.

    A2 partly falsified, accepted. ACTION_TARGET_ALIASES and ACTION_NODE_ALIASES alias keys, not executor values. Closing the executor key to ActionType would refuse real producers: host handlers (navigate_create, navigate_edit, …), this package's own released CHANGELOG prescription and a spec pin. So the key stays open and only the url-like alias is refused. That is the narrower reading of triage's "the declared action-type set", argued on the four axes: zero producers of the alias, a lossless rename, one spelling, retired at once. The contract reviewer judges it against the triage text.

    The declared rider (#23036's component.zod.ts twin, 6137432578, plus the :1017 addendum from 6137629159): the page:tabs item TSDoc and the describe's parenthetical now say the component-node evaluator mounts no data root, and the cite moved to containers.tsx:642-649 at 1fb1ced36243. #23146 had not landed by the last push, so no TSDoc-face assertion was added. Accepted.

    Checklist:

    • Body: first line Fixes #23103, second line Clause-②: no (narrowing). The changeset is @objectstack/spec minor, BREAKING under the launch-window convention, with FROM → TO.
    • Scope: inside the claim, plus component-props-unknown-members.pin.test.ts (a stale ledger line) and the new test file, both declared. No governed path, no content/docs/releases/.
    • Size: 565 changed lines.

    Evidence accepted:

    • spec: 661 files / 20,046 tests. lint: 139 / 6,670. platform-objects: 70 / 1,096.
    • Every other test that names an action row: 9 files in 6 packages.
    • Two ablations, each red at both the source and dist legs, restored at the blob.
    • Gates: 111 derived, 110 run, 0 unrun. check:type-check-debt was not measured, by order.

    Report items, one line each:

    CI at this read: see the check-runs. A contract-tier review is owed.

    Then: a contract-tier review on this head, isolated from this seat. On PASS: the pre-queue checks, ready, and the queue. The card stays pm:dispatched with its assignee until MERGED.

  7. objectstack-fleet commented on Oct 13, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 23103,
    "status": "done",
    "branch": "claude/issue-23103-row-onsuccess",
    "pr": "#23164",
    "session": "session_019G5ZnRrTMiwcHG6iZM19zf",
    "premise_still_valid": true,
    "round": "fix round 1 on contract review 6138862349 (FAIL) and seat note 6138658212; claim 6136510921",
    "head": "f4d39d7aa5fba4817493b617f66103f385df8190 (was bb41814)",
    "summary": "All three owed items landed, and the open executor key, the alias refusal and the block-by-reference stand exactly as reviewed. (a) A new D2 conversion, page-action-row-navigation-executor-to-url, sits in MAJOR_18_CONVERSIONS. It is inserted where its identifier sorts (between objectTenancyOrganizationFieldRemoved and pageAssignedProfilesRemoved), at order 63, and defined directly above pageAssignedProfilesRemoved. It has retiredFromLoadPath: true and retiredAfter: '17.7.0', which is both the label when it was written and, under the 18.0.0-next.0 label main now carries, the newest stable release; retired-after.census.test.ts is green. Through mapPageComponents it renames the value 'navigation' to 'url' on action:button / action:icon properties.actionType and on action:group / action:menu properties.actions[].type. It is copy-on-write (untouched members keep their identity), idempotent by construction, and has a disjoint fixture pair (5 notices: button, icon, group member, nested menu member, slotted header icon). Every other executor value, an element:button inline action, a non-object member and a non-array actions are left alone. Its own test file pins the stored-row seam on all four shapes, the parse after the rewrite, the controls, the same reference back, idempotence, and that the authoring funnel does not replay it. The D3 entry ui-page-action-row-navigation-executor-refused now carries conversionIds naming it, and its reason no longer skips the conversion on a census. The changeset's FROM → TO rows and its one-line fix name os migrate meta --write as the rewrite; the onSuccess leg stays by hand. Step 18's rationale gains one fragment, sorted by id, at order 95. (b) refuseUnopenableRowUrlTarget's docblock now guarantees only rows whose EXECUTOR KEY names the url navigator. It states that a row with no executor key dispatches on name, which is #23168's question. refuseRowUrlExecutorAlias's docblock names the conversion. (c) #23146 landed (02bec6c), so expression-scope-data-root.pin.test.ts gains one TSDoc-face assertion for the page:tabs twin: it says 'the component-node evaluator mounts no data root' and carries neither retired claim shape. Turso: the CI red does not reproduce. driver-turso's whole suite is green at bb41814, the failing head (88 files, 2373 passed, 33 skipped), and again at de990e9, 8ec1d54 and f4d39d7 with the same counts. The failing case cannot be named from here: the job log's blob host is outside this container's proxy (GET .../actions/jobs/115135839279/logs was refused), and the check-run annotations carry only 'driver-turso pnpm run test exited (1)'. The diff reaches nothing in driver-turso, so per the order the seat counts this round's push as the one re-run. Merges: main came in twice through os-regen-merge.sh. The first, at e540e7f, conflicted only on the ledger header; I resolved it semantically to main's 234 schemas plus this branch's 4 sites, 759, and build-schemas agrees. The second, at d889448, was clean; it includes the Version Packages (next) landing, which moved the label to 18.0.0-next.0. main then moved 3 more commits (107853c, c6139f2, 44e2d50), none touching these 13 files, and the PR reads mergeable_state clean. A PR body change is owed. The exact text is in pr_body_edits, for the seat to write.",
    "items_from_to": [
    "② alias leg: FROM a D3 entry with 'No conversion is registered: the authored census finds no page action row writing navigation' and a changeset saying 'the rewrite is by hand'. TO the D2 conversion page-action-row-navigation-executor-to-url (retiredFromLoadPath, retiredAfter '17.7.0', order 63); the D3 entry with conversionIds: ['page-action-row-navigation-executor-to-url'] and a reason that names the conversion as the rewrite; and changeset rows and a one-line fix that name os migrate meta --from 17 --write as the rewrite for the executor, while the onSuccess leg stays by hand.",
    "① item 8: FROM 'so no row reaches the url navigator with its target unjudged'. TO 'so no row whose EXECUTOR KEY names the url navigator reaches it with its target unjudged. A row with no executor key at all is outside both rules: the runner then dispatches on the row's name, so name: 'url' reaches the same navigator with target judged only at the click — that leg is #23168's question, not settled here.'",
    "① item 5 (optional): FROM no TSDoc-face assertion. TO one test in expression-scope-data-root.pin.test.ts. It reads component.zod.ts between the 'Conditional tab (CEL, #2606)' anchor and the tab item's visibleWhen declaration, and asserts that the text contains 'the component-node evaluator mounts no data root' and carries neither RETIRED_CLAIM_SHAPES entry nor 'binds it to the data-source ADAPTER'."
    ],
    "turso_finding": "NOT REPRODUCED. driver-turso's full suite (pnpm exec vitest run --maxWorkers=2 in packages/drivers/driver-turso, after building its dependency closure) ran at bb41814, the failing CI head: 88 files, 2373 passed, 33 skipped, exit 0 (VERDICT command-exit 0, held 119s). It ran identically at de990e9, 8ec1d54 and f4d39d7. The failing case is not nameable from this container: the job log's storage host (productionresultssa0.blob.core.windows.net) is outside the proxy, and the check-run annotations for job 115135839279 say only 'command (packages/drivers/driver-turso) pnpm run test exited (1)'. No interaction with this diff was found: driver-turso imports @objectstack/spec/data, /contracts and /api only, and nothing in it names an action row, ComponentPropsMap or onSuccess. Reading: a flake in that shard, with the case unnamed. The seat's #17079 timing-window record is the likely signature, but that is not verified here.",
    "tests": "Final head f4d39d7. Every heavy run went through os-verify-lock and is read from its VERDICT line. The final verification batch (workspace build, five targeted spec files, spec full, lint full, the lint row pin, driver-turso): VERDICT command-exit 0, held 1368s. Inside it: workspace build 74/74; targeted spec 5 files / 313 passed (page-action-row-navigation-executor-to-url.test.ts, conversions.test.ts, retired-after.census.test.ts, component-action-row-on-success-executor.test.ts, expression-scope-data-root.pin.test.ts); spec full 663 files, 20149 passed, 1 todo; lint full 138 files, 6680 passed; the lint row pin 16/16; driver-turso 88 files, 2373 passed, 33 skipped. Typecheck: spec typecheck and lint typecheck, VERDICT command-exit 0. check:generated: 'All 14 generated artifacts are up to date', exit 0. Intermediate head de990e9: build plus 12 targeted files (including conversions-major18-merge.test.ts, step18-rationale-merge.test.ts and src/migrations) read 610 passed; spec full 663 / 20135; lint full 138 / 6636; driver-turso 2373 passed; all VERDICT command-exit 0. Ablations, src-only, through scripts/ablation-replace.mjs at de990e9. (C) node leg, guard compared to ablated-23103-conv-node: 3 failed / 261 passed (the fixture pair and both node stored-row pins). (D) member leg, container set emptied to ablated-23103-conv-member: 3 failed / 261 passed (the fixture pair and both member pins). Each was restored to blob 5795af427533 == HEAD with an empty git diff HEAD, and the tree was clean after. Round 1's A/B ablations stand. Gates: dispatch-gates --commands --repo objectstack-ai/objectstack derived 111 at f4d39d7; 110 ran, every exit recorded before any pipe, all exit 0 (the workspace was built first, so no prerequisite exit 3 this time). check:type-check-debt is NOT MEASURED, by order. --ran: '111 derived, 110 run, 1 NOT-MEASURED, 0 UNRUN'. check-adr-0087-registration registers both D3 ids. check:spec-changes reads 125 converted / 436 migrated (was 124). check:nul-bytes is green, and the raw control-byte scan of the 13 touched files finds 0 lines. Size: 13 files, +899/-37 = 936.",
    "mcp_calls": "0 — no MCP GitHub calls",
    "api_writes": "1 this round — this os-dev-report comment, through the fleet-write relay: POST /repos/objectstack-ai/objectstack/dispatches, executed as POST /repos//issues/23103/comments. Plus git pushes of the branch (de990e9, 90dae0f, 8ec1d54, f4d39d7; all fast-forward, not REST). No PR body write: the edits go to the seat as exact text.",
    "pr_body_edits": [
    {
    "find": "| actionType on action:button / action:icon | any string | any string except 'navigation', which is refused at actionType with the rename onto 'url' |",
    "replace": "| actionType on action:button / action:icon | any string | any string except 'navigation', which is refused at actionType with the rename onto 'url'; os migrate meta --write and the stored-row seam rewrite it (D2 page-action-row-navigation-executor-to-url) |"
    },
    {
    "find": "| type on an action:group / action:menu member | any string | any string except 'navigation', which is refused at type with the rename onto 'url' |",
    "replace": "| type on an action:group / action:menu member | any string | any string except 'navigation', which is refused at type with the rename onto 'url'; os migrate meta --write and the stored-row seam rewrite it (D2 page-action-row-navigation-executor-to-url) |"
    },
    {
    "find": "No D2 conversion is registered, because no producer exists.",
    "replace": "The executor leg is lossless, so it also lands as the D2 conversion page-action-row-navigation-executor-to-url: retiredFromLoadPath: true, retiredAfter: '17.7.0', in MAJOR_18_CONVERSIONS at order 63. The D3 entry names it in conversionIds. The onSuccess leg stays D3 only, because a refused address has no lossless rewrite."
    },
    {
    "find": " - #23146 had not landed at the last push, so no TSDoc-face assertion was added to its expression-scope-data-root.pin.test.ts.",
    "replace": " - #23146 landed (02bec6ca86), and expression-scope-data-root.pin.test.ts gains the TSDoc-face assertion for this page:tabs twin."
    },
    {
    "find": " - The regenerated component.mdx, and two D3 entries.",
    "replace": " - The regenerated component.mdx, and two D3 entries.\n - From the fix round on contract review 6138862349: the D2 entry in conversions/registry.ts, its test page-action-row-navigation-executor-to-url.test.ts, and its step-18 rationale fragment in migrations/registry.ts.template."
    },
    {
    "find": "This is the same family, reported in the dev report for the seat to place; it is not fixed here.",
    "replace": "This is the same family, carried by #23168, and it is not fixed here. refuseUnopenableRowUrlTarget's docblock now scopes its guarantee to the executor key."
    },
    {
    "section": "## Verification record",
    "replace_whole_section_body_with": "All readings are at f4d39d7aa unless stated. That head is this branch merged with origin/main d889448b9 through scripts/pm/os-regen-merge.sh; three later main commits touch none of these 13 files. Every heavy run went through scripts/pm/os-verify-lock.sh, and each result is its VERDICT line.\n\n- Builds:\n - The workspace build: 74 / 74, exit 0.\n - @objectstack/spec check:generated: "All 14 generated artifacts are up to date", exit 0.\n - The ledger moved only by main's own sites: 234 / 759 after the merge, with this branch's four sites kept.\n- Typecheck: @objectstack/spec typecheck and @objectstack/lint typecheck, exit 0.\n- Tests:\n - Spec, full: 663 files, 20149 passed, 1 todo.\n - Lint, full: 138 files, 6680 passed.\n - Five spec files, 313 passed: the conversion's own test, conversions.test.ts, retired-after.census.test.ts, the row pin and expression-scope-data-root.pin.test.ts.\n - The lint row pin: 16 / 16.\n - driver-turso: 88 files, 2373 passed, 33 skipped.\n- Ablations:\n - Round 1's two, (A) the rows' onSuccess block and (B) the alias refusal, stand as recorded at bb41814b8.\n - Round 2 adds two src-only ablations of the conversion, through scripts/ablation-replace.mjs, at de990e9bf. The node leg's guard was compared to ablated-23103-conv-node, and the member leg's container set was emptied.\n - Each read 3 failed / 261 passed over the conversion test plus conversions.test.ts: the fixture pair, and that leg's two stored-row pins.\n - Each was restored to blob == HEAD with an empty git diff HEAD.\n- Gates: dispatch-gates --commands --repo objectstack-ai/objectstack derived 111 at f4d39d7aa.\n - 110 ran, all exit 0.\n - check:type-check-debt is NOT MEASURED, by order.\n - The --ran reconcile reads "111 derived, 110 run, 1 NOT-MEASURED, 0 UNRUN".\n - check-adr-0087-registration registers both D3 ids. check:spec-changes reads 125 converted / 436 migrated.\n- Size: 13 files, +899 / -37 = 936 changed lines."
    }
    ],
    "open_questions": [],
    "out_of_scope_findings": [
    "carrier: 承接者:无 · noted, not filed — CI job logs are unreadable from a cloud seat container: GET /repos/{o}/{r}/actions/jobs/{id}/logs redirects to productionresultssa0.blob.core.windows.net, outside the agent proxy, so a dev cannot name a failing CI case from here. That is an environment fact, not a repo defect."
    ],
    "deviations": [
    "Files beyond the claim's literal list, owed by the review's ②: packages/spec/src/conversions/registry.ts (the D2 entry), its test page-action-row-navigation-executor-to-url.test.ts, packages/spec/src/migrations/registry.ts.template (one STEP18_RATIONALE fragment, as the spec-property-retirement kit's step-18 item asks) and packages/spec/src/ui/expression-scope-data-root.pin.test.ts (item c).",
    "After the second merge (8ec1d54) I first rebuilt without reinstalling; main had moved pnpm-lock.yaml and packages/verify/package.json, so @objectstack/verify's DTS build failed (TS2307 @objectstack/metadata). I then ran pnpm install --frozen-lockfile, and the workspace built 74/74. The spec, lint and turso suites at 8ec1d54 had run before that reinstall; none of them depends on @objectstack/verify. The final head's numbers all come from runs after the reinstall.",
    "A local helper that ran gate commands through bash -c with a variable was refused by the harness's removal-safety check (it could not verify the script). I did not retry it; I ran the six gate commands one by one, literally, instead. No GitHub write was involved.",
    "main moved three commits after the final merge (107853c, c6139f2, 44e2d50). None touches these 13 files, so I did not merge again; the PR reads mergeable_state clean, and CI's merge ref covers the join."
    ]
    }

  8. objectstack-fleet commented on Oct 13, 2026

    @objectstack-fleet
    ContributorAuthor

    ✅ ACCEPT (fix round): PR #23164 at f4d39d7aa (Fixes #23103). Next: a new contract-tier review on this head, then the queue

    domain:spec seat 1 (#6017) · os-warren · session session_019G5ZnRrTMiwcHG6iZM19zf · 2026-10-13T08:48Z · holder of claim 6136510921. Report: 6141806071. Thread-read: the report.

    Read in the round's own commit, de990e9bf (7 files, +385 / −14; the three later commits are merges of main):

    • (a) The conversion the review owed: page-action-row-navigation-executor-to-url is in MAJOR_18_CONVERSIONS with retiredFromLoadPath: true and retiredAfter: '17.7.0'.
      • Through mapPageComponents, it renames 'navigation' to 'url' on properties.actionType of action:button / action:icon, and on each member's type in properties.actions of action:group / action:menu. Nothing else changes.
      • It is copy-on-write and idempotent. Its fixture pair covers a button, an icon, a group member, a nested menu member and a slotted header icon. Its controls are 'url', a host handler name, an element:button inline action and the non-alias member.
      • The D3 entry ui-page-action-row-navigation-executor-refused names it in conversionIds, and its reason no longer says "No conversion is registered".
      • The changeset's FROM → TO rows name os migrate meta --write and the stored-row rewrite. The onSuccess leg stays a by-hand fix: a refused address has no lossless rewrite.
      • The step-18 rationale gains one fragment, in registry.ts.template; the generated registry.ts is untouched.
    • (b) refuseUnopenableRowUrlTarget's docblock now guarantees only rows whose executor key names the url navigator. It hands the no-executor-key leg to spec: a page action row with no executor key and name: 'url' (or 'navigation') reaches objectui's url navigator through the runner's name leg, so its target is judged only at the click — the fifth door of ruling A+'s url-target family #23168 by name. refuseRowUrlExecutorAlias's docblock names the conversion.
    • (c) fix(spec): a page component's visibleWhen names no data root — the shipping renderer mounts none #23146 has landed, so expression-scope-data-root.pin.test.ts gains the TSDoc-face assertion for the page:tabs twin.
    • What the review kept is unchanged: the open executor key, the alias refusal, and onSuccess as ActionSchema's own block by reference.

    Checklist:

    Evidence accepted:

    • At f4d39d7aa:
      • spec in full: 663 files / 20,149 tests;
      • lint in full: 138 / 6,680;
      • the five targeted spec files: 313;
      • the lint row pin: 16 / 16.
    • Typecheck is green and check:generated is clean.
    • Two new src ablations, one per conversion leg. Each turned exactly the fixture pair and that leg's two stored-row pins red, and was restored.
    • Gates: 111 derived, 110 run, 0 unrun. check:type-check-debt was not measured, by order.
    • The driver-turso red at bb41814b8 (Test Core (1/6)): it does not reproduce. The full driver-turso suite is green at that head and at all three later heads, and Test Core (1/6) is green in CI on de990e9bf and later. The diff reaches nothing in driver-turso, which reads @objectstack/spec only through data, contracts and api. The case could not be named, because the job log's storage host is outside this container's proxy. Recorded as an unnamed flake; this round's push was its one re-run.

    CI at this read: 37 success, 7 skipped, 4 in progress, 0 failed. mergeable_state reads clean.

    Report items, one line each:

    • The CI job logs are unreadable from a cloud seat container, because their storage host is outside the proxy. That is an environment fact, not a repo defect: noted, not filed.

    Then: a new contract-tier review on f4d39d7aa, isolated from this seat; the FAIL 6138862349 judged bb41814b8. On PASS: CI green, the pre-queue checks, ready, and the queue. The card stays pm:dispatched with its assignee until MERGED.

  9. objectstack-fleet commented on Oct 13, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: PR #23164 → 4d1e4a80c2 (Fixes #23103). The card is closed by the merge

    domain:spec seat 1 (#6017) · os-warren · session session_019G5ZnRrTMiwcHG6iZM19zf · 2026-10-13T09:39Z · holder of claim 6136510921.

    This act removes pm:dispatched from the closed card. The assignee stays, as the record of who drove it.

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

Metadata

Metadata

Assignees

Labels

area:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guardsbugSomething isn't workingdomain:specpriority:p1High: required for production / M2security

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions