Skip to content

feat(spec,ui)!: a record opens one way per object — navigation.mode keeps drawer (the default), page, new_window and none; modal, split and popover are retired; the split pane becomes a reader preference - #26804

Merged
os-zhuang merged 15 commits into
mainfrom
claude/issue-26677-navigation-mode-retirement
Oct 27, 2026
Merged

os-zhuang merged 15 commits into
mainfrom
claude/issue-26677-navigation-mode-retirement

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #26677

Clause-②: no (narrowing)
Metadata-protocol: narrowed

Child 1 of the record-page-v2 epic #26676. The maintainer's ruling, verbatim from the card: 「1. 一个对象一个默认打开方式:默认抽屉(带展开整页),page 只给显式声明的对象,modal 退役,split / popover 变成用户个人偏好;抽屉和整页是同一张页在两个宽度。」 This PR ships its declaration half and the console's read of it.

What changes

Spec (@objectstack/spec)

  • NavigationModeSchema is drawer | page | new_window | none, built with enumWithRetiredValues: modal, split and popover are refused by name, each with its prescription (first sentence: "navigation.mode: 'modal' was removed in @objectstack/spec 18.3.0 (ADR-0087 D2) — …"). NavigationConfigSchema.mode defaults to drawer (it was page). The one def sits behind every carrier: a list view, an object's listViews, and the eight page list blocks (list-view, object-grid, object-kanban, object-calendar, object-map, object-gantt, object-tree, object-timeline).
  • InterfacePageConfigSchema.recordAction is drawer | page | none; modal is refused with its prescription. page.form.ts drops the Modal option.
  • The retirement kit, value-level route (spec-property-retirement §2, re-read after dbf7e04adf):
    • D2 conversion navigation-mode-overlay-values-removed (retiredFromLoadPath: true, retiredAfter: '18.2.0', MAJOR_18 order 77): deletes a retired member from every carrier above and from interfaceConfig.recordAction, so a stored sys_metadata row (Studio's list-view inspector offered all seven modes and PUT /meta/view persisted them) converts on read and lands on the drawer. Fixture: 5 notices, disjoint.
    • D3 semantic ui-record-open-overlay-modes-retired (names the conversion in conversionIds), plus its STEP18_RATIONALE fragment.
    • The default change is declared in DEFAULT_CHANGES_BY_MAJOR (ui/NavigationConfig:mode "page" → "drawer"); authorable-defaults/ui.json regenerated.
    • No RETIRED_KEYS_BY_MAJOR row and no liveness row: no key changed. As the skill predicts for an enum-value narrowing, authorable-surface/, api-surface/ and json-schema.manifest/ are byte-identical; check:generated reports all 14 artifacts current.
    • Removal-version pin: a JOIN row for the conversion. Describes updated (the three component navigation describes say four values; the react-blocks navigation type), reference docs and skills/objectstack-ui/references/react-blocks.md regenerated.
  • Pin: packages/spec/src/ui/navigation-mode-retirement.test.ts (in the repo project; added to vitest.repo-tests.json): every carrier refuses each retired member with invalid_value at the member's path and the prescription's first sentence; defineStack refuses with STACK_SCHEMA_INVALID / 422; live members and the drawer default; a misspelt member keeps zod's own message; a @ts-expect-error at the authoring site; the conversion over stored view / page / object rows, idempotence, retired from the load path, wiring and the D3 entry; the page form's three options; a tree-scoped absence walk over the radius @objectstack/spec#test already declares.

Console (ui/)

  • useNavigationOverlay: a block without mode falls back to drawer (the spec default), and isOverlay is mode === 'drawer'. The absent-block branch is untouched: it stays the host's call (the hook's docblock explains why a host withholds navigation that way).
  • NavigationOverlay: the modal and popover faces are deleted, with popoverTrigger, popoverAnchorRef and the useOverlayAnchor export — measured unreachable (below). The drawer face and the split face stay; NavigationOverlayMode is page | drawer | split | new_window | none.
  • Grid, calendar, kanban, gantt and tree drop their split branches and popover anchor plumbing; their navIsOverlay reads drawer only.
  • The field-count guess is retired from the list → record path: objectViewRecordNavigation.ts resolves an un-authored list to the drawer whatever the field count (the drawer's width still follows the count), and plugin-view's ObjectView uses layout || (mobile ? page : drawer). Mobile still pages there; the app-shell path never passed a viewport to the guess, so its comment claiming "mobile always pages" was corrected rather than given a new behaviour. The record FORM's surface (recordFormNavigation.ts, AppContent.tsx) is untouched, as ruled.
  • The split pane is a reader preference: ui/packages/app-shell/src/utils/recordOpenPreference.ts (drawer | split, default drawer, localStorage key os:record-open-mode, default on SSR, a throwing storage or an unknown value). The console's ObjectView draws the split face when the preference is split AND the resolved mode is drawer; a declared page keeps its declaration. The split face reuses the drawer's URL state (?recordId=). The Display-menu control that writes the key is console list Display menu: 「打开记录」 (drawer | split) as a per-user preference; an object or view declaring page shows the declaration instead; the name cell's hover ↗ opens the record as a full page #26681.
  • appObjectScope.ts: IN_PAGE_RECORD_OVERLAYS and its route check are removed (below).
  • InterfaceListPage narrows recordAction to the three live values. The Studio list-view inspector's mode-label map is keyed by the spec's NavigationMode, and the three retired labels leave its i18n table.
  • Registry navigation input descriptions (eight plugins) and sdui.manifest.json regenerated (gen-sdui-manifest-node.mjs; the diff is those eight descriptions only). Docs: plugin-view page and README stop teaching the field-count guess; the calendar, kanban, gantt, map, timeline, tree, grid and detail docs stop teaching the retired modes; ui/packages/types describes and docblocks citing default('page') updated.
  • e2e/toast-under-modal.spec.ts: the centred-dialog case mounted NavigationOverlay's modal face, which is gone; it now mounts the Shadcn Dialog primitive, so the objectui#13340 top-centre path under a centred Radix dialog keeps its coverage.

Mechanism assumptions, measured

  1. ViewNavigationConfig is the spec alias, so the narrowing reds Type Check UI by itself — held. The first type-check after the spec build read object-kanban-calendar-navigation-8652.test.ts(233,17): error TS2322: Type '"modal"' is not assignable to type '"none" | "page" | "drawer" | "new_window" | undefined' (and the same in timeline, kanban, calendar and plugin-view tests). That red is also this PR's reverse verification that tsc read the rebuilt declarations.
  2. The hook mentions the three values only at its fallback and isOverlay — held; the pins that rested on the page fallback were flipped (list below).
  3. NavigationOverlay mounts — there are 19 mounts on this tree, not nine. All but three spread the hook's state (mode from metadata); RecordDetailDrawer passes drawer; plugin-view's two passed split / popover derived from navigationConfig.mode. With the values gone from metadata no caller reaches modal or popover, so those faces were deleted; split is reached by a non-metadata caller (the preference) and stays.
  4. IN_PAGE_RECORD_OVERLAYS — its one consumer widened the object scope for a list whose views open records in an overlay the URL does not carry. No such mode remains, and the split preference rides ?recordId=, which hasRecordOverlay already covers, so the set and its check were removed.
  5. The two guess sites and the two doc pages — held as listed.
  6. HotCRM and in-repo authorings — HotCRM 1f3f8880 declares page (lead) and drawer (opportunity, task, case) only. In this repo no source outside ui/ authors a retired member (control: 39 drawer / page authorings found by the same expression), and the tree-scoped pin holds that.

Tests (final head eb20d22070 unless stated)

  • pnpm --filter @objectstack/spec test: 730 files passed, 1 failed (scripts/liveness/check-liveness.test.ts, a child process's JSON truncated near 64 KB: macOS pipes are asynchronous, a known local-only failure, green in CI). test:repo: 79 passed, 1 failed (build-schemas-check-mode.manifest.test.ts generation stamp, the other known local-only failure).
  • pnpm --filter @objectstack/spec typecheck: exit 0 (it includes check:test-typecheck, so the new @ts-expect-error is compiled).
  • UI type-check, 14 packages via turbo (types, react, components, the eight plugins, plugin-view, plugin-detail, app-shell): 77 tasks successful.
  • UI vitest: packages/app-shell/ 1759 files / 16173 tests passed (pre-merge); the twelve other touched packages 1799 files passed with 10 failures, all fixed and re-run green except three git grep -E word-boundary LIT CONTROLs (BSD regex has no \b; local-only). Post-merge re-run of every changed test file plus plugin-grid/ and types tests: 674 passed, the same three local-only failures.
  • Ablations (each committed first, mutated through scripts/ablation-replace.mjs, restored to the HEAD blob with git diff HEAD empty): enumWithRetiredValues → z.enum reds 12 prescription rows of the spec pin; default('drawer') → default('page') reds 2 rows; the hook's ?? 'drawer' → ?? 'page' reds 4 rows across the two hook pins. Direction expected and observed: red.
  • e2e/toast-under-modal.spec.ts: NOT MEASURED locally — the cached Playwright browsers are build 1234 and the runner asks for 1243; nothing was installed. Build & E2E UI runs it.

Pins flipped, not deleted

Hook default (useNavigationOverlay.modeDefault, hostRecordNavigator-11293, offline-nav-performance-spec-parity); the per-renderer mode-less rows (kanban, calendar, timeline, map, tree, grid gridNavigationMembers-8071, gantt objectGanttInputs-11168, navigationModeResolution-7334, navigationPageSink-7334, objectTimelineInputs-11168); the five overlayShellModes-9299 files (a retired mode reaching a renderer past the parse now opens no shell, against a drawer lit control); ObjectView.pageSurfaceFallback-11015 (the field-heavy object with an onNavigate used to route to a page — it now opens the drawer and never routes); ObjectView.splitPopoverCreate-10975 (a stale split opens on the fallback surface, never beside the list); the types parity pins (four members, the three retired asserted absent); the spec-default controls in object-grid-properties-bag-11276 and object-gantt-properties-bag-10859-b6; dialogViewportGutterCensus-12603 (the record modal dialog is gone, not converged). The app-shell split tests (recordCountFooter-10636, viewRefusal-12112) now open the pane through the preference, and ObjectView.recordOpenPreference-26677.test.tsx pins the three cases (preference split → the pane; no preference → the drawer; a view declaring page → neither).

Tests deleted

ui/packages/plugin-view/src/__tests__/ObjectView.overlayTitleI18n.test.tsx and ObjectView.overlayTitleNoProviderFallback.test.tsx pinned the heading of plugin-view's split panel, a face this PR removes; the subject no longer exists. Its absence is pinned by the FLIPPED rows of ObjectView.splitPopoverCreate-10975.test.tsx (no Tasks Detail heading, no Close panel control).

Gates

node scripts/pm/dispatch-gates.mjs --commands on this branch derives 125 families; all were run with exit codes recorded and reconciled with --ran: 124 run, 1 NOT MEASURED (pnpm check:dual-build-cjs-loads, exit 3, PREREQUISITE NOT MET — it needs a full build). Two exit 1s are local-environment readings: pnpm check:bash32-floor (its self-test probes bash 4 syntax; this host's bash is 3.2.57), and pnpm --dir ui exec vitest run scripts/ (two tests hit their 15 s timeout under load and pass on a re-run with a longer timeout; two need mapfile, absent from bash 3.2). pnpm check:i18n and the spec check:skill-examples first refused their prerequisite and passed after building their closures; pnpm check:cross-package-test-inputs first named the new tree-walking pin, which now sits in the spec repo project. Also run: pnpm --dir ui run check:spec-symbols, check:doc-example-readers, check:unreferenced-sources, type-check:coverage, check:handler-key-reads, check:phantom-deps — all exit 0; ui eslint over the changed files: 0 errors. Final-head union re-run: reported in the issue comment.

Declared narrowing — verification ran UNLOCKED

scripts/pm/os-verify-lock.sh could not take the shared verify lock on this host: no usable flock. The shared verify lock is declared Linux-only (flock is util-linux, and a stock macOS does not ship it), so every heavy command in this PR (spec build, spec tests, ui type-check and vitest runs, the SDUI manifest regeneration, the ablations) ran directly, without the lock — a declared narrowing, not a silent one. No serialization guarantee held for these runs, nor for any sibling agent in this container while they ran. A second dispatched agent (#26678) shared the container.

The downstream sweep pnpm --filter '...@objectstack/spec' typecheck was not run; in its place, a source census found no package outside ui/ that names a retired member, and TypeScript Type Check runs every package.

Acceptance notes

skills/** readings

  • skills/objectstack-ui/rules/navigation.md: 162 → 162 lines (the Surface bullet rewritten in place: the drawer, page only where declared, no modal mode).
  • skills/objectstack-ui/references/react-blocks.md (generated): 115 → 115.
  • All SKILL.md: 4606 → 4606. The objectstack-ui package, every file: 2259 → 2259. No re-wrap, no growth.

维护者速读(草稿)

  • 改了什么: 列表点开一条记录的方式收成一种默认:抽屉(可展开成整页);只有声明了 page 的对象才直接打开整页。元数据里的 navigation.mode 不再接受 modal、split、popover,列表页的 recordAction 不再接受 modal,写了会在校验时被拒并给出改法;省略 mode 时默认值从 page 改成 drawer。控制台不再按字段多少替用户猜"整页还是抽屉"。左右分栏改成每个用户自己的浏览器偏好(本卡只做读取,设置开关在 console list Display menu: 「打开记录」 (drawer | split) as a per-user preference; an object or view declaring page shows the declaration instead; the name cell's hover ↗ opens the record as a full page #26681)。
  • 为什么改: 维护者裁决:一个对象一个默认打开方式,抽屉和整页是同一张页的两种宽度;弹窗和浮层是同一条记录的多余入口,分栏是读者自己的选择,不该由作者替所有人决定。
  • 风险与代价(含回滚): ① 写了这三个值的源码会在 18.3 校验失败,需要删掉该值(os migrate meta --from 17 列出改法,--write 可直接改);数据库里已保存的视图会在读取时自动转换为抽屉,不会报错。② 以前因字段多而自动整页打开的对象,现在默认用抽屉打开;需要整页的对象要显式声明 page。③ 由旧工具链解析过、已把 page 写死的产物仍按整页打开,删掉该值即可改用抽屉。④ 组件包删除了 useOverlayAnchor 导出和弹窗、浮层两种外壳,外部若直接用了会编译失败。回滚即 revert 本 PR,不写数据,没有不可逆动作。
  • 席位意见:
  • 你要做的: 本 PR 触及 skills/**(Tier H)且改动超过 3000 行,需要 GOVERNED_APPROVERS 身份批准后再由席位落地。

Generated by Claude Code

hotlong and others added 15 commits October 27, 2026 21:06
…ault; split as a reader preference

Claude-Session: https://claude.ai/code/session_cdb7a717-def7-4e71-9988-f089fbaa18ff
Co-authored-by: Claude <noreply@anthropic.com>
…ow that the record overlay's modal face is retired

Claude-Session: https://claude.ai/code/session_cdb7a717-def7-4e71-9988-f089fbaa18ff
Co-authored-by: Claude <noreply@anthropic.com>
… so it runs in the repo project

Claude-Session: https://claude.ai/code/session_cdb7a717-def7-4e71-9988-f089fbaa18ff
Co-authored-by: Claude <noreply@anthropic.com>
…vigation-mode-retirement

# Conflicts:
#	ui/packages/types/src/zod/objectql.zod.ts
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec, touching 23 documentable anchor(s). ⚠️ 3 changed file(s) yielded no anchor (packages/spec/authorable-defaults/ui.json, packages/spec/src/migrations/registry.ts.template, packages/spec/vitest.repo-tests.json), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

3 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/getting-started/common-patterns.mdx (via nav_contacts (literal, a string literal in fixture))
  • content/docs/ui/apps.mdx (via nav_board (literal, a string literal in fixture), nav_contacts (literal, a string literal in fixture))
  • content/docs/ui/audience-based-interfaces.mdx (via nav_contacts (literal, a string literal in fixture))

⛔ 2 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v17/17-5.mdx (via retiredAfter (symbol, a field of const object navigationModeOverlayValuesRemoved), retiredFromLoadPath (symbol, a field of const object navigationModeOverlayValuesRemoved))
  • content/docs/releases/v17/17-7.mdx (via NavigationConfigSchema (symbol, a top-level const))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 3 changed file(s) yielded no anchor (packages/spec/authorable-defaults/ui.json, packages/spec/src/migrations/registry.ts.template, packages/spec/vitest.repo-tests.json) — pages documenting those are invisible to this run
  • 11 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 55 of 207 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 147 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 8b276385e6f1e2b94a4061160c148cfa56f81588 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 11b31d0597ca77ea285e4d20b7d6034c9de0c527 — the merge of head eb20d2207077b470ee491c8bed8c7f782890aa54 into base 8b276385e6f1e2b94a4061160c148cfa56f81588, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 11b31d0597ca77ea285e4d20b7d6034c9de0c527 && git checkout 11b31d0597ca77ea285e4d20b7d6034c9de0c527
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 8b276385e6f1e2b94a4061160c148cfa56f81588 eb20d2207077b470ee491c8bed8c7f782890aa54 && git checkout -B drift-repro 8b276385e6f1e2b94a4061160c148cfa56f81588 && git merge --no-ff eb20d2207077b470ee491c8bed8c7f782890aa54

node scripts/docs-audit/affected-docs.mjs --json 8b276385e6f1e2b94a4061160c148cfa56f81588

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 8b276385e6f1e2b94a4061160c148cfa56f81588 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对 · PR #26804 ← #26677 · epic PM session_cdb7a717-def7-4e71-9988-f089fbaa18ff · 2026-10-27T17:23Z

Verdict: ACCEPT — Tier H landing (governed register: skills/objectstack-ui/rules/navigation.md, skills/objectstack-ui/references/react-blocks.md; the generated-surface exception does not lift the second because this PR also edits its generator; and +2134 / −1920 = 4054 changed lines, over the 3000-line human-merge threshold). No seat flips it ready, enqueues it or arms auto-merge: an APPROVED review from GOVERNED_APPROVERS (any commit) and then the owning seat lands it through the queue, or the maintainer merges it.

Checklist, judged on GitHub and on the branch:

Landing: the four-piece Tier H terminal in this stroke — needs-user-decision on this PR, the final 维护者速读 below, review requested from os-zhuang and hotlong. After an APPROVED review the owning seat lands it through the queue.


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

维护者速读(终稿)· PR #26804 ← #26677 · epic PM session_cdb7a717-def7-4e71-9988-f089fbaa18ff · 2026-10-27T17:23Z

  • 改了什么: 列表点开一条记录的方式收成一种默认——抽屉(可展开成整页);只有声明了 page 的对象才直接打开整页。元数据里的 navigation.mode 不再接受 modal、split、popover,列表页的 recordAction 不再接受 modal,写了会在校验时被拒并给出改法;省略 mode 时默认值从 page 改成 drawer。控制台不再按字段多少替用户猜「整页还是抽屉」。左右分栏改成每个用户自己的浏览器偏好(本 PR 只做读取,Display 菜单里的开关是 console list Display menu: 「打开记录」 (drawer | split) as a per-user preference; an object or view declaring page shows the declaration instead; the name cell's hover ↗ opens the record as a full page #26681)。
  • 为什么改: 你 2026-10-27 的裁决:「一个对象一个默认打开方式:默认抽屉(带展开整页),page 只给显式声明的对象,modal 退役,split / popover 变成用户个人偏好;抽屉和整页是同一张页在两个宽度。」
  • 风险与代价(含回滚): ① 写了这三个值的源码会在 18.3 校验失败,要删掉该值(os migrate meta --from 17 列出改法,--write 直接改);数据库里已保存的视图在读取时自动转换成抽屉,不报错。② 以前因字段多而自动整页打开的对象,现在默认抽屉;要整页的对象显式写 page。③ 旧工具链解析过、已把 page 写死的产物仍整页打开,删掉该值即用抽屉。④ 组件包删了 useOverlayAnchor 导出和弹窗、浮层两种外壳,外部若直接用会编译失败(changeset 带迁移句)。回滚 = revert 本 PR,不写数据,无不可逆动作。
  • 席位意见: 队前契约复核(本席,CONTRACT_REVIEW_TIER)PASS:收窄走 enumWithRetiredValues,每个退役值逐值带 18.3.0 / ADR-0087 D2 处方和 os migrate meta 句;D2 转换只删值、落到默认,覆盖 view 三种持久化拼写、object listViews、page recordAction 和八种列表块;D3 语义条目带验收判据;测试有反控(拼错值保留 zod 原话)、ADR-0112 code+status 信封断言、tsc 拒收、存储行回放与幂等、树内缺席 pin 带反空洞对照。实测影响面:HotCRM(1f3f8880)只声明 page(线索)和 drawer(商机、任务、工单),不受影响;仓内 examples / fixtures 的树内缺席 pin 绿。建议批准。
  • 你要做的: 用 os-zhuang 或 hotlong 账号在本 PR 上 APPROVE(任一 commit 即可);批准后由本席经队列落地(本 PR 碰 skills/** 且超 3000 行,Tier H)。

Generated by Claude Code

@os-zhuang
os-zhuang marked this pull request as ready for review October 27, 2026 22:49
@os-zhuang
os-zhuang enabled auto-merge October 27, 2026 22:49
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Director note · needs-user-decision leaves this PR · 2026-10-27T22:52Z

Recorded by the director seat (session_01KrKXQVvgYk2AJ4VkqMobyb, ledger objectstack#12708). Since objectstack#26671 (the maintainer, 2026-10-27: 「所以总监席你呈给我完全没必要啊」 → 「立卡」) the director seat no longer presents governed draft PRs in a ruling batch and the decision box holds decision cards only. This PR's Tier H terminal is complete on its own: seat ACCEPT, 维护者速读, review requested from os-zhuang and hotlong, head green. The one act is the maintainer's APPROVE on GitHub; the owning seat then readies, enqueues and lands. Nothing else changes here. ⛔ Not a claim.

@os-zhuang
os-zhuang added this pull request to the merge queue Oct 27, 2026
Merged via the queue into main with commit 17b5152 Oct 27, 2026
71 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-26677-navigation-mode-retirement branch October 27, 2026 23:43
This was referenced Oct 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation protocol:ui size/xl tests tooling

Projects

None yet

3 participants