You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
record:path ticks a stage the record skipped — a contract type with no legal review shows 「审查中 ✓」 after approval #26581
Ruled: 6442747364 · letter A (with the current_user guardrail) · 2026-10-27T13:14Z
Filing class: ① user-visible defect (misleading status) — reach: public door, measured in a browser on the 18.1.0 Console with objectstack-ai/hotclm main. Reader: the UI lane (record:path renderer) and the spec owner of RecordPathProps (packages/spec/src/ui/component.zod.ts:2187).
Business reason
In a contract system, 「审查中 ✓」 reads as "legal reviewed this". hotclm order contracts (ORD) skip legal review by design and go straight to approval. Yet their page path shows 审查中 as a passed step. An auditor or a requester reading the page is told something that did not happen.
Measured
ORD-2026-0017 (submitted → in_approval → approved, never in_review): the path shows 草稿 ✓ 已提交 ✓ 审查中 ✓ 审批中 ✓ 已批准. The renderer ticks every stage before the current one by position. A stage takes only value, label and terminal; there is no way to say a stage applies conditionally or was skipped.
Expected (either is enough)
A per-stage predicate (e.g. visibleWhen over the record) so an app can drop 审查中 for a type that skips it; or
the renderer reads the record's history and shows a skipped stage as skipped (grey, no tick), not passed.
Dedupe: objectstack issues (2,000 most recent) and archived objectui issues grepped for record:path, skip, stage + path — none.
Filed from the hotclm UI/UX review (2026-10-27); maintainer: 「平台的问题应该提交issue到平台」.
p2:record:path ticks every stage before the current one by position, so a stage the record skipped reads as passed. That is a misleading status on an audit-facing page. First measure whether the record's state history is already readable on the page: if it is, tick only the visited stages. If a stage needs a new declared key on RecordPathProps (packages/spec/src/ui/component.zod.ts:2187), that is a protocol addition, p1, domain:spec, and it comes back to triage before it is built.
Claim: PM loop round 1
Session: session_01QsR2jTfvjNTaR29ehLk26T
Account: zhuangjianguo
Branch: claude/project-thread-ziacft (platform-assigned card-thread branch; a card thread can push only its assigned branch, and the name carries no issue number, so this claim is the identity record)
Worktree: objectstack-issue-26581
Domain: domain:ui
Seat: domain:ui#4
File surface: ui/packages/plugin-detail/src/renderers/record-path.tsx, its tests, one root changeset; ⛔ packages/spec/** and server packages (stop on breach; explain in the report)
Container & model: M, mode:thread, model: TIER_DEFAULT (dispatch-gates --tier: no path-derived mandate; reason: the maintainer's standing rule 「只认领不需要 fable 的卡」, 2026-10-27T10:39Z), effort: high
Clause-②: no
Metadata-protocol: none
Responsibility: RecordPathRenderer (plugin-detail, this lane) ticks every stage before the current one by position · the platform path that may cover it: the record's status history, if readable on the page (unmeasured) · reached by every reader of a record:path whose flow skips a stage (hotclm ORD contracts)
Thread-read: 6437783999
Premise: holds (action, card-reference and work-item surfaces re-read on origin/main 498e8f40 at 2026-10-27T11:40Z)
Serial constraints cleared: none in flight; #26612 (not yet triaged) may later land on record-path.tsx; no open PR touches it (scan 2026-10-27T11:40Z)
Dispatched at 2026-10-27T11:43Z to the card thread on that branch; the order lives in the thread's opening message.
{
"issue": 26581,
"status": "needs_decision",
"branch": "claude/project-thread-ziacft",
"pr": null,
"session": "session_01QsR2jTgHpQBzM5GaoKifNh",
"premise_still_valid": true,
"summary": "Stop condition met: no history read is reachable from record:path without a new server read, so nothing was changed and no PR opened. The defect is real on origin/main 498e8f40: record-path.tsx:443 (desktop) and :514 (mobile) tick every stage with idx before currentIdx, and the only record input is ctx.data[statusField] (:231). The renderer's one channel, RecordContextValue (ui/packages/react/src/context/RecordContext.tsx:113), carries objectName, recordId, dataSource, data (current row only), objectSchema, refresh and header members; no status history. The record page does read history, but not into anything record:path can reach: RecordDetailView.tsx:2092-2096 reads sys_audit_log old_value/new_value only when enable.trackHistory === true (opt-in, :1965), only on the synthesized page, only once the History tab is on screen (objectui#13010 deferral), $top 50 desc, and hands the rows only to the record:history node's entries (:3225); the sys_activity read (:2550, metadata {old,new} per audit-writers.ts:2309) is deferred to a feed block on screen and lands in DiscussionContext as FeedItems. Even a full read is not proof of a skip: activities can be opted out (enable.activities false), rows for fields the reader is not served are withheld (activity-field-redaction.ts), unstamped rows are withheld until backfill, and both reads are capped, so a missing in_review row is not evidence the record skipped it. A smaller route exists that needs no history and no spec key: the status field option's existing, server-enforced visibleWhen (open question 1).",
"tests": "none run: no code change (stop condition)",
"gates": "none run: empty diff; dispatch-gates --commands not derived (no changeset, no files changed)",
"dogfood_battery": "n/a — no PR",
"mcp_calls": "0 — no GitHub MCP calls",
"api_writes": "1 — POST /repos/objectstack-ai/objectstack/issues/26581/comments (this os-dev-report, via scripts/pm/post-stamped.mjs)",
"line_budget": "n/a — empty diff",
"deviations": [],
"files_changed": [],
"open_questions": [
{
"question": "How should record:path stop presenting a stage this record could not have passed?",
"options": [
"A — Honor the status field option's declared visibleWhen (packages/spec/src/data/field.zod.ts:431: 'option is offered only when TRUE (else omitted)', and the server refuses a write of a value whose predicate is false, packages/objectql/src/validation/rule-validator.ts:185-200). record:path omits from both rows any stage whose matching option's predicate is FALSE for this record; a faulting predicate keeps the stage (render side stays fail-open, rule-validator.ts D3). Matches authored stages by value, the way declaredStageKinds already reads option kind. Zero spec keys, zero new server reads, file surface record-path.tsx plus tests plus changeset. hotclm then declares visibleWhen on its in_review option (app-side, existing key; hotclm not in this container, so not measured). Does not cover an ad-hoc skip no rule declares.",
"B — record:path self-fetches sys_activity (or sys_audit_log) for this record and ticks only statusField values seen in metadata old/new. A new server read on every record page that draws a path, against the objectui#13010 deferral; sys_audit_log needs trackHistory opt-in; withheld rows, opt-out and $top caps make absence ambiguous, so it would show a guessed skipped state unless a third 'unknown' presentation is added (product decision).",
"C — New per-stage key on RecordPathProps.stages[] (e.g. visibleWhen) for authored stages that are not field options. Protocol addition: p1, domain:spec, back to triage per comment 6437783999. No measured pull beyond what A covers."
],
"recommendation": "A. Business need: the measured case (hotclm ORD skips legal review by contract type, by design) is a rule about the record's own columns, which is exactly what an option visibleWhen declares. Long-term: it honors an already-declared, server-enforced contract (declared-but-unhonored is an implementation gap, not a new surface). AI-error: declaration-driven and enforced on write, no consumer-side guessing from incomplete history. Startup focus: no new spec key, no new read, no new gate. B adds a read and an ambiguous state; C expands the protocol without pull. A needs the seat to amend the order, since the order's stop condition said change nothing."
}
],
"out_of_scope_findings": [
"class: none — noted, not filed · RecordDetailView.tsx:1951-1963 comment says the audit read requests 'only the safe projection (created_at, action, user_id) so the browser never receives serialized old_value/new_value', but :2096 selects old_value and new_value; stale comment, observation only · carrier: none"
]
}
Governing text:packages/spec/src/data/field.zod.ts(选项 visibleWhen):"option is offered only when TRUE (else omitted) … the rule validator refuses a write of a value whose predicate is false … so a user-gated CHOICE belongs here"。packages/objectql/src/validation/rule-validator.ts:186-188:"That gate is the server's enforcement of who may pick an option (ADR-0124 D1)"。分诊评论 6437783999:新的阶段键属协议新增,p1、domain:spec,须回分诊。
Director seat, summon #51 continuation (ledger objectstack#12708). Presented to the maintainer in batch #413 as item 2 of 3 — the ui seat's question 6441171471 after the card thread's stop (6441110297). Seat increment (re-verified on origin/mainfe53769a):
Premises hold: ui/packages/plugin-detail/src/renderers/record-path.tsx:443 and :514 tick every stage with idx < currentIdx; the renderer's only record input is the current row. packages/spec/src/data/field.zod.ts option visibleWhen: "option is offered only when TRUE (else omitted)"; packages/objectql/src/validation/rule-validator.ts:188 calls that predicate "the server's enforcement of who may pick an option (ADR-0124 D1)". Triage 6437783999 graded the card p2 domain:ui and read a new per-stage key as a protocol addition (p1, spec).
Half-state: needs-user-decision alone (the thread's claim released); the ruling's stroke returns it to pm:queue.
Facets (the seat's block, re-read): ① A reuses a declared, server-enforced contract — an option this record cannot take is not a stage this record walks; C adds a second place to declare the same fact; B makes the renderer depend on incomplete history. ② hotclm ORD contracts today, an auditor reads 「审查中 ✓」 as "legal reviewed this". ③ A is declaration-driven and loud on write; B guesses silently; C drifts between two declarations. ④ A adds no key, read or gate.
The one product semantic inside A, which is why it is in the box: visibleWhen answers "who may pick this value", the path asks "does this record walk this stage"; they coincide for predicates over the record's own columns and diverge for predicates over current_user. The seat's guardrail: a predicate that references current_user does not hide a stage; a faulting predicate keeps the stage (render side fail-open, ADR-0137 D2/D3). Unmeasured: whether hotclm can express "ORD skips review" as an option visibleWhen — the hotclm seat confirms on its own card.
The seat agrees with A with that guardrail. Not B (a new read per record page against the objectui#13010 deferral, and an ambiguous "unknown" state to design) and not C (a protocol key with no pull beyond A). Workload: ui · 1 PR (record-path.tsx + tests + changeset) · S; Clause-②: no.
Prior rulings read: ADR-0124 D1, ADR-0137 D2/D3, objectui#13010; thread: 6437783999, 6441171471.
Ruling: batch #413 item 2 · letter A · maintainer 「同意」 2026-10-27T13:11Z
Recorded by the director seat (summon #51 continuation, session_01KrKXQVvgYk2AJ4VkqMobyb, ledger objectstack#12708). The maintainer answered batch #413 with 「同意」 — every seat letter. Brief 6441900906; the ui seat's question 6441171471. Letter A, with the guardrail: record:path omits from both rows any stage whose matching status-field option declares a visibleWhen that evaluates FALSE for this record (the option contract of packages/spec/src/data/field.zod.ts, enforced on write by rule-validator.ts per ADR-0124 D1); a faulting predicate keeps the stage (render side fail-open, ADR-0137 D2/D3); a predicate that references current_user does not hide a stage — that predicate answers "who may pick the value", not "does this record walk this stage". Stages are matched to options by value, as declaredStageKinds already reads option kind. Not B (a new read per record page against objectui#13010, and an "unknown" state to design) and not C (a protocol key with no pull beyond A).
Workload (card column): ui · 1 PR (ui/packages/plugin-detail/src/renderers/record-path.tsx + tests + changeset) · S; Clause-②: no, Metadata-protocol: none. hotclm declares the option visibleWhen on its contract-type rule on its own card; whether that rule is expressible as an option predicate is the hotclm seat's reading.
Execution: needs-user-decision → pm:queue in this act (domain:ui, p2); the ui seat amends its dispatch order (the "change nothing" stop condition lifts) and the same card thread implements. Ruling-ref: names this comment.
The "change nothing" stop condition of claim 6441053217 lifts. Letter A with the guardrail is the order. The same card thread (claude/project-thread-ziacft, session_01QsR2jTgHpQBzM5GaoKifNh) implements it.
File surface: ui/packages/plugin-detail/src/renderers/record-path.tsx, its tests, one root changeset; ⛔ packages/spec/**, server packages, new keys or reads (stop on breach)
Clause-②: no
Metadata-protocol: none
Container & model: M, mode:thread, model: TIER_DEFAULT, effort: high
Order (per the ruling): record:path omits from both rows (desktop and mobile) every stage whose matching status-field option declares a visibleWhen that evaluates FALSE for this record. Stages match options by value, the way declaredStageKinds already reads option kind. A faulting predicate keeps the stage (fail-open, ADR-0137 D2/D3). A predicate that references current_user does not hide a stage.
Serial constraint: PR #26689 (#26612) is in the merge queue on the same file. The thread starts from origin/main after #26689 merges.
Out of scope: the lost-record "last open step" stays on #26694.
{
"issue": 26581,
"status": "done",
"branch": "claude/project-thread-ziacft",
"pr": "https://github.com/objectstack-ai/objectstack/pull/26784",
"session": "session_01QsR2jTgHpQBzM5GaoKifNh",
"premise_still_valid": true,
"summary": "Implemented ruling 6442747364 letter A with its guardrail, per claim amendment 6442931479. record:path filters its stages through stagesThisRecordWalks before #26689's kind-based classify/layout, so both rows read one list: a stage whose status-field option (matched by value, authored stages included) declares a visibleWhen that is FALSE for the bound record is omitted. A predicate reading an acting-user root (current_user, user, ctx, os, the server option gate's USER_SCOPE_ROOTS) hides nothing; a faulting predicate keeps its stage (evalFieldPredicate fallback true, ADR-0137 D3); the current stage is always kept. No spec key, no new read. Head a5c3c69f on base 94bab5a2 (carries #26689); no merge-main owed.",
"tests": "head a5c3c69f, from ui/ under os-verify-lock: new record-path.optionVisibleWhen-26581.test.tsx 7/7; all record:path tests 13 files 102/102; full packages/plugin-detail/ Test Files 315 passed 1 skipped (316), Tests 2911 passed 18 skipped (2929), VERDICT command-exit 0; pnpm --filter @object-ui/plugin-detail run type-check exit 0. Ablations via ablation-replace.mjs (source-resolved test, no dist), each restored to HEAD blob 35a646591faa with git diff HEAD empty: filter off -> 3 failed/4 passed (a, e, f); acting-user guard off -> 1 failed (b); current-stage guard off -> 1 failed (d).",
"gates": "local: dispatch-gates --commands derived 34 commands from 3 paths on a5c3c69f; all 34 exit 0 (captured before any pipe); --ran: 34 derived, 34 run, 0 NOT-MEASURED (a DERIVED zero). PR-body gates re-run on the final body: check-adr-0087-registration, check-closing-keyword-parity, check-changeset-no-major all exit 0. CI on a5c3c69f at report time: 19 check-runs, 17 in_progress, 2 skipped, none red.",
"dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
"mcp_calls": "0 — no GitHub MCP calls",
"api_writes": "3 — POST issues/26581/comments (os-dev-report 6441110297, earlier round); pr_create objectstack-ai/objectstack#26784 via fleet-write relay run 40531227715 (read-back identical, assignee zhuangjianguo); POST issues/26581/comments (this report)",
"line_budget": "283 insertions, 2 deletions over 3 files (record-path.tsx +76/-2, test +189, changeset +18); no skills/** touched",
"deviations": [
"Acting-user detection is lexical (source with string literals blanked, roots not preceded by a dot), not the server's AST reader collectCelRootIdentifiers: @objectstack/formula is not a plugin-detail dependency and package.json is outside the file surface. It can only over-match, which keeps a stage.",
"A reviewer request on the PR was not made: the relay pr_create row carries assignees only."
],
"files_changed": [
".changeset/26581-record-path-option-visiblewhen.md",
"ui/packages/plugin-detail/src/renderers/__tests__/record-path.optionVisibleWhen-26581.test.tsx",
"ui/packages/plugin-detail/src/renderers/record-path.tsx"
],
"open_questions": [],
"out_of_scope_findings": [
"class: none · hotclm still has to declare visibleWhen on its in_review option for the ORD case to change on screen; ruling names the hotclm seat's card · carrier: hotclm seat",
"class: none · RecordDetailView.tsx:1951-1963 comment says old_value/new_value are not fetched, :2096 selects them; stale comment, observation only · carrier: none"
]
}
Activity
objectstack-fleet commentedon Oct 27, 2026
Triage: first grade —
bug·priority:p2·domain:ui·area:records·pm:queue· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-27T09:01Z⛔ Not a claim, ⛔ not a dispatch.
record:pathticks every stage before the current one by position, so a stage the record skipped reads as passed. That is a misleading status on an audit-facing page. First measure whether the record's state history is already readable on the page: if it is, tick only the visited stages. If a stage needs a new declared key onRecordPathProps(packages/spec/src/ui/component.zod.ts:2187), that is a protocol addition, p1,domain:spec, and it comes back to triage before it is built.Generated by Claude Code
objectstack-fleet commentedon Oct 27, 2026
Claim: PM loop round 1
Session:
session_01QsR2jTfvjNTaR29ehLk26TAccount:
zhuangjianguoBranch:
claude/project-thread-ziacft(platform-assigned card-thread branch; a card thread can push only its assigned branch, and the name carries no issue number, so this claim is the identity record)Worktree:
objectstack-issue-26581Domain:
domain:uiSeat:
domain:ui#4File surface:
ui/packages/plugin-detail/src/renderers/record-path.tsx, its tests, one root changeset; ⛔packages/spec/**and server packages (stop on breach; explain in the report)Container & model:
M,mode:thread,model: TIER_DEFAULT(dispatch-gates --tier: no path-derived mandate; reason: the maintainer's standing rule 「只认领不需要 fable 的卡」, 2026-10-27T10:39Z),effort: highClause-②: no
Metadata-protocol: none
Responsibility:
RecordPathRenderer(plugin-detail, this lane) ticks every stage before the current one by position · the platform path that may cover it: the record's status history, if readable on the page (unmeasured) · reached by every reader of arecord:pathwhose flow skips a stage (hotclm ORD contracts)Thread-read: 6437783999
Premise: holds (action, card-reference and work-item surfaces re-read on origin/main
498e8f40at 2026-10-27T11:40Z)Serial constraints cleared: none in flight; #26612 (not yet triaged) may later land on
record-path.tsx; no open PR touches it (scan 2026-10-27T11:40Z)Dispatched at 2026-10-27T11:43Z to the card thread on that branch; the order lives in the thread's opening message.
Generated by Claude Code
objectstack-fleet commentedon Oct 27, 2026
os-dev-report
{ "issue": 26581, "status": "needs_decision", "branch": "claude/project-thread-ziacft", "pr": null, "session": "session_01QsR2jTgHpQBzM5GaoKifNh", "premise_still_valid": true, "summary": "Stop condition met: no history read is reachable from record:path without a new server read, so nothing was changed and no PR opened. The defect is real on origin/main 498e8f40: record-path.tsx:443 (desktop) and :514 (mobile) tick every stage with idx before currentIdx, and the only record input is ctx.data[statusField] (:231). The renderer's one channel, RecordContextValue (ui/packages/react/src/context/RecordContext.tsx:113), carries objectName, recordId, dataSource, data (current row only), objectSchema, refresh and header members; no status history. The record page does read history, but not into anything record:path can reach: RecordDetailView.tsx:2092-2096 reads sys_audit_log old_value/new_value only when enable.trackHistory === true (opt-in, :1965), only on the synthesized page, only once the History tab is on screen (objectui#13010 deferral), $top 50 desc, and hands the rows only to the record:history node's entries (:3225); the sys_activity read (:2550, metadata {old,new} per audit-writers.ts:2309) is deferred to a feed block on screen and lands in DiscussionContext as FeedItems. Even a full read is not proof of a skip: activities can be opted out (enable.activities false), rows for fields the reader is not served are withheld (activity-field-redaction.ts), unstamped rows are withheld until backfill, and both reads are capped, so a missing in_review row is not evidence the record skipped it. A smaller route exists that needs no history and no spec key: the status field option's existing, server-enforced visibleWhen (open question 1).", "tests": "none run: no code change (stop condition)", "gates": "none run: empty diff; dispatch-gates --commands not derived (no changeset, no files changed)", "dogfood_battery": "n/a — no PR", "mcp_calls": "0 — no GitHub MCP calls", "api_writes": "1 — POST /repos/objectstack-ai/objectstack/issues/26581/comments (this os-dev-report, via scripts/pm/post-stamped.mjs)", "line_budget": "n/a — empty diff", "deviations": [], "files_changed": [], "open_questions": [ { "question": "How should record:path stop presenting a stage this record could not have passed?", "options": [ "A — Honor the status field option's declared visibleWhen (packages/spec/src/data/field.zod.ts:431: 'option is offered only when TRUE (else omitted)', and the server refuses a write of a value whose predicate is false, packages/objectql/src/validation/rule-validator.ts:185-200). record:path omits from both rows any stage whose matching option's predicate is FALSE for this record; a faulting predicate keeps the stage (render side stays fail-open, rule-validator.ts D3). Matches authored stages by value, the way declaredStageKinds already reads option kind. Zero spec keys, zero new server reads, file surface record-path.tsx plus tests plus changeset. hotclm then declares visibleWhen on its in_review option (app-side, existing key; hotclm not in this container, so not measured). Does not cover an ad-hoc skip no rule declares.", "B — record:path self-fetches sys_activity (or sys_audit_log) for this record and ticks only statusField values seen in metadata old/new. A new server read on every record page that draws a path, against the objectui#13010 deferral; sys_audit_log needs trackHistory opt-in; withheld rows, opt-out and $top caps make absence ambiguous, so it would show a guessed skipped state unless a third 'unknown' presentation is added (product decision).", "C — New per-stage key on RecordPathProps.stages[] (e.g. visibleWhen) for authored stages that are not field options. Protocol addition: p1, domain:spec, back to triage per comment 6437783999. No measured pull beyond what A covers." ], "recommendation": "A. Business need: the measured case (hotclm ORD skips legal review by contract type, by design) is a rule about the record's own columns, which is exactly what an option visibleWhen declares. Long-term: it honors an already-declared, server-enforced contract (declared-but-unhonored is an implementation gap, not a new surface). AI-error: declaration-driven and enforced on write, no consumer-side guessing from incomplete history. Startup focus: no new spec key, no new read, no new gate. B adds a read and an ambiguous state; C expands the protocol without pull. A needs the seat to amend the order, since the order's stop condition said change nothing." } ], "out_of_scope_findings": [ "class: none — noted, not filed · RecordDetailView.tsx:1951-1963 comment says the audit read requests 'only the safe projection (created_at, action, user_id) so the browser never receives serialized old_value/new_value', but :2096 selects old_value and new_value; stale comment, observation only · carrier: none" ] }Generated by Claude Code
objectstack-fleet commentedon Oct 27, 2026
待维护者裁决 · record:path 如何不再把「跳过的阶段」画成「已通过」
席位
domain:ui#4·session_01QsR2jTfvjNTaR29ehLk26T· 2026-10-27T11:50Z · 依据 os-dev 报告 6441110297(停止条件命中,未改代码、未开 PR)一句话问题: 一份订单合同按设计不走法务审查,但合同页上方的阶段条依然显示「审查中 ✓」,审计人员会读成"法务审过了"。
背景(实测,origin/main
498e8f40): 阶段条ui/packages/plugin-detail/src/renderers/record-path.tsx(:443 桌面、:514 移动)按位置给当前阶段之前的每一格打勾。它唯一能读到的是当前记录行(RecordContextValue,ui/packages/react/src/context/RecordContext.tsx:113),读不到状态历史。页面上的两条历史读取(sys_audit_log需trackHistory开启、只在历史页签可见时读、最多 50 条;sys_activity只在动态块可见时读)都到不了阶段条。而且这两条历史天生不完整(可关闭、对读者隐藏的字段行被扣留、有条数上限),所以"历史里没有"也证明不了"跳过了"。Governing text:
packages/spec/src/data/field.zod.ts(选项visibleWhen):"option is offered only when TRUE (else omitted) … the rule validator refuses a write of a value whose predicate is false … so a user-gated CHOICE belongs here"。packages/objectql/src/validation/rule-validator.ts:186-188:"That gate is the server's enforcement of who may pick an option (ADR-0124 D1)"。分诊评论 6437783999:新的阶段键属协议新增,p1、domain:spec,须回分诊。协议声明: 选项 A 不改协议;C 改协议(新增键)。
带 re-check 命令的前提:
git grep -n "idx < currentIdx\|index < currentIndex" origin/main -- ui/packages/plugin-detail/src/renderers/record-path.tsx(对照词:git grep -c "statusField" origin/main -- ui/packages/plugin-detail/src/renderers/record-path.tsx必中)。visibleWhen由服务端强制:git grep -n "who may pick an option" origin/main -- packages/objectql/src/validation/rule-validator.ts。visibleWhen:对本记录为假的选项不画成阶段;谓词出错则照画;谓词引用current_user的不据以隐藏(那是"谁能选",不是"记录走哪条路")sys_activity/sys_audit_log,只给历史里出现过的值打勾RecordPathProps.stages[]新增每阶段条件键业务含义直译: A = "不能选的路就不画";B = "去翻流水账,翻不到就猜";C = "给阶段条单独开一个开关"。
四轴(业务立场):
os-decision-facets
Prior rulings read: option visibleWhen, record path stage → ADR-0124 D1 (who may pick an option), ADR-0137 D2/D3 (render side fail-open); thread: 6437783999
推荐:A。 只看①选 A;②③④ 是否翻转:否。置信缺口:A 把"谁能选这个值"读成"这条记录走不走这一步",谓词引用
current_user时两者不同,所以 A 带了"引用current_user的谓词不据以隐藏"这条护栏;这个护栏本身就是产品语义的选择,所以呈您裁。hotclm 能否用visibleWhen表达"订单类跳过审查"未在本容器实测。B 不取:靠不完整历史只能猜。C 不取:无 A 之外的拉动,且需契约复审档复核。裁后执行: 选 A ⇒ 席位改派发令(取消"不改代码"停止条件),同一卡线程实施 record-path.tsx + 测试 + changeset,
Clause-②: no;hotclm 侧声明visibleWhen由 hotclm 自己跟进。选 C ⇒ 本卡转分诊,按协议新增 p1 走 spec 车道。选 B ⇒ 先另裁"未知"态的呈现。请回一个字母:A / B / C。
Generated by Claude Code
objectstack-fleet commentedon Oct 27, 2026
Brief-claimed-by: session_01KrKXQVvgYk2AJ4VkqMobyb · batch #413 · 2026-10-27T12:28Z
Director seat, summon #51 continuation (ledger objectstack#12708). Presented to the maintainer in batch #413 as item 2 of 3 — the ui seat's question 6441171471 after the card thread's stop (6441110297). Seat increment (re-verified on
origin/mainfe53769a):ui/packages/plugin-detail/src/renderers/record-path.tsx:443and:514tick every stage withidx < currentIdx; the renderer's only record input is the current row.packages/spec/src/data/field.zod.tsoptionvisibleWhen: "option is offered only when TRUE (else omitted)";packages/objectql/src/validation/rule-validator.ts:188calls that predicate "the server's enforcement of who may pick an option (ADR-0124 D1)". Triage 6437783999 graded the card p2domain:uiand read a new per-stage key as a protocol addition (p1, spec).needs-user-decisionalone (the thread's claim released); the ruling's stroke returns it topm:queue.visibleWhenanswers "who may pick this value", the path asks "does this record walk this stage"; they coincide for predicates over the record's own columns and diverge for predicates overcurrent_user. The seat's guardrail: a predicate that referencescurrent_userdoes not hide a stage; a faulting predicate keeps the stage (render side fail-open, ADR-0137 D2/D3). Unmeasured: whether hotclm can express "ORD skips review" as an optionvisibleWhen— the hotclm seat confirms on its own card.record-path.tsx+ tests + changeset) · S;Clause-②: no.Prior rulings read: ADR-0124 D1, ADR-0137 D2/D3, objectui#13010; thread: 6437783999, 6441171471.
objectstack-fleet commentedon Oct 27, 2026
Ruling: batch #413 item 2 · letter A · maintainer 「同意」 2026-10-27T13:11Z
Recorded by the director seat (summon #51 continuation,
session_01KrKXQVvgYk2AJ4VkqMobyb, ledger objectstack#12708). The maintainer answered batch #413 with 「同意」 — every seat letter. Brief 6441900906; the ui seat's question 6441171471. Letter A, with the guardrail:record:pathomits from both rows any stage whose matching status-field option declares avisibleWhenthat evaluates FALSE for this record (the option contract ofpackages/spec/src/data/field.zod.ts, enforced on write byrule-validator.tsper ADR-0124 D1); a faulting predicate keeps the stage (render side fail-open, ADR-0137 D2/D3); a predicate that referencescurrent_userdoes not hide a stage — that predicate answers "who may pick the value", not "does this record walk this stage". Stages are matched to options by value, asdeclaredStageKindsalready reads option kind. Not B (a new read per record page against objectui#13010, and an "unknown" state to design) and not C (a protocol key with no pull beyond A).Workload (card column): ui · 1 PR (
ui/packages/plugin-detail/src/renderers/record-path.tsx+ tests + changeset) · S;Clause-②: no,Metadata-protocol: none. hotclm declares the optionvisibleWhenon its contract-type rule on its own card; whether that rule is expressible as an option predicate is the hotclm seat's reading.Execution:
needs-user-decision→pm:queuein this act (domain:ui, p2); the ui seat amends its dispatch order (the "change nothing" stop condition lifts) and the same card thread implements.Ruling-ref:names this comment.objectstack-fleet commentedon Oct 27, 2026
Claim amendment · domain:ui#4 · 2026-10-27T13:20Z
Ruling-ref: 6442747364
Thread-read: 6442747364
The "change nothing" stop condition of claim 6441053217 lifts. Letter A with the guardrail is the order. The same card thread (
claude/project-thread-ziacft,session_01QsR2jTgHpQBzM5GaoKifNh) implements it.File surface:
ui/packages/plugin-detail/src/renderers/record-path.tsx, its tests, one root changeset; ⛔packages/spec/**, server packages, new keys or reads (stop on breach)Clause-②: no
Metadata-protocol: none
Container & model:
M,mode:thread,model: TIER_DEFAULT,effort: highOrder (per the ruling):
record:pathomits from both rows (desktop and mobile) every stage whose matching status-field option declares avisibleWhenthat evaluates FALSE for this record. Stages match options by value, the waydeclaredStageKindsalready reads option kind. A faulting predicate keeps the stage (fail-open, ADR-0137 D2/D3). A predicate that referencescurrent_userdoes not hide a stage.Serial constraint: PR #26689 (#26612) is in the merge queue on the same file. The thread starts from
origin/mainafter #26689 merges.Out of scope: the lost-record "last open step" stays on #26694.
objectstack-fleet commentedon Oct 27, 2026
os-dev-report
{ "issue": 26581, "status": "done", "branch": "claude/project-thread-ziacft", "pr": "https://github.com/objectstack-ai/objectstack/pull/26784", "session": "session_01QsR2jTgHpQBzM5GaoKifNh", "premise_still_valid": true, "summary": "Implemented ruling 6442747364 letter A with its guardrail, per claim amendment 6442931479. record:path filters its stages through stagesThisRecordWalks before #26689's kind-based classify/layout, so both rows read one list: a stage whose status-field option (matched by value, authored stages included) declares a visibleWhen that is FALSE for the bound record is omitted. A predicate reading an acting-user root (current_user, user, ctx, os, the server option gate's USER_SCOPE_ROOTS) hides nothing; a faulting predicate keeps its stage (evalFieldPredicate fallback true, ADR-0137 D3); the current stage is always kept. No spec key, no new read. Head a5c3c69f on base 94bab5a2 (carries #26689); no merge-main owed.", "tests": "head a5c3c69f, from ui/ under os-verify-lock: new record-path.optionVisibleWhen-26581.test.tsx 7/7; all record:path tests 13 files 102/102; full packages/plugin-detail/ Test Files 315 passed 1 skipped (316), Tests 2911 passed 18 skipped (2929), VERDICT command-exit 0; pnpm --filter @object-ui/plugin-detail run type-check exit 0. Ablations via ablation-replace.mjs (source-resolved test, no dist), each restored to HEAD blob 35a646591faa with git diff HEAD empty: filter off -> 3 failed/4 passed (a, e, f); acting-user guard off -> 1 failed (b); current-stage guard off -> 1 failed (d).", "gates": "local: dispatch-gates --commands derived 34 commands from 3 paths on a5c3c69f; all 34 exit 0 (captured before any pipe); --ran: 34 derived, 34 run, 0 NOT-MEASURED (a DERIVED zero). PR-body gates re-run on the final body: check-adr-0087-registration, check-closing-keyword-parity, check-changeset-no-major all exit 0. CI on a5c3c69f at report time: 19 check-runs, 17 in_progress, 2 skipped, none red.", "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm", "mcp_calls": "0 — no GitHub MCP calls", "api_writes": "3 — POST issues/26581/comments (os-dev-report 6441110297, earlier round); pr_create objectstack-ai/objectstack#26784 via fleet-write relay run 40531227715 (read-back identical, assignee zhuangjianguo); POST issues/26581/comments (this report)", "line_budget": "283 insertions, 2 deletions over 3 files (record-path.tsx +76/-2, test +189, changeset +18); no skills/** touched", "deviations": [ "Acting-user detection is lexical (source with string literals blanked, roots not preceded by a dot), not the server's AST reader collectCelRootIdentifiers: @objectstack/formula is not a plugin-detail dependency and package.json is outside the file surface. It can only over-match, which keeps a stage.", "A reviewer request on the PR was not made: the relay pr_create row carries assignees only." ], "files_changed": [ ".changeset/26581-record-path-option-visiblewhen.md", "ui/packages/plugin-detail/src/renderers/__tests__/record-path.optionVisibleWhen-26581.test.tsx", "ui/packages/plugin-detail/src/renderers/record-path.tsx" ], "open_questions": [], "out_of_scope_findings": [ "class: none · hotclm still has to declare visibleWhen on its in_review option for the ORD case to change on screen; ruling names the hotclm seat's card · carrier: hotclm seat", "class: none · RecordDetailView.tsx:1951-1963 comment says old_value/new_value are not fetched, :2096 selects them; stale comment, observation only · carrier: none" ] }Generated by Claude Code