From c5ee37f6c91e8d5396cecdeb9bff6ae396a13027 Mon Sep 17 00:00:00 2001 From: "claude[bot]" <209825114+claude[bot]@users.noreply.github.com> Date: Tue, 8 Sep 2026 17:46:44 +0000 Subject: [PATCH] docs(spec): two docblocks that promised a future which has already arrived (#16835, #15239) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `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. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_016N6xmWt5hYm94ffVEwGH8x --- .../spec-expired-until-it-lands-docblocks.md | 13 +++++++++ .../references/automation/control-flow.mdx | 28 +++++++++++++------ .../spec/src/automation/control-flow.zod.ts | 28 +++++++++++++------ packages/spec/src/security/sharing.zod.ts | 12 ++++---- 4 files changed, 60 insertions(+), 21 deletions(-) create mode 100644 .changeset/spec-expired-until-it-lands-docblocks.md diff --git a/.changeset/spec-expired-until-it-lands-docblocks.md b/.changeset/spec-expired-until-it-lands-docblocks.md new file mode 100644 index 0000000000..90d066fc1b --- /dev/null +++ b/.changeset/spec-expired-until-it-lands-docblocks.md @@ -0,0 +1,13 @@ +--- +'@objectstack/spec': patch +--- + +Two published docblocks now describe the tree as it is, instead of promising a change that has already landed (#16835, #15239). + +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. + +**`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. + +**`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. + +⚠️ 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. diff --git a/content/docs/references/automation/control-flow.mdx b/content/docs/references/automation/control-flow.mdx index 2c17467549..7f4080f16d 100644 --- a/content/docs/references/automation/control-flow.mdx +++ b/content/docs/references/automation/control-flow.mdx @@ -60,14 +60,26 @@ a swallowed branch key is a branch that runs without what it was given. `validateControlFlow` is a **sibling guard, not a key gate** — it answers "is this region single-entry / single-exit / acyclic", which no amount of -key strictness can answer. The two do not overlap and cannot fight: the -schema rejects undeclared KEYS, the analysis rejects malformed STRUCTURE. -They do now meet at one seam, deliberately — `validateControlFlow` -`safeParse`s each region slot before analyzing it, so from #4001 that parse -is also where a region's undeclared key surfaces, reported as -`: invalid region — `. Nothing was -duplicated and nothing was removed; the structural prose this guard exists -for is untouched, and it simply stopped silently repairing its own input. +key strictness can answer. The two are no longer disjoint, and since #16134 +that is deliberate: the schema rejects undeclared KEYS *and* one structural +fact — a duplicate node id — while the analysis rejects malformed STRUCTURE. +They meet at two seams. + +**#4001** — `validateControlFlow` `safeParse`s each region slot before +analyzing it, so that parse is also where a region's undeclared key +surfaces, reported as `: invalid region — `. That seam duplicated nothing and removed nothing, and left the +structural prose this guard exists for untouched; it simply stopped silently +repairing its own input. + +**#16134** — `FlowSchema`'s `superRefine` holds ONE node-id space across the +top-level `nodes[]` and every region body, 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, reached through this guard, is the only refusal of +a within-region duplicate (a cross-region collision beyond the ceiling is +not judged). The two guards overlap there by design and hand off at that +measured boundary. **Source:** `packages/spec/src/automation/control-flow.zod.ts` diff --git a/packages/spec/src/automation/control-flow.zod.ts b/packages/spec/src/automation/control-flow.zod.ts index 91919189b6..3b043691cd 100644 --- a/packages/spec/src/automation/control-flow.zod.ts +++ b/packages/spec/src/automation/control-flow.zod.ts @@ -58,14 +58,26 @@ * * `validateControlFlow` is a **sibling guard, not a key gate** — it answers * "is this region single-entry / single-exit / acyclic", which no amount of - * key strictness can answer. The two do not overlap and cannot fight: the - * schema rejects undeclared KEYS, the analysis rejects malformed STRUCTURE. - * They do now meet at one seam, deliberately — `validateControlFlow` - * `safeParse`s each region slot before analyzing it, so from #4001 that parse - * is also where a region's undeclared key surfaces, reported as - * `: invalid region — `. Nothing was - * duplicated and nothing was removed; the structural prose this guard exists - * for is untouched, and it simply stopped silently repairing its own input. + * key strictness can answer. The two are no longer disjoint, and since #16134 + * that is deliberate: the schema rejects undeclared KEYS *and* one structural + * fact — a duplicate node id — while the analysis rejects malformed STRUCTURE. + * They meet at two seams. + * + * **#4001** — `validateControlFlow` `safeParse`s each region slot before + * analyzing it, so that parse is also where a region's undeclared key + * surfaces, reported as `: invalid region — `. That seam duplicated nothing and removed nothing, and left the + * structural prose this guard exists for untouched; it simply stopped silently + * repairing its own input. + * + * **#16134** — `FlowSchema`'s `superRefine` holds ONE node-id space across the + * top-level `nodes[]` and every region body, 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, reached through this guard, is the only refusal of + * a within-region duplicate (a cross-region collision beyond the ceiling is + * not judged). The two guards overlap there by design and hand off at that + * measured boundary. */ import { z } from 'zod'; diff --git a/packages/spec/src/security/sharing.zod.ts b/packages/spec/src/security/sharing.zod.ts index c31b6d682b..398fc58901 100644 --- a/packages/spec/src/security/sharing.zod.ts +++ b/packages/spec/src/security/sharing.zod.ts @@ -88,9 +88,10 @@ export const SharingLevel = z.enum([ * principal). Unlike every member above, which resolves ONCE PER RULE, a * `field` recipient expands ONCE PER MATCHED RECORD, and its grants * re-materialise when the record's own write changes that column. That - * executor half is #15072 (`plugin-sharing`); until it lands, the - * declared-rule bootstrap skips a `field` rule with a logged warning - * (`mapRecipientType` → null) — it never seeds one as anything wider. + * executor half landed as #15072 (`plugin-sharing`): the declared-rule + * bootstrap's `mapRecipientType` maps `field` through, and + * `SharingRuleService.expandRecipientForRecord` reads the named column on + * each matched record — never rule-wide, and never wider than the column. * * ⛔ No `manager` member (same ruling). "Share with the owner's manager" is * authored as a user field the application stores on the record — a snapshot @@ -258,8 +259,9 @@ export const CriteriaSharingRuleSchema = lazySchema(() => BaseSharingRuleSchema. * `unit_and_subordinates` / `business_unit` (ADR-0057 D5; ADR-0090 D3) — every * authorable recipient expands at runtime (`plugin-sharing` `expandRecipient`) * — plus `field`, the record-relative recipient (#14103): resolved per matched - * record from a user-typed column on it; its executor is the services half, - * #15072, and until that lands a `field` rule is skipped LOUDLY at seed. + * record from a user-typed column on it. Its services half, #15072, landed: + * `expandRecipientForRecord` resolves it per record, so a `field` rule seeds + * at bootstrap like any other instead of being skipped there. * * The whole authorable surface is enforced — nothing here validates and then * silently does nothing (ADR-0078). Removed to keep it that way: `owner`-type