Skip to content

spec(storyboards): first-class branch_set phase field (#2633)#2646

Merged
bokelley merged 2 commits into
mainfrom
bokelley/branch-set-first
Apr 21, 2026
Merged

spec(storyboards): first-class branch_set phase field (#2633)#2646
bokelley merged 2 commits into
mainfrom
bokelley/branch-set-first

Conversation

@bokelley

Copy link
Copy Markdown
Contributor

Summary

  • Promotes branch-set membership from implicit contributes_to correlation to an explicit branch_set: { id, semantics } phase field, per Promote branch_set to first-class storyboard schema construct #2633.
  • Adds lint-storyboard-branch-sets.cjs (wired into build:compliance and npm test) enforcing the authoring rules.
  • Migrates universal/schema-validation.yaml (past_start_handled) and universal/security.yaml (auth_mechanism_verified) to the explicit form.

Why now

Expert review on #2631 flagged the implicit detection as fragile: a typo in contributes_to: silently breaks branch-set membership, and a conformant agent on the "correct" branch can look like it failed everywhere. Making the relationship declared (not inferred) closes that gap.

This also sets up #2634 (contradiction lint) — keying contradiction detection off an explicit declaration is much simpler than reverse-engineering topology from contributes_to heuristics. Shipping #2633 first avoids rewriting #2634 a week after it lands.

Rules the lint enforces

  1. Phases with branch_set: must be optional: true (otherwise unconditional-fail defeats the any_of semantics).
  2. branch_set.semantics must be a supported value (any_of today; at_least_n/all_of reserved).
  3. Peer phases sharing branch_set.id share semantics.
  4. Every declared branch_set.id has a matching assert_contribution step with check: any_of, allowed_values: [<id>] — otherwise the branch set is dead.
  5. Any step inside a branch-set phase with contributes_to: X has X === branch_set.id — catches the exact typo class this PR exists for.

Implicit detection stays as a runner fallback so pre-#2633 storyboards keep working unchanged.

Test plan

  • npm run test:storyboard-branch-sets — 7 tests (1 source-tree guard + 5 per-rule synthetic fixtures + 1 helper test)
  • npm run test:storyboard-scoping — unchanged, passes
  • npm run build:compliance — new lint runs alongside scoping lint; 10 universal / 6 protocols / 24 specialisms built
  • npm run test:unit + npm run typecheck (precommit) — 631 tests pass
  • Reviewer: confirm the schema-YAML copy reads cleanly and branch_set.semantics: any_of is the right v1-only value (vs. leaving semantics open)

Sequel: #2634 contradiction lint, now trivially keyed off branch_set: rather than heuristic detection.

🤖 Generated with Claude Code

Phases in an any_of branch set previously declared membership implicitly by
sharing a contributes_to flag value that matched a later assert_contribution
step's any_of target. A typo in contributes_to silently broke membership and
made a conformant agent on the correct branch look like it failed everywhere.

Adds an optional branch_set: { id, semantics } object on phases and a new
lint-storyboard-branch-sets script that enforces:

  1. Branch-set phases are optional: true.
  2. branch_set.semantics is a supported value (any_of today).
  3. Peer phases sharing branch_set.id share semantics.
  4. Every declared branch_set.id has a matching assert_contribution any_of.
  5. contributes_to inside a branch-set phase equals branch_set.id.

Implicit detection is preserved as a fallback so pre-#2633 storyboards keep
working. Migrates schema-validation.yaml (past_start_handled) and
security.yaml (auth_mechanism_verified) to the explicit form. Sets up #2634
contradiction lint to key off an explicit declaration rather than reverse-
engineering topology.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Expert review on PR #2646 converged on five must-fix items.

1. Lint rule `peer_not_declared`: a mixed-mode storyboard (one peer declares
   branch_set: explicitly, a sibling optional peer still relies on implicit
   contributes_to for the same flag) would be graded by a runner preferring
   the explicit declaration as a single-member set — the legacy peer's
   failing steps would surface as `failed` instead of `peer_branch_taken`.
   New rule: if any phase declares branch_set:{id:X}, every optional phase
   whose steps contribute to X must also declare branch_set:{id:X}. Storyboards
   in pure-implicit mode (no branch_set: anywhere) are unaffected.

2. Lint refactor: export lintDoc(doc, { supportedSemantics }) and tag every
   violation with a stable `rule` field. Tests assert on rule IDs instead of
   message prose, and the semantics-conflict test injects a synthetic
   supported set rather than monkey-patching module state.

3. Doc rewrite for storyboard-schema.yaml "Branch sets":
   - Minimal YAML example moved ahead of the constraint list.
   - New storyboards MUST declare branch_set: (was SHOULD); the implicit
     fallback is explicitly DEPRECATED for new work.
   - `at_least_n`/`all_of` reserved-name sentence dropped — a documented
     value the lint rejects is a footgun.
   - contributes_to: on contributing steps is now called out as still
     REQUIRED; the phase-level branch_set: does not replace per-step
     contribution signalling.
   - Terminology tightened: "aggregation flag" dropped in favor of
     branch_set.id throughout.
   - New "When to use a branch set" note: only when the spec permits
     multiple conformant behaviors, not for optional features or progressive
     disclosure.

4. runner-output-contract.yaml peer_branch_taken cross-reference updated to
   point at the new "Branch sets" heading (the old "Per-step grading in
   any_of branch patterns" heading no longer exists after this PR). Detail
   shape updated to reference branch_set.id rather than the legacy
   "aggregation flag" term.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant