Skip to content

Commit c3ce76c

Browse files
claude[bot]claude
andauthored
docs(spec): two docblocks that promised a future which has already arrived (#16835, #15239) (#16947)
`automation/control-flow.zod.ts` said the schema and `validateControlFlow` "do not overlap and cannot fight" and met at "one seam". #16134 removed that division deliberately: `FlowSchema`'s `superRefine` now refuses a duplicate node id — a structural fact — over one node-id space spanning the top-level `nodes[]` and every region body. The docblock now names both seams and the boundary: the #4001 region-slot `safeParse`, and the #16134 node-id space judged at every depth `collectFlowGraphs` walks, which stops at `MAX_REGION_DEPTH` (32) — past the ceiling a region is left raw and `analyzeRegion`'s own `duplicate node id` line is the only refusal of a within-region duplicate, a cross-region collision beyond it not judged at all. `security/sharing.zod.ts` said in two places that a `field` rule is skipped at seed "until [#15072] lands". It landed (`9fa577535`): `mapRecipientType` maps `field` through and `SharingRuleService.expandRecipientForRecord` reads the named column per matched record. Both sentences now name that executor rather than a schedule. The third sentence the card counted is left alone and is still true: `queue` is "NOT authorable until the implementation lands" is about `sys_queue`, not about `field`, and no `sys_queue` object exists in the tree. Comment text only — no schema, export, key or predicate moves. `content/docs/references/automation/control-flow.mdx` regenerated by `gen:docs`; `sharing.zod.ts`'s blocks do not reach the generated tree. Claude-Session: https://claude.ai/code/session_016N6xmWt5hYm94ffVEwGH8x Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
1 parent fcddd15 commit c3ce76c

4 files changed

Lines changed: 60 additions & 21 deletions

File tree

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
'@objectstack/spec': patch
3+
---
4+
5+
Two published docblocks now describe the tree as it is, instead of promising a change that has already landed (#16835, #15239).
6+
7+
Both files ship: `@objectstack/spec` lists `src/**/*.zod.ts` in its `files[]`, so a TSDoc comment in either is a published byte and a reader gets it from the package as well as from `content/docs/references/`. No schema, export, key or predicate changes — comment text only.
8+
9+
**`automation/control-flow.zod.ts`** said the schema and `validateControlFlow` "do not overlap and cannot fight: the schema rejects undeclared KEYS, the analysis rejects malformed STRUCTURE", meeting at "one seam". #16134 removed exactly that division: `FlowSchema`'s `superRefine` now refuses a duplicate node id — a structural fact — across one node-id space spanning the top-level `nodes[]` and every region body. The docblock now names both seams and the boundary between them: the #4001 region-slot `safeParse`, and the #16134 node-id space judged at every depth `collectFlowGraphs` walks. That walk stops at `MAX_REGION_DEPTH` (32), so past the ceiling a region is left raw and `analyzeRegion`'s own `duplicate node id` line is the only refusal of a within-region duplicate — a cross-region collision beyond the ceiling is not judged at all. The two guards overlap there by design and hand off at that measured boundary.
10+
11+
**`security/sharing.zod.ts`** said, in two places, that a `field` sharing rule is skipped at seed "until [#15072] lands". It landed: `mapRecipientType` maps `field` through and `SharingRuleService.expandRecipientForRecord` reads the named column on each matched record. Both sentences now name that executor rather than a schedule.
12+
13+
⚠️ The third sentence the card counted — `queue` is "deliberately NOT authorable until the implementation lands" — is **still true** and deliberately untouched: it is about `sys_queue`, not about `field`, and there is no `sys_queue` object in the tree.

content/docs/references/automation/control-flow.mdx

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -60,14 +60,26 @@ a swallowed branch key is a branch that runs without what it was given.
6060

6161
`validateControlFlow` is a **sibling guard, not a key gate** — it answers
6262
"is this region single-entry / single-exit / acyclic", which no amount of
63-
key strictness can answer. The two do not overlap and cannot fight: the
64-
schema rejects undeclared KEYS, the analysis rejects malformed STRUCTURE.
65-
They do now meet at one seam, deliberately — `validateControlFlow`
66-
`safeParse`s each region slot before analyzing it, so from #4001 that parse
67-
is also where a region's undeclared key surfaces, reported as
68-
`<where>: invalid region — <the strictObject message>`. Nothing was
69-
duplicated and nothing was removed; the structural prose this guard exists
70-
for is untouched, and it simply stopped silently repairing its own input.
63+
key strictness can answer. The two are no longer disjoint, and since #16134
64+
that is deliberate: the schema rejects undeclared KEYS *and* one structural
65+
fact — a duplicate node id — while the analysis rejects malformed STRUCTURE.
66+
They meet at two seams.
67+
68+
**#4001**`validateControlFlow` `safeParse`s each region slot before
69+
analyzing it, so that parse is also where a region's undeclared key
70+
surfaces, reported as `<where>: invalid region — <the strictObject
71+
message>`. That seam duplicated nothing and removed nothing, and left the
72+
structural prose this guard exists for untouched; it simply stopped silently
73+
repairing its own input.
74+
75+
**#16134**`FlowSchema`'s `superRefine` holds ONE node-id space across the
76+
top-level `nodes[]` and every region body, judged at every depth
77+
`collectFlowGraphs` walks. That walk stops at `MAX_REGION_DEPTH` (32), so
78+
past the ceiling a region is left raw and `analyzeRegion`'s own
79+
`duplicate node id` line, reached through this guard, is the only refusal of
80+
a within-region duplicate (a cross-region collision beyond the ceiling is
81+
not judged). The two guards overlap there by design and hand off at that
82+
measured boundary.
7183

7284
<Callout type="info">
7385
**Source:** `packages/spec/src/automation/control-flow.zod.ts`

packages/spec/src/automation/control-flow.zod.ts

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -58,14 +58,26 @@
5858
*
5959
* `validateControlFlow` is a **sibling guard, not a key gate** — it answers
6060
* "is this region single-entry / single-exit / acyclic", which no amount of
61-
* key strictness can answer. The two do not overlap and cannot fight: the
62-
* schema rejects undeclared KEYS, the analysis rejects malformed STRUCTURE.
63-
* They do now meet at one seam, deliberately — `validateControlFlow`
64-
* `safeParse`s each region slot before analyzing it, so from #4001 that parse
65-
* is also where a region's undeclared key surfaces, reported as
66-
* `<where>: invalid region — <the strictObject message>`. Nothing was
67-
* duplicated and nothing was removed; the structural prose this guard exists
68-
* for is untouched, and it simply stopped silently repairing its own input.
61+
* key strictness can answer. The two are no longer disjoint, and since #16134
62+
* that is deliberate: the schema rejects undeclared KEYS *and* one structural
63+
* fact — a duplicate node id — while the analysis rejects malformed STRUCTURE.
64+
* They meet at two seams.
65+
*
66+
* **#4001** — `validateControlFlow` `safeParse`s each region slot before
67+
* analyzing it, so that parse is also where a region's undeclared key
68+
* surfaces, reported as `<where>: invalid region — <the strictObject
69+
* message>`. That seam duplicated nothing and removed nothing, and left the
70+
* structural prose this guard exists for untouched; it simply stopped silently
71+
* repairing its own input.
72+
*
73+
* **#16134** — `FlowSchema`'s `superRefine` holds ONE node-id space across the
74+
* top-level `nodes[]` and every region body, judged at every depth
75+
* `collectFlowGraphs` walks. That walk stops at `MAX_REGION_DEPTH` (32), so
76+
* past the ceiling a region is left raw and `analyzeRegion`'s own
77+
* `duplicate node id` line, reached through this guard, is the only refusal of
78+
* a within-region duplicate (a cross-region collision beyond the ceiling is
79+
* not judged). The two guards overlap there by design and hand off at that
80+
* measured boundary.
6981
*/
7082

7183
import { z } from 'zod';

packages/spec/src/security/sharing.zod.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,10 @@ export const SharingLevel = z.enum([
8888
* principal). Unlike every member above, which resolves ONCE PER RULE, a
8989
* `field` recipient expands ONCE PER MATCHED RECORD, and its grants
9090
* re-materialise when the record's own write changes that column. That
91-
* executor half is #15072 (`plugin-sharing`); until it lands, the
92-
* declared-rule bootstrap skips a `field` rule with a logged warning
93-
* (`mapRecipientType` → null) — it never seeds one as anything wider.
91+
* executor half landed as #15072 (`plugin-sharing`): the declared-rule
92+
* bootstrap's `mapRecipientType` maps `field` through, and
93+
* `SharingRuleService.expandRecipientForRecord` reads the named column on
94+
* each matched record — never rule-wide, and never wider than the column.
9495
*
9596
* ⛔ No `manager` member (same ruling). "Share with the owner's manager" is
9697
* authored as a user field the application stores on the record — a snapshot
@@ -258,8 +259,9 @@ export const CriteriaSharingRuleSchema = lazySchema(() => BaseSharingRuleSchema.
258259
* `unit_and_subordinates` / `business_unit` (ADR-0057 D5; ADR-0090 D3) — every
259260
* authorable recipient expands at runtime (`plugin-sharing` `expandRecipient`)
260261
* — plus `field`, the record-relative recipient (#14103): resolved per matched
261-
* record from a user-typed column on it; its executor is the services half,
262-
* #15072, and until that lands a `field` rule is skipped LOUDLY at seed.
262+
* record from a user-typed column on it. Its services half, #15072, landed:
263+
* `expandRecipientForRecord` resolves it per record, so a `field` rule seeds
264+
* at bootstrap like any other instead of being skipped there.
263265
*
264266
* The whole authorable surface is enforced — nothing here validates and then
265267
* silently does nothing (ADR-0078). Removed to keep it that way: `owner`-type

0 commit comments

Comments
 (0)