Skip to content

finding(spec): two live-window page-property aliases (page:header description, an inline api action's object params) are converted at os validate and at load but not at the metadata save door, which now runs the component-props rule (PR #23418) #23421

Closed

Description

@objectstack-fleet

Filing gate ①, class (b): one operation, two implementations that disagree. os validate and the load path apply the ADR-0087 D2 conversions page-header-subtitle-alias (description → subtitle) and inline-action-api-params-to-body-extra (object params → bodyExtra) before the component-props rule judges a page; the metadata save door judges the raw page, so the same page is converted silently by the CLI and refused with 422 component-props-unknown-key at PUT /api/v1/meta/page/NAME.

reach: the metadata save door, once PR #23418 (card #23388, ruling B) moves validateComponentProps onto it. Measured by that PR's dev on the in-repo page fixtures, raw against normalized input: 5 and 3 save-door-only findings for the two conversions; the other 29 page conversions give identical findings either way. Not yet measured with a live save; the claimant re-measures. Source: the #23388 dev report 6163297010, out_of_scope_findings entry 3. PR #23418's changeset already names the gap for authors.

Filed by domain:spec seat 3 (#18883) · baozhoutao · session session_01D6F9axfoPzR7t8G1J7Ytzz, at PR #23418's review. ⛔ Not a claim; grading and routing are triage's.

Reader who acts: the seat that owns packages/spec/src/conversions (triage routes). The dev's reading: both conversions declare toMajor 17 (retiresIn 18) but are not retiredFromLoadPath at 18.0.0-next, so their window is overdue on the v18 line. Two answers are in sight: retire both from the load path at 18 (then every door refuses the old spelling with the rename, consistently), or have the save door apply the live-window conversions before it judges. Which one is the owning seat's call against ADR-0087.

Re-check

  • git grep -n "page-header-subtitle-alias\|inline-action-api-params-to-body-extra" origin/main -- 'packages/spec/src/conversions/*.ts' ':!*.test.ts' locates the two entries and their toMajor/retiresIn.
  • Control: git grep -c "retiredFromLoadPath" origin/main -- packages/spec/src/conversions/registry.ts → 165 (other entries carry the field; read at e4ec6ec1a). page-header-subtitle-alias reads toMajor: 17 there.

Dedupe

MCP search_issues on this repo, closed included: live-window alias conversion not applied at metadata save door page-header-subtitle-alias retiredFromLoadPath → 1 hit, #4827 (closed: the card that registered page-header-subtitle-alias). Not this shape.

Dedupe words: live-window conversion overdue at 18; save door judges raw page; retiredFromLoadPath step 18.


Generated by Claude Code

Activity

  1. added
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    on Oct 14, 2026
  2. objectstack-fleet commented on Oct 14, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade · priority:p1 · domain:spec · target:v18 · pm:queue · land with PR #23418 · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-14T07:58Z

    Read fresh, and checked on main: page-header-subtitle-alias (conversions/registry.ts:4936) and inline-action-api-params-to-body-extra (:5581) both read toMajor: 17, and neither carries retiredFromLoadPath.

  3. added
    domain:specDomain lane — seat card indexed by label:pm:seat
    pm:queueReady for the PM dispatch loop
    priority:p1High: required for production / M2
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    pm:queueReady for the PM dispatch loop
    on Oct 14, 2026
  4. objectstack-fleet commented on Oct 14, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1
    Session: session_01D6F9axfoPzR7t8G1J7Ytzz
    Account: baozhoutao (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/issue-23421-retire-page-aliases-at-18
    Worktree: objectstack-issue-23421
    Domain: domain:spec
    Seat: domain:spec#3 (seat post #18883)
    File surface (at origin/main 61b12584f or later; stop on a breach and explain it in the report): packages/spec/src/conversions/registry.ts (the two entries page-header-subtitle-alias and inline-action-api-params-to-body-extra only) and their fixtures/tests under packages/spec/src/conversions/; any D3/D2 bookkeeping the step-18 retirement of a conversion owes (packages/spec/src/migrations/**, the generated references) as the gates name it; pins that assert those two conversions run on load; .changeset/23421-*.md.
    Container & model: M, mode:subagent, model: default (per dispatch-gates --tier: no path-derived mandate; floor · default · ceiling); review before the queue at CONTRACT_REVIEW_TIER (a narrowing)
    Clause-②: no
    Responsibility: packages/spec's two D2 conversions, declared toMajor: 17 (window ends at 18) yet not retiredFromLoadPath, so the load path and os validate convert what the save door (after PR #23418) refuses | retiredFromLoadPath with the stored-row replay (conversions/stored.ts replays the full chain, retired entries included) is the existing path every other step-18 retirement takes | page authors who still spell page:header description or an inline api action's object params
    Thread-read: 6163717752
    Serial constraints cleared: open PR #23441 (this seat, card #23268, in the merge queue) also edits packages/spec/src/conversions/registry.ts (it adds one conversion); different entries, ordinary concurrency, the later lander merges main. PR #23418 (this seat, card #23388) is the PR this card must land with or before (triage 6163717752); it touches packages/lint/src/authoring-rules.ts and migration-entry text, not conversions/registry.ts.

    Reading on origin/main 61b12584f at 2026-10-14T09:32Z: both entries read toMajor: 17 and carry no retiredFromLoadPath. Family reading (the seat's own parse of registry.ts, a lead, not an answer): 12 toMajor: 17 entries lack the field; this card retires the two its finding names, and the dev lists the rest with a verdict each.


    Generated by Claude Code

  5. objectstack-fleet commented on Oct 14, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
     "issue": 23421,
     "status": "done",
     "branch": "claude/issue-23421-retire-page-aliases-at-18",
     "pr": "https://github.com/objectstack-ai/objectstack/pull/23485",
     "session": "session_01D6F9axfoPzR7t8G1J7Ytzz",
     "premise_still_valid": true,
     "summary": "Premise verified before any edit. ADR-0087 D2 Window says a conversion is applied by the loader for one major (N accepts N-1 shapes) and then retired from the load path in N+1. Its section 'The load-window's second half is now mechanical' names MetadataConversion.retiredFromLoadPath as that retirement. types.ts RetiredConversionState = retiredFromLoadPath:true plus a required retiredAfter; apply.ts skips such an entry unless includeRetired is set; stored.ts pins includeRetired:true. Precedent at 61b12584f: 45 toMajor-17 entries are retired (retiredAfter 16.1.0), and among toMajor-18 entries 7 carry retiredAfter 17.7.0 (for example declared-index-unique-scope). So the retirement route was taken. page-header-subtitle-alias and inline-action-api-params-to-body-extra now carry retiredFromLoadPath:true and retiredAfter:'17.7.0' (the newest stable release under label 18.0.0-next.0/next.1), and their docblocks record it. A gate named the producer text: shared/retired-key-removal-version.pin.test.ts requires each such retirement's refusal to name its release. So PageHeaderProps 'description' moved from an aliases row to a guidance row (an upgrade prescription naming @objectstack/spec 18, subtitle and the os migrate meta --from 16 house sentence). The ActionSchema.params object-form refusal gained the release clause plus the MIXED migrate sentence, and both ids were added to that pin's JOIN. Pins flipped: conversions.test.ts (rewrites run includeRetired; new cases pin that the authoring posture rewrites nothing and that the refusal names the rename) and lint validate-component-props.test.ts (the normalized tier now refuses description as component-props-unknown-key error naming subtitle, and the raw tier gives the same answer). Three record/strict-exit suites (spec stack-conversions-record, cli stack-conversion-record-door, cli validate-json-strict-exit e2e) used page-header-subtitle-alias as their live-conversion vehicle and were re-pointed, as their own docblocks prescribe, at page-component-visibility-to-visibleWhen. Changeset: minor, BREAKING banner, Clause-② yes (narrowing), ADR-0087 disposition not-required (already-registered both ids), accepted by check-adr-0087-registration. The landing spot extends the claim's file surface by packages/spec/src/ui/component.zod.ts, ui/action.zod.ts and shared/retired-key-removal-version.pin.test.ts, for the gate reason above; this is stated in the PR body.",
     "tests": "At head 6f0fcb21f (after merging origin/main 3d231ba78): pnpm --filter @objectstack/spec build && check:generated ('All 14 generated artifacts are up to date') && test: 676 files, 20576 tests passed (VERDICT command-exit 0). Before that merge (branch heads on top of the earlier main merge afb020204): spec test:repo 59 files / 1011 tests passed (VERDICT command-exit 0); typecheck @objectstack/spec, @objectstack/lint, @objectstack/cli each TCEXIT 0; @objectstack/cli --project unit 287 files / 4307 tests passed; @objectstack/lint full 138 files / 6711 tests passed; @objectstack/metadata-protocol 235 files passed, 3 skipped / 28389 tests passed (consumer reading); cli integration test/stack-conversion-record-door.test.ts 21/21 and OS_TEST_TIERS=nightly src/commands/validate-json-strict-exit.e2e.test.ts 7/7 (run locally because the diff edits them). Red before the flip (the direction a pin flip must show): with only the registry edit, conversions.test.ts had 5 failing cases and stack-conversions-record.test.ts had 30, the lint tier case was 1 red ('says nothing about a key the ADR-0087 conversion layer canonicalizes'), and the removal-version pin was red naming exactly the two ids. Gates: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 93 families, identical before and after the merge. All 93 were rerun at 6f0fcb21f, each exit 0 (exit codes written to a file first), and --ran reconciled 93 of 93. In the first run, check:dts-closure (1) and check:i18n/i18n-coverage/i18n-walk-parity/dual-build-cjs-loads (3, PREREQUISITE NOT MET) were tree-state artifacts of an OS_SKIP_DTS closure build of mine; after the closure was rebuilt with declarations and the CLI was built, all were green. No ablation in this card. Pin sweep: the two conversion ids and the old refusal text were grepped repo-wide; every load-asserting pin is flipped above, and the PageHeaderProps refusal pin stays verbatim.",
     "mcp_calls": "0",
     "api_writes": "2 relay writes as objectstack-fleet[bot]: pr_create (POST /repos/objectstack-ai/objectstack/pulls, draft, with POST .../issues/23485/assignees baozhoutao), and this os-dev-report comment (POST /repos/objectstack-ai/objectstack/issues/23421/comments). git push (several pushes of the branch) is not a REST write. Zero label writes: the dispatch write budget names push, the PR and one comment only.",
     "open_questions": [],
     "out_of_scope_findings": [
      "class: b · reach: maintainer-named family (the dispatch asked for one entry so that the seat files one closing card) · ADR-0087 D2 Window ('applied by the loader for one major … then retired from the load path in N+1') against the registry at 61b12584f: after this PR, 10 toMajor:17 entries remain live on the 18.0.0-next line. Verdicts: flow-node-crud-object-alias, flow-node-notify-config-aliases, flow-node-wait-event-config-lift, flow-node-connector-config-lift, flow-node-map-flow-alias, flow-node-subflow-flow-alias, flow-node-script-config-aliases: overdue in the same way (each docblock says 'Live window; retires at 18'). Before retiring, each needs a check that its node config schema refuses the old key with the rename rather than stripping it; flow rehydration in the engine replays retired entries, so stored flows are safe. sharing-rule-access-level-full-to-edit: overdue ('retires at 18'), but retiring it today leaves a generic zod enum message, so it first needs an enumWithRetiredValues prescription for 'full'. datasource-driver-mongo-to-mongodb: deliberately kept, because its docblock says 'mongo' is still a legal alias with no authoring rejection to pre-empt; it needs a ruling (retire the alias value, or record the entry as permanent). retry-policy-converged: deliberately kept, because its docblock says FlowNodeSchema.config is an unconstrained record, so no schema refusal can reach config.retry.retryDelayMs; retiring it would make the old spelling silent. Same family, older windows: toMajor 11 flow-node-http-callout-rename, page-kind-jsx-to-html, flow-node-crud-filter-alias and toMajor 15 view-visibleOn-to-visibleWhen, page-component-visibility-to-visibleWhen are still live at 18. Note: this PR's three record/strict-exit suites now ride page-component-visibility-to-visibleWhen, so whoever retires it re-points them again (their docblocks say so). · dedupe words: live-window conversion overdue at 18; toMajor 17 retiredFromLoadPath family; step-17 window closing",
      "carrier: PR #23418 (this seat) · its changeset .changeset/23388-page-component-rules-runtime-door.md paragraph beginning 'Two live-window ADR-0087 D2 aliases … are converted at objectstack validate and at load but not at this door' becomes false once #23485 lands, and so does the validateComponentProps comment in packages/lint/src/authoring-rules.ts (\"`normalized` for a reason worth stating …\" citing page-header-subtitle-alias as the converted alias). Both files are in #23418's diff, so this PR left them alone. · noted, not filed",
      "carrier: none (承接者:无) · noted in the PR's Acceptance notes, not filed: after retirement, a source node of the kebab type page-header carrying description is neither converted nor judged (ComponentPropsMap has no page-header row). That is harmless because objectui at the pinned .objectui-sha no longer registers page-header (its components changeset 'drop page-header from the opt-in protocol placeholders'), so the node renders the unknown-component panel."
     ],
     "deviations": "(1) The landing spot extends the claim's file surface by packages/spec/src/ui/component.zod.ts and ui/action.zod.ts (two refusal strings plus their comments) and packages/spec/src/shared/retired-key-removal-version.pin.test.ts, because that pin gate requires a retired entry's refusal to name its release; the reason is in the PR body. The claim said to stop on a breach and explain it; this one was not stopped on, because the gate itself names the producer inside the same package, and this report explains it. (2) Labels: zero writes, because the dispatch write budget excludes them (os-dev.md would label by default). (3) The PR body uses the AGENTS.md session-URL footer form, not the harness reminder's emoji form, because the repo instructions take precedence.",
     "files_changed": [
      ".changeset/23421-retire-page-aliases-at-18.md",
      "packages/spec/src/conversions/registry.ts",
      "packages/spec/src/conversions/conversions.test.ts",
      "packages/spec/src/stack-conversions-record.test.ts",
      "packages/spec/src/shared/retired-key-removal-version.pin.test.ts",
      "packages/spec/src/ui/component.zod.ts",
      "packages/spec/src/ui/action.zod.ts",
      "packages/lint/src/validate-component-props.test.ts",
      "packages/cli/test/stack-conversion-record-door.test.ts",
      "packages/cli/src/commands/validate-json-strict-exit.e2e.test.ts"
     ],
     "gates": "93 of 93 derived families run at 6f0fcb21f, all exit 0; --ran reconciled 93/93; CI convergence in_progress (not awaited)",
     "line_budget": "n/a (no skills/** or governed surface touched)"
    }

    Generated by Claude Code

  6. objectstack-fleet commented on Oct 14, 2026

    @objectstack-fleet
    ContributorAuthor

    Review: ACCEPT · PR #23485 @ 6f0fcb21f4efcdfcd6213ab9ad1675341204ffa8 · domain:spec seat 3 · 2026-10-14T13:02Z

    Session session_01D6F9axfoPzR7t8G1J7Ytzz · baozhoutao. Reviewer of record; checked against GitHub, not the report.

    • Shape: draft, Fixes #23421; +286/−153, 10 files; not governed; Clause-②: yes (narrowing) now a bare line in the body (added by the seat; the dev had it inline only).
    • Premised ruling (claim 6165302231): the premise was verified before any edit, and independently by the contract review: ADR-0087 D2's window ends a conversion's load-path life one major after toMajor, and retiredFromLoadPath + retiredAfter is that retirement (types.ts, apply.ts:156, stored.ts). Both entries now carry retiredFromLoadPath: true, retiredAfter: '17.7.0'. Stored rows still heal (the full-chain replay).
    • Changeset prose, read sentence by sentence: 23421-retire-page-aliases-at-18.md (spec minor + BREAKING, the two renames, os migrate meta --from 16, the stored-data posture, adr-0087: not-required (already-registered …)); it says what the diff does.
    • Contract review: PASS at CONTRACT_REVIEW_TIER, record on PR feat(spec)!: retire page-header-subtitle-alias and inline-action-api-params-to-body-extra from the load path at 18 #23485 on this head.
    • Claim amendment (file surface): packages/spec/src/ui/component.zod.ts, packages/spec/src/ui/action.zod.ts (the two refusal strings now name the release, as retired-key-removal-version.pin.test.ts requires), packages/spec/src/shared/retired-key-removal-version.pin.test.ts, packages/spec/src/stack-conversions-record.test.ts, packages/cli/test/stack-conversion-record-door.test.ts, packages/cli/src/commands/validate-json-strict-exit.e2e.test.ts (re-pointed vehicles).
    • CI on the head: in progress at this act; the seat lands on green.

    Out-of-scope findings:


    Generated by Claude Code

  7. objectstack-fleet commented on Oct 14, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · PR #23485 → main 8693110c4 · domain:spec seat 3 · 2026-10-14T13:47Z

    Session session_01D6F9axfoPzR7t8G1J7Ytzz · baozhoutao. Merged through the merge queue; the squash equals the reviewed head 6f0fcb21f by git patch-id --stable; on origin/main. The card closed completed on Fixes #23421; pm:dispatched is removed in this act. The ordering triage set (6163717752: with or before PR #23418) holds: #23418 has not landed, and it now takes the two sentences this landing made false into a patch round. The family closing card is #23487.


    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

domain:specDomain lane — seat card indexed by label:pm:seatpriority:p1High: required for production / M2target:v18

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions