Skip to content

docs: nullable record.amount compared unguarded in gated positions on nine more docs pages and the validation spec example (after #31206) #31334

Closed

Description

@objectstack-fleet

domain:skills#3 · huangyiirene · session_01J8VVAj7CvjkGXvgXzd6RZ7

What is wrong

PR #31322 (#31206) guards the nullable amount in the automation skill's approval flow and in the HookSchema.condition example. The publish-time null-guard rule (packages/lint/src/validate-null-guards.ts) refuses an unguarded comparison on a nullable field in a gated position, and the formula skill teaches the guard (record.amount != null && …, skills/objectstack-formula/SKILL.md ≈L112-L122; flow start conditions ≈L404). The PR's dev grep and its contract review (6715980957) found these sites outside that card's claim. Read on main:

Site Surface Gated?
content/docs/getting-started/common-patterns.mdx L371-L372, edges e2/e3 (record.amount < 100 / >= 100) record_change flow on expense_report; amount is { type: 'currency' } with no required/default (L312) and the start condition does not guard it Yes, refused as written
content/docs/automation/approvals.mdx L145, start condition (record.amount >= 50000 && …) record_change flow start condition on opportunity Yes, if amount is nullable
content/docs/automation/hook-bodies.mdx L74 (record.status == "open" && record.amount > 1000) hook condition Yes, if amount is nullable
content/docs/data-modeling/formulas.mdx L119, L356 (requiredWhen: P\record.amount > 10000``) field requiredWhen (fail-open: silently enforces nothing) Yes, if amount is nullable
content/docs/data-modeling/formulas.mdx L394 (record.amount <= 0) validation rule Yes, if amount is nullable
content/docs/data-modeling/validation.mdx L329 (record.amount <= 0) validation rule fragment Yes, if amount is nullable
content/docs/automation/flows.mdx L2473 (record.amount > 500, start-node condition in the syntax table) flow start condition Yes on a record_change flow, if nullable
packages/spec/src/data/validation.zod.ts L180, generated into content/docs/references/data/validation.mdx L170/L291/L505/L571 (record.amount < 0) validation condition Yes, if nullable (spec example; regenerate, never hand-edit)
ui/packages/app-shell/src/views/metadata-admin/SchemaForm.erasedArmMark-9830.test.tsx L110 hand-copies the old HookSchema.condition describe string Not gated; the test reads its own constant and passes, but lags the served text once #31322 lands

Not a site: content/docs/data-modeling/validation.mdx L51 (amount is required: true at L31). Release notes and CHANGELOGs are left alone.

Evidence

PR #31322 body § Other sites found; contract review 6715980957 ③.

Suggested shape

Where an example declares its object, guard as the formula skill teaches, or declare the field required: true if that is the honest fix. Where it does not, guard anyway. A flow's start-condition guard covers its edges and decision branches (validate-expressions.ts L2967-L2976), so guard the start condition rather than each edge. The spec example rides with a spec-lane note and a regenerated reference page. The UI test fixture is the UI lane's.

Activity

  1. added
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    documentationImprovements or additions to documentation
    domain:devxDomain lane — seat card indexed by label:pm:seat
    pm:queueReady for the PM dispatch loop
    and removed
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    on Nov 8, 2026
  2. objectstack-fleet commented on Nov 8, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: audit · p2 · domain:devx · 2026-11-08T02:53Z

    ⛔ Not a claim.


    Generated by Claude Code

  3. objectstack-fleet commented on Nov 8, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:devx#3 · os-project-manager · session_01FEkQ5njkh3xbcDN6EtSfw7

    Claim: PM loop round 1
    Session: session_01FEkQ5njkh3xbcDN6EtSfw7
    Account: os-project-manager
    Branch: claude/project-thread-2zrqid (a project card thread, session_01FEkQ5kcxM8mwtPqSQpzbVT; the thread can push only its harness branch, a measured platform exception to claude/issue-<n>-<slug>)
    Worktree: objectstack-issue-31334
    Domain: domain:devx
    Seat: domain:devx#3
    File surface: the seven content/docs/** pages in the card's table, packages/spec/src/data/validation.zod.ts (example string) plus the regenerated content/docs/references/data/validation.mdx, and one changeset if the gate asks. ⛔ The ui/ SchemaForm test constant (UI lane, after #31322), release notes and CHANGELOGs are out.
    Container & model: M, mode:thread, model: default — read dispatch-gates --tier on the real diff, effort: medium
    Clause-②: no — an example/description string on a published schema, with no accept-set change. The thread reports yes if its diff or the recognisers read otherwise.
    Metadata-protocol: none
    Responsibility: n/a — docs and spec example text, no user door
    Thread-read: 6716403823
    Premise: holds on main dc7f5c4b4. PR #31322 is still open, and the card's sites compare record.amount unguarded.
    Ruling-ref: p2 by triage 6716403823; dispatched under the maintainer's 「并发5」 (2026-11-07T23:53Z).

    Order: pm-orders/devx/order-31334.md (project files), handed to the card thread in its opening message.

  4. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Nov 8, 2026
  5. objectstack-fleet commented on Nov 8, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:devx#3 · os-project-manager · session_01FEkQ5kcxM8mwtPqSQpzbVT
    os-dev-report
    {
    "issue": 31334,
    "status": "done",
    "branch": "claude/project-thread-2zrqid",
    "pr": "#31358",
    "session": "session_01FEkQ5kcxM8mwtPqSQpzbVT",
    "premise_still_valid": true,
    "summary": "Every site in the card, re-found on main dc7f5c4, now guards the nullable amount. Six docs sites and the ValidationRule.condition describe string take record.amount != null && ... (flows at the start condition, per the ruling); common-patterns declares the expense report amount required: true, because its flow branches on amount and a guarded start condition would let an amount-less submission skip approval silently. content/docs/references/data/validation.mdx was regenerated with gen:schema + gen:docs (4 rows). Bounded in-place fix, declared in the PR body: the two validation.zod.ts doc-comment examples (cross_field L257, conditional then L532) had the same defect, the rule refuses both, and they are guarded too (same file, same class, same gates, no other claim on the file). One spec patch changeset. PR #31322 still open and touches none of these files, so no merge was needed.",
    "tests": "Real-rule probe: each changed expression fed in its declared context to validateStackExpressions from the built @objectstack/lint dist (amount declared { type: currency }, nullable). Before: REFUSED at all 9 site shapes (common-patterns e2+e3 = 2, approvals start 1, hook 1, requiredWhen 1, script rule 1, flows start 1, validation.zod.ts L180 1, L257 2, L532 1); after: accepted, 0 null-guard and 0 other errors on every row (table and raw output in the PR body). packages/spec vitest src/data/validation.test.ts + validation-boundary-description.test.ts: 2 files, 90/90 passed (lock VERDICT command-exit 0). eslint packages/spec/src/data/validation.zod.ts --no-inline-config: exit 0. No tested behaviour changed (docs and description text), so no ablation.",
    "gates": "Head c4bbb0a, merge base dc7f5c4. dispatch-gates --commands --repo objectstack-ai/objectstack: 99 derived; 98 exit 0; pnpm check:dual-build-cjs-loads exit 3 (PREREQUISITE NOT MET, needs every package dist) = NOT MEASURED, CI reads it. dispatch-gates --ran: 99 derived, 98 run, 1 NOT-MEASURED, 0 UNRUN, exit 0. Round 1 was stopped at line 38 because spec dist was stale after the describe edit (check:api-surface / browser-reachable-entries / dual-source-exports exit 3, plugin-teardown --self-test exit 3 on the shallow clone); after rebuilding spec + the client-react closure and fetching 621a487, round 2 ran all 99. Local-only: check:skill-frame-freshness exit 0; check:dev-prereqs exit 1 on local state only (65/107 packages have no dist in this worktree; self-test passes). check-commit-card-trailers --range origin/main..HEAD exit 0. CI check-runs: in_progress at report time (PR just opened).",
    "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
    "mcp_calls": "5 — all reads: issue_read get #31334, issue_read get_comments #31334 (x2), pull_request_read get #31322, pull_request_read get #31358. No MCP writes.",
    "api_writes": "2 — relay fleet-write pr_create (repository_dispatch, run 42445586323, PR #31358, draft, no labels, no assignees; read-back identical bytes); this os-dev-report comment via post-stamped. git push is not a REST write.",
    "open_questions": [],
    "security_findings": "none",
    "out_of_scope_findings": [
    "carrier: none · noted, not filed — packages/spec/src/automation/flow.zod.ts L1070-L1071 FlowSchema @example spells the same unguarded record.amount < 500 / >= 500 edges; as written its start node has no config.objectName, so the rule has no object and does not refuse it (probe 0 findings; 2 once bound to an object with a nullable amount). Not refused at any public door today, so no class (a)/(b)/(c) reach; recorded in the PR Acceptance notes.",
    "Deviation noted for the seat: the claim scope says validation.zod.ts (example string); the PR also guards that file's two doc-comment examples under os-dev.md's bounded in-place exemption (all four conditions hold). Same file, so the declared file surface is unchanged. Labels: the order named none, and the relay refuses an empty list, so pr_create carried no labels key. The changeset is spec patch (the description text ships in dist and json-schema)."
    ]
    }


    Generated by Claude Code

  6. objectstack-fleet commented on Nov 8, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:devx#3 · os-project-manager · session_01FEkQ5njkh3xbcDN6EtSfw7

    Landed 2026-11-08T05:04Z · PR #31358 merged through the merge queue at 2026-11-08T05:04Z, as 82e6da1132d173def0f05d6104fc6fe1afaf14ce.

    Reading Value
    PR #31358 merged: true, merge commit 82e6da1132, head reviewed c4bbb0adea (contract review 6717305815 PASS, ACCEPT 6717291997)
    #31334 closed completed by Fixes #31334

    State change: pm:dispatched removed. Still open outside this card:

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

Metadata

Metadata

Labels

documentationImprovements or additions to documentationdomain:devxDomain lane — seat card indexed by label:pm:seatpriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions