Repository navigation
In the demo, the administrator adjudicated every deviation legal ever raised: clm_deviation.decided_by is DEMO_USER on every decided row - #66
Merged
Conversation
Every decided deviation in the demo named the dev admin, so the 决定人 of each one said the administrator adjudicated the clause playbook. Measured on 3d11db8, one clean-database `pnpm demo`: 25 deviations, 17 decided, `decided_by` resolving to `Dev Admin` x17. `decided_by` is an audit stamp rather than a recipient — nothing is routed to it — so the cost is not a broken notification path but a column that contradicts the model: DESIGN.md §04 gives `clm_legal` the only non-admin `U` on `clm_deviation`, so moving one out of `open` is a lawyer's write. The deal is `counselFor`, the counterparty relationship the fixture already uses for `legal_owner` and for compliance obligations, and NOT `legalOwnerOf`: enumerated on this fixture, 3 of the 17 decided deviations sit on contracts of a type whose `requiresLegalReview` is false, where `legalOwnerOf` returns null. That would leave 3 rows decided, carrying a `decided_at`, and naming nobody — a pair `deviation_state_machine` never writes, since it stamps the acting user on any decision that leaves the column empty. No new account: the same two `Legal Counsel` names README already asks for, 10 / 7 across the 17. No deliberate unassigned set either — no flow reads this column, so an empty one exercises no edge and would assert the one thing the column cannot mean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R3n3GGzobdegM4HUzah1iR
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 #61
clm_deviation.decided_bynamed the dev admin on every decided row, so the demo's决定人 column said the administrator adjudicated every clause deviation legal ever
raised. This deals the column to a lawyer, by the same counterparty relationship
#52 (PR #65) established for
clm_obligation.owner.What changed
src/data/keys.tsdeviationDeciderOf(deviation)—nullwhile open, elsecounselFor(contract);DEMO_USER's doc block andcounselFor's doc block re-enumeratedsrc/data/negotiation.seed.tsdecided_by: deviationDeciderOf(deviation)replaces... ? null : DEMO_USERREADME.mdLegal Counselrow now also names the 17 decided deviations (10 / 7); "all three columns" is now fourscripts/demo.mjsDEMO_USERmirror's NOT-list gainsclm_deviation.decided_by; "all three" is now fourObject, playbook, F2 and the stamping hook are untouched: the field and its
behaviour are correct as built.
The premise, re-measured on this branch first
Card readings were the PM seat's on a stale checkout. Taken again from the
running system at
3d11db8, clean database, onepnpm demo:Premise holds: 17 decided, every one of them the dev admin, the 8 open ones NULL.
Why
counselForand notlegalOwnerOf— the real choice on this cardBoth name a lawyer; they differ exactly on the rows where F2 never assigned one.
Enumerated over the plan (not sampled): the 17 decided deviations sit on 17
distinct contracts, and 3 of those 17 are of a type whose
requiresLegalReviewis false — contracts #17 and #48 (
in_approval) and #44 (approved) — solegalOwnerOfreturnsnullfor them.Dealing by
legalOwnerOfwould therefore leave 3 rows that are accepted/rejected,carry a
decided_at, and name nobody. The write layer never produces that pair:deviation_state_machine(src/objects/contract.hook.ts) stampsdecided_bywiththe acting user on any decision that leaves the column empty. The fixture's standing
rule is that no seeded row sits in a state the write layer would have refused or
would never have written — the same rule that keeps all 8 open deviations on
in_reviewcontracts because F6 would not let anin_approvalcontract carry one.counselForclaims only what the row needs — the counterparty's lawyer ruled on theclause — and not the extra fact
legalOwnerOfcarries, that the contract wentthrough a legal review at all. That is the distinction
keys.tsalready draws, andthe same reason
obligationOwnerOfdeals compliance work bycounselFor.Grounding for "a lawyer at all": DESIGN.md §04 gives
clm_requesteronlyRC(本人合同)onclm_deviationandclm_legalRCU — the only non-admin setwith
U. Moving a deviation out ofopenis a lawyer's write and nobody else's.No deliberate unassigned set here, and that is not the #52 half being dropped
carelessly.
legalOwnerOfandobligationOwnerOfeach keep one row empty because aflow takes a "nobody to tell" edge over that column (F3, F10). Nothing reads
decided_by: it is written once and displayed. An empty one would exercise nothingand would instead assert the one thing the column cannot mean.
No new account. The two
Legal Counselnames README already asks for, 10 / 7.Deals by structure — both locales identical, row for row
The deal reads
partyIndexonly; no locale bundle is consulted.Gates — exit codes captured before any pipe
The 21 lint warnings are the pre-existing
field-no-consumersfamily onclm_clause,clm_contract_type,clm_contract_version,clm_review,clm_signatureandclm_deviation.deviation_text/.justification— fields thisdiff does not touch.
decided_byis a page column, so it has a consumer.Control-character self-scan over the four changed files, beyond the repo gates:
grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]'-> no match.Verified in the browser and on the wire
Chromium at
/opt/pw-browsers/chromium-1194/chrome-linux/chrome, console localezh-CN, demo on a non-default port 3517. Three boots.Boot 2 — post-change, accounts absent (the #65 check). Clean database:
The row survives, as an optional reference must. The loader says so per row:
Loader ERROR lines: 388 before this change, 405 after, and the delta is exactly the
17 new
clm_deviation.decided_by stays NULLlines — same class as the 120owner_id,68
legal_ownerand 199obligation.ownerlines already there, and the demo's closingnote already frames them.
Boot 3 — the operator step, then the upsert handover.
Legal Counsel 1andLegal Counsel 2created through Setup 人员与组织 › 用户 › 创建用户 (no seeded users;DESIGN.md §10 respected), then
pnpm demoagain on the same database:The reading a person gets. Signed in, HotCLM › 偏离 ›
Insurance cover · Accepted(on
Supplier Agreement — Bramble Office Supplies, SUP-2026-0011):Before this change that field read
Dev Admin. No scheduled job was run: nothingsweeps this column.
Enumerated, not sampled
grep -rn DEMO_USER srcafter the change: the declaration, four cross-referencesin
keys.tsprose,clm_review.reviewer, and theour-side entry ofclm_signature.signers(a JSON string, not a reference).decided_byis gone fromthat set, so the doc block that named it was rewritten rather than left stale.
clm_review.revieweris the one reference that still has to name thedev admin" — read and confirmed, not assumed. Enumerating every user reference in
src/objects/:clm_contract.owner_id,clm_contract.legal_owner,clm_contract_version.submitted_by,clm_obligation.owner,clm_deviation.decided_by,clm_review.reviewer. Onlyreviewercarriesrequired: true+storage.notNull, so the claim stays true.counselFor's own doc said "both columns that name a lawyer". With this changethey are three, so that sentence now enumerates them.
Acceptance notes
record:related_listthe user columnsrender the raw
sys_userid rather than the account name: on the contract page's审查与偏离 tab, 决定人 shows
8fDg2i1MlSIuG16syiHbJfHEsPcPBjW3and the review grid's审查人 shows
kO22t1Gio3hllWxLo22L3Ejs4FzXfANl, while the same fields on a recordpage render
[LC] Legal Counsel 1. Platform@objectstack/*17.4.0, consolezh-CN. Pre-existing and independent of this card (it hitsreviewertoo);reported for the PM seat to route to objectstack-ai/objectstack per AGENTS.md.
🤖 Generated with Claude Code
https://claude.ai/code/session_01R3n3GGzobdegM4HUzah1iR
Generated by Claude Code