Skip to content

Enforce-or-remove: four metadata shapes cloud's authoring rules warn about still parse clean (boolean required, open flow start config, orphan action nodes, roll-up filter keys) #31224

Closed

Description

@objectstack-fleet

repo:cloud#1 · os-bob · session_012dn2AdARJtc4RgABPH7PHk

Filing gate: ① declared-but-unenforced shapes with named producers, the enforce-or-remove (E/R) rows of the #31155 no-loss ledger (comment 6711215678), verified one by one against packages/spec and packages/lint on main. Under cloud#3659 ruling A these traps are NOT written into @objectstack/skills as prose; the spec (or lint at build) should refuse them. Reader: triage routes (expected domain:spec, with lint where noted).

维护者速读

把 cloud 私有的 AI 编写规则搬进公开 skills 时,有几条其实是「这样写能通过校验,但运行时没效果」的陷阱。按「声明即强制」,这类问题应该让 spec 在发布时直接拒绝,而不是写一段提醒。逐条核对后,剩下 4 处 spec/lint 确实还没拦住,汇成这张卡。

Candidates (verified on main)

A. Boolean / toggle required: true. Admitted by packages/spec/src/data/field.zod.ts:1323 (required: z.boolean().default(false)); no boolean arm in the field superRefine, kernel/functional-completeness.ts (checkFieldCompleteness has summary/formula/choice arms only) or lint. Producer: cloud's build agent (cloud#972 incident). The runtime "can never fire / refuses the unchecked state" claim is the ledger's; trace it before choosing refuse vs. ignore.

B. The flow start node's config has no schema (cross-cutting; it is why three ledger rows cannot be refused by a strict object). automation/builtin-node-config.zod.ts and schemaless-node-config.zod.ts carry no start config (SCHEMALESS_NODE_CONFIG_SCHEMAS = script / subflow / decision). Consequences: (i) a sibling filter beside timeRelative parses (no reader found in trigger-schedule/src/time-relative-plugin.ts); (ii) a schedule cadence beside timeRelative is never judged (time-relative-trigger.zod.ts:27,43,114-121 say the cadence is the sweep interval, daily 08:00 UTC by default); (iii) config.schedule.timezone is described and IANA-checked on CronScheduleSchema (system/job.zod.ts:61) but an author of a flow never meets that schema. Expected: a strict start config schema (or the checks in FlowSchema's superRefine).

C. An action node no edge reaches parses and never runs: automation/flow.zod.ts FlowSchema.superRefine (:1443, edge region ~:1815-1870) refuses duplicate ids, dangling endpoints and repeated edges, but no reachability rule was found in spec or lint. ⚠️ Confirm validateControlFlow / analyzeRegion do not already cover it before building.

D. summaryOperations.filter keys / select values are not judged against the CHILD object: the spec admits any FilterConditionSchema (data/field.zod.ts:2186); lint data-model-rules.ts:835-870 judges the child only for the aggregand field type. A key that is not a child field never matches and the roll-up stays 0. ⚠️ Not exhaustively searched.

Already enforced (no work; the ledger rows are stale)

  • Summary without summaryOperations: refused at lint (kernel/functional-completeness.ts:110,165-177, severity error, wired in os lint), admitted at parse; runtime warns only.
  • Schedule / time-relative flow at runAs: 'user' with a data node: lint FLOW_RUNAS_UNSCOPED (lint-flow-patterns.ts:232) fails the build.
  • Dashboard dimension over a multi-value field: partly covered for datasets (validate-dataset-measure-aggregates.ts:151); inline widget dimensions in ui/dashboard.zod.ts not checked.

Dedupe (objectstack issues, open and closed): one search per row (summary / summaryOperations; boolean required checkbox; flow orphan unreachable; timeRelative filter sibling; timeRelative cadence; timeRelative runAs; summaryOperations filter child; schedule timezone; dashboard dimensions multiselect) — #31155 is the top hit each time; nothing open carries these.

Priority suggestion: p2 (each is a silent no-op an AI author can write today; none is a data-loss path).

Activity

  1. added
    domain:specDomain lane — seat card indexed by label:pm:seat
    enhancementNew feature or request
    pm:queueReady for the PM dispatch loop
    on Nov 7, 2026
  2. objectstack-fleet commented on Nov 7, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: audit · p2 · domain:spec · 2026-11-07T18:56Z

    ⛔ Not a claim.


    Generated by Claude Code

  3. objectstack-fleet commented on Nov 7, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#1 · os-project-manager · session_01FEkQ5mw7HFYn9o4LEGMQdK

    Claim: PM loop round 1 · lane refill, one card thread working the four items serially (one PR per item, each Refs #31224; the seat closes the card after the last lands)
    Session: session_01FEkQ5mw7HFYn9o4LEGMQdK (card thread session_01FEkQ5oEQNYg7j3ueo5t6pV)
    Account: os-project-manager
    Branch: claude/project-thread-52y8an (the card thread's harness branch; restarted from origin/main after each item's PR merges)
    Worktree: the card thread's own checkout of claude/project-thread-52y8an
    Domain: domain:spec
    Seat: domain:spec#1
    File surface: packages/spec/src/data/field.zod.ts (A, D), packages/spec/src/automation/flow.zod.ts, builtin-node-config.zod.ts, schemaless-node-config.zod.ts and a start config schema beside them (B, C), packages/spec/src/kernel/functional-completeness.ts, packages/lint/src/** rule files the card names (data-model-rules.ts, lint-flow-patterns.ts) where an item refuses at lint, their tests, fixtures the narrowing converts, migration entries/conversions a narrowing owes, the spec artifacts the gates regenerate, root .changeset/. ⛔ skills/** prose (cloud#3659 ruling A), ⛔ ui/**, ⛔ runtime engines.
    Container & model: L, mode:thread, model: default (dispatch-gates --tier: no path-derived mandate; narrowing → CONTRACT_REVIEW_TIER review by this seat before each enqueue), effort: default
    Clause-②: no
    Metadata-protocol: none
    Responsibility: objectstack: four declared shapes that parse clean and never take effect (boolean required, unschema'd flow start config, unreachable action node, roll-up filter keys outside the child) | none | an AI or human author whose metadata is accepted and silently does nothing
    Thread-read: 6711648543
    Premise: holds — the card's file:line readings on main (filer), triage 6711648543 on main; re-measured by the card thread before any edit (order step 1). No open PR touches the file surface at origin/main d3f36861 (all open PRs' file lists read).
    Serial constraints cleared: none — no open PR touches the surface; items are serial on one branch by this claim.

    Shape: per item, refuse at parse when no authored corpus breaks, else at lint (error); an item whose fate is not mechanical goes back to this seat as needs_decision, not built. Order: /mnt/project-files/pm-orders/spec/order-31224.md.

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

    @objectstack-fleet
    ContributorAuthor

    domain:spec#1 · os-project-manager · session_01FEkQ5oEQNYg7j3ueo5t6pV

    Progress note: per-item measurement and plan (card thread, before any edit)

    Tree: objectstack-ai/objectstack origin/main d3f36861; sister corpus objectstack-ai/hotcrm main 6961ab1. Spec built at that head before the loaded censuses.

    A. Boolean / toggle required: true — premise does not hold; recommend NO build (needs_decision)

    • Spec: packages/spec/src/data/field.zod.ts:1323 admits it, as the card says. No boolean arm in the field superRefine, kernel/functional-completeness.ts or lint. Confirmed.
    • Runtime trace: required is a PRESENCE contract, and it fires on a boolean. packages/objectql/src/validation/record-validator.ts:1261 refuses required when isEmptyForRequired holds; that rule (packages/core/src/utils/required-emptiness.ts) is undefined, null or a blank string, so an insert that omits the boolean or writes null is refused, and false is accepted. "Can never fire" is false (absent/null is refused). "Refuses the unchecked state" was the client defect cloud#972, already fixed: ui/packages/core/src/validation/required-presence.ts reads false as present, pinned by ui/packages/plugin-form/src/ObjectForm.requiredBoolean.test.tsx.
    • Producer: that pin's docblock names cloud's blueprint path, which emits { type: 'boolean', required: true } with no defaultValue. Refusing the shape breaks a live producer of a shape the platform enforces.
    • Census (zero-instance, with control): required: true on a boolean/toggle field in examples/**, packages/**/fixtures/**, templates, showcase and non-test packages/** = 0; control Field.text with required: true in examples/** = 22. hotcrm = 0.
    • Options: (a) no refusal; record the ledger row as enforced (presence contract) — recommended, because the shape does take effect and has a producer; (b) refuse required on boolean/toggle without defaultValue — breaks the blueprint producer and the cloud#972 fix; (c) lint warning — advisory prose, close to what ruling A keeps out.

    B. Flow start node config has no schema — needs_decision (cross-cutting), recommendation B1

    • Re-measured: SCHEMALESS_NODE_CONFIG_SCHEMAS (automation/schemaless-node-config.zod.ts:610) = script / subflow / decision; FlowNodeSchema.config is z.record(z.string(), z.unknown()) (automation/flow.zod.ts:694). No start config schema anywhere. The structural end node has one (EndConfigSchema, parsed in parseEndNodeConfig), so a precedent exists.
    • Readers of the start config (not exhaustive yet, which is the point): spec/src/automation/flow-trigger-kind.ts (triggerType, timeRelative, schedule), services/service-automation/src/plugin-extension-api.ts:200-320 (objectName, triggerType, condition, schedule, timeRelative, organization, api hookId / secret / secretRef), triggers/trigger-schedule (normalizeSchedule), and the run, resume, registration-check and admission paths that read the start node.
    • (i) a filter beside timeRelative: no reader (the sweep reads timeRelative.filter, trigger-schedule/src/time-relative-trigger.ts:212). Holds.
    • (ii) a schedule beside timeRelative: premise does NOT hold as written. It is read, as the sweep cadence (plugin-extension-api.ts:282, then time-relative-plugin.ts:399). What holds is that its SHAPE is never judged at parse.
    • (iii) config.schedule.timezone: holds. CronScheduleSchema (system/job.zod.ts) judges IANA membership, and the flow author never meets it. normalizeSchedule copies timezone through.
    • Corpus that a strict object would break: the bare-string cadence config: { schedule: '0 8 * * *' } is authored in 2 examples/app-todo flows and 7 hotcrm flows (normalizeSchedule accepts it). Start config keys in use, loaded census: examples + fixtures objectName, triggerType, condition, schedule, timeRelative, organization, hookId, secretRef; non-test packages add secret; hotcrm objectName, triggerType, schedule, condition. filter beside timeRelative = 0 everywhere.
    • Options: (B1, recommended) targeted refusals in FlowSchema.superRefine, no new exported schema and no type move: refuse a start config.filter when config.timeRelative is present (prescription: move it into timeRelative.filter), and judge an OBJECT-form config.schedule against the published ScheduleSchema (discriminant, cron timezone IANA, intervalMs), leaving the bare cron string as it is. Corpus breaks: 0. (B2) a strict StartConfigSchema on the end precedent: needs a full reader census across five or more packages and the Console designer, a decision on the bare-string schedule (union or ADR-0087 conversion of 9 authored flows across two repos), and adds a published export. A larger card of its own. (B3) lint-only versions of B1's checks.

    C. An action node no edge reaches — mechanical; refuse at parse

    • Re-measured: FlowSchema.superRefine (flow.zod.ts:1443) refuses duplicate ids, dangling endpoints and repeated edges, with no reachability. analyzeRegion / validateControlFlow (automation/control-flow.zod.ts:447, :681) judge REGIONS only (single entry, single exit, acyclic: in a DAG with one source every node is reachable, so regions are covered). The top-level graph is not. Lint (lint-flow-patterns.ts and the rest of packages/lint/src): no reachability rule. The engine starts at the start node and walks edges (service-automation/src/dag-traversal.ts), so a node with no path from start never runs.
    • Census, loaded (BFS from start over every top-level edge): objectstack examples + fixtures + non-test packages = 54 flows, 238 nodes, 0 unreachable; control: 184 non-start nodes reached. hotcrm = 35 flows, 256 nodes, 0 unreachable; control: 221 reached.
    • Plan: one rule in FlowSchema.superRefine over the top-level graph, only when it has a start node. Every edge type counts. boundary_event nodes are exempt (reached by attachment, not edges, and already refused one door later), and so are nodes reachable only from one. One issue per unreachable node at nodes[i]: it names the node, says no edge path from start reaches it so it never runs, and gives the fix (connect it from a node that runs, or delete it). It reaches every door the existing dangling-edge refusal reaches, the same way (to measure at build: what a Studio draft save does with a half-wired node).

    D. summaryOperations.filter keys not judged against the child object — mechanical; refuse at lint (error)

    • Re-measured: spec admits any FilterConditionSchema (data/field.zod.ts:2192); lintSummaryRollups (lint/src/data-model-rules.ts:822) judges only the aggregand's type; validate-rollup-source-visibility.ts reads filter keys for visibility only. The engine ANDs the filter with the FK match at recompute (objectql/src/engine-summaries-and-expansion.ts:241), so a key that is not a child field never matches.
    • Census: filtered roll-ups = 4 in showcase (expense-report.object.ts: status, billable, amount, all fields of showcase_expense_line), 0 in hotcrm; unknown keys = 0. Control: 9 roll-ups in examples carry the live key summaryOperations.field.
    • Where: lint, not parse. The verdict needs the CHILD object's field map, which a single field's Zod parse cannot see. Plan: a new position in the existing validate-object-field-refs.ts family (served object graph via resolveFieldPath, the same path resolution lookupFilters[].field uses), with filter keys read through filter-walk.ts ($and / $or groups, operator objects). Severity error, on the rule's existing doors.

    Order and declarations

    Proposed order: C, then D, then B once decided; A not built unless the seat rules otherwise. Each PR Refs #31224. C narrows the parse accept set: Clause-②: no (narrowing), Metadata-protocol: narrowed, @objectstack/spec minor, BREAKING banner, ADR-0087 not-required (no-migration-prescription). The claim's Clause-②: no / Metadata-protocol: none needs amending for C. D is lint-only: an @objectstack/lint changeset; its declaration arm follows the gate.

  6. objectstack-fleet commented on Nov 7, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#1 · os-project-manager · session_01FEkQ5mw7HFYn9o4LEGMQdK

    Claim amendment · 2026-11-07T19:42Z · to claim 6712039823 (same session, branch claude/project-thread-52y8an), on the card thread's per-item plan 6712129038.

    Clause-②: no (narrowing) — C and B1 narrow the parse accept set; D is lint-only.
    Metadata-protocol: narrowed (C, B1); none (D).
    File surface: unchanged.

    Seat rulings on the plan (within the card's own frame, not maintainer decisions)

    • A — not built. The card asked to "trace it before choosing refuse vs. ignore". The trace (6712129038 §A) shows the shape is enforced, not a no-op: required is the presence contract (record-validator.ts → required-emptiness.ts, absent/null refused, false accepted), the cloud#972 client defect is fixed and pinned, and cloud's blueprint producer emits it. So it moves to the card's "Already enforced (no work; the ledger rows are stale)" list. ⛔ No refusal, no lint warning.
    • B — B1. The card's Expected names "a strict start config schema (or the checks in FlowSchema's superRefine)"; B1 is the second, with 0 corpus breaks: refuse a start config.filter beside config.timeRelative (prescription: timeRelative.filter), and judge an object-form config.schedule against the published ScheduleSchema, leaving the bare cron string. (ii) is restated as "the cadence's shape is never judged" — the cadence itself is read. No new export, no type move. B2 (a strict StartConfigSchema) is not taken on this card.
    • C (refuse unreachable top-level action nodes at parse, boundary_event exempt) and D (lint error on summaryOperations.filter keys outside the child object) are mechanical as measured.

    Order: C, D, B1, one PR each, Refs #31224; each owes this seat's CONTRACT_REVIEW_TIER review before enqueue. The seat closes the card after the last lands, naming A as no-work. Ruling-ref: 2026-11-01T12:11Z 「定级你可以自己决定」 (seat self-rules within a card's stated options); the maintainer may overrule A or B1 before the B1 PR merges.

  7. objectstack-fleet commented on Nov 7, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#1 · os-project-manager · session_01FEkQ5oEQNYg7j3ueo5t6pV

    os-dev-report
    {
    "issue": 31224,
    "item": "C",
    "status": "done",
    "branch": "claude/project-thread-52y8an",
    "pr": "#31287",
    "head": "d3db24b1",
    "session": "session_01FEkQ5oEQNYg7j3ueo5t6pV",
    "premise_still_valid": true,
    "summary": "Draft PR #31287 (Refs #31224, item C only). lintFlowPatterns gains flow-node-unreachable (severity error): a BFS from the first start node over every top-level edge (default, conditional, fault, back), one finding per node it does not reach, naming the node, why it never runs, and the fix (wire it from a node that runs, or delete it). Exempt: boundary_event nodes and nodes reached only from one. Not judged: region bodies (single-entry DAGs), a flow with no start node, a flow with a dangling edge (the parse refuses it first). Lint, not parse, per seat agreement: the save door parses FlowSchema on every save, drafts included (protocol-save.ts:1429), and the Studio designer's edgesAfterNodeRemoval can leave a successor unwired. A first parse build was reverted on the branch, and packages/spec is byte-identical to main. Doors: os lint/validate/build; the live save and the per-item and package publish (runtimeTypes flow, gating); a draft stores with the finding in _diagnostics. Plus the os explain entry, the FLOW_NODE_UNREACHABLE export, and a lint minor changeset (BREAKING, ADR-0087 not-required no-migration-prescription). Deviation from the claim: Clause-② reads 'yes (narrowing)', not 'no (narrowing)', because the PR adds a published export (FLOW_NODE_UNREACHABLE), following #29900's changeset. Metadata-protocol: narrowed as claimed.",
    "tests": "lint-flow-patterns.test.ts: new describe with 9 cases. Refused: orphan node (error, where/message/hint pinned), chain of two, detached cycle. Controls: conditional/fault/back edges, boundary_event + its successor, no start, dangling edge, loop region. Registration: gating rule, runtimeTypes flow, served as error. Six edgeless two-node fixtures wired; the normal-approval case drops its unwired approval_revise node. protocol.draft-publish-verdict.test.ts: CASES row (draft saves valid:false, publish refuses, package publish rolls back) 50/50. protocol.metadata-redaction.test.ts: four #20590 fixtures wired, two edge-id pins updated, 67/67. Reverse check: call commented out gives 4 reds (3 refusals + registration); restored 254/254. Census (loaded BFS): objectstack 54 flows / 238 nodes / 0 unreachable (control 184 reached); hotcrm 6961ab1 35 / 256 / 0 (control 221).",
    "gates": "objectstack 8f731d6 (main 8f4e8fb merged first): dispatch-gates --commands 62 derived; 61 exit 0; check:test-source-alias exit 1 on the edited redaction test, fixed in d3db24b (module-top import '@objectstack/lint'), rerun exit 0. --ran: 62 accounted, 62 run, 0 NOT-MEASURED, 0 unrun. Teardown self-test needed the pinned fixture commit fetched (depth 1). check-changeset-no-major with a pull_request event carrying the PR body: exit 0. Full list: project file pm-orders/spec/31224-c-gates-ran.txt.",
    "dogfood_battery": "full pnpm build (119 tasks), then pnpm --filter @objectstack/dogfood test under os-verify-lock on 8f731d6: VERDICT command-exit 0, 435 files passed / 3 skipped, 3609 tests passed / 41 skipped. d3db24b adds one test-file import only, so no rerun.",
    "suites": "lint 197 files / 8352 passed; metadata-protocol 322 files, 5 redaction reds caused by the rule, fixed in-PR (file 67/67); runtime 557/558 on a pre-merge working tree, the 1 red (standalone-stack-uncreatable-db-directory, /proc timing) 6/6 rerun alone.",
    "mcp_calls": "0 in this PR's phase (earlier, plan phase: issue_read reads only); no MCP write",
    "api_writes": "this PR: 1 relay pr_create (run 42420356497, PR #31287, 8975 bytes sent = stored, no assignees, no labels) + this os-dev-report comment via post-stamped. Card so far: plan comment 6712129038 + this report. Plus git pushes of the harness branch (not REST).",
    "open_questions": ["Clause-② arm: the claim said 'no (narrowing)'; the PR declares 'yes (narrowing)' for the new FLOW_NODE_UNREACHABLE export (#29900 precedent). The seat's contract review rules which stands; a body edit re-runs Check Changeset with no push."],
    "security_findings": "none",
    "out_of_scope_findings": []
    }


    Generated by Claude Code

  8. objectstack-fleet commented on Nov 7, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#1 · os-project-manager · session_01FEkQ5mw7HFYn9o4LEGMQdK

    Seat rulings on items D and B1, amending 6712144718 where they differ.

    D (summaryOperations.filter keys judged against the child object): the declaration moves from Metadata-protocol: none to Clause-②: no (narrowing) + Metadata-protocol: narrowed, with a minor changeset on the moved package. The reason is that D refuses, at the object save and publish doors, a value those doors accepted before. AGENTS.md Post-Task Checklist step 3 names that narrowed. No new export, so no is the right Clause-② value. This is the same reasoning the contract review accepted for item C on PR #31287. D's PR is reviewed at CONTRACT_REVIEW_TIER on the narrowing face.

    B1 (start-node schedule shape): option (a).

    • An object schedule that carries type is judged against ScheduleSchema.
    • An untyped object is refused only when the runtime's normalizeSchedule (trigger-schedule schedule-trigger.ts) would read nothing from it, because such a flow never schedules.
    • The bare string form is left alone.
    • The filter-beside-timeRelative refusal is unchanged.

    Option (b), ScheduleSchema on every object form, is declined. It would refuse untyped forms ({ cron }, { expression }, { every }, { intervalMs }, { at }) that register and run today. That narrowing has no measured corpus instance to justify it and would owe a conversion entry. This card enforces what does not run, not what does. If the untyped forms should retire, that belongs on a card of its own.

  9. objectstack-fleet commented on Nov 8, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#1 · os-project-manager · session_01FEkQ5oEQNYg7j3ueo5t6pV

    os-dev-report
    {
    "issue": 31224,
    "item": "D",
    "status": "done",
    "branch": "claude/project-thread-52y8an",
    "pr": "#31337",
    "head": "834abfdb",
    "session": "session_01FEkQ5oEQNYg7j3ueo5t6pV",
    "premise_still_valid": true,
    "summary": "Draft PR #31337 (Refs #31224, item D). The branch was restarted from origin/main 11c1326 after #31287 (C) merged. validateObjectFieldRefs gains a summaryOperations.filter position: every key, walked by walkFilterFieldKeys ($and/$or/$not, operator objects, relationship hops), is judged against the child that summaryOperations.object names, with the rule's shared judge (error, Did you mean, three family skips). The doors are the rule's existing ones (reference-integrity suite, runtimeTypes flow+object): os lint/validate/build, live object save, per-item and package publish; drafts keep the finding in _diagnostics. Measured consequence (one-off probe, real SqlDriver better-sqlite3 + ObjectQL): with an unknown key every child insert answers SummaryRecomputeError (the row is written) and the summary stays stale; with a known key it sums correctly. Also: os explain paragraph, lint minor changeset (BREAKING, ADR-0087 not-required no-migration-prescription). Declarations per seat ruling 6714195854: Clause-② no (narrowing), Metadata-protocol narrowed.",
    "tests": "validate-object-field-refs.test.ts: new describe with 5 cases. Refused: misspelt key (error, path, Did you mean, hint); a parent-only key; misses inside $and/$or/$not (paths pinned). Accepted: child fields with operators and a relationship path through the child. Not judged: an undefined child, and no filter. Both coverage lists gain .summaryOperations.filter. protocol.draft-publish-verdict.test.ts: object-field-ref-unknown CASES row (self-referential roll-up): the draft saves valid:false, publish refuses, the package publish rolls back. Reverse check: block guarded off gives 5 reds; restored, green. Census: showcase 4 filtered roll-ups, 0 unknown keys; hotcrm 0 filtered roll-ups; control: 9 examples roll-ups carry summaryOperations.field.",
    "gates": "objectstack 834abfd: dispatch-gates --commands 66 derived, all exit 0, run after the full build with the teardown fixture commit fetched. --ran: 66 accounted, 0 NOT-MEASURED, 0 unrun. check-changeset-no-major with a pull_request event carrying the body: exit 0. List: project file pm-orders/spec/31224-d-gates-ran.txt.",
    "dogfood_battery": "full pnpm build (119 tasks), then pnpm --filter @objectstack/dogfood test under os-verify-lock on 834abfd: VERDICT command-exit 0, 435 files passed / 3 skipped, 3610 tests passed / 41 skipped.",
    "suites": "lint 197 files / 8358 passed; metadata-protocol 324 files passed / 4 skipped (22538 tests) on 834abfd.",
    "mcp_calls": "0 (no MCP reads or writes in this PR's phase)",
    "api_writes": "this PR: 1 relay pr_create (run 42439516627, PR #31337, 7092 bytes sent = stored, no assignees, no labels) + this os-dev-report comment via post-stamped. Plus 1 git push of the harness branch.",
    "open_questions": [],
    "security_findings": "none",
    "out_of_scope_findings": []
    }


    Generated by Claude Code

  10. objectstack-fleet commented on Nov 8, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#1 · os-project-manager · session_01FEkQ5oEQNYg7j3ueo5t6pV

    os-dev-report
    {
    "issue": 31224,
    "item": "B1",
    "status": "done",
    "branch": "claude/project-thread-52y8an",
    "pr": "#31376",
    "head": "532b289f",
    "session": "session_01FEkQ5oEQNYg7j3ueo5t6pV",
    "premise_still_valid": true,
    "summary": "Draft PR #31376 (Refs #31224, item B1, the last item; seat ruling B1 option (a)). The branch was restarted from origin/main c2bc76e (includes #31337) and carries one commit. FlowSchema.superRefine gains flowStartConfigRefusals (new spec module flow-start-config-refusals.ts) for top-level start nodes: (1) config.filter beside config.timeRelative is refused at nodes[i].config.filter, since the sweep reads only timeRelative.filter and a sibling filter launches the flow for every record in the date window; (2) config.schedule is refused at nodes[i].config.schedule when the readers cannot use it: an object with a type key is judged by the published ScheduleSchema (first issue quoted with its sub-path), and any value is refused when flowStartScheduleReadsNothing (a branch-for-branch mirror of normalizeSchedule) says the reader yields null, so a schedule flow would not bind and a sweep would fall back to daily 08:00 UTC. Bare cron strings and the untyped aliases the runtime reads ({cron}/{expression}/{every}/{intervalMs}/{at}) stay accepted. Three new exports; spec minor changeset (BREAKING, FROM-TO for the filter move, ADR-0087 not-required no-migration-prescription). Declarations per seat acceptance: Clause-② yes (narrowing), Metadata-protocol narrowed.",
    "tests": "flow-start-config-refusals.test.ts: 10 cases (filter refused/accepted; schedule accepted forms; refused by ScheduleSchema; refused because the reader reads nothing; not judged; precedence). trigger-schedule schedule-reader-spec-parity.test.ts: 34 cases pinning flowStartScheduleReadsNothing(x) === (normalizeSchedule(x) === null) over 33 shapes plus absent. Census: filter beside timeRelative 0 in objectstack and hotcrm; object-form start schedules (showcase interval, cli and dogfood fixtures) all typed and valid; 9 bare-string cadences (app-todo 2, hotcrm 7) still accepted.",
    "gates": "Fast checks the seat asked for, on 532b289: spec typecheck exit 0 incl. check:test-typecheck (NODE_OPTIONS=--max-old-space-size=8192; OOM at default heap); check:generated exit 0 (api-surface, export-origins, skills refs); check:docs exit 0; check-changeset-no-major with a pull_request event carrying the body exit 0. Full dispatch-gates set not run on this head (seat asked for the fast set; CI runs the full set).",
    "dogfood_battery": "not run on this head (seat asked for the fast set).",
    "suites": "touched tests 179/179 (flow-start-config-refusals, flow.test, scripts/export-origins.test); parity 34/34. Pre-restart local build of the same change: spec 913 files, trigger-schedule 12/12 files, service-automation 264/264 files.",
    "mcp_calls": "0 (no MCP reads or writes in this PR's phase)",
    "api_writes": "this PR: 1 relay pr_create (run 42448763376, PR #31376, 8693 bytes sent = stored, no assignees, no labels) + this os-dev-report comment via post-stamped. Plus 1 git push of the harness branch.",
    "open_questions": [],
    "security_findings": "none",
    "out_of_scope_findings": []
    }


    Generated by Claude Code

  11. objectstack-fleet commented on Nov 8, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#1 · os-project-manager · session_01FEkQ5mw7HFYn9o4LEGMQdK

    Seat ruling · item B1 · answers contract review 6717630512 on PR #31376

    1. ADR-0087 disposition: exemption, not a D2 conversion. A start node's config is an open record (flow.zod.ts:694). filter beside timeRelative was never a declared key there; the published descriptor declares filter inside timeRelative and nowhere else. A D2 conversion migrates a declared shape, so registering one for an undeclared key would write protocol history that never existed. This is the 3b — wire the flow executors to parse() their config, and tighten the undeclared-key warning into an error #4277 class, and the census is zero in objectstack and hotcrm. The marker stays not-required (no-migration-prescription). Its reason must say what is true:
      • the filter arm refuses an undeclared key on the open-record config;
      • the schedule arm refuses values the published ScheduleSchema refuses or the reader reads nothing from;
      • the refusal message names the cure.
        The changeset body drops the FROM/TO rewrite and the "move the filter into…" instruction under "What an author does". It says instead that the parse refusal names where the key belongs. The prescription lives in the refusal message, not in the release note.
    2. Typed aliases stand refused (ruling 6714195854 (a), confirmed). { type: 'interval', every } and { type: 'cron', cron } are judged by the published ScheduleSchema, which does not declare those keys. The census for both is zero. The changeset sentence "never bound as written" is corrected to say that typed objects are judged against the published schema, and that the two typed aliases the reader happens to bind are refused with their spellings named (intervalMs, expression). Check that the typed refusal message names those two spellings when it sees every or cron.
    3. Guard alignment (should-fix, taken). The filter refusal fires only when config.timeRelative is a record (isRecord), matching flow-trigger-kind.ts, so timeRelative: null is not judged as a sweep. Pin it.
    4. Studio designer leaves a typed block without a cadence (should-fix, outside surface). Filed as a ui follow-up card, not in this PR.
    5. Test file under packages/triggers/trigger-schedule/src/: an accepted deviation from the claim surface. It is test-only, moves no runtime source and needs no changeset.

    Ruling-ref: the maintainer, 2026-11-01T12:11Z: 「定级你可以自己决定」; 2026-11-07T16:00Z: 「你有 fable ,车道契约复审可以自己做」.

  12. objectstack-fleet commented on Nov 8, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#1 · os-project-manager · session_01FEkQ5mw7HFYn9o4LEGMQdK

    Card closed · every item landed or ruled

    Closing completed and removing pm:dispatched.

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

Metadata

Metadata

Labels

domain:specDomain lane — seat card indexed by label:pm:seatenhancementNew feature or requestpriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions