Skip to content

The signature-round form asks a lawyer to type JSON for signers, and "Completed At" promises a stamp the round never gets #138

Closed

Description

@objectstack-fleet

Filing class: ① user-visible defect (two items on one form) — reach: public door, measured in a browser on main f534eef (18.1.0, pnpm demo:zh). Reader: the hotclm lane.

Symptom

Legal Counsel 1 opens NDA-2026-0023 (signing) › 签署与归档 › 新建 签署记录:

  1. 签署人 is a plain JSON textarea, and its help text is the schema: 「由 { side: our | counterparty, name, email, order, status, signed_at } 组成的数组,按签署顺序一人一行。」 (src/translations/zh-CN/objects.lifecycle.ts:174, src/objects/signature.object.ts:92). That is the implementation note PR Implementation notes are shown to users as help text — field and dashboard-widget descriptions cite hook files, card numbers, DESIGN.md sections and PRs #131 removed elsewhere.
  2. A wet-ink round saved with 状态 = 已完成 and an executed copy: the contract then activates fine, but the round's 完成时间 stays empty (clm_signature.completed_at: null read back), although its help text says 「本轮完成时自动写入,除非显式指定」. The contract's executed_at is the activation time, and signed_date is empty.

Fix

  1. Help text in the user's terms (who signs, in which order; leave empty for a wet-ink round), and either hide signers from the manual form or render it as rows — if the platform has no row editor for a json field, say so and keep it hidden.
  2. Either stamp completed_at when a round reaches completed (the F7 flow already reads signatureRecord.completed_at for executed_at), or change the help text to say it must be entered.

Dedupe: hotclm issues (all states) grepped for signers, completed_at — hits #116, #121, #38 (field removals), none this.


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-138-signature-form
    Worktree: hotclm-issue-138
    Domain: repo:hotclm (single-lane sister repo — no domain:*)
    Seat: repo:hotclm#1
    File surface: src/objects/signature.object.ts · src/objects/*.hook.ts that owns clm_signature (stamp completed_at) · src/translations/{en,zh-CN}/objects.lifecycle.ts — 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 signature-round form and its completion stamp | none known | every lawyer registering a signature round
    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": 138,
      "status": "done",
      "branch": "claude/issue-138-signature-form",
      "pr": "https://github.com/objectstack-ai/hotclm/pull/145",
      "session": "session_017nzWFRUo2U7evMkPdVniAn",
      "premise_still_valid": true,
      "summary": "Both items reproduced on f534eef as Legal Counsel 1 in Chromium, and both are fixed. (1) clm_signature.signers is kept as declared (json, DESIGN §03) but now has hidden: true, and its help text speaks the user's terms in the field and both bundles. 18.1.0 has no row editor for a json field: the Console maps json to field:object, a JSON text box. The row editor field:grid belongs to the repeater type, so showing rows means either a §03 type change or a new src/views form view, which is outside the file surface. That choice goes to the seat as an open question. (2) signature_state_machine (contract.hook.ts) now also binds beforeInsert. On insert it only fills in completed_at when status is completed and completed_at is empty; an explicit value is kept, and the transition table stays update-only. 18.1.0 has no declarative stamp-on-transition: defaultValue runs on insert only and NOW() cannot see the status. The completed_at help text was reworded and now holds on both write paths. Effect on the contract: F7 and the signing to active guard now take executed_at and signed_at from the round's completion instead of the activation moment. clm_contract has no signed_date field, and signed_at read back set (to the activation time) on f534eef, so the card's 'signed_date stayed empty' does not reproduce here; it is a separate observation, out of scope.",
      "tests": "No test harness in this metadata-only repo; measured in Chromium (/opt/pw-browsers/chromium) on pnpm demo:zh, port 3138, a throwaway DB per head; personas set up the README way (admin create-user, sys_user_position by name, change-password because mustChangePassword: true). BEFORE f534eef: the new-round form shows the signers JSON box and the schema help text in zh-CN and en; saving wet_ink/completed with an executed copy gives POST /api/v1/data/clm_signature 201, read back completed_at null, contract executed_at null; activation as Records Manager (POST /api/v1/actions/clm_contract/activate_contract 200) stamps executed_at = signed_at = activated_at = 14:00:43.574Z; an explicit 2026/10/20 10:00 is kept. AFTER 20b2bb9: form signers control count 0 (zh-CN and en), new help shown; saving the same round reads back completed_at 2026-10-26T14:07:30.235Z, executed_at the same instant, final_signed v1 created; after activation executed_at = signed_at = 14:07:30.235Z and activated_at = 14:07:52.233Z; the explicit 2026-10-20T10:00:00.000Z is kept and becomes executed_at and signed_at; en run on SUP-2026-0011 with the formality ticked gives completed_at = executed_at = signed_at = 14:09:18.397Z; a seeded round's record page and edit form no longer show signers, and an edit-form save (PATCH 200) leaves signers intact in the API read-back; API insert of a draft then PATCH to completed stamps 14:10:34.116Z (update leg unchanged); seeded completed rounds keep their seeded completed_at; seed totals identical on both heads (inserted 830, errored 404 = persona-name deferred refs); no degraded or no such table in the banner; browser console showed the same two resource errors (404, 401) on both heads.",
      "gates": "Final head 20b2bb9, 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)); 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. New advisory warning field-no-consumers on clm_signature.signers (base f534eef lint: 0 warnings, 6 suggestions, measured by checking out base src and restoring with git checkout HEAD; git diff HEAD empty afterwards). It is the honest state of a hidden field whose consumer (the e-signature connector, #83) lives outside the stack, the case the rule's own explain text says to ignore. PR check-runs at report time: Validate success (a second Validate run in_progress).",
      "dogfood_battery": "n/a: no ! in the PR title and no (narrowing) arm; sister repo",
      "mcp_calls": "0",
      "api_writes": "3: git push (not REST); 1 fleet-write relay dispatch pr_create (POST /repos/objectstack-ai/hotclm/pulls draft + POST /issues/145/assignees), read back identical 7861 bytes; 1 post-stamped comment on #138 (this report)",
      "open_questions": [
        {
          "question": "Should clm_signature.signers be shown as rows instead of being hidden?",
          "options": [
            "A: change the field type to repeater with declared sub-fields. This is a DESIGN.md §03 decision (the column is declared json); renders as field:grid.",
            "B: add a clm_signature form view rendering the column as type repeater with sub-fields. Needs the file surface widened to src/views; whether the related list's 新建 dialog uses that form view is unmeasured.",
            "C: keep it hidden until the connector card #83 shows signers its own way."
          ],
          "recommendation": "C, because nobody types signers by hand on a manual round, and A or B would put an editor on a column whose real writer is the connector."
        },
        {
          "question": "Dispatch said pr_create with no draft key, while os-dev.md asks for a draft PR",
          "options": ["A: no conflict in effect: the relay forces draft: true (ops.mjs), and PR #145 reads draft: true"],
          "recommendation": "A, no action."
        }
      ],
      "out_of_scope_findings": [
        "class: b · reach: public door, POST /api/v1/data/clm_signature {method: esign, status: declined} as Legal Counsel 1 answers 201 on 20b2bb9 (same code path as f534eef) · evidence: signature_state_machine applies its transition table on beforeUpdate only, so a round can be created in any status; DESIGN §03 says child state machines are enforced in the write layer · Seam: spec:DESIGN §03 clm_signature.status machine → runtime:contract.hook.ts signature_state_machine (insert leg) · dedupe words: signature insert status, born declined, clm_signature transition insert, esign completed from draft",
        "carrier: none · noted, not filed: a manually created round's display_name reads 'Wet ink · Completed' in English in the zh-CN UI (mirror.hook.ts writes English), same on f534eef; recorded in the PR's Acceptance notes",
        "carrier: none · noted, not filed: card premise 'signed_date stayed empty' — clm_contract has no signed_date (it is the backfill action param and HotCRM crm_contract's field); clm_contract.signed_at read back set on f534eef"
      ]
    }
  3. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed — PR #145 · 2026-10-26

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

    Release: the signature-round form no longer asks for JSON (signers is read-only until the e-signature connector, #83, writes it), and a round saved as completed carries its completion time, which the contract's execution date follows. The insert-side state check is #147. State: pm:dispatched removed, assignee cleared.


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    Seat note from the regression thread, for item 1: the platform has ruled that an object-level repeater / composite stays untyped JSON and "a typed list is a child object" (objectstack-ai/objectstack#25681 → #25755, #25741). So there is no platform row editor to wait for: the signers either become a child object of clm_signature (one row per signer, side · name · email · order · status · signed_at) or stay off the manual form with plain help text. Which of the two is the lane's call; no platform card is needed.

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