Repository navigation
feat(plugin-detail): the default record page lets an editor advance the stage from the path - #30221
Merged
objectstack-fleet[bot] merged 4 commits intoNov 5, 2026
Conversation
…ffering only state-machine-admitted moves Claude-Session: https://claude.ai/code/session_01QSeKYpFBMr917PmVkG9up7 Co-authored-by: Claude <noreply@anthropic.com>
…n; assertions unchanged Claude-Session: https://claude.ai/code/session_01QSeKYpFBMr917PmVkG9up7 Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QSeKYpFBMr917PmVkG9up7 Co-authored-by: Claude <noreply@anthropic.com>
…fault-page-stage-advance
objectstack-fleet
Bot
deleted the
claude/issue-30075-default-page-stage-advance
branch
November 5, 2026 02:56
This was referenced Nov 5, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #30075
Clause-②: no
Metadata-protocol: none
维护者速读
buildDefaultPageSchema.ts里那一个allowStageChange: true。ui/packages/plugin-detail/src/synth/buildDefaultPageSchema.ts(默认页写入开关的那一行);ui/packages/plugin-detail/src/renderers/recordPathStageChange.tsx(admits,按状态机收窄可点阶段);ui/packages/plugin-detail/src/renderers/record-path.tsx(每个阶段是否渲染为按钮)。pnpm dev:crm后以管理员打开任一商机记录页,点击下一个阶段,应弹出「Move this record to …?」确认框,确认后阶段前移;以只读用户打开同一页,阶段路径不可点击。What changes
The landing point is the producer of the default record page,
buildDefaultHighlightsinui/packages/plugin-detail/src/synth/buildDefaultPageSchema.ts: therecord:pathnode it emits now carriesallowStageChange: true, the existing spec key (RecordPathProps.allowStageChange). No key is added and nothing underpackages/**is edited. The console's record page (RecordDetailView) renders that synthesized page whenever no page is assigned, so this is where "a default record page" is built.Who may press is unchanged and decided entirely by the existing opt-in (
useStageChange): the page's edit session may edit the record, the stage field is not read-only, computed or a system field, and the caller's field edit grant holds for the stored row. Everyone else, and an editor whose inline edit holds unsaved changes, keeps the read-only path. Every press opens a confirmation, then saves through the record's ordinary update with the record version (updateVia, the inline save bar's path), so the server's validation andstate_machinerule judge it; a refusal is shown under the path.One change to the renderer, made because the default page now puts the press in front of every app: a stage the stage field's blocking
state_machinecannot reach from the stored value is drawn as a readout, not a button. It reads the samestateMachineNextValuesthe record form, the grid's inline editor and the board use, so the path offers exactly the moves the edit dialog offers. A field without such a rule offers every stage, and the server still judges each write. This applies to every path with the opt-in, authored ones included.A declared page keeps its own setting: an assigned page writes its own
record:pathnode and never reaches the synthesizer, and an authoredhighlightsslot replaces the default strip, so itstrue,falseor absent key is kept as written (pinned below).record-path.inertReadout.test.tsxencodes the 2026-08-23 "separate card" ruling. Its assertions are unchanged: every mount there is a node without the opt-in, which is still the readout. Only its docblock changes, to say that the separate card landed as the opt-in and that the default page now carries it, so that file's pins now describe authored pages that leave the key out or set itfalse.Tests
New pins, as two top-level
describeblocks in the existing shared harnessrecord-path.stageChange-28199.test.tsx. The synthesized node is mounted as the JSON documentbuildDefaultPageSchemaemits, through the realSchemaRenderer:allowStageChange: trueon its path node.{ status: 'in_review' }with the record version and re-reads.highlightsslot keepsallowStageChange: true,false, and the key left out, each rendered accordingly.state_machine(drafttoin_reviewordeclinedonly),Acceptedis a readout and not a button. A warning-severity machine does not narrow the press (control).Amount: is required ...under the path; the current stage and the record are unchanged.The card listed the fill-in path (a stage move that needs another required field) as not measured. Measured: the press refuses in place and names the missing field by label (pin 6, client half). The server half is the existing objectql pin
validate-update-stored-row.test.ts(b): an update whose patch turns arequiredWhentrue without supplying the field answersVALIDATION_FAILEDwithfields: [{ field: 'note', code: 'required' }]and leaves the row unchanged (18/18 green on this branch). There is no in-place fill-in; the reader opens the edit dialog to supply the field. Nothing worse than #28303's refusal in place was found.Ablation (each through
scripts/ablation-replace.mjs, which checks the mutation landed and restores to the HEAD blob; the subjects resolve to source, so no rebuild is involved):allowStageChange: truefrom the synthesizer fails 5 of 24 in the harness (pins 1, 2, 5 and its control, 6). Restored, blob equals HEAD.&& stageChange.admits(o.stage.value)from the renderer fails exactly pin 5 (1 of 24). Restored, blob equals HEAD. A first attempt with the anchor ending in a bare;was refused by the tool (its replacement count could not move) and changed nothing; the anchor was respelled.Test runs
vitest run --isolate packages/plugin-detail/(whole package, isolated as the CI shard runs it): Test Files 369 passed, 1 skipped (370); Tests 3270 passed, 18 skipped. This ran beforeorigin/mainwas merged in; the merge changed nothing in these files.record-path.stageChange-28199.test.tsxverbose: 24/24, including the 10 new#30075cases.record-path*+synth/: 30 files, 320 tests passed.packages/objectqlvalidate-update-stored-row.test.ts: 18/18.Acceptance notes
ui/packages/plugin-detail/src/index.tsxand the spec.describe()still say the key's default is off. Both describe the key, and the key's default has not changed: the default page sets it explicitly. They are left as they are, sosdui.manifest.jsonand the spec docs are not regenerated.PagePreview) renders the same synthesized page. Its preview host mounts no edit session, so the path stays a readout there.Verification
Local gate families derived by
node scripts/pm/dispatch-gates.mjs --commandson this branch. Results and the commit they ran against are listed in the os-dev report comment on #30075. CI runs the full set.Generated by Claude Code
🤖 Generated with Claude Code
https://claude.ai/code/session_01QSeKYpFBMr917PmVkG9up7