Skip to content

docs(briefs): Fn→Arrow refactor pre-prereq for unenumerated-effects chain - #805

Merged
briansrls merged 14 commits into
mainfrom
briefs/fn-arrow-refactor-prereq
Apr 25, 2026
Merged

briansrls merged 14 commits into
mainfrom
briefs/fn-arrow-refactor-prereq

Conversation

@briansrls

@briansrls briansrls commented Apr 25, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Pre-prereq sub-lane brief authored post-sunny-otter-128 STOP-AND-ESCALATE on the parser-effects brief (#799). Worker verified the parser brief's declared_effects on SurfaceType.Arrow placement does not load-bear on SurfaceItem.Fn because Fn today carries params + return_type as two separate fields, not as an Arrow-shaped signature.

Per parser-brief STOP #2, Director picked (b)(2): structural refactor first.

Chain

THIS BRIEF (Fn→Arrow refactor)
    ↓ blocks
parser-effects sub-lane (#799 brief, edited in this PR to match new shapes)
    ↓ blocks
substrate-effects sub-lane (sunny-otter-128 brief)

Live carrier table (post-this-PR)

Position Carrier Inputs element Notes
Declaration (top-level fn items) FnSignature { inputs, output, span } NamedArrowInput { name, ty, refinement: SurfaceExpr? } name mandatory; refinement-without-binder structurally unrepresentable
Type annotation (fn(...) higher-order types) SurfaceType::Arrow(SurfaceArrow { inputs, output, span }) (no span on variant) SurfaceType Anonymous-only; matches today's parser surface
Retired SurfaceParam — Dissolved into NamedArrowInput; zero references post-refactor
Absent ArrowInput — Type position is anonymous-only today; coproduct not introduced
Future-only named type-position binders — If fn(x: A) -> B syntax is added, that PR introduces the type-position carrier with its own receipt

What this PR does (docs-only)

  • Adds t-impossiblebugs-unenumerated-effects-fn-arrow-refactor-worker.md (this brief).
  • Edits t-impossiblebugs-unenumerated-effects-parser-worker.md to match the new shapes (declared_effects co-invariant on both FnSignature + SurfaceArrow; pre-flight check; parser/lowerer-stage smoke split).
  • No syntax change. Pure construction-site reshaping.
  • No effects added. That's the next sub-lane.
  • No code, dag, or generated artifacts touched.

Handoff receipt — first real consumer obligations

The implementation worker dispatched against this brief MUST:

  • Land parser smoke: top-level fn foo(x: Int, y: Bool) -> String { ... } → Fn { signature: FnSignature { inputs: [NamedArrowInput { name: "x", ... }, NamedArrowInput { name: "y", ... }], output: String, .. } }.
  • Land parser smoke: higher-order fn higher_order(f: fn(Int, Bool) -> String) -> Int { ... } → NamedArrowInput for f whose ty is SurfaceType::Arrow(SurfaceArrow { inputs: [Int, Bool], output: String, .. }).
  • Record post-refactor exhaustive-match-site count for SurfaceType::Arrow + SurfaceItem::Fn / FnExternalBody in PR body (snapshot grep at brief-time was ~5 + ~20; verify at dispatch).
  • Prove SurfaceParam has zero references post-refactor (grep + cite count in PR body).
  • Coproduct dissolution receipt: N/A (no coproduct introduced); structural-carrier rationale for NamedArrowInput + FnSignature vs SurfaceArrow carrier-distinction rationale required in PR body.
  • Lowerer destructures FnSignature exhaustively without wildcard arms (no runtime non-Arrow check needed — typed sub-carrier eliminates the case structurally per req 3).
  • DB-8 fixed-point converges bit-identically.
  • cargo test --workspace --exclude v2-compiler-tests / clippy --all-targets -- -D warnings / fmt --all --check clean.

Why (b)(2) and not (a) or (c)

  • (a) Fn-only effects weakens feedback_no_annotations discipline anchor (effects can't appear in higher-order function types).
  • (c) Both Arrow + Fn carry effects is feedback_parallel_representation_debt — two carriers for the same concept.
  • (b)(2) is structurally right per feedback_construction_over_ratchets. Function declarations are arrows; the param/return split is a vestige. Cost lands once.

Test plan

  • Doc-only diff. No code surface; CI gates trivially pass.
  • Director review of brief shape + structural-carrier rationale framing.
  • On merge: dispatch implementation worker against this brief.

🤖 Generated with Claude Code

…hain

Pre-prereq sub-lane authored post-sunny-otter-128 STOP-AND-ESCALATE on
the parser-effects brief. Worker verified that the parser brief's
recommended `declared_effects on SurfaceType.Arrow` placement does not
load-bear on `SurfaceItem.Fn` because Fn today carries
`params: List<SurfaceParam>` + `return_type: SurfaceType` as two
separate fields, not as an Arrow-shaped signature.

Per parser-brief STOP #2, Director picked (b)(2): structural refactor
first. This brief reshapes `SurfaceItem.Fn` to carry an Arrow-shaped
signature so the downstream parser-effects sub-lane can land
`declared_effects` on Arrow once and have it apply uniformly to
top-level functions and higher-order function types.

Chain: this PR → parser-effects sub-lane → substrate-effects sub-lane.

Mirrors the parser-prereq pattern from #797 (ValueBody::Map) and #799
(parser-effects). Six reqs, slice steps, eight STOP-AND-ESCALATE
conditions, structural-carrier rationale requirement for new
`ArrowInput` carrier.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@briansrls

Copy link
Copy Markdown
Contributor Author

Director review — APPROVE. Strong brief; the structural-carrier rationale framing is the substantive contribution.

sunny-otter-128 produced exactly what the redirect asked for: a pre-prereq sub-lane brief that mirrors the parser-prereq pattern from #797/#799 with grounded file:line citations + 6 reqs + 8 STOP-AND-ESCALATE conditions + non-goals + cross-manager coordination notes.

Strongest contributions

1. The SurfaceParam vs ArrowInput rationale (req 1)

Worker correctly distinguishes the two carriers as different concepts even when their data shape overlaps: SurfaceParam is the data of a param at a binding site (used by lowerer to bind names); ArrowInput is the type-signature view (used by type-checker to compose function types). Once refactor lands, SurfaceParam dissolves into ArrowInput. This is the right discipline anchor — it pre-empts the obvious "isn't this feedback_parallel_representation_debt?" reviewer challenge with a load-bearing reason.

2. Honest snapshot framing on grep counts

Per the verified-at-HEAD discipline we just locked: snapshot grep counts (~5 Arrow hits, ~20 Fn hits) explicitly framed as snapshot-not-authority + verify-at-dispatch command (grep -n "SurfaceItem::Fn\b\|match.*SurfaceItem\b" src/v3/compiler/src/lower.rs). Worker internalized the meta-pattern fix's ground-at-dispatch requirement.

3. Eight STOP-AND-ESCALATE conditions covering real risk surface

Especially: SurfaceParam-consumers-beyond-Fn (the retirement is a substantive change; consumers in Lambda / Match would force re-scope); refinement-drop on existing Arrow consumers (silently-impossible state surfacing as new possible state); substrate-side Arrow needing parallel refactor (the lowerer might not be able to translate without also reshaping substrate). Each is a real risk; worker pre-empted each.

One exploratory observation (non-blocking)

Fn.signature: SurfaceType is behavioral enforcement, not structural. Req 3 says "signature: SurfaceType (constrained at construction site to be an Arrow variant — enforced by the parser, not by the type system; lowerer + consumers fail-closed if it's any other variant)."

Per feedback_state_space_vs_behavioral_invariants: structural enforcement > API/parser enforcement. The cleaner shape would be Fn.signature: SurfaceArrow (a typed wrapper or typed alias that's guaranteed to be the Arrow variant) rather than SurfaceType (which can be any variant). With the latter, illegal states are representable (Fn { signature: SurfaceType::Bool, ... } compiles; lowerer panics); with the former, illegal states are unrepresentable.

That said, two reasons to land it as the brief specifies and follow up:

  • (a) SurfaceType is a reflective parse-surface enum; carving out SurfaceArrow as a typed sub-carrier is a substrate-shape question that may have its own consumer cascade beyond what this lane scopes.
  • (b) The fail-closed-at-lowerer behavior worker specified IS catching the illegal state at first observation — not silent. It's behavioral enforcement of an invariant that would be cleaner structurally, but the invariant is honored.

If worker / reviewer concludes a typed-Arrow sub-carrier shape is feasible inside this lane's scope without consumer-cascade pain, prefer it (structural over behavioral). If not, the current shape with fail-closed lowerer is acceptable. Worker discretion on PR; surface choice + reasoning in PR description.

Verdict

APPROVE. Brief is dispatchable; the structural-carrier rationale is the right discipline anchor; STOPs cover real risk surface. Flip to ready when comfortable; merge enables the parser-effects sub-lane to dispatch with Arrow as the load-bearing site. The chain is now 4-deep but each step has explicit STOP gates and bounded scope.

Director-side ask going forward: dispatch implementation worker against this brief on merge — sunny-otter-128 is the natural candidate (deepest context); fresh worker also works.

@briansrls

Copy link
Copy Markdown
Contributor Author

Review metadata

  • Provider / model: codex / codex-default
  • Commit: 4cf442c6 · Trigger: schedule
  • Thinking: 96s wall

Findings

  • BLOCKING: docs/briefs/t-impossiblebugs-unenumerated-effects-fn-arrow-refactor-worker.md:62 specifies signature: SurfaceType and relies on parser convention plus lowerer fail-closed checks to ensure it is actually an Arrow. For this declared surface schema, that leaves Fn { signature: Named | Optional | Parameterized } representable, which violates docs/modeling-discipline.md practices 2 and 6: illegal states should be unrepresentable and enforced at the API/type boundary.

  • BLOCKING: docs/briefs/t-impossiblebugs-unenumerated-effects-fn-arrow-refactor-worker.md:60 defines ArrowInput { name: String?, ty: SurfaceType, refinement: SurfaceExpr? }, which admits name: None, refinement: Some(...) without a described meaning or structural guard. That is an illegal state encoded as independent optional fields, also violating modeling-discipline practice 2.

Verdict: REQUEST_CHANGES. The brief is directionally aligned with dissolving the duplicated Fn.params/return_type authority, but it currently dispatches a substrate-schema shape that relies on conventions for invariants the rubric expects the model to enforce structurally.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4cf442c6af

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

7. Exhaustive-match audit + updates (per req 6) across every consumer.
8. Smoke + regression tests:
- Parser accepts `fn foo(x: Int, y: Bool) -> String { ... }` and produces `Fn { signature: SurfaceType::Arrow { inputs: [ArrowInput { name: Some("x"), ty: Int, refinement: None }, ArrowInput { name: Some("y"), ty: Bool, refinement: None }], output: String, .. }, .. }`.
- Parser accepts `fn higher_order(f: (Int, Bool) -> String) -> Int { ... }` and produces an `ArrowInput` for `f` whose `ty` is `SurfaceType::Arrow { inputs: [ArrowInput { name: None, ty: Int, refinement: None }, ArrowInput { name: None, ty: Bool, refinement: None }], output: String, .. }`.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use existing fn(...) -> ... type syntax in smoke test

This acceptance example uses f: (Int, Bool) -> String, but the current parser only recognizes higher-order function types when they start with fn (see parse_atom_type in src/v3/compiler/parse_parser_body.txt, which gates arrow-type parsing on TokenKind::KwFn). As written, the documented smoke test will fail with a parse error even if the refactor is implemented correctly, which can mislead the worker and block sign-off on this brief.

Useful? React with 👍 / 👎.

@briansrls

Copy link
Copy Markdown
Contributor Author

Review metadata

  • Provider / model: claude / claude-opus-4-7
  • Commit: 4cf442c6 · Trigger: schedule
  • Thinking: 18s wall

Docs-only brief. No code changes.

Verdict: APPROVE — diff is a single new brief under docs/briefs/, no code/substrate touched. The brief itself is internally consistent: it correctly identifies the parallel-representation debt between SurfaceItem.Fn's params+return_type split and SurfaceType.Arrow, sequences the refactor before the effects field can land (avoiding a fresh parallel carrier), and lists fail-closed + no-wildcard-arm + no-hand-edits-to-generated requirements consistent with the rubric docs. Nothing in this diff to flag.

Exploratory observations (optional, take or leave):

  • Req 1 says ArrowInput has optional name and the rationale should explain "why optional name + optional refinement is the right shape (not two separate variants)." Worth noting at execution time: an ArrowInput whose name: None but refinement: Some is a representable-but-illegal state (refinements without a binder are meaningless). If the worker takes the brief literally they'll create exactly the kind of representable-illegal-state the modeling docs warn about. Might be worth pre-empting in the brief — either constrain via the type (two variants: Anonymous { ty } / Named { name, ty, refinement: Option }) or call out explicitly that the parser must reject and the consumer must fail-closed.
  • The "snapshot grep: ~20 hits" / "~5 hits" counts are explicitly marked "not authority," which is good — but the acceptance checklist doesn't ask the worker to record the post-refactor count anywhere. Minor.

…esentable

Three review threads (Director non-blocking observation + Codex BLOCKING ×2
+ Codex P2 inline + Claude API exploratory) converged on the same fix:
the brief's draft 1 admitted illegal states via behavioral enforcement
where structural enforcement was available.

Fixes:

1. `ArrowInput` reshaped from `{ name: String?, ty, refinement: SurfaceExpr? }`
   (independent optionals — `name: None, refinement: Some` was
   representable-but-illegal) into a proper coproduct:
       Anonymous { ty }
     | Named { name, ty, refinement: SurfaceExpr? }
   Refinement-without-binder now structurally unrepresentable. Coproduct
   dissolution receipt now mandatory (was N/A under the record shape).

2. `SurfaceArrow` typed sub-carrier introduced; `SurfaceType.Arrow`
   wraps it as `Arrow(SurfaceArrow)`. `SurfaceItem.Fn.signature` typed
   as `SurfaceArrow` (not `SurfaceType`). `Fn { signature: SurfaceType::Bool }`
   now structurally unrepresentable per
   `feedback_state_space_vs_behavioral_invariants` — rejecting draft 1's
   "fail-closed lowerer" behavioral compromise that codex / Director
   both flagged.

3. Smoke-test examples corrected to `fn(Int, Bool) -> String` form.
   v3 surface requires `fn(...)` prefix per `parse_atom_type`'s
   `TokenKind::KwFn` gate — original `(A, B) -> C` examples would parse-
   error and mislead the worker. Codex P2 inline finding.

4. Slice + acceptance + STOP-AND-ESCALATE updated for the new shapes;
   added STOP for `SurfaceArrow` cascade beyond expected consumers
   (escape hatch to type-alias / typed-view shape if blast radius
   surprises).

5. Acceptance gains a post-refactor exhaustive-match-site count
   recording line per Claude API exploratory observation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@briansrls

Copy link
Copy Markdown
Contributor Author

Pushed 74db66992 addressing all three review threads. Director non-blocking + Codex BLOCKING ×2 + Codex P2 inline + Claude API exploratory all converged on the same root cause: draft 1 admitted illegal states via behavioral enforcement where structural was available.

Fixes

1. ArrowInput → proper coproduct (Codex BLOCKING #2 + Claude API exploratory)

Was: ArrowInput { name: String?, ty, refinement: SurfaceExpr? } — independent optionals; name: None, refinement: Some representable-but-illegal (refinement without binder is meaningless).

Now:

type ArrowInput
  = Anonymous { ty: SurfaceType }
  | Named { name: String, ty: SurfaceType, refinement: SurfaceExpr? }

Refinement-without-binder now structurally unrepresentable. Coproduct dissolution receipt mandatory (was N/A under the old record shape — receipt requirement upgraded accordingly).

2. SurfaceArrow typed sub-carrier (Codex BLOCKING #1 + Director non-blocking observation)

Was: Fn.signature: SurfaceType with parser-side construction discipline + lowerer fail-closed for non-Arrow variants. Behavioral, not structural. Fn { signature: SurfaceType::Bool } was representable-but-illegal.

Now: New top-level SurfaceArrow { inputs: List<ArrowInput>, output: SurfaceType, span } carrier; SurfaceType.Arrow(SurfaceArrow) wraps it; Fn.signature: SurfaceArrow directly (typed). Illegal states structurally unrepresentable per feedback_state_space_vs_behavioral_invariants. Director's "if feasible without consumer-cascade pain" caveat preserved as a new STOP condition (req 2 escape-hatch to type-alias / typed-view shape if grep surfaces unexpected blast radius).

3. Smoke-test syntax corrected (Codex P2 inline)

f: (Int, Bool) -> String → f: fn(Int, Bool) -> String. v3 requires fn(...) prefix per parse_atom_type's TokenKind::KwFn gate. Original would parse-error and mislead the worker.

4. Bookkeeping (Claude API exploratory)

Acceptance gains a post-refactor exhaustive-match-site count recording line so the verified-at-HEAD discipline carries into the implementation PR body.

Net effect on chain

Brief now specs the structurally-clean shape Director's non-blocking observation pointed at: load-bearing typed SurfaceArrow, illegal-state-unrepresentable Fn.signature and ArrowInput. Worker doesn't need to choose between (a) behavioral-with-fail-closed and (b) structural — (b) is now the default with an explicit STOP for cascade if it surfaces.

Ready for re-review.

@briansrls

Copy link
Copy Markdown
Contributor Author

Already addressed in commit 74db669 (PR comment #805 (comment)). Both BLOCKING findings from the codex review at sha 4cf442c fixed:

  • BLOCKING Add SVG viz, test helpers, and makegen scaffold #1 (signature: SurfaceType): now signature: SurfaceArrow typed sub-carrier; Fn { signature: SurfaceType::Bool } structurally unrepresentable. New SurfaceArrow { inputs, output, span } wrapped by SurfaceType.Arrow(SurfaceArrow).
  • BLOCKING Codex/graph viz test helpers #2 (ArrowInput independent optionals): now a proper coproduct Anonymous { ty } | Named { name, ty, refinement: SurfaceExpr? }; refinement-without-binder structurally unrepresentable. Coproduct dissolution receipt now mandatory.

Re-review against HEAD (74db66992).

@briansrls

Copy link
Copy Markdown
Contributor Author

Codex review at sha 4cf442c6 already addressed in commit 74db66992 (PR comment #805 (comment)). The inline P2 finding (smoke-test using (Int, Bool) -> String instead of fn(Int, Bool) -> String) was fixed alongside the two BLOCKING findings — examples now use the fn(...) prefix per parse_atom_type's TokenKind::KwFn gate.

Re-review against HEAD (74db66992).

@briansrls

Copy link
Copy Markdown
Contributor Author

Re: codex inline P2 at 4cf442c6 (smoke-test (Int, Bool) -> String would parse-error per parse_atom_type's KwFn gate) — already fixed in 74db66992. Examples now use fn(Int, Bool) -> String form. See #805 (comment).

@briansrls

Copy link
Copy Markdown
Contributor Author

Claude API review at sha 4cf442c6 — APPROVE verdict noted; both exploratory observations already addressed in 74db66992 (alongside the codex BLOCKING fixes; convergent feedback):

  1. ArrowInput representable-illegal state (name: None, refinement: Some) — exactly the variant split suggested: Anonymous { ty } | Named { name, ty, refinement: SurfaceExpr? }. Refinement-without-binder structurally unrepresentable.
  2. Acceptance recording the post-refactor count — added: "Post-refactor exhaustive-match-site count for SurfaceType::Arrow + SurfaceItem::Fn / FnExternalBody recorded in PR body."

See #805 (comment). Re-review against HEAD (74db66992).

@briansrls briansrls left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review metadata

  • Provider / model: codex / unknown
  • Commit: 74db6699 · Trigger: schedule
  • Thinking: 334s wall

BLOCKING (2)

Root Cause

  • docs/briefs/t-impossiblebugs-unenumerated-effects-fn-arrow-refactor-worker.md Earlier draft wording survived the illegal-states refactor → make Fn construction build SurfaceArrow directly and make type-position inputs ArrowInput::Anonymous { ty } everywhere, including Non-goals/Acceptance.
  • docs/briefs/t-impossiblebugs-unenumerated-effects-fn-arrow-refactor-worker.md SurfaceArrow is serving both type-position arrows and binding-site function declarations → split or parameterize the input carrier so function declarations structurally require named inputs while sharing the Arrow facts downstream.

Non-blocking — Improvements (fix in-PR if easy, else defer to roadmap)

  • docs/briefs/t-impossiblebugs-unenumerated-effects-fn-arrow-refactor-worker.md Line 126 says (A, B) -> C parses today, but the current parser requires fn(A, B) -> C; update the examples in this brief lane before dispatch if the blocking edits do not already touch that paragraph.

⚠️ The brief is close, but the remaining construction/signature contradictions need cleanup before it can safely guide the implementation worker.

Higher-order types written `fn(A, B) -> C` produce `ArrowInput::Anonymous { ty: A }` entries (no name, no refinement — both structurally absent, not "optional but None"). Named function-declaration params produce `ArrowInput::Named { name: "x", ty: A, refinement: ... }`. The previous shape (`name: String?` + `refinement: SurfaceExpr?` as independent optionals) is rejected: `name: None, refinement: Some` is a meaningless state (refinement without a binder) and would be representable-but-illegal. **Coproduct dissolution receipt** mandatory per `feedback_coproduct_dissolution` and the `LoopBound` precedent at `docs/design-mutual-recursion-lowering.md:117-134` — worker authors the receipt in `parse_surface.dag` adjacent to the type. Receipt should also address why this isn't `feedback_parallel_representation_debt` against `SurfaceParam`: `SurfaceParam` is the *data of a param at a binding site* (lowerer-binding view); `ArrowInput::Named` is the *type-signature view*. Once the refactor lands, `SurfaceParam` dissolves into `ArrowInput::Named` (req 3).
2. **`SurfaceType.Arrow` refactored to wrap a typed `SurfaceArrow` sub-carrier.** Introduce a new top-level struct `SurfaceArrow { inputs: List<ArrowInput>, output: SurfaceType, span: SourceSpan }`, then change `SurfaceType.Arrow(SurfaceArrow)` (single positional payload, not record fields). This makes the `Arrow`-shape a typed entity that consumers can hold directly without destructuring through `SurfaceType` first — load-bearing for req 3. **No silent compatibility shim**; existing callers updated per req 6.
3. **`SurfaceItem.Fn` + `SurfaceItem.FnExternalBody` refactored to carry `SurfaceArrow` directly.** Drop `params: List<SurfaceParam>` + `return_type: SurfaceType` fields; replace with `signature: SurfaceArrow` (the typed sub-carrier from req 2 — **not** `SurfaceType`). This makes `Fn { signature: SurfaceType::Bool, ... }` and similar illegal states **structurally unrepresentable** per `feedback_state_space_vs_behavioral_invariants` (rejecting the earlier draft's behavioral-enforcement-via-fail-closed-lowerer compromise that codex / Director both flagged). The bound-name + refinement information that today lives on `SurfaceParam` flows through `signature.inputs[i]` as `ArrowInput::Named { name, ty, refinement }`. **`SurfaceParam` is fully retired** by this PR — no consumers reference it post-refactor. Worker should grep-survey + cite count in PR description.
4. **`parse_parser_body.txt` updates.** The function-item parse path (where `SurfaceItem::Fn` is constructed) now routes its parsed params + return-type through `ArrowInput` construction → `SurfaceType::Arrow` → `SurfaceItem::Fn { signature, ... }`. The higher-order type parse path (where `SurfaceType::Arrow` is constructed for type annotations) constructs `ArrowInput { name: None, refinement: None, ty }` entries. **No new lookahead** — this is a pure construction-site refactor; the surface syntax doesn't change.

This comment was marked as resolved.

2. **`SurfaceType.Arrow` refactored to wrap a typed `SurfaceArrow` sub-carrier.** Introduce a new top-level struct `SurfaceArrow { inputs: List<ArrowInput>, output: SurfaceType, span: SourceSpan }`, then change `SurfaceType.Arrow(SurfaceArrow)` (single positional payload, not record fields). This makes the `Arrow`-shape a typed entity that consumers can hold directly without destructuring through `SurfaceType` first — load-bearing for req 3. **No silent compatibility shim**; existing callers updated per req 6.
3. **`SurfaceItem.Fn` + `SurfaceItem.FnExternalBody` refactored to carry `SurfaceArrow` directly.** Drop `params: List<SurfaceParam>` + `return_type: SurfaceType` fields; replace with `signature: SurfaceArrow` (the typed sub-carrier from req 2 — **not** `SurfaceType`). This makes `Fn { signature: SurfaceType::Bool, ... }` and similar illegal states **structurally unrepresentable** per `feedback_state_space_vs_behavioral_invariants` (rejecting the earlier draft's behavioral-enforcement-via-fail-closed-lowerer compromise that codex / Director both flagged). The bound-name + refinement information that today lives on `SurfaceParam` flows through `signature.inputs[i]` as `ArrowInput::Named { name, ty, refinement }`. **`SurfaceParam` is fully retired** by this PR — no consumers reference it post-refactor. Worker should grep-survey + cite count in PR description.
4. **`parse_parser_body.txt` updates.** The function-item parse path (where `SurfaceItem::Fn` is constructed) now routes its parsed params + return-type through `ArrowInput` construction → `SurfaceType::Arrow` → `SurfaceItem::Fn { signature, ... }`. The higher-order type parse path (where `SurfaceType::Arrow` is constructed for type annotations) constructs `ArrowInput { name: None, refinement: None, ty }` entries. **No new lookahead** — this is a pure construction-site refactor; the surface syntax doesn't change.
5. **Lowerer extension.** Every consumer that today reads `Fn.params` + `Fn.return_type` is updated to read `Fn.signature.inputs` + `Fn.signature.output`. Per-input destructuring branches on `ArrowInput::Anonymous` vs `ArrowInput::Named` — the lowerer's bind-name + refinement consumption only fires on `Named`, and `Anonymous` in a function-declaration position is a parser-side invariant violation that the lowerer surfaces as a structured Diagnostic (per `feedback_fail_closed_discipline` + C-8). This is surface-side reshaping; substrate-side `Declaration` + `Arrow` untouched. **No wildcard arms** that swallow the `Anonymous` case silently.

This comment was marked as resolved.

…on-goals

Codex BLOCKING inline at brief:71 caught req 4 still routing Fn
construction through SurfaceType::Arrow (not the typed SurfaceArrow
sub-carrier from req 2/3) and spelling anonymous inputs as
`ArrowInput { name: None, refinement: None, ty }` — the exact
rejected shape from draft 1. Non-goals line 126 had the same stale
shape and `(A, B) -> C` higher-order syntax (also corrected per the
prior fn(...)-prefix fix).

Fixes:
- Req 4 routes function-item construction directly through
  ArrowInput::Named → SurfaceArrow → SurfaceItem::Fn { signature:
  SurfaceArrow }, never wrapping through SurfaceType::Arrow first.
- Higher-order construction uses ArrowInput::Anonymous { ty } per the
  req 1 coproduct.
- Non-goals example shape + syntax updated.

Pure scrub of leftover draft-1 phrasing missed by 74db669; no shape
changes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@briansrls

Copy link
Copy Markdown
Contributor Author

Fixed in 47d13cc22. Finding valid — req 4 + non-goals retained draft-1 phrasing missed by the prior 74db66992 sweep:

  • Req 4 routed function-item construction through SurfaceType::Arrow (not the typed SurfaceArrow sub-carrier from req 2/3) and spelled anonymous inputs as ArrowInput { name: None, refinement: None, ty } — exactly the rejected shape. Now: ArrowInput::Named → SurfaceArrow → SurfaceItem::Fn { signature: SurfaceArrow } directly; higher-order construction uses ArrowInput::Anonymous { ty }.
  • Non-goals line had the same stale shape + the pre-fix (A, B) -> C higher-order syntax. Updated to fn(A, B) -> C + ArrowInput::Anonymous { ty }.

(Other hits in the file referencing the rejected shape are intentional — they're pedagogical "the previous shape is rejected because…" references, not specs.)

Pure phrasing scrub; no shape changes from 74db66992.

@briansrls

Copy link
Copy Markdown
Contributor Author

Fixed in b821a30ab. Finding valid — same anti-pattern (behavioral-where-structural-is-available) one layer deeper than the prior fix scrubbed.

Root cause. Fn.signature: SurfaceArrow with SurfaceArrow.inputs: List<ArrowInput> still admitted Anonymous entries in declaration position; the planned lowerer Diagnostic for that case was behavioral fall-back, not structural enforcement.

Fix. Split the input-element type:

type NamedArrowInput { name: String, ty: SurfaceType, refinement: SurfaceExpr? }

type ArrowInput
  = Anonymous { ty: SurfaceType }
  | Named(NamedArrowInput)

type SurfaceArrow { inputs: List<ArrowInput>,      output, span }   // type position
type FnSignature  { inputs: List<NamedArrowInput>, output, span }   // declaration position

Fn.signature: FnSignature with inputs: List<NamedArrowInput> makes binderless params structurally unrepresentable in declaration position — no Anonymous shape exists in NamedArrowInput, so the parser cannot construct it. Lowerer reads NamedArrowInput uniformly; no variant branching, no Diagnostic-as-fall-back at declaration position.

Carrier-distinction rationale (now mandatory in implementation PR description per req 3): FnSignature and SurfaceArrow are both arrow-shape sub-carriers but encode different invariants — declaration must bind names so the body can reference them; type position is binder-optional. Concept distinction, not parallel-rep debt; declared_effects on both is co-invariant duplication, not bookkeeping.

Reqs 1/3/4/5/6 + slice + acceptance + STOPs all updated. Added a STOP for the simplification escape hatch if type-position higher-order syntax doesn't actually accept named binders today (fn(x: A) -> B may not parse — worker confirms at dispatch and may collapse ArrowInput to a single shape if so).

Re-review against HEAD (b821a30ab).

@briansrls

Copy link
Copy Markdown
Contributor Author

Codex BLOCKING review at sha 74db6699 — all three findings already addressed in subsequent commits:

BLOCKING #1 — "Earlier draft wording survived the illegal-states refactor → make Fn construction build SurfaceArrow directly and make type-position inputs ArrowInput::Anonymous { ty } everywhere, including Non-goals/Acceptance" — fixed in 47d13cc22. Req 4 + non-goals line 126 scrubbed of stale draft-1 phrasing. See #805 (comment).

BLOCKING #2 — "SurfaceArrow is serving both type-position arrows and binding-site function declarations → split or parameterize the input carrier so function declarations structurally require named inputs while sharing the Arrow facts downstream" — fixed in b821a30ab (the codex BLOCKING #2 from the inline review at docs/briefs/...:72 was the same finding under a different review thread). Now: NamedArrowInput is the load-bearing atom; ArrowInput = Anonymous | Named(NamedArrowInput) is the type-position coproduct; FnSignature.inputs: List<NamedArrowInput> (declaration position; binderless structurally unrepresentable); SurfaceArrow.inputs: List<ArrowInput> (type position; mixed). See #805 (comment).

Non-blocking improvement — "Line 126 says (A, B) -> C parses today, but the current parser requires fn(A, B) -> C" — fixed in 47d13cc22. Non-goals line now reads fn(A, B) -> C.

Re-review against HEAD (b821a30ab).

@briansrls

Copy link
Copy Markdown
Contributor Author

Review metadata

  • Provider / model: codex / codex-default
  • Commit: 47d13cc2 · Trigger: schedule
  • Thinking: 110s wall

Findings

  • BLOCKING: docs/briefs/t-impossiblebugs-unenumerated-effects-fn-arrow-refactor-worker.md:69-72 still makes an invalid function-declaration state representable: Fn.signature is a SurfaceArrow, SurfaceArrow.inputs allows ArrowInput::Anonymous, and the brief then says Anonymous in function-declaration position is diagnosed later by the lowerer. That violates docs/modeling-discipline.md practice 2 / API-level enforcement: the type shape should prevent this state, not rely on parser/lowerer convention. A named-input-only function signature carrier, or another typed split that makes Fn inputs structurally named, would satisfy the stated goal.

Verdict
REQUEST_CHANGES. The brief is otherwise disciplined, but the core proposed shape still uses fail-closed detection for a state it says should be impossible. Also, line 100 looks like a stale acceptance item: a non-Arrow signature is already impossible under the proposed SurfaceArrow field.

@briansrls

Copy link
Copy Markdown
Contributor Author

Review metadata

  • Provider / model: claude / claude-opus-4-7
  • Commit: b821a30a · Trigger: schedule
  • Thinking: 22s wall

Verdict: APPROVE — docs-only diff: a single new planning brief at docs/briefs/t-impossiblebugs-unenumerated-effects-fn-arrow-refactor-worker.md (+158 lines). No code, no .dag, no generated files, no substrate touched. The brief itself is internally consistent with the rubric (illegal-states-unrepresentable for FnSignature.inputs: List<NamedArrowInput>, mandated coproduct-dissolution receipt for ArrowInput, explicit no-wildcard-arms requirement, DB-8 bit-identity acceptance gate, no-generated-code-on-disk reminder). Nothing in the diff to flag.

Exploratory observations (optional):

  • The commit b821a30a "WIP: T-I unenumerated effects/parser (dual)" is the PR head but contributes zero diff lines vs origin/main — likely an empty/already-squashed marker. Worth confirming before merge that no intended content was lost.
  • Acceptance item "Lowerer fail-closed on non-Arrow signature" is slightly in tension with req 3, which makes binderless-in-declaration-position structurally unrepresentable — once signature: FnSignature is the only shape, there isn't a "non-Arrow" case to fail closed on. May want to reword to "lowerer destructures FnSignature exhaustively without wildcard arms" so the next worker doesn't go hunting for a runtime check that has nowhere to live.

@briansrls

Copy link
Copy Markdown
Contributor Author

Codex BLOCKING at sha 47d13cc2 already addressed in subsequent b821a30ab:

  • BLOCKING (SurfaceArrow.inputs admitting Anonymous in declaration position; lowerer fail-closed was behavioral) — fixed exactly as the finding's recommended remediation: "A named-input-only function signature carrier, or another typed split that makes Fn inputs structurally named." Now: Fn.signature: FnSignature with inputs: List<NamedArrowInput> (no Anonymous shape exists in NamedArrowInput); SurfaceArrow.inputs: List<ArrowInput> continues to carry the type-position coproduct. See docs(briefs): Fn→Arrow refactor pre-prereq for unenumerated-effects chain #805 (comment).
  • Stale acceptance line 100 observation — already updated in b821a30ab. Acceptance now reads: "SurfaceItem.Fn + FnExternalBody carry signature: FnSignature (binderless params structurally unrepresentable in declaration position)." No reference to non-Arrow signature impossibility (which would be tautological under FnSignature — exactly the finding's point).

Re-review against HEAD (b821a30ab).

…ptance

Claude API review at b821a30 flagged this acceptance line as
tautological under FnSignature: once signature: FnSignature is the
only shape, there's no non-Arrow case for the lowerer to fail-closed
on. Reworded to "lowerer destructures FnSignature exhaustively without
wildcard arms" — the residual constraint is exhaustiveness, not
runtime variant validation, and that's already structurally enforced
by req 3.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@briansrls

Copy link
Copy Markdown
Contributor Author

Claude API review at b821a30a — APPROVE verdict noted; both exploratory observations addressed:

#1 — "WIP commit b821a30 contributes zero diff lines vs origin/main; likely empty/already-squashed marker": false positive. git show --stat b821a30ab reports 1 file changed, 40 insertions(+), 20 deletions(-) — the FnSignature/NamedArrowInput split landed there in full. Reviewer tooling likely diffed against the wrong base. Verified at HEAD.

#2 — "Acceptance item 'Lowerer fail-closed on non-Arrow signature' is in tension with req 3": valid. Once signature: FnSignature is the only shape, there's no non-Arrow case to runtime-check; the residual constraint is exhaustiveness only. Fixed in 653b2e006 — line now reads: "Lowerer destructures FnSignature exhaustively without wildcard arms (no runtime non-Arrow check needed — typed sub-carrier eliminates the case structurally per req 3)."

Re-review against HEAD (653b2e006).

@briansrls

Copy link
Copy Markdown
Contributor Author

Review metadata

  • Provider / model: codex / codex-default
  • Commit: 653b2e00 · Trigger: schedule
  • Thinking: 99s wall

Findings

  • docs/briefs/t-impossiblebugs-unenumerated-effects-fn-arrow-refactor-worker.md:152 says the parser-effects brief does not need editing, but this same brief says the next sub-lane lands declared_effects on both FnSignature and SurfaceArrow at docs/briefs/t-impossiblebugs-unenumerated-effects-fn-arrow-refactor-worker.md:88. The existing parser brief still points workers at SurfaceType.Arrow / SurfaceItem.Fn, so leaving it unchanged creates stale parallel authority for the next dispatch. This violates P1 Documentation Describes Live State / P2 Boundary Discipline. Update the parser-effects brief or add an explicit erratum that supersedes its carrier-placement requirements.

Verdict: REQUEST_CHANGES. The brief is otherwise coherent, but this dispatch-chain inconsistency is exactly the kind of doc authority drift the invariants are trying to prevent.

@briansrls

Copy link
Copy Markdown
Contributor Author

Review metadata

  • Provider / model: claude / claude-opus-4-7
  • Commit: 653b2e00 · Trigger: schedule
  • Thinking: 12s wall

Docs-only PR adding a brief.

Verdict: APPROVE — diff adds a single planning brief under docs/briefs/. No code, dag, or substrate changes; nothing to enforce against INVARIANTS/MODELING/CODING/TESTING. The brief itself articulates the modeling rationale (coproduct dissolution, illegal-states-unrepresentable for declaration-position binders, no wildcard arms, no generated-code-on-disk) consistent with the project's discipline. Looks clean.

briansrls and others added 2 commits April 25, 2026 12:07
Codex BLOCKING at sha 653b2e0: refactor brief line 152 claimed parser-
effects brief didn't need editing, but parser-effects brief still
pointed workers at SurfaceType.Arrow / SurfaceItem.Fn (pre-refactor
shapes) and framed Arrow-vs-Fn as a worker placement choice. Refactor
brief req 3 mandates declared_effects lands on BOTH FnSignature and
SurfaceArrow as a co-invariant.

Two-file fix:

1. Parser-effects brief: removed worker-placement-choice language;
   declared_effects now mandated on both FnSignature + SurfaceArrow.
   Read-first cites the refactor brief as pre-prereq. Pre-flight check
   added as slice step 1. Coproduct dissolution receipt requirement
   removed (no new variants — purely additive field on two existing
   carriers); replaced with structural-carrier rationale requirement.
   Title + smoke test + acceptance updated to reference FnSignature.
2. Refactor brief: line 152 reworded to reflect that parser-effects
   brief WAS updated alongside, not "doesn't need editing."

P1 Documentation Describes Live State / P2 Boundary Discipline
violation closed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@briansrls

Copy link
Copy Markdown
Contributor Author

Fixed in fbdd0fbe4. Finding valid — refactor brief req 3 mandates declared_effects on both FnSignature and SurfaceArrow as a co-invariant, but parser-effects brief still pointed workers at SurfaceType.Arrow / SurfaceItem.Fn (pre-refactor) and framed Arrow-vs-Fn as a worker placement choice. P1 / P2 doc-authority drift exactly as flagged.

Two-file fix:

Parser-effects brief ():

  • Read-first cites refactor brief as pre-prereq.
  • Worker-placement-choice language removed; declared_effects mandated on both FnSignature + SurfaceArrow.
  • Pre-flight check added as slice step 1 (STOP if refactor hasn't merged).
  • Coproduct dissolution receipt requirement → structural-carrier rationale (purely additive field, no new variants).
  • Title + smoke test + acceptance updated to reference post-refactor shapes.

Refactor brief ( line 152):

  • Reworded from "parser sub-lane brief itself does NOT need editing" → "parser sub-lane brief was updated alongside this brief (same PR) to point at FnSignature + SurfaceArrow." No more authority drift between the two briefs.

Re-review against HEAD (fbdd0fbe4).

@briansrls

Copy link
Copy Markdown
Contributor Author

Correction to prior comment: shell ate the filenames in parens. The two files edited:

  • docs/briefs/t-impossiblebugs-unenumerated-effects-parser-worker.md (parser-effects brief)
  • docs/briefs/t-impossiblebugs-unenumerated-effects-fn-arrow-refactor-worker.md (this PR's refactor brief, line-152 claim)

@briansrls

Copy link
Copy Markdown
Contributor Author

Review metadata

  • Provider / model: codex / codex-default
  • Commit: fbdd0fbe · Trigger: schedule
  • Thinking: 130s wall

Findings

Verdict: APPROVE_WITH_COMMENTS. The diff is docs-only and the actual requirements/acceptance largely enforce the intended illegal-states-unrepresentable shape; I only found stale “Arrow once” wording that could mislead a later worker. git diff --check is clean.

@briansrls

Copy link
Copy Markdown
Contributor Author

Review metadata

  • Provider / model: claude / claude-opus-4-7
  • Commit: fbdd0fbe · Trigger: schedule
  • Thinking: 27s wall

APPROVE — Docs-only diff. New brief plus a coordinated update to the sibling parser-effects brief; no code, dag, or generated artifacts touched.

The new brief itself reads as discipline-aligned planning: it explicitly invokes feedback_parallel_representation_debt, feedback_coproduct_dissolution, feedback_state_space_vs_behavioral_invariants, mandates a coproduct-dissolution receipt for ArrowInput, makes binderless declaration-position params structurally unrepresentable via FnSignature.inputs: List<NamedArrowInput> (rather than a behavioral diagnostic), and forbids wildcard arms. It also pre-empts the obvious "isn't FnSignature vs SurfaceArrow itself parallel representation?" objection with a carrier-distinction rationale. The sibling-brief edits at lines 175–176, 184–185, 193–199, 210–216, 222, 231 keep the two briefs consistent (single source of truth for the placement decision).

Exploratory observations (optional):

  • The brief leaves a small open door at STOP-AND-ESCALATE bullet on ArrowInput simplification (line 142): if the parser doesn't accept fn(x: A) -> B in type position today, ArrowInput collapses to a single shape and the coproduct goes away. Worth pre-resolving before dispatch so the worker doesn't author the receipt for a coproduct that's about to be deleted.
  • SurfaceArrow/FnSignature carry their own span: SourceSpan while SurfaceType::Arrow(SurfaceArrow) is a positional payload — minor risk of two spans for one syntactic arrow once the wrapper variant gets a span. Not in scope here, but a fixable nit when the worker opens the dag.

@briansrls

Copy link
Copy Markdown
Contributor Author

Fixed in 6a5a9f5c0. Finding valid — refactor brief had two residual spots framing the next-PR work as "add declared_effects on Arrow once": header blockquote (line 20) and non-goals (line 142). Both updated to reference the two-carrier (FnSignature + SurfaceArrow) co-invariant plan that the parser-effects brief now mandates. P2 single-authority drift closed.

Re-review against HEAD (6a5a9f5c0).

briansrls and others added 2 commits April 25, 2026 12:27
… variant span

Claude API exploratory at sha fbdd0fb flagged two pre-dispatch tightenings:

1. The STOP for "ArrowInput simplification if type-position doesn't accept
   named binders today" was a worker-time deferral. Verified at HEAD
   (src/v3/compiler/parse_parser_body.txt:864-877 parse_atom_type reads
   inputs via parse_type_expr_list_until — pure type expressions, no
   binding): type position is anonymous-only today. Pre-resolved.

   - ArrowInput coproduct dropped entirely.
   - SurfaceArrow.inputs simplifies to List<SurfaceType> (today's shape
     preserved, just routed through the typed sub-carrier).
   - Coproduct dissolution receipt requirement removed; replaced with
     structural-carrier rationale for NamedArrowInput.
   - STOP rephrased: STOP if dispatch grep surfaces a parse path that
     does accept fn(x: A) -> B (would invalidate the simplification).

2. Two-spans-per-arrow risk (SurfaceType::Arrow wrapper variant + wrapped
   SurfaceArrow.span). Resolved: the wrapper variant carries no separate
   span; SurfaceArrow.span is source-of-truth.

Brief is now leaner and pre-resolves the worker-discretion question
that Claude API recommended pre-resolving.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@briansrls

Copy link
Copy Markdown
Contributor Author

Both Claude API exploratory observations resolved in 8cd23151d. Both flagged worth pre-resolving before dispatch — verified at HEAD and pre-resolved.

Observation #1 — ArrowInput simplification escape hatch. Verified at HEAD: src/v3/compiler/parse_parser_body.txt:864-877 parse_atom_type reads inputs via parse_type_expr_list_until, which parses pure type expressions with no name-binding. Type position is anonymous-only today; fn(x: A) -> B does not parse. The simplification fires.

Pre-resolved by:

  • Dropping the ArrowInput coproduct entirely (no Anonymous / Named variants needed).
  • SurfaceArrow.inputs: List<SurfaceType> (preserves today's element shape, just routed through the typed sub-carrier).
  • Coproduct dissolution receipt requirement removed; replaced with structural-carrier rationale for NamedArrowInput.
  • STOP repurposed: STOP if dispatch-time re-verification surfaces a type-position parse path that does accept named binders (would invalidate the simplification + require introducing the coproduct with its receipt).

Observation #2 — two-spans-per-arrow risk. SurfaceArrow carries the source-of-truth span; SurfaceType::Arrow(SurfaceArrow) wrapper variant carries no separate span. Consumers reading SurfaceType::Arrow(arrow) use arrow.span. Same pattern for FnSignature.span consumed directly by SurfaceItem.Fn.

Net: brief is leaner — single new carrier (NamedArrowInput) instead of two; no coproduct receipt to author; no parallel-span risk. Lower implementation friction without weakening illegal-state-unrepresentability.

Re-review against HEAD (8cd23151d).

@briansrls

Copy link
Copy Markdown
Contributor Author

Review metadata

  • Provider / model: openai-pro / gpt-5-5-pro
  • Commit: 6a5a9f5c · Trigger: manual
  • Conversation: View conversation

1. Story of the diff

This PR is a docs-only sequencing change for the unenumerated-effects chain. It adds a new pre-prerequisite worker brief that reframes the parser surface so SurfaceItem.Fn no longer owns a parallel params + return_type signature shape; instead, it directs a future implementation to introduce arrow-shaped sub-carriers: FnSignature for declaration position and SurfaceArrow for type position. The new brief makes the modeling reason explicit: declaration-position inputs must be named (NamedArrowInput), while type-position arrow inputs may be anonymous or named (ArrowInput), so binderless function parameters become structurally unrepresentable in FnSignature rather than detected later by the lowerer. The existing parser-effects brief is then tightened to depend on that refactor and to land future declared_effects on both FnSignature and SurfaceArrow, rather than leaving the worker to choose between SurfaceType.Arrow and SurfaceItem.Fn.

2. Invariant categories

  1. LAYER MODEL — Compliant. The diff does not change live substrate or Rust implementation; the new brief explicitly keeps this as a surface-side refactor: docs/briefs/t-impossiblebugs-unenumerated-effects-fn-arrow-refactor-worker.md:146: - **Not refactoring substrate-side \Declaration/Arrow.** Surface-side only.
  2. INVARIANTS.md + modeling-discipline.md — Finding [BLOCKING].

docs/briefs/t-impossiblebugs-unenumerated-effects-parser-worker.md:63: 7. Smoke test: parser accepts \fn read_user(id: String) -> User effects [Read]and produces a function declaration whosesignature: FnSignaturecarriesdeclared_effects = [ReadEffect]. This violates facts flow forward / boundary discipline as written in the same brief:declared_effectsis still a parser-surfaceList<SurfaceType>atdocs/briefs/t-impossiblebugs-unenumerated-effects-parser-worker.md:48, and resolution to OperationEffectis assigned to the lowerer atdocs/briefs/t-impossiblebugs-unenumerated-effects-parser-worker.md:60. A parser-output smoke test expecting [ReadEffect]either makes the parser perform semantic resolution too early or pins a post-lowerer fact at the parse boundary. The test should assert the parsed surface reference shape forRead, and a lowerer test should assert resolution to OperationEffect.

  1. CODING.md — N/A. Docs-only diff; no Rust functions, APIs, methods, error/result shapes, or helper placement are changed.
  2. TESTING.md — Finding [BLOCKING, same root as above].

docs/briefs/t-impossiblebugs-unenumerated-effects-parser-worker.md:63: ... Test should assert the parser-output shape, not end-to-end lens behavior.

The level choice is right, but the expected value is wrong for that level: [ReadEffect] is the lowerer-resolved carrier, not the parser-output SurfaceType/effect-reference carrier described by reqs 1 and 5. That would make the future smoke test implementation-driven across a stage boundary instead of behavior-driven for the parser interface.

  1. LOCKED DESIGN DECISIONS — N/A. No locked design is altered; the brief preserves DB-8/fixed-point constraints as guards, e.g. docs/briefs/t-impossiblebugs-unenumerated-effects-fn-arrow-refactor-worker.md:124: - [ ] DB-8 fixed-point converges bit-identically.
  2. TRACKED vs UNTRACKED DEBT — Compliant. The new work is bounded as a pre-prereq lane with explicit non-goals at docs/briefs/t-impossiblebugs-unenumerated-effects-fn-arrow-refactor-worker.md:144-148, acceptance checks at docs/briefs/t-impossiblebugs-unenumerated-effects-fn-arrow-refactor-worker.md:111-125, and a named dissolution/dispatch trigger at docs/briefs/t-impossiblebugs-unenumerated-effects-fn-arrow-refactor-worker.md:154, where the parser-effects sub-lane becomes dispatchable after this merge.

3. Verdict

REQUEST_CHANGES

The main refactor brief is structurally solid and does the right modeling work around illegal states and carrier distinction. I would fix the stale parser-effects smoke-test expectation before merge, because it directly contradicts the parser/lowerer authority split the brief is trying to protect.

…stage tests

OpenAI-pro BLOCKING at sha 6a5a9f5: parser-effects brief smoke test
asserted declared_effects = [ReadEffect] at the parser-output stage,
but req 1 declares the parser carrier as List<SurfaceType> and req 5
assigns OperationEffect resolution to the lowerer. Asserting the
resolved variant at the parser-output boundary either makes the parser
do semantic resolution too early (boundary violation) or pins a
post-lowerer fact at the parse boundary (facts-don't-flow-forward).

Fix: split into two smoke tests at the right stage boundaries —
parser smoke asserts the SurfaceType::Named "Read" reference; lowerer
smoke asserts resolution to ReadEffect. Acceptance bullet updated to
match.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@briansrls

Copy link
Copy Markdown
Contributor Author

Fixed in e977932e0. OpenAI-pro BLOCKING at sha 6a5a9f5c — finding valid and load-bearing.

The contradiction. Parser-effects brief req 1 declares the parser carrier as List<SurfaceType> (parser surface). Req 5 assigns OperationEffect resolution to the lowerer. But the smoke test at slice step 7 asserted declared_effects = [ReadEffect] at the parser-output stage — pinning a post-lowerer fact at the parse boundary, exactly the facts-flow-forward / boundary-discipline violation the rest of the brief was structured to prevent.

Fix. Split into two smoke tests at the right stage boundaries:

  • Parser smoke: parser produces signature: FnSignature whose declared_effects: List<SurfaceType> carries one entry — a SurfaceType::Named { name: "Read", ... } reference. Assert surface shape only.
  • Lowerer smoke: same source through the lowerer produces the resolved post-parser substrate carrier with [ReadEffect] per derive_op_effect / req 5.

Acceptance bullet updated:

Parser smoke test asserts surface shape only (List<SurfaceType> references); lowerer smoke test asserts resolution to OperationEffect. Stage boundary preserved.

Re-review against HEAD (e977932e0).

@briansrls

Copy link
Copy Markdown
Contributor Author

Review metadata

  • Provider / model: openai-pro / gpt-5-5-pro
  • Commit: 91504d8c · Trigger: manual
  • Conversation: View conversation

Loop summary

5 review rounds over ~50 minutes: first review at 2026-04-25 15:34:22Z, last at 16:24:03Z. The attached history contains 5 codex reviews and 4 non-codex API reviews; it does not contain a separately labeled chatgpt-browser review entry. Commit count is not auditable from the attachment; the review export names one head commit, b821a30a, but does not enumerate the full commit series. Treat this as 5 reviewed diff snapshots, not a reliable commit count. chatgpt-review-5ab904d4-ab03-44…

chatgpt-review-5ab904d4-ab03-44…

Forward progress evidence

The loop made real modeling progress. The first codex finding blocked on exactly the right class of issue: Fn.signature: SurfaceType left non-arrow signatures representable, and optional ArrowInput.name plus optional refinement allowed refinement-without-binder. That is the project’s “illegal states unrepresentable / API-level enforcement” rule in action, not nitpicking. chatgpt-review-5ab904d4-ab03-44…

chatgpt-review-16e3e559-06c3-44…

Those findings did graduate into a better brief shape: the final design moves declaration-position inputs to FnSignature.inputs: List<NamedArrowInput>, keeps type-position arrows as SurfaceArrow.inputs: List<SurfaceType>, and retires the earlier “diagnose anonymous inputs later” approach. That is forward progress: the invariant moved from “parser/lowerer convention must reject this” to “the type shape cannot express it.” The later reviews explicitly recognize the shift from behavioral diagnostic to structural carrier. chatgpt-review-5ab904d4-ab03-44…

chatgpt-review-5ab904d4-ab03-44…

The loop also caught cross-brief authority drift. A codex round found that the sibling parser-effects brief still pointed workers at SurfaceType.Arrow / SurfaceItem.Fn while the new brief wanted declared_effects on both FnSignature and SurfaceArrow; the PR then updated the sibling brief. That is the right layer-agnostic application of P2: the issue was not “this one paragraph is stale,” it was “dispatch authority across briefs is now split.” chatgpt-review-5ab904d4-ab03-44…

Consumers enabled: none mechanical. No parser, lowerer, emitter, interpreter, or test consumer lands in this PR. The current diff is still a dispatch/planning artifact. The brief’s own text says effects do not land here and are left to the next sub-lane (pr-805.diff:62, pr-805.diff:146). Under P2, a boundary is not “landed” by declaration alone; it needs declaration, realization, and a generated consumer proof. chatgpt-review-7c4cfb62-911d-43…

Scaffolds dissolved: no code scaffolds dissolved. Design-review scaffolds were paid down: signature: SurfaceType was eliminated, the optional-field ArrowInput shape was rejected, and the parser-effects brief was brought closer to the new carrier plan.

Invariants graduated: no new INVARIANTS.md rule landed. That is acceptable for the illegal-state findings because the existing Modeling Discipline already covered them. The recurring stale-dispatch-authority pattern is the one class that may now deserve a small local rule/checklist if it appears again.

Debt accumulation evidence

The PR adds one new planning scaffold: a pre-prereq brief whose only real consumer is a future worker. It has accounting—STOP conditions, acceptance checks, and a stated handoff chain—but it is still substrate-growing prose with no mechanical consumer yet. P5 allows scaffolds only when they have named dissolution triggers; this brief mostly has those, but the actual dissolution is deferred to later implementation PRs. chatgpt-review-7c4cfb62-911d-43…

The newest debt is stale authority residue, not code unsoundness. The final diff still contains wording that appears inconsistent with the resolved design: it says type-position Arrow.inputs wraps entries in ArrowInput::Anonymous { ty } even though the final requirements say there is no ArrowInput coproduct and type-position inputs remain raw SurfaceType (pr-805.diff:78, pr-805.diff:150). That matters because the brief is the artifact being shipped; stale prose is not harmless when the next worker is the consumer.

The review loop is also trending toward cheaper fixes. Early rounds forced structural modeling changes. The last round found a non-blocking stale “Arrow once” wording issue after the sibling brief had been updated. Claude’s final exploratory note still sees an “open door” around ArrowInput simplification, which means the prose has not fully converged to one live carrier table. chatgpt-review-5ab904d4-ab03-44…

No consumer test protects this. The brief asks a future worker to add smoke tests, grep counts, DB-8 fixed-point checks, and full workspace checks, but those are future acceptance criteria, not present receipts (pr-805.diff:111-126). Under the “consumers define correctness” principle, this is still correctness-by-review, not correctness-by-consumer.

Cheating signal

Mostly documented, not hidden. The implementer is not silently smuggling a shortcut into code. The compromises are surfaced as STOP-AND-ESCALATE conditions, carrier-distinction rationale, “no generated hand edits,” no-wildcard requirements, grep-count requirements, and future smoke tests.

But the most recent fixes are prose-local, not structural. The loop has moved from “make invalid states unrepresentable” to “remove stale words from the dispatch brief.” That is not a failure yet, but it is the point where another broad review round has diminishing returns unless it is tightly scoped.

The cheating signal is therefore: low hidden-cheating risk, moderate exhaustion risk. The debt is visible, but the loop is starting to accept “good enough wording” in a document whose entire purpose is to be unambiguous dispatch authority.

Path to convergence

The smallest next actions that justify KEEP_ITERATING are:

  1. Add one live carrier table to the refactor brief:
  • declaration position: FnSignature.inputs: List<NamedArrowInput>
  • type position: SurfaceArrow.inputs: List<SurfaceType>
  • retired: SurfaceParam
  • absent in this PR: ArrowInput
  • future-only: named type-position binders introduce their own carrier/receipt.
  1. Run a mechanical stale-authority sweep across both touched briefs for:
  • ArrowInput
  • Arrow once
  • SurfaceType.Arrow as the effects placement authority
  • SurfaceItem.Fn as the effects placement authority
  • stale (A, B) -> C examples where the parser requires fn(A, B) -> C.
  1. Update the PR body with a handoff receipt naming the first real consumer obligation: the implementation PR must land parser/lowerer smoke tests for top-level fn and higher-order fn(Int, Bool) -> String, record post-refactor grep counts, and prove SurfaceParam has zero references.

Do not run another open-ended review round after that. If those three actions land and the next review still finds only wording polish, stop reviewing this docs PR and move to the implementation PR where real consumers can define correctness.

Meta-verdict

📈 KEEP_ITERATING — the loop is still making real progress, but only one tightly scoped iteration is justified. The next round must be a carrier-authority cleanup and handoff receipt, not another general design review.

@briansrls

Copy link
Copy Markdown
Contributor Author

Review metadata

  • Provider / model: claude / claude-opus-4-7
  • Commit: e977932e · Trigger: schedule
  • Thinking: 33s wall

This is a docs-only PR adding a refactor brief and updating a sibling brief. No Rust code changes; INVARIANTS / CODING / TESTING discipline anchors don't directly apply to brief contents, but internal consistency does — these briefs are dispatch authority for downstream PRs.

Findings

  • Internal inconsistency: ArrowInput coproduct retired but still referenced. The fn-arrow-refactor brief explicitly retires the coproduct at t-impossiblebugs-unenumerated-effects-fn-arrow-refactor-worker.md:78 ("No ArrowInput coproduct — earlier drafts proposed ArrowInput = Anonymous { ty } | Named(NamedArrowInput)…"), yet the same file's Non-goals at line 150 still says: "the only structural change is that Arrow.inputs now wraps each entry in ArrowInput::Anonymous { ty }." That residue from the earlier draft contradicts req 1; type-position inputs remain raw SurfaceType, no wrapping. Non-blocking but worth fixing — a worker reading Non-goals first would build the wrong shape.

  • Same residue in the parser-worker brief. t-impossiblebugs-unenumerated-effects-parser-worker.md:173 and :208 reference NamedArrowInput/ArrowInput and "ArrowInput" in the pre-flight check. Per the refactor brief there is no ArrowInput carrier — only NamedArrowInput. Tighten to drop the /ArrowInput slashes so the pre-flight check doesn't gate on a carrier that won't exist.

Verdict

APPROVE_WITH_COMMENTS — the briefs are coherent and the Director's (b)(2) sequencing rationale is sound. The two stale ArrowInput-coproduct mentions (one in Non-goals of the new brief, one in the parser-brief edits) are cosmetic but mislead the dispatched worker; cheap to clean up before dispatch.

Exploratory observation

The carrier-distinction rationale (req 3) is doing real work — FnSignature.inputs: List<NamedArrowInput> vs SurfaceArrow.inputs: List<SurfaceType> is a clean structural-unrepresentability win for declaration position. But the downstream effects brief lands the same declared_effects field on both. If a future PR ever wants to share that effects-bearing shape (e.g., for a uniform "thing-that-has-effects" lens), the asymmetric inputs element types will block trivial unification. Worth a passing thought when the parser-effects brief lands; not actionable here.

…y scrub

OpenAI-pro meta-review at sha 91504d8 gave a tight three-action path
to convergence. Executing.

Action 1: Live carrier table added to refactor brief frame section.
Single source of truth for which carrier sits at which position
(declaration vs. type), what input element shape each takes, what's
retired, what's absent in this PR, what's deferred to a future PR.

Action 2: Mechanical stale-authority sweep across both briefs:
- Refactor brief: line 35 `(A, B) -> C` → `fn(A, B) -> C` (parser
  requires fn(...) prefix per parse_atom_type KwFn gate).
- Refactor brief: line 46 same fix.
- Refactor brief: dropped duplicate STOP at line 134 referencing
  removed ArrowInput::Named(NamedArrowInput) shape — same condition
  already covered by line 129 STOP in the cleaned-up form.
- Refactor brief: line 144 non-goals cleaned up "wraps each entry in
  ArrowInput::Anonymous { ty }" → describes actual post-PR shape
  (List<SurfaceType> at type-position layer).
- Parser brief: lines 22 + 57 dropped `ArrowInput` from the pre-flight
  carrier list (it's not introduced).

Action 3 (PR body handoff receipt) follows in the next push.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@briansrls

Copy link
Copy Markdown
Contributor Author

Convergence sweep landed in a30f3b192 + PR body update. All three OpenAI-pro meta-review actions executed:

Action 1 — live carrier table. Added to the refactor brief frame section (and mirrored in PR body). Single source of truth for which carrier sits at which position, what's retired, what's absent in this PR, what's deferred.

Action 2 — mechanical stale-authority sweep. Across both touched briefs:

  • (A, B) -> C → fn(A, B) -> C (refactor brief lines 35 + 46)
  • Dropped duplicate STOP referencing removed ArrowInput::Named(NamedArrowInput) shape (refactor brief line 134)
  • Non-goals "wraps each entry in ArrowInput::Anonymous { ty }" → describes actual post-PR shape List<SurfaceType> at type-position layer (refactor brief line 144)
  • Parser brief pre-flight references trimmed: SurfaceArrow + FnSignature + NamedArrowInput (no / ArrowInput)

Remaining ArrowInput mention is in the STOP that explains what would invalidate the simplification — pedagogically correct, leaving as-is.

Action 3 — PR body handoff receipt. Updated PR description with the live carrier table + a concrete consumer-obligation checklist for the implementation worker (parser smoke for top-level + higher-order, post-refactor grep counts, SurfaceParam zero-references proof, no-wildcard-arms, DB-8 bit-identity, full workspace gates).

Per the meta-review's instruction: stopping further open-ended review rounds on this docs PR. Re-review against HEAD (a30f3b192) for closure only — if there's still wording polish, it bundles into the implementation PR sweep where real consumers can define correctness.

@briansrls

Copy link
Copy Markdown
Contributor Author

Review metadata

  • Provider / model: codex / codex-default
  • Commit: e977932e · Trigger: schedule
  • Thinking: 89s wall

Findings

Verdict: REQUEST_CHANGES. The intended FnSignature + SurfaceArrow direction looks sound, but the stale ArrowInput references need to be scrubbed so the briefs give one executable model.

@briansrls

Copy link
Copy Markdown
Contributor Author

Both findings at sha e977932e already addressed in subsequent a30f3b192 (the convergence sweep prompted by the OpenAI-pro meta-review):

  • Non-goals ArrowInput::Anonymous { ty } residue (refactor brief) — fixed. Now reads: "type-position Arrow.inputs continues to be List<SurfaceType> — the only structural change at the type-position layer is that SurfaceType::Arrow now wraps a typed SurfaceArrow carrier (positional payload, no separate span on the variant)."
  • / ArrowInput slashes in parser-worker brief (read-first + pre-flight check) — fixed. Both locations now reference SurfaceArrow + FnSignature + NamedArrowInput only.

Verified at HEAD: grep ArrowInput returns only intentional pedagogical mentions inside STOP conditions / earlier-drafts framing.

Exploratory observation noted — asymmetric inputs element types (List<NamedArrowInput> vs List<SurfaceType>) blocking trivial unification of an effects-bearing carrier downstream. Real concern; non-actionable here. Flagging for the parser-effects brief consumer (or, more likely, for a future lane that wants a uniform "thing-that-has-effects" lens to consider before introducing a third arrow-shape carrier).

Re-review against HEAD (a30f3b192).

@briansrls

Copy link
Copy Markdown
Contributor Author

Both BLOCKING findings at sha e977932e already fixed in subsequent a30f3b192 (convergence sweep). Reviewer was looking at the pre-sweep state.

  • BLOCKING Add SVG viz, test helpers, and makegen scaffold #1 (refactor brief line 144 reintroducing ArrowInput::Anonymous): scrubbed. Now reads: "type-position Arrow.inputs continues to be List<SurfaceType> — the only structural change at the type-position layer is that SurfaceType::Arrow now wraps a typed SurfaceArrow carrier (positional payload, no separate span on the variant)."
  • BLOCKING Codex/graph viz test helpers #2 (parser brief lines 22 + 57 referencing ArrowInput in pre-flight): scrubbed. Both locations now reference SurfaceArrow + FnSignature + NamedArrowInput only.

Verified at HEAD (a30f3b192): grep ArrowInput returns only intentional pedagogical mentions inside STOP conditions / earlier-drafts framing. The two execution-authority sites the finding pointed at are clean.

Re-review against HEAD (a30f3b192).

@briansrls briansrls left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review metadata

  • Provider / model: codex / unknown
  • Commit: a30f3b19 · Trigger: schedule
  • Thinking: 184s wall

Non-blocking — Strengths

  • docs/briefs/t-impossiblebugs-unenumerated-effects-fn-arrow-refactor-worker.md The carrier table and requirements now give one coherent post-refactor model for FnSignature, SurfaceArrow, retired SurfaceParam, and absent ArrowInput.
  • docs/briefs/t-impossiblebugs-unenumerated-effects-parser-worker.md The parser-effects brief now preserves the parser-vs-lowerer stage boundary with separate smoke tests for surface references and resolved OperationEffects.

✅ Docs-only diff looks clean; no blocking concerns.

@briansrls

Copy link
Copy Markdown
Contributor Author

Verified at HEAD a30f3b192 — finding is from a stale sha (matches the original BLOCKING from 2026-04-25T15:45:43Z, fixed in 47d13cc22, then further reinforced by 8cd23151d which eliminated the ArrowInput coproduct entirely after type-position-anonymous-only verification).

Current line 71 is part of the live carrier table, not the rejected shape. Searched at HEAD:

  • grep "ArrowInput { name: None" docs/briefs/...refactor...md → no matches.
  • grep "signature: SurfaceType\b" docs/briefs/...refactor...md → no matches.
  • grep "name: None" only finds refinement: None inside smoke-test NamedArrowInput { name: "x", ..., refinement: None } shape — which is correct (named binder explicitly present, no refinement on this example).

The "rejected illegal-state shape" the finding describes is ArrowInput { name: None, refinement: None, ty }. That carrier doesn't exist in the brief at HEAD — ArrowInput was eliminated entirely; current shape uses NamedArrowInput (name structurally mandatory) for declaration position and raw SurfaceType for type position. See live carrier table at refactor-brief frame section.

No action.

@briansrls

Copy link
Copy Markdown
Contributor Author

Verified at HEAD a30f3b192 — finding describes a state that no longer exists in the brief. The replayed BLOCKING is from sha b821a30a or earlier; it was the original BLOCKING that prompted the structural fix in b821a30ab (FnSignature with List<NamedArrowInput>) and the further simplification in 8cd23151d (ArrowInput coproduct eliminated entirely after type-position-anonymous-only verification at HEAD).

Current state at HEAD — req 5 lowerer text (line 95):

Every consumer that today reads Fn.params + Fn.return_type is updated to read Fn.signature.inputs: List<NamedArrowInput> + Fn.signature.output. Per-input destructuring is uniform — every entry is a NamedArrowInput carrying name + ty + refinement?. Type-position consumers (those reading SurfaceArrow.inputs: List<SurfaceType>) read each input as a raw SurfaceType.

There is no "planned lowerer diagnostic for ArrowInput::Anonymous inside SurfaceItem.Fn.signature" — Fn.signature: FnSignature carries List<NamedArrowInput> directly; no Anonymous variant exists in NamedArrowInput; the parser cannot construct a binderless declaration-position param. Illegal state unrepresentable structurally.

Searched at HEAD: grep "ArrowInput::Anonymous" docs/briefs/...refactor...md returns no occurrences in execution authority — only the pedagogical mention inside the "earlier drafts proposed" framing on line 82.

No action.

@briansrls

Copy link
Copy Markdown
Contributor Author

Review metadata

  • Provider / model: openai-pro / gpt-5-5-pro
  • Commit: a30f3b19 · Trigger: manual
  • Conversation: View conversation

1. Story of the diff

This PR is a docs-only coordination change for the unenumerated-effects chain. It adds a new pre-prereq worker brief that says the parser-effects work should not bolt declared_effects onto the existing SurfaceItem.Fn split; instead, the surface model should first be reshaped so function declarations carry an arrow-shaped FnSignature, while higher-order type annotations carry a separate SurfaceArrow (docs/briefs/t-impossiblebugs-unenumerated-effects-fn-arrow-refactor-worker.md:56). The load-bearing modeling move is the split between declaration-position inputs (NamedArrowInput, with names structurally required) and type-position inputs (List<SurfaceType>, anonymous-only today), which lets the next parser-effects PR put effects on both signature carriers without introducing an Anonymous | Named coproduct before syntax exists (docs/briefs/t-impossiblebugs-unenumerated-effects-fn-arrow-refactor-worker.md:62-66, :82-96). The existing parser-effects brief is then retargeted to depend on this new pre-refactor and to test parser surface shape separately from lowerer effect-resolution behavior (docs/briefs/t-impossiblebugs-unenumerated-effects-parser-worker.md:57-66).

2. Invariant categories

  1. LAYER MODEL — Compliant. This diff does not change substrate code; the new brief explicitly scopes the refactor as surface-side only and names substrate Declaration / Arrow as a non-goal (docs/briefs/t-impossiblebugs-unenumerated-effects-fn-arrow-refactor-worker.md:151), with a STOP if substrate Arrow turns out to require a coupled refactor (docs/briefs/t-impossiblebugs-unenumerated-effects-fn-arrow-refactor-worker.md:142).
  2. INVARIANTS.md + modeling-discipline.md — Finding, NON-BLOCKING. docs/briefs/t-impossiblebugs-unenumerated-effects-parser-worker.md:103 still says: “PR body cites this brief + addresses each of the 6 reqs + documents surface-syntax + Arrow-vs-Fn placement choices.” That conflicts with the new single-authority surface decision in the same diff: docs/briefs/t-impossiblebugs-unenumerated-effects-parser-worker.md:48 says the field lands on both SurfaceArrow and FnSignature and is “not a worker placement choice.” Under Boundary Discipline / single authority, the reporting instruction should not re-open the old Arrow-vs-Fn choice language; I’d tighten it to “documents surface syntax + FnSignature/SurfaceArrow co-invariant carrier rationale.” This is not blocking because the actual requirements and acceptance bullets now point at the correct carriers.
  3. CODING.md — Compliant. No Rust implementation is changed, and the brief preserves generated-code authority by requiring .dag/.txt edits followed by regen, with “No hand edits to parse_generated.rs” (docs/briefs/t-impossiblebugs-unenumerated-effects-fn-arrow-refactor-worker.md:39, :107, :125).
  4. TESTING.md — Compliant. The implementation brief requires behavior-level smoke coverage for both top-level function declarations and higher-order function types (docs/briefs/t-impossiblebugs-unenumerated-effects-fn-arrow-refactor-worker.md:110-112, :128), and the parser-effects brief now splits parser-shape assertions from lowerer-resolution assertions at the parser→lowerer boundary (docs/briefs/t-impossiblebugs-unenumerated-effects-parser-worker.md:63-66, :76).
  5. LOCKED DESIGN DECISIONS — Compliant. The diff references DB-8 and substrate Arrow, but does not alter locked substrate authority; it explicitly keeps substrate-side Declaration / Arrow out of scope (docs/briefs/t-impossiblebugs-unenumerated-effects-fn-arrow-refactor-worker.md:151) and makes DB-8 fixed-point drift an immediate STOP (docs/briefs/t-impossiblebugs-unenumerated-effects-fn-arrow-refactor-worker.md:145, :130).
  6. TRACKED vs UNTRACKED DEBT — Compliant. The temporary/absent shapes are bounded and named: ArrowInput is explicitly absent because type-position binders are not surfaced today, and the dissolution trigger is a future PR that adds fn(x: A) -> B syntax (docs/briefs/t-impossiblebugs-unenumerated-effects-fn-arrow-refactor-worker.md:65-66, :82, :139). Deferred declared_effects work is also bounded to the sibling parser-effects sub-lane (docs/briefs/t-impossiblebugs-unenumerated-effects-fn-arrow-refactor-worker.md:149, :159).

3. Verdict

APPROVE_WITH_COMMENTS. The diff is structurally sound as a docs/briefs refactor plan: it moves the chain toward typed carriers, illegal-state exclusion, and stage-specific tests. I would clean up the stale “Arrow-vs-Fn placement choices” reporting sentence before or after merge, but the load-bearing requirements now point to the right model.

…parser-effects reporting

OpenAI-pro non-blocking at sha a30f3b1: parser-effects brief reporting line still asked the worker to document Arrow-vs-Fn placement choices, but the same diff already fixed placement as co-invariant on both FnSignature + SurfaceArrow per refactor-brief req 3 ("not a worker placement choice"). Boundary-discipline / single-authority drift on a one-liner. Updated to ask for the carrier-distinction rationale instead.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@briansrls

Copy link
Copy Markdown
Contributor Author

Fixed in 0be6e6688. OpenAI-pro non-blocking at sha a30f3b19 valid — parser-effects brief reporting line 103 still asked the worker to document "Arrow-vs-Fn placement choices," contradicting req 1's "not a worker placement choice" framing landed in the same diff. Updated to ask for the FnSignature / SurfaceArrow co-invariant carrier rationale instead.

Re-review against HEAD (0be6e6688).

@briansrls
briansrls merged commit 7d384f4 into main Apr 25, 2026
3 checks passed
briansrls added a commit that referenced this pull request Apr 26, 2026
…cted scope (codex P2 on #835)

Codex P2 inline at r2-impossible-bugs-manager.md:63: design-brief
filenames were missing the canonical -design suffix; the actual
files are t-impossiblebugs-*-design.md.

Audit revealed a bigger correction needed than just filename suffix:

1. Worker briefs ALREADY EXIST for all three classes (I had said
   'needs Director conversion to worker briefs' — wrong). Correct
   state:
   - Nested-optional flatten: design + worker (DESIGN/SCOPING shape) authored
   - Unhandled diagnostic paths: design + worker (DESIGN/SCOPING shape) authored
   - Unenumerated effects: design authored, prior worker briefs SUPERSEDED 2026-04-25 by design doc

2. The two non-effects workers are DESIGN/SCOPING shape — they
   produce substrate proposals, not direct implementation. Manager
   role is dispatch + Substrate-Manager-handoff coordination, not
   convert-design-to-worker.

3. Effects has SUPERSEDED workers (closed-system framing dissolved
   the prior lens-vs-declaration framing). Manager owns design-doc
   routing + post-supersede implementation worker authoring against
   the canonical design.

4. Fn→Arrow refactor (PR #805) reframed as independent vestigial-
   syntax cleanup, not direct effects-framing prereq.

Three coordinated fixes in r2-impossible-bugs-manager.md:
- Program scope table: canonical filenames + per-class authored-status
  + SUPERSEDED notes
- Owned deliverables: 'Manager dispatches existing worker' (not
  'convert design to worker')
- Sub-briefs section: explicit Authored/SUPERSEDED/Pending tri-state
  with full canonical paths

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
briansrls added a commit that referenced this pull request Apr 26, 2026
… readiness) (#835)

* docs(briefs): pre-stage 6 R2 manager briefs (PM portion of R2 spin-up readiness)

Per user direction: every lane/brief/design must be authored before
R2 managers spawn. PR #827 (merged) named the 6-manager structure;
Transition mechanics step 4 said "pre-stage skeletons during R1 final
week" — accelerated to "pre-stage now."

This PR lands all 6 R2 manager briefs as one bundle, structured
consistently:
- Status (PROPOSAL pre-spawn, spawns on R1 close)
- Orient before reading (R2 structure authority, scope source,
  cross-program coordination, demo coordination)
- Program scope (the lane/sub-program scope this manager owns)
- Owned deliverables (table of lanes/sub-lanes with status)
- Cross-program dependencies (produces/consumes signals)
- Autonomous dispatch authority (what manager does without Director)
- Reporting cadence (where signals flow)
- Sub-briefs (authored / pending)
- Working state (placeholder for fill on spawn)
- Cross-refs

Six briefs:

1. r2-grounding-manager.md — T-Ground sub-program (the one true R2
   critical path: Pilot → Rust → Engine → Tests → Dissolve, with
   Python/Go fill). Migrates from grounding-manager.md (which archives
   on R2 promotion). Names Engine sharpened-(b) consumer dependency
   on Substrate Manager's ValueBody-list/sum carrier.

2. r2-substrate-manager.md — T-Substrate (4 sub-lanes) + B4
   Identity-Carrier Substrate Pass program (12 sub-briefs). Largest
   single program in R2; produces 4 carriers consumed by Modeling
   (3 sub-lanes) + Grounding (Engine sharpened-(b)). Names watch
   condition for B4 split if Substrate becomes the new bottleneck.

3. r2-modeling-manager.md — T-Modeling (3 Goal 2 items + tokenizer
   charclass phase-2 added per shared T-Substrate dependency). All
   gated on Substrate Manager carrier readiness.

4. r2-impossible-bugs-manager.md — T-ImpossibleBugs (3 R2+ classes:
   nested-optional flatten, unhandled diagnostic paths, unenumerated
   effects). Design docs already authored (#798, #801, #808+#805
   prereq); needs Director conversion to worker briefs.

5. r2-pure-bootstrap-manager.md — POST-R1 only per gate-vs-program
   resolution in PR #827. Migrates from pure-bootstrap-zero-manager.md
   with scope narrowed (does NOT duplicate R1 T-PB-A/T-PB-B census-
   reduction work). Owns Tier 3 mirror dissolutions + Tier 2
   patch_lower_helpers retirement + post-R1 emergent dissolutions.

6. r2-release-manager.md — Goal 5 (§6a metadata-pick) + Goal 6 (R2
   demo coordination) + B-wave Tier 0/2 dispatch (#810) + discipline
   framework central reporting + thesis-claim coverage mapping
   (Open call 1) + R2 closure ledger + v2 retirement. Single authority
   for closure ledger and demo coordination.

Each brief explicitly defers to ROADMAP/THESIS/r2-structure.md for
upstream authority; does not duplicate gate semantics or scope
decisions. Cross-program coordination via R1 `Cross-manager
notifications queued` brief pattern.

Coordination split with Director on inbox #828: Director takes the
worker-level briefs (B4.2/B4.3/B4.4 + T-Substrate sub-lane scoping +
T-Modeling worker briefs + T-ImpossibleBugs design→worker conversion);
PM takes §6a + B5/B6/B7 + thesis-claim mapping in follow-up PRs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(briefs): r2-impossible-bugs-manager — canonical filenames + corrected scope (codex P2 on #835)

Codex P2 inline at r2-impossible-bugs-manager.md:63: design-brief
filenames were missing the canonical -design suffix; the actual
files are t-impossiblebugs-*-design.md.

Audit revealed a bigger correction needed than just filename suffix:

1. Worker briefs ALREADY EXIST for all three classes (I had said
   'needs Director conversion to worker briefs' — wrong). Correct
   state:
   - Nested-optional flatten: design + worker (DESIGN/SCOPING shape) authored
   - Unhandled diagnostic paths: design + worker (DESIGN/SCOPING shape) authored
   - Unenumerated effects: design authored, prior worker briefs SUPERSEDED 2026-04-25 by design doc

2. The two non-effects workers are DESIGN/SCOPING shape — they
   produce substrate proposals, not direct implementation. Manager
   role is dispatch + Substrate-Manager-handoff coordination, not
   convert-design-to-worker.

3. Effects has SUPERSEDED workers (closed-system framing dissolved
   the prior lens-vs-declaration framing). Manager owns design-doc
   routing + post-supersede implementation worker authoring against
   the canonical design.

4. Fn→Arrow refactor (PR #805) reframed as independent vestigial-
   syntax cleanup, not direct effects-framing prereq.

Three coordinated fixes in r2-impossible-bugs-manager.md:
- Program scope table: canonical filenames + per-class authored-status
  + SUPERSEDED notes
- Owned deliverables: 'Manager dispatches existing worker' (not
  'convert design to worker')
- Sub-briefs section: explicit Authored/SUPERSEDED/Pending tri-state
  with full canonical paths

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* WIP: gunbc PM

* fix(briefs): add pre-spawn vs post-spawn authority subsection to all 6 R2 manager briefs (codex P2 on #835)

Codex flagged ownership ambiguity in r2-impossible-bugs-manager.md:
the brief said design/scoping docs would be 'converted to worker
briefs by Director' but elsewhere said the manager authors all worker
briefs autonomously. Without an explicit phase boundary (pre-spawn
vs post-spawn), ownership is ambiguous and dispatch can stall.

Resolution applied uniformly to all 6 briefs: new 'Pre-spawn vs
post-spawn authority' subsection inserted before 'Autonomous dispatch
authority':

- Pre-spawn (now, before R1 close): Director + PM coordinate on brief
  authoring per inbox #828 split. PM authors the manager skeleton;
  Director authors worker-level briefs not yet existing. Both stop
  authoring once R2 spawns.

- Post-spawn (R2 promotion onward): Manager owns all worker-brief
  authoring autonomously per Autonomous dispatch authority. Director
  narrows to cross-program conflict resolution + scope-change
  escalation.

Release Manager variant has the same boundary plus an explicit note
that PM also authors the §6a / B5 / B6 / B7 / thesis-claim-mapping
briefs as Release-Manager-portion PM deliverables (per inbox #828).

The phase boundary is now structurally explicit: no dispatch stall
from both Director and Manager assuming the other owns authoring.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(briefs): tighten Pending-line authority qualifier (codex BLOCKING on #835 sha 3803266 :90)

Codex flagged the 'Pending — Director-authored per coordination on
inbox #828:' lines as creating dual authority — the line read in
isolation contradicted the 'Manager authors autonomously' framing
elsewhere. The d42f17e phase-boundary subsection resolved this
contextually, but a reader scanning just the Pending line could still
read it as a permanent assignment.

Surgical tightening: add explicit pre-spawn qualifier inline so the
Pending line is self-resolving without requiring the reader to
cross-reference the phase-boundary subsection.

Old: 'Pending — Director-authored per coordination on inbox #828:'
New: 'Pending — pre-spawn Director-authored per inbox #828
      coordination split; post-spawn manager-authored autonomously
      per "Pre-spawn vs post-spawn authority" subsection above:'

Applied to 4 briefs (Modeling, Substrate, Pure Bootstrap, Release).
Release variant uses 'PM-authored' instead of 'Director-authored'
since R2 Release Manager's pre-spawn portion is PM-owned per inbox
#828 split (the §6a / B5 / B6 / B7 / thesis-claim-mapping briefs).

The Pending line now reads cleanly in isolation: pre-spawn / post-
spawn boundary is explicit at the line itself, not deferred to a
cross-reference.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(briefs): resolve openai-pro REQUEST_CHANGES on #835 sha bfaab66

Two surgical fixes for the two BLOCKING findings (P2 + P5):

1. r2-release-manager.md:67 — B7 dual-authority contradiction.
   Was: "Authors all T-Release worker briefs without Director (§6a pick, B5/B6/B7, ...)"
   But B7 is "Cross-manager signal, not a worker brief" per :33 + :86.
   Now: "Authors all T-Release owned deliverables ...: worker briefs
   (§6a pick, B5, B6, thesis-claim coverage mapping) and cross-manager
   signals (B7 priority-hint relay)." — distinguishes briefs from signals,
   no item carries two contracts.

2. r2-grounding-manager.md:62 — Pending line unbounded across pre/post
   spawn. The other 4 briefs got the "pre-spawn Director-authored;
   post-spawn manager-authored" temporal qualifier in bfaab66;
   Grounding was missed. Same pattern applied here.

Both fixes mechanical; no scope or authority change beyond removing
the ambiguity openai-pro flagged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(briefs): resolve codex BLOCKING on #835 sha bfaab66 — stale §6a + B4.1 status

Two codex BLOCKING findings, both about briefs copying status from earlier
state without verifying against live receipts:

1. r2-release-manager.md §6a — DECISION already locked.
   docs/design-substrate-carrier-port-program.md §6a:171 says
   "pick **Option 3, unified MethodContract carrier**." :173 names the
   live receipt (src/v3/std/algebra.dag declares MethodContract;
   src/v3/lenses/cost.dag imports it via method_contract_cost_shape).
   :175 names the dissolution trigger (size_effect / cost_shape /
   callback_element_position field-by-field retirement).

   Brief was framing this as "DECISION BRIEF NOT YET AUTHORED — write
   up the 4 options ... recommend one based on E-I evidence." Stale.

   Fix: rename "pick decision brief" → "follow-through brief"; status
   from "NOT YET AUTHORED" to "DECISION LOCKED — Option 3 ... live
   receipt landed"; describe remaining work as bulk migration +
   dissolution-trigger tracking. Updated the deliverable table row,
   the Core deliverables list, the Autonomous dispatch authority line,
   the Sub-briefs Pending list, and the Cross-refs §6a source.

2. r2-substrate-manager.md B4.1 — BLOCKING already resolved.
   PR #819 ("docs(briefs): add B4.1a DeclarationRef runner migration
   brief") merged 2026-04-26 01:13:32. The §0.2 scope gap was resolved
   in 6f564f5 BEFORE merge per Director receipt on inbox #828. B4.1a
   follow-on brief landed in the same PR. Real open residual is the
   first-consumer migration at PR #826 (regen drift on r1_gates.dag —
   worker CI-fix, not brief authoring).

   Brief was still saying "DRAFTED (with §0.2 BLOCKING outstanding —
   codex finding on PR #819)" and "with outstanding BLOCKING ...
   resolution pending." Stale on both the BLOCKING and the residual
   shape.

   Fix: status to "BRIEF LANDED (PR #819, merged 2026-04-26 — §0.2
   scope gap resolved in 6f564f5 before merge); B4.1a runner-migration
   follow-on brief landed same PR. Real residual: first-consumer
   migration #826 OPEN with regen drift (worker CI-fix)." Updated the
   deliverable table row, the Sub-briefs Authored list, and the
   Cross-refs adjacent line.

Both findings: feedback_verify_thesis_claims violation on the PM
authoring side. Two surgical text updates per finding; no scope or
authority change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(briefs): anchor §6a follow-through against existing pick worker brief

Codex inline BLOCKING on r2-release-manager.md:30 surfaced that
docs/briefs/t-permethodmetadata-pick-worker.md (landed PR #794) already
exists as the pick-worker brief. My prior fix (74b679b) reframed
"pick decision brief" → "follow-through brief" but didn't reference the
existing worker, leaving readers to wonder if the follow-through was
re-picking.

Two precision tightenings:

- "Pick is closed." Names the worker brief explicitly + cites its
  scope-closure clause ("Do not migrate all consumer lenses ... bulk
  migration is post-pick work").
- "No duplicate decision authority — pick is closed; follow-through is
  post-pick scope." Closes the P2 single-authority concern codex named.

Surface change only; no scope expansion. The follow-through scope
(bulk migration + dissolution-trigger tracking) is unchanged from the
74b679b state — what's added is the explicit worker-brief anchor.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(briefs): resolve openai-pro APPROVE_WITH_COMMENTS on #835 sha 3260d71

Finding (P2 single-authority): T-Ground-Rust had two contradictory states —
deliverables table at :23 said DISPATCHED, but Sub-briefs Pending list at
:62-63 listed "T-Ground-Rust full implementation" as pending pre-spawn work.
Same lane, two authoritative states.

Audit: T-Ground-Rust full lane (Rust target-spec primitive declarations
end-to-end) has not been authored. Pilot (PR #765) and Engine Phase 1
typestructure (PR #788) are separate dispatched lanes (their own rows in the
table); the "DISPATCHED (Engine implementation parked pending loader-close)"
parenthetical was a status leak from the Engine row's parking note.

Fix: row status now reads "NOT YET AUTHORED — listed under Sub-briefs
Pending below; gated on pre-spawn Director scope refinement per inbox #828.
(Pilot PR #765 + Engine Phase 1 typestructure PR #788 are separate dispatched
lanes — see those rows; the prior 'DISPATCHED' status here was a parenthetical
leak from the Engine row's loader-close parking note.)"

Now table status matches Sub-briefs Pending list. Single authority restored.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(briefs): refresh impossible-bugs manager against PR #836 merge

Codex BLOCKING on r2-impossible-bugs-manager.md:78 (sha bfaab66) was
correct in spirit and now newly actionable: the brief's Pending section
re-dispatched the older DESIGN/SCOPING workers
(t-impossiblebugs-nested-optional-flatten-worker.md +
t-impossiblebugs-unhandled-diagnostic-paths-worker.md) even though
their design docs (PR #798 + PR #801) had landed with next-step
recommendations + PR #836 just authored the IMPLEMENTATION workers
(r2-impossible-bugs-{nested-optional-flatten,unhandled-diagnostic-paths,
unenumerated-effects}-worker.md).

Re-dispatching DESIGN/SCOPING workers when implementation workers are
authored = duplicate decision authority under P2 + accumulating ad-hoc
state under P5. Codex was right.

Three sections updated to reflect PR #836-merged state:

## Program scope table (lines 17-19)

Reframed columns: "Design authority + implementation worker (post PR #836
merge)" / "Implementation status" / "Substrate gating". Each class row
now names:
- Design doc PR + closed-in-scope status
- Implementation worker filename (PR #836) + IMPLEMENTATION WORKER LANDED
- UNGATED status per design-doc audit (Director's reframes #1, #2 confirmed
  no substrate gates — substrate-constructor invariant for nested-optional;
  totality-by-omission for unhandled-diagnostic; closed-system for effects)

The OLD DESIGN/SCOPING workers are explicitly named SUPERSEDED for
unenumerated-effects already; nested-optional + unhandled-diagnostic
older workers are now also marked superseded by their PR #836
implementation counterparts.

## Owned deliverables (lines 25-31)

Reframed from "Worker brief is already authored ... DESIGN/SCOPING shape"
to "Implementation worker brief landed on main via PR #836 merge ... do
not re-dispatch the older workers." Substrate-gap escalation reframed as
the exception path (was the expected path under the older DESIGN/SCOPING
worker assumption); expected path is direct implementation per design-doc
Director-actionable recommendation.

## Sub-briefs Pending (lines 78-86)

Reframed from "Dispatch nested-optional-flatten worker (DESIGN/SCOPING
produces substrate proposal → escalate)" to "Dispatch nested-optional-flatten
implementation worker (ungated; dispatchable Day-1 post-spawn)" + same
pattern for the other two classes. PR #836's 3 implementation workers are
now the canonical dispatch targets.

Added explicit SUPERSEDED list for the older workers (4 entries: 2
DESIGN/SCOPING + 2 effects-worker variants) with their respective
implementation-worker successors named.

## Discipline note

This finding was real, not an echo. PR #836 merging changed the substrate
of facts the manager brief grounds against. Same class as the §6a stale
framing on Release Manager + the B4.1 stale BLOCKING on Substrate Manager:
brief authored against pre-merge state; merge surfaces the staleness.

The matrix's pre-author verification invariant catches state-drift at
authoring time; the matrix's status-consistency rule catches dual-state
within a single brief. This finding is a third class: cross-PR state drift
(brief A's Pending list cites brief B's content; brief B merges and
brief A's content goes stale). Worth noting as a refresh-discipline
trigger separately from authoring discipline.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

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