Skip to content

"新建" on the contract list and on the contract page's Renewals list opens the raw contract form, not the launch wizard — status, legal fields and six child grids for a requester #136

Closed

Description

@objectstack-fleet

Filing class: ① user-visible defect — reach: public door, measured in a browser on main f534eef (18.1.0, pnpm demo:zh). Reader: the hotclm lane (object userActions.create, possibly a ruling on whether legal/admin keep a raw create).

Symptom

As Business Requester 1, on NDA-2026-0023 (active) the 续签合同 list (PR #123) shows a 新建 button although the component declares actions: [] (src/pages/contract_detail.page.ts, contract_renewals). It opens ?form=new&formObject=clm_contract&formLink=renewed_from:… — the platform's full object form:

  • 状态 is a select offering all eleven statuses (草稿 … 生效中 … 已作废);
  • legal-owned fields are on it (风险等级, 法务经办人, 档案号, 当前轮次);
  • six inline child grids (Versions, Deviations, Obligations, Payment Plan, Reviews, Signatures) — their headings in English, platform card i18n: a master-detail field's inlineTitle has no translation slot — the inline child grids on a zh-CN create form read "Versions / Deviations / Payment Plan" objectstack#26142.
    The server guards hold (measured: a requester's create with status: active is refused "A contract is created as a draft…", with risk_level is refused by FLS), so nothing wrong is stored — but the requester is handed a form that is mostly refusals, and a draft made this way has no first version and no routing, i.e. it skips everything F1 does. The contract list's header also shows a generic 新建 beside 发起合同 (the wizard); it is the same create affordance (not opened separately in this pass).

Why

actions: [] removes the list's toolbar actions; the related list's New is a separate affordance the host draws unless userActions.create.visibleWhen is false for the parent (objectui RelatedList.tsx, #4646). clm_contract does not declare userActions.create.

Fix

Declare userActions.create on clm_contract so the generic New is not offered (contracts are launched through F1 and backfilled through F16, §06). If legal or the CLM admin should keep a raw create, gate it with visibleWhen on their permission instead — that is the one question for the lane.

Dedupe: hotclm issues (all states) grepped for New button, 新建, raw form, create form, bypass … intake — 0 hits.


Filed from the browser regression pass on main (2026-10-26).

Activity

  1. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 15
    Session: session_017nzWFRUo2U7evMkPdVniAn
    Account: zhuangjianguo (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/issue-136-no-raw-create
    Worktree: hotclm-issue-136
    Domain: repo:hotclm (single-lane sister repo — no domain:*)
    Seat: repo:hotclm#1
    File surface: src/objects/contract.object.ts (userActions.create) · src/pages/contract_detail.page.ts only if the Renewals list needs it · both translation bundles only if a string changes — 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 contract create doors (F1 and F16 are the only ones) | none known | every member who presses New on a contract list
    Thread-read: none
    Premise: measured in the 2026-10-26 browser regression pass on main f534eef with real personas; re-measured by the dev on origin/main first
    Serial constraints cleared: nothing in flight on these files; #135, #136, #137 and #138 are dispatched together on disjoint surfaces (#136 and #137 share contract_detail.page.ts only if #136 needs the Renewals list — different lines).


    Generated by Claude Code

  2. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 136,
      "status": "done",
      "branch": "claude/issue-136-no-raw-create",
      "pr": "https://github.com/objectstack-ai/hotclm/pull/146",
      "session": "session_017nzWFRUo2U7evMkPdVniAn",
      "premise_still_valid": true,
      "summary": "Re-measured the premise on origin/main f534eef first: the contract list header showed 新建 · 导入 · 发起合同 and the contract page's 续签合同 related list carried a 新建 for all three personas, and GET /api/v1/meta/objects/clm_contract answered userActions = null. The fix is one block in src/objects/contract.object.ts: userActions: { create: false }. That is 18.1.0's declared mechanism and it was measured: resolveCrudAffordances narrows the managedBy bucket default, and both the console's listNew and a related list's relatedNew read that same resolved row through resolveAffordance in @object-ui/core, so one declaration closes both doors that actions: [] (PR #123) could not. No role keeps a raw create — legal and the CLM admin launch through F1 too and F16 is the backfill door — and the per-role form would not have worked anyway: a toolbar visibleWhen binds to the record in scope, which on a standalone list is nothing, so a record.* predicate hides it for everyone by the fail-closed rule. No platform gap. The change is UI-only on 18.1.0 (plugin-security's affordance write guard covers engine-owned and append-only buckets only), and the data API answers are unchanged, which is reported, not altered.",
      "tests": "Metadata-only app: no unit suite exists (package.json has no test script). Evidence is the browser and the API, one throwaway DB on port 3136, pnpm demo, Chromium /opt/pw-browsers/chromium at 1440x1000, zh-CN, before = f534eef and after = 30cfe08 on the same database. Personas made the README way with no refusal: admin create-user 200 for Business Requester 1 and Legal Counsel 1, sys_user_position clm_legal_counsel 201 for the lawyer, change-password 200 each (mustChangePassword was stamped), plus the dev admin. Boots: '✓ Server is ready', no 'degraded capabilities', no 'no such table'; the six author-time warnings are the pre-existing approval-slate notes on contract_approval. Served metadata: userActions = null before, {\"create\":false} after. Contract list header: 新建·导入·发起合同 before → 导入·发起合同 after, all three personas. 概要 tab 续签合同 list: one New before → zero after, all three. Other related lists unchanged in both directions: 版本 (all three), 审查/偏离 (偏离 only for the requester; both for legal and admin), 履约义务 + 收付款计划 and 签署记录 (legal and admin) each keep their New. Record headers unchanged (发起续签 for the requester and admin; 终止·发起续签·编辑 for legal). F1 after the change, as Business Requester 1: the dialog opens (合同类型 9 / 我方签约主体 3 / 相对方 38 options), POST /api/v1/actions/clm_contract/launch_contract → 200, the 合同信息 screen opens, submitting created SUP-2026-0026 (status draft, read back over the data API) and went on to the 上传首个版本 screen; the scripted run stopped there for want of a file ('文件不能为空'), which is user input, not a refusal. F12 after the change, same persona on SUP-2026-0003: 续签合同 0 · 暂无相关记录 with no New → POST …/start_renewal → 200 → the list refetched to 续签合同 1 showing SUP-2026-0025 · Renewal — Supplier Agreement — Vantage Staffing · 草稿, still with no New. GET /api/v1/meta/actions still serves all ten clm_contract actions to all three personas, launch_contract@list_toolbar and executed_upload@list_toolbar among them. Direct POST /api/v1/data/clm_contract, same body: 201 draft for all three personas before (SUP-2026-0019/0020/0021) and after (SUP-2026-0022/0023/0024) — unchanged. NOT MEASURED: the F16 button in the browser — executed_upload is gated on execute_contract, which none of the three dispatched personas holds (the dev admin deliberately holds no position, README), so it was absent from the list header before and after; its declaration is untouched and it is still served. No ablation: the diff changes no behaviour a test pins, and this repo has no test suite to pin one.",
      "gates": "Local, each exit code captured before any pipe into its own log file: pnpm install EXIT=0 ('Done in 1.2s using pnpm v10.28.0'); pnpm validate EXIT=0 ('✓ Validation passed (1212ms)'); pnpm lint EXIT=0 ('6 suggestion(s) (1183ms)' — the six pre-existing approval-approvers-may-resolve-empty notes, unchanged by this diff); pnpm typecheck EXIT=0 (tsc --noEmit, no output); pnpm lint:i18n-gate EXIT=0 ('COVERAGE : 0 missing keys across 2 locale(s)'); CLM_DEMO_SEED=1 pnpm validate EXIT=0 ('✓ Validation passed (1263ms)'). All six run on the final commit 30cfe08. hotclm has no scripts/pm/dispatch-gates.mjs and no .changeset directory, so there is no derived-family reconciliation and no changeset to write; CI's own conclusions on PR #146 are not read here — the report is delivered at the end of local verification.",
      "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
      "mcp_calls": "0 — no MCP GitHub tool was called",
      "api_writes": "2 — both through the relay from /home/claude/objectstack, as objectstack-fleet[bot]: POST /repos/objectstack-ai/hotclm/pulls (plus its POST /repos/objectstack-ai/hotclm/issues/146/assignees leg, one action) and POST /repos/objectstack-ai/hotclm/issues/136/comments (this report). git push -u origin claude/issue-136-no-raw-create is not a REST write. The pr_create read-back: 8191 bytes sent, 8191 stored, identical; assignees as the pull lists them: zhuangjianguo.",
      "open_questions": [],
      "out_of_scope_findings": [
        "carrier: none — noted in the PR's Acceptance notes, not filed. The contract list header still offers 导入 (CSV import), a second generic write door into clm_contract that bypasses F1 exactly as the raw New did. It is a separate flag (userActions.import), the regression pass did not drive the wizard, and whether legal or the CLM admin should keep a bulk import is a product question. Observation only: not reproduced as a defect in this pass."
      ]
    }
  3. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed — PR #146 · 2026-10-26

    reading value
    PR #146 MERGED (squash on main), merged at its ACCEPT head 30cfe08; Fixes #136 closes this card
    authority maintainer, project thread, verbatim: 「你可以自己审核的就自己合并」

    Release: no generic New on the contract list or the Renewals list for anyone; contracts are created through F1, F12 and F16. The remaining bulk door (导入) is filed for the decision box (#148). State: pm:dispatched removed, assignee cleared.


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    Platform card filed: objectstack-ai/objectstack#26182 — record:related_list with actions: [] still draws New. The app-side fix here (object-level userActions.create) stays valid on its own; the platform card is what lets a page drop New from one list only.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions