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
A review saved with decision Approved leaves 结论时间 (decided_at) empty — the deviation stamps it, the review does not #159
Filing gate: ① user-visible defect, reproduced in the browser on @objectstack 18.2.0 (hotclm PR #155 branch).
Symptom. Legal counsel creates a review on 审查与偏离 with 结论 = 通过 and saves. The row reads decision: approved, decided_at: null; 结论时间 stays empty on the page.
Where.clm_review.decided_at (src/objects/review.object.ts) has no stamping hook and no description. clm_deviation stamps its own decided_at / decided_by when it is decided (src/objects/contract.hook.ts, the deviation guard); clm_signature stamps completed_at since #145. DESIGN.md §03 lists decided_at on the review with no "manual" note.
Expected. Saving a review whose decision leaves pending stamps decided_at (unless the user typed one), the way the deviation and the signing round do.
Business reason. The review page and any report on review turnaround read when legal decided; with the field empty, a decided review carries no decision time at all and the lawyer has to type it by hand.
Filed from the 18.2.0 browser pass (hotclm PR #155, landed as 21f2319). Defect, user-visible, reproduced: accepted.
Route: app-side, in the deviation and signing-round pattern. Stamp clm_review.decided_at when decision leaves pending, unless the user typed one, and give the field a description. DESIGN.md §03 needs no change.
Claim: PM loop round 17
Ruling-ref: none
Session: session_017nzWFRUo2U7evMkPdVniAn
Account: zhuangjianguo (the seat's linked user as GET /user answers it; the card's assignee)
Branch: claude/issue-159-review-decided-at
Worktree: hotclm-issue-159
Domain: repo:hotclm (single-lane sister repo — no domain:*)
Seat: repo:hotclm#1
File surface: src/objects/contract.hook.ts (review stamping, in the deviation pattern) · src/objects/review.object.ts (description of decided_at) · translations for that description if the i18n gate asks — stop on breach; explain in the report
Container & model: S, mode:subagent, model: opus (TIER_DEFAULT; hand-derived for a sister repo), effort: medium
Clause-②: no
Metadata-protocol: none
Responsibility: this app's review record (when legal decided) | none | every lawyer deciding a review
Thread-read: 6437440077
Premise: re-measured by the dev on origin/main21f2319 (@objectstack 18.2.0) with README personas first
Serial constraints cleared: Triaged 2026-10-27; round 17 dispatches #134, #127, #64 and #159 together on disjoint files.
{
"issue": 159,
"status": "done",
"branch": "claude/issue-159-review-decided-at",
"pr": "https://github.com/objectstack-ai/hotclm/pull/163",
"session": "session_017nzWFRUo2U7evMkPdVniAn",
"premise_still_valid": true,
"summary": "Premise re-measured on origin/main 21f2319 (18.2.0) as Legal Counsel 1 in zh-CN Chromium: a review created with 结论 = 通过 stored decided_at null, and the 结论时间 column was absent from the contract's review grid. New hook review_decision_stamp in src/objects/contract.hook.ts (clm_review, beforeInsert + beforeUpdate, priority 200), in the deviation pattern: when a write gives the review a decision (approved / changes_requested / rejected) different from its previous one and no decided_at is present in the write or on the row, it stamps now. It never overwrites or clears a value. decided_at got a description in review.object.ts, and the i18n gate asked for its zh-CN help, which was added along with the en line. No decided_by exists on clm_review or in DESIGN.md §03 (the reviewer decides), so none was added. Back to pending: allowed (§03 gives the review decision no transition table) and the stamp is left in place, matching the deviation's fill-only, never-erase stamp. A re-decision after that keeps the first time; see open_questions. One step beyond the card's wording: a decided-to-decided change on a row with an empty decided_at (the rows this defect left behind) is stamped too.",
"tests": "No unit-test harness in this app; behaviour measured on 18.2.0, pnpm demo:zh --port 3144, fresh DB before (21f2319) and after (4041dd1). Persona: admin create-user Legal Counsel 1 -> 200 (mustChangePassword true), sys_user_position clm_legal_counsel -> 201, persona change-password -> 200, security/explain clm_review create allowed. BROWSER before: DPA-2026-0005 › 审查与偏离 › 新建, 结论=通过, POST clm_review 201, row {decision: approved, decided_at: null}; the record page shows no 结论时间. BROWSER after: same steps, decided_at 2026-10-27T09:04:05.678Z; the record page reads 结论时间 2026/10/27 上午9:04, and the grid shows the column. Typed 2026/10/01 09:30 -> kept as 2026-10-01T09:30:00.000Z. Pending (待定) -> decided_at null. Edit pending -> 通过 -> stamped 09:05:42.961Z, and the page shows it. Edit back to 待定 -> stamp left in place. API before/after as the persona: POST approved null -> stamped; POST approved + typed kept/kept; POST pending and POST with no decision null/null; PATCH pending->changes_requested null -> stamped; PATCH ->pending null -> left in place; PATCH pending->rejected + typed kept/kept; PATCH comments only unchanged/unchanged; after only: re-decide after the back-edge keeps the first time; approved->rejected on a cleared row -> stamped. Seed: 67 reviews (60 seeded + 7 measured); exactly 4 have decided_at on or after 2026-10-27, all from the measurement; 0 decided reviews are empty. The seed summary is identical before and after (inserted 830, errored 404: the README's documented unnamed personas). Browser console: the same 404 and 401 resource lines before and after. No ablation: there is no test file to pin; before/after is the proof.",
"gates": "Final head 4041dd1, exit codes captured before any pipe: pnpm install EXIT=0; pnpm validate EXIT=0 (Validation passed); pnpm lint EXIT=0 (1 warning(s), 6 suggestion(s): the one warning is main's security-controlled-by-parent-owner-policy-without-opt-in, so no new warning); pnpm typecheck EXIT=0; pnpm lint:i18n-gate EXIT=0 (0 missing keys across 2 locale(s)); CLM_DEMO_SEED=1 pnpm validate EXIT=0. Mid-way, before the translation: lint:i18n-gate EXIT=1 on [zh-CN] objects.clm_review.fields.decided_at.help, and lint showed a second warning for the same key; both cleared. PR check-runs on 4041dd1: Validate in_progress at report time. No dispatch-gates derivation: this is a sister repo with no scripts/pm/dispatch-gates.mjs, and the gate list is AGENTS.md's.",
"dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
"mcp_calls": "0",
"api_writes": "2 — fleet-write relay: pr_create objectstack-ai/hotclm (#163); comment on objectstack-ai/hotclm#159 (this report). git push of the branch is not a REST write.",
"open_questions": [
{
"question": "When a review's decision goes back to pending, should decided_at stay (as shipped), be cleared, or should the back-edge be refused?",
"options": [
"A keep the stamp (shipped): matches the deviation's fill-only stamp and never erases a typed time, but a review decided again after pending keeps its first decision time",
"B clear decided_at on the edge into pending, unless the same write sets one: a later re-decision gets a fresh time, but a typed time is erased and a hook writes null into a user-editable field",
"C refuse decided -> pending, the deviation's 'decided once' rule: a new transition table on clm_review.decision that DESIGN.md §03 does not give, which is product semantics and a §03 change"
],
"recommendation": "A, because it is the deviation's stamping behaviour, needs no §03 change, and the lawyer can correct the editable field. Revisit with B if review-turnaround reports show re-decided reviews."
}
],
"out_of_scope_findings": [
"class: none (renderer observation) · reach: browser, contract › 审查与偏离 review grid: the configured decided_at column is omitted while every row's value is empty (before the fix), and appears once one row has a value · carrier: none — noted in PR Acceptance notes, not filed"
]
}
Activity
objectstack-fleet commentedon Oct 27, 2026
Triage — 2026-10-27
Filed from the 18.2.0 browser pass (hotclm PR #155, landed as
21f2319). Defect, user-visible, reproduced: accepted.Route: app-side, in the deviation and signing-round pattern. Stamp
clm_review.decided_atwhendecisionleavespending, unless the user typed one, and give the field a description. DESIGN.md §03 needs no change.State: →
pm:queue+bug.Generated by Claude Code
objectstack-fleet commentedon Oct 27, 2026
Claim: PM loop round 17
Ruling-ref: none
Session:
session_017nzWFRUo2U7evMkPdVniAnAccount:
zhuangjianguo(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-159-review-decided-atWorktree:
hotclm-issue-159Domain:
repo:hotclm(single-lane sister repo — nodomain:*)Seat:
repo:hotclm#1File surface:
src/objects/contract.hook.ts(review stamping, in the deviation pattern) ·src/objects/review.object.ts(description ofdecided_at) · translations for that description if the i18n gate asks — stop on breach; explain in the reportContainer & model:
S,mode:subagent,model: opus(TIER_DEFAULT; hand-derived for a sister repo),effort: mediumClause-②: no
Metadata-protocol: none
Responsibility:
this app's review record (when legal decided) | none | every lawyer deciding a reviewThread-read: 6437440077
Premise: re-measured by the dev on
origin/main21f2319(@objectstack 18.2.0) with README personas firstSerial constraints cleared: Triaged 2026-10-27; round 17 dispatches #134, #127, #64 and #159 together on disjoint files.
Generated by Claude Code
objectstack-fleet commentedon Oct 27, 2026
os-dev-report
{ "issue": 159, "status": "done", "branch": "claude/issue-159-review-decided-at", "pr": "https://github.com/objectstack-ai/hotclm/pull/163", "session": "session_017nzWFRUo2U7evMkPdVniAn", "premise_still_valid": true, "summary": "Premise re-measured on origin/main 21f2319 (18.2.0) as Legal Counsel 1 in zh-CN Chromium: a review created with 结论 = 通过 stored decided_at null, and the 结论时间 column was absent from the contract's review grid. New hook review_decision_stamp in src/objects/contract.hook.ts (clm_review, beforeInsert + beforeUpdate, priority 200), in the deviation pattern: when a write gives the review a decision (approved / changes_requested / rejected) different from its previous one and no decided_at is present in the write or on the row, it stamps now. It never overwrites or clears a value. decided_at got a description in review.object.ts, and the i18n gate asked for its zh-CN help, which was added along with the en line. No decided_by exists on clm_review or in DESIGN.md §03 (the reviewer decides), so none was added. Back to pending: allowed (§03 gives the review decision no transition table) and the stamp is left in place, matching the deviation's fill-only, never-erase stamp. A re-decision after that keeps the first time; see open_questions. One step beyond the card's wording: a decided-to-decided change on a row with an empty decided_at (the rows this defect left behind) is stamped too.", "tests": "No unit-test harness in this app; behaviour measured on 18.2.0, pnpm demo:zh --port 3144, fresh DB before (21f2319) and after (4041dd1). Persona: admin create-user Legal Counsel 1 -> 200 (mustChangePassword true), sys_user_position clm_legal_counsel -> 201, persona change-password -> 200, security/explain clm_review create allowed. BROWSER before: DPA-2026-0005 › 审查与偏离 › 新建, 结论=通过, POST clm_review 201, row {decision: approved, decided_at: null}; the record page shows no 结论时间. BROWSER after: same steps, decided_at 2026-10-27T09:04:05.678Z; the record page reads 结论时间 2026/10/27 上午9:04, and the grid shows the column. Typed 2026/10/01 09:30 -> kept as 2026-10-01T09:30:00.000Z. Pending (待定) -> decided_at null. Edit pending -> 通过 -> stamped 09:05:42.961Z, and the page shows it. Edit back to 待定 -> stamp left in place. API before/after as the persona: POST approved null -> stamped; POST approved + typed kept/kept; POST pending and POST with no decision null/null; PATCH pending->changes_requested null -> stamped; PATCH ->pending null -> left in place; PATCH pending->rejected + typed kept/kept; PATCH comments only unchanged/unchanged; after only: re-decide after the back-edge keeps the first time; approved->rejected on a cleared row -> stamped. Seed: 67 reviews (60 seeded + 7 measured); exactly 4 have decided_at on or after 2026-10-27, all from the measurement; 0 decided reviews are empty. The seed summary is identical before and after (inserted 830, errored 404: the README's documented unnamed personas). Browser console: the same 404 and 401 resource lines before and after. No ablation: there is no test file to pin; before/after is the proof.", "gates": "Final head 4041dd1, exit codes captured before any pipe: pnpm install EXIT=0; pnpm validate EXIT=0 (Validation passed); pnpm lint EXIT=0 (1 warning(s), 6 suggestion(s): the one warning is main's security-controlled-by-parent-owner-policy-without-opt-in, so no new warning); pnpm typecheck EXIT=0; pnpm lint:i18n-gate EXIT=0 (0 missing keys across 2 locale(s)); CLM_DEMO_SEED=1 pnpm validate EXIT=0. Mid-way, before the translation: lint:i18n-gate EXIT=1 on [zh-CN] objects.clm_review.fields.decided_at.help, and lint showed a second warning for the same key; both cleared. PR check-runs on 4041dd1: Validate in_progress at report time. No dispatch-gates derivation: this is a sister repo with no scripts/pm/dispatch-gates.mjs, and the gate list is AGENTS.md's.", "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm", "mcp_calls": "0", "api_writes": "2 — fleet-write relay: pr_create objectstack-ai/hotclm (#163); comment on objectstack-ai/hotclm#159 (this report). git push of the branch is not a REST write.", "open_questions": [ { "question": "When a review's decision goes back to pending, should decided_at stay (as shipped), be cleared, or should the back-edge be refused?", "options": [ "A keep the stamp (shipped): matches the deviation's fill-only stamp and never erases a typed time, but a review decided again after pending keeps its first decision time", "B clear decided_at on the edge into pending, unless the same write sets one: a later re-decision gets a fresh time, but a typed time is erased and a hook writes null into a user-editable field", "C refuse decided -> pending, the deviation's 'decided once' rule: a new transition table on clm_review.decision that DESIGN.md §03 does not give, which is product semantics and a §03 change" ], "recommendation": "A, because it is the deviation's stamping behaviour, needs no §03 change, and the lawyer can correct the editable field. Revisit with B if review-turnaround reports show re-decided reviews." } ], "out_of_scope_findings": [ "class: none (renderer observation) · reach: browser, contract › 审查与偏离 review grid: the configured decided_at column is omitted while every row's value is empty (before the fix), and appears once one row has a value · carrier: none — noted in PR Acceptance notes, not filed" ] }A decided review records when legal decided (#163)
objectstack-fleet commentedon Oct 27, 2026
Ruling-ref: none (triaged defect, comment 6437440077)
Landed — PR #163 · 2026-10-27
MERGED(squash onmain), merged at its ACCEPT head4041dd1;Fixes #159closes this cardRelease: a review saved with a decision now records 结论时间 automatically; a typed time is kept. State:
pm:dispatchedremoved, assignee cleared.Generated by Claude Code