Skip to content

β - #534

Merged
briansrls merged 37 commits into
mainfrom
session/eager-fox-851
Apr 18, 2026
Merged

β#534
briansrls merged 37 commits into
mainfrom
session/eager-fox-851

Conversation

@briansrls

Copy link
Copy Markdown
Contributor

Opened from session-dashboard for session eager-fox-851.

@briansrls

Copy link
Copy Markdown
Contributor Author

ChatGPT review in progress... (view conversation)

Check back in ~30 minutes for the full review.

@briansrls

Copy link
Copy Markdown
Contributor Author

claude-review · director-mode · β (#534)

✅ Ready to merge with one note + one coordination flag. This is an XL lane: DB-18 (Stage 2b WorkflowEffect + idempotency lens) and DB-15 R2 (Stage 2c test infrastructure + v3.std.resources bootstrap port). Scope match to dispatch is good.

DB-18 substrate audit (effects.dag WorkflowEffect)

Q Result
Q1 Cardinality ✅ LinearEffect { ops: NonEmptyList<OperationEffect> }, BranchEffect { arms: NonSingletonList<BranchArm> }, ParallelEffect { branches: NonSingletonList<WorkflowEffect> } — Track 9 primitives used correctly
Q2 Index/handle ⚠️ See note below — .dag side carries raw PortId; Rust side constructor-validates via branch_arm_of
Q3 Duplicated fact ✅ R3 ComposedEffect removal landed — no outer record pairing verdict with input list
Q4 Coproduct compression ✅ 4 variants, each structurally distinct (Linear / Branch / Loop / Parallel)
Q5 Construction authority ✅ Dag::branch_arm_of is the sole Rust constructor; Bool check is the typed-handle pattern
Q6 Representation duality ✅ Single canonical shape per variant

Q2 note (not a blocker this PR)

BranchArm.condition: PortId on the .dag side carries the raw substrate handle. Rust-side branch_arm_of gates construction to Bool-typed ports — but when the .dag authority becomes emit-table for match WorkflowEffect, a user writing BranchArm { condition: some_port, body: ... } would bypass that gate. DB-18 R2's BoolPortRef was the named recovery. The gap is acceptable today because:

  1. User code doesn't construct WorkflowEffect directly (analyzer is Rust);
  2. The lens-emit-table gap is what deferred the .dag dispatcher (see lenses/idempotency.dag staging note).

Follow-up not blocking this PR: when the class-5 gap closes and match on user sums becomes emit-table, re-visit whether BoolPortRef should land. Track 9 has the vocabulary. Log against the same dissolution trigger.

DB-15 R2 substrate audit

  • TestClaim.requires: List<ResourceReference> ✅
  • TestPredicate grows BehavioralObservation + MockBackedInvariant with independent-oracle / independent-predicate shapes — R2's tautology-avoidance rule is preserved structurally (not verified by re-running the producing lens).
  • TestObligation + obligation_for_claim + materialize_test_obligations — dependency-walk projection, no workflow structure.
  • v3.std.resources as a new module (not inline in verification.dag) — single authority. ResourceReference { target: DeclarationRef } uses the typed handle, not an opaque String identifier. This is the right shape (clears Q3 + Q2 at once).

Coordination flag — β vs γ (#535)

γ independently re-implements DB-15 R2 inline in verification.dag with ResourceReference { identifier: String }. β's placement (separate v3.std.resources module, DeclarationRef-typed target) is strictly cleaner. γ should close as superseded by this PR. I'll post that note on γ.

Fail-closed discipline (DB-18 Q3)

WorkflowIdempotencyReport = WorkflowCompositionVerdict(CompositionVerdict) | IdempotencyUnsupported(IdempotencyUnsupportedDetail) — diagnostic path has explicit carriers (variant_name, downstream_stage, reason), not silent Nones. Tests at lane2_stage_2b_db18_test::diagnostic_paths_name_stage2b verify each non-Linear variant produces a named diagnostic pointing at lane2_stage2b_idempotency_lens. ✅

Staging honesty (class-5 gap)

lenses/idempotency.dag as staging stub + Rust workflow_idempotency.rs as active authority: correct pattern. The .dag stub punts to report_unsupported_workflow_variant with an explicit reason; class-5 emit gap is explicitly named. This matches the PR #522 materialize_substituted_refined_decl staging discipline.

Summary

Merge-ready. Do the coordination with γ first (close γ), then β lands cleanly.

@briansrls briansrls mentioned this pull request Apr 18, 2026
Closed
@briansrls

This comment has been minimized.

@briansrls

Copy link
Copy Markdown
Contributor Author

ChatGPT review in progress... (view conversation)

Check back in ~30 minutes for the full review.

@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.

codex · gpt-5.4 · e7033711

⚠️ Review (blocking: 2, non-blocking: 1+/0-)

BLOCKING (2)

Root Cause

  • src/v3/compiler/src/workflow_idempotency.rs Stage 2b modeled workflow structure as a caller-authored mirror instead of a projection from L1 behavior authority → make the lens take NodeId/Dag authority (or derive a private implementation enum from it) and keep WorkflowEffect out of the public substrate until it is derived.
  • src/v3/std/resources.dag The bootstrap v3 port sketched a convenient record instead of preserving the authoritative resource-handle contract → either delete ResourceHandle for now or port the opaque/non-forgeable fields faithfully.

Non-blocking — Strengths

  • src/v3/std/verification.dag BehavioralObservation and MockBackedInvariant point at typed DeclarationRef and ResourceReference edges instead of inventing another string taxonomy, which matches the verification-substrate-consumer invariant.

ROADMAP — Incomplete

  • Lane 2 Stage 2b doc sync: The linked lane and dimension docs still describe the old NodeId plus record-report API, so the new shipped note is ahead of the cited design authority.
  • DB-15 R2 lock state: The linked docs/design-test-infra.md on this branch still marks R2 as a discussion draft with open questions 1–3 unresolved, so the Stage 2c status line is ahead of its cited lock.

⚠️ The verification-side typed edges look good, but the workflow proof path currently hangs off a parallel workflow mirror and the new resource handle weakens the existing resource authority.

CompositionVerdict::IdempotentComposition
}

pub fn analyze_workflow(_d: &Dag, workflow: &WorkflowEffect) -> WorkflowIdempotencyReport {

This comment was marked as resolved.

Comment thread src/v3/std/resources.dag
module v3.std.resources

import v3.spec.v3_l1 { DeclarationRef }

This comment was marked as resolved.

@briansrls

This comment has been minimized.

@briansrls

Copy link
Copy Markdown
Contributor Author

ChatGPT review in progress... (view conversation)

Check back in ~30 minutes for the full review.

@briansrls

This comment has been minimized.

Aligns session branch with main (36e29bc): v3 roadmap lives at repo root.

Made-with: Cursor
@briansrls

Copy link
Copy Markdown
Contributor Author

ChatGPT review in progress... (view conversation)

Check back in ~30 minutes for the full review.

@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.

codex · gpt-5.4 · 2c54138e

⚠️ Review (blocking: 2, non-blocking: 1+/0-)

BLOCKING (2)

Root Cause

  • src/v3/std/resources.dag the bootstrap port copied the resource model semantically but not mechanically -> reuse the authoritative field labels exactly so downstream consumers never need a rename bridge.
  • src/v3/std/verification.dag DB-15 models the same runtime resource in both the predicate payload and the claim-level dependency list -> make one surface authoritative and derive the other so obligation materialization cannot miss the mock transport.

Non-blocking — Strengths

  • src/v3/std/effects.dag BranchPredicateRef and IdempotencyUnsupportedDetail keep Stage 2b honest about predicate identity and unsupported control-flow cases instead of collapsing them to raw ports or silent None.

⚠️ The earlier Dag-authority and cap proof issues are fixed, but the v3 resource port still diverges from the dsl authority and DB-15 still duplicates the mock-resource fact across two substrates.

Comment thread src/v3/std/resources.dag Outdated
import v3.spec.v3_l1 { DeclarationRef }

type ResourceHandle {
resource_type: String

This comment was marked as resolved.

name: String
source: String
file_name: String
predicate: TestPredicate

This comment was marked as resolved.

@briansrls

This comment has been minimized.

@briansrls

This comment has been minimized.

@briansrls

Copy link
Copy Markdown
Contributor Author

ChatGPT Review

Based on the context provided from the documents you've uploaded, the review for the PR #534 is aligned with the modeling discipline outlined in several documents, particularly focusing on correctness, facts flow, and structure consistency.

Here are the key modeling principles to check for in your review:

1. Fail-closed

  • Ensure that if a failure occurs, it triggers a diagnostic and does not silently propagate errors or return None unexpectedly.

2. Illegal States Unrepresentable

  • Validate that the data models avoid combinations of fields that violate the design invariants, ensuring Option<T> doesn't hold dual meanings (such as "Not inferred" and "Failed" for the same field).

3. Facts Flow Forward

  • Ensure facts or computed information are not silently discarded across boundary stages (e.g., parsing → lowering → inference). If SourceSpan is computed, ensure it's passed along through the stages rather than dropped.

4. Coproduct Dissolution

  • Make sure any coproducts (enums with multiple variants) are either terminal (no further decomposition possible), scaffolded (requires future work to dissolve), or dissolved now (easily decomposed into simpler structures).

5. Single-authority Metadata

  • Check for any duplicate representations of facts (e.g., a type, span, or diagnostic value), ensuring that metadata is always represented in one canonical location.

6. API-level Enforcement

  • Ensure that any invariants that must be upheld are enforced at the API level, using the type system to prevent violations rather than relying on convention or documentation alone.

Calibration: Blocking vs Non-blocking

  • Any critical issues affecting facts flow, coproduct dissolution, or single-authority metadata are blocking and need to be resolved before merging.
  • Performance issues, unless they block correctness, are generally non-blocking and can be addressed later.

To confirm compliance, ensure that all changes in this PR adhere to the above principles and make use of structural checks like ensuring facts flow forward and coproduct dissolution as well as enforcing single-authority for metadata.

Let me know if you need additional specific checks based on the PR diff or the contents from the docs!


View conversation

- Add claim_obligation_resources (requires-only) and wire obligation_for_claim
- Document mechanical parity with dsl/std/resources.dag field order
- Ratchet ordered ResourceHandle fields + declare claim_obligation_resources in bootstrap

Made-with: Cursor
@briansrls

Copy link
Copy Markdown
Contributor Author

ChatGPT review in progress... (view conversation)

Check back in ~30 minutes for the full review.

@briansrls

Copy link
Copy Markdown
Contributor Author

claude-review · director-mode · β (#534) — round-3 resolution

✅ Both codex round-3 blockers resolved. Verified directly at PR head c5dfa4b.

Blocker 2: lib.rs crate-privacy

Codex: "workflow_idempotency.rs is a staged bridge... lib.rs exports its helper internals as durable API → keep the bridge crate-private and expose only the supported analyze_workflow entrypoint."

Verified at head: grep -nE "pub use (workflow_idempotency|lens_idempotency|compose_operation|operation_to_breaker|analyze_workflow)" lib.rs:

143: pub use lens_idempotency::analyze_workflow;

Only the supported entrypoint remains. compose_operation_effects and operation_to_breaker dropped from public API — they're now crate-private helpers inside workflow_idempotency.rs. ✅

Blocker 1: per-enum receipts in dag.rs DB-18 section

Codex: "DB-18 copied the new workflow/effect sums into the Rust substrate mirror as a bulk section, but the project's receipt discipline lives on each coproduct, not on a section header → add per-enum terminal/scaffold receipts."

Verified at head: 13 occurrences of TERMINAL / SCAFFOLD / 🟢 / 🟡 / "Dissolution trigger" within the DB-18 mirror section. Each coproduct (HttpMethodScalar, KeySource, CreateCause, IdempotentShape, BreakingShape, EffectShape, CompositionVerdict, WorkflowEffect, WorkflowIdempotencyReport, BranchArm, IdempotencyUnsupportedDetail) now carries its own receipt rather than inheriting the section-level framing. ✅

Bonus: main merge reconciled

Commit 401a98c: "merge: origin/main into session/eager-fox-851 — keep variant_payload". δ's (#536) variant_payload landing conflicted with β's effects surface; the merge message confirms the conflict resolution preserved δ's authority on variant_payload. No re-work needed from β's end.

Merge-ready

With both codex round-3 blockers resolved AND the main merge clean, β is merge-ready. Three review rounds deep, each round resolved the blockers from the prior — codex #1 (Python migration sequencing), codex #2 (ResourceHandle label parity + mock-transport single authority), codex #3 (per-enum receipts + crate-privacy). All resolved structurally.

γ (#535) remains closed as superseded.

@briansrls

Copy link
Copy Markdown
Contributor Author

claude-review · director-mode · β (#534) — merge authorization

All three codex review rounds resolved. Both recent blockers (per-enum receipts in dag.rs, crate-private helpers in lib.rs) verified at head. origin/main merged in with δ's variant_payload work preserved. CI: COMPLETED. Mergeable: MERGEABLE.

@eager-fox-851 — cleared to merge. Thanks for the three-round iteration; the final substrate is materially stronger than round 1 (BranchPredicateRef typed handle, MockBackedInvariant.mock_transport dissolved into single-authority requires, ResourceHandle label-parity enforced structurally via test, per-enum scaffold/terminal receipts on the DB-18 mirror).

γ (#535) remains closed as superseded.

@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.

codex · gpt-5.4 · c5dfa4b8

⚠️ Review (blocking: 1, non-blocking: 3+/1-)

BLOCKING (1)

Root Cause

  • src/v3/std/effects.dag DB-18 copied the receipt discipline into the Rust mirror in dag.rs but not into the .dag authority -> add per-type classification comments directly above WorkflowEffect and WorkflowIdempotencyReport.

Non-blocking — Strengths

  • src/v3/std/verification.dag TestClaim.requires is now the sole resource-edge authority and materialize_test_obligations projects only that list, which closes the earlier divergence path cleanly.
  • src/v3/std/resources.dag ResourceHandle now matches dsl/std/resources.dag on field labels, field order, and cap: Secret, so the bootstrap port no longer banks a second handle spelling.
  • docs/lane2-compile-time-proofs.md The Stage 2b writeup now accurately states the current native-only workflow hook instead of implying real service-lowered workflows are already wired.

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

  • src/v3/std/effects.dag BranchPredicateRef still carries a raw PortId with Bool validity enforced only by Rust-side construction; fold that witness into the reflected-handle work under ROADMAP Track 9.

ROADMAP — Verified

  • DB-15 schema lock: The shipped verification/resources shapes now match the docs' single-authority requires story and the faithful ResourceHandle port.
  • DB-18 API boundary: The code now matches the docs' claim that analyze_workflow is the only public Stage 2b Rust entrypoint.

ROADMAP — Incomplete

  • Stage 2b substrate reflection: Workflow facts still live only on native Value/Bind fields and src/v3/lenses/idempotency.dag remains a fail-closed stub until that fact is reflected through substrate.

⚠️ The earlier single-authority/resource issues are fixed, but the new Stage 2b sums still need explicit dissolution receipts in src/v3/std/effects.dag.

Comment thread src/v3/std/effects.dag
@@ -431,6 +433,66 @@ fn compose_effects(effects: List<OperationEffect>) -> CompositionVerdict {
}

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.

BLOCKING: The new DB-18 workflow/report sums in this source-of-truth .dag block still lack explicit 🟢/🟡 dissolution receipts, which violates modeling-discipline principle 4 at the substrate layer.

@briansrls

Copy link
Copy Markdown
Contributor Author

codex · gpt-5.4 · c5dfa4b8

⚠️ Review (blocking: 1, non-blocking: 3+/1-)

BLOCKING (1)

Root Cause

  • src/v3/std/effects.dag DB-18 copied the receipt discipline into the Rust mirror in dag.rs but not into the .dag authority -> add per-type classification comments directly above WorkflowEffect and WorkflowIdempotencyReport.

Non-blocking — Strengths

  • src/v3/std/verification.dag TestClaim.requires is now the sole resource-edge authority and materialize_test_obligations projects only that list, which closes the earlier divergence path cleanly.
  • src/v3/std/resources.dag ResourceHandle now matches dsl/std/resources.dag on field labels, field order, and cap: Secret, so the bootstrap port no longer banks a second handle spelling.
  • docs/lane2-compile-time-proofs.md The Stage 2b writeup now accurately states the current native-only workflow hook instead of implying real service-lowered workflows are already wired.

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

  • src/v3/std/effects.dag BranchPredicateRef still carries a raw PortId with Bool validity enforced only by Rust-side construction; fold that witness into the reflected-handle work under ROADMAP Track 9.

ROADMAP — Verified

  • DB-15 schema lock: The shipped verification/resources shapes now match the docs' single-authority requires story and the faithful ResourceHandle port.
  • DB-18 API boundary: The code now matches the docs' claim that analyze_workflow is the only public Stage 2b Rust entrypoint.

ROADMAP — Incomplete

  • Stage 2b substrate reflection: Workflow facts still live only on native Value/Bind fields and src/v3/lenses/idempotency.dag remains a fail-closed stub until that fact is reflected through substrate.

⚠️ The earlier single-authority/resource issues are fixed, but the new Stage 2b sums still need explicit dissolution receipts in src/v3/std/effects.dag.

- End-marker after WorkflowIdempotencyReport; clarify Track 9 cluster types
  are outside the effects algebra
- Add TERMINAL receipts for MemberDescent, IntraClusterCall, Cluster
- Note why no 🔴 appears in the DB-18 block (principle 4 alignment)

Made-with: Cursor
Per modeling-discipline principle 4: stamp BranchPredicateRef, BranchArm,
WorkflowEffect, IdempotencyUnsupportedDetail, and WorkflowIdempotencyReport
with explicit TERMINAL/SCAFFOLD receipts at the .dag authority.

Made-with: Cursor
briansrls added a commit that referenced this pull request Apr 18, 2026
…ferred)

Per director: doc should describe live state, not aspirational state.
β's PR #534 (eager-fox-851) shipped DB-18 Part 2 as Rust mirrors on
the computation substrate — WorkflowEffect / BranchArm /
BranchPredicateRef in dag.rs, Dag::branch_arm_of constructor,
ValueNode.lane2_workflow authority field, workflow_idempotency::
analyze_workflow consumer. The data-declaration authoring surface
(data my_flow: WorkflowEffect = ...) is NOT shipped; it's Part 3
follow-up.

Changes:
- Authority site: rewrote §"Authority site for WorkflowEffect" from
  "user-declared data declarations typed WorkflowEffect" (aspirational)
  to "ValueNode.lane2_workflow on the computation-substrate Value
  node at the workflow root" (shipped). Explicitly names Part 3 as
  reflection + data-declaration surface work.
- Naming: renamed BoolPortRef → BranchPredicateRef throughout to
  match β's shipped Rust type. bool_port_of → Dag::branch_arm_of
  (which takes root/port/body and builds the full BranchArm rather
  than a bare predicate ref).
- Source-to-handle contract: reframed as Part 3 forward-looking
  contract. The three escape hatches (named-port lookup, raw-id
  literal, synthesis) remain explicitly rejected.
- Consumer contract: rewrote the analyzer pseudocode as Rust to
  match β's shipped workflow_idempotency::analyze_workflow signature
  (takes `(&Dag, NodeId)`, returns WorkflowIdempotencyReport with
  Linear{verdict} / Unsupported{detail} variants).
- Acceptance: split into Part 2 (shipped in #534) and Part 3
  (follow-up). Part 3 items explicitly enumerated.
- STOP-AND-ESCALATE: reframed as Part 3 dispatch rules; added a rule
  for the ValueBody/FieldValue encoding question (substrate extension
  requires escalation, not in-flight patch).
- Worked example C: rewrote in Rust using Dag::branch_arm_of +
  try_register_lane2_workflow_effect to match shipped API.
- Rejected alternatives: updated the hosting discussion — the
  adopted host is ValueNode.lane2_workflow; data-declaration surface
  reuse (not a new declaration kind) is still the Part 3 authoring
  surface.
- Cross-references: added PR #534 reference (β's shipped impl).
- lane2-compile-time-proofs.md §Stage 2b subsection: updated type
  block to show shipped Rust shape; rewrote the analyzer signature
  + authority-site prose to match live state; Part 3 follow-up
  named explicitly.

Also addresses the codex BLOCKING at d7f226a and dea9fb4 (ValueBody
substrate encoding for BoolPortRef — now obviated because the
authority site is ValueNode.lane2_workflow, not ValueBody; Part 3
explicitly names the data-declaration-surface-vs-ValueBody question
as an escalation trigger rather than an in-flight patch).

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

Copy link
Copy Markdown
Contributor Author

ChatGPT review in progress... (view conversation)

Check back in ~30 minutes for the full review.

@briansrls

Copy link
Copy Markdown
Contributor Author

Meta-review in progress... (view conversation)

Loop-health check: is this review cycle making forward progress, or shifting debt? Posts in ~5-15 minutes.

try_register_lane2_workflow_effect only attaches to Value/Bind; the first
node in allocation order is not guaranteed to be one (CI saw Transform/etc.
first). Scan for the first Value or Bind instead.

Made-with: Cursor
briansrls added a commit that referenced this pull request Apr 18, 2026
Inline review at 2026-04-18T21:27:28Z flagged specific verifiable
discrepancies between my remediation and β's actual PR #534 impl.
Verified each claim against β's current head (a4dfa2e) and fixed
all five:

1. **PR #534 is OPEN, not merged.** Changed "shipped in PR #534" to
   "implemented in PR #534 (OPEN)" throughout. The doc now tracks
   #534's head SHA; "OPEN" marker becomes the merge SHA when #534
   lands.

2. **WorkflowEffect is 🟡 SCAFFOLD, not 🟢 terminal.** β's impl
   explicitly marks it as scaffold ("Four-variant workflow sum
   aligned with effects.dag; graduates to 🟢 when all four variants
   have consumers"). The doc now reflects this with the graduation
   trigger spelled out (all four variants have consumers + reflected
   into substrate.dag).

3. **LinearEffect.ops is NonEmptyList<OperationEffect>, not
   List<OperationEffect>.** β did NOT adopt R2's monoidal-identity
   relaxation; shipped NonEmptyList. The doc now reflects live:
   - Substrate-changes block shows NonEmptyList<OperationEffect>
   - Q1 audit explains why β chose the tighter carrier (no
     representation duality between "zero-ops LinearEffect" and
     "no workflow here")
   - Pattern 3 algebraic-form explains the free-semigroup vs
     free-monoid distinction (workflow input carrier is the
     semigroup; monoidal identity lives at the algebra boundary)
   - Q6 no-representation-duality argument reworked: no-workflow
     is lane2_workflow: None, NOT a zero-ops LinearEffect
   - Worked Examples A and C use NonEmptyList constructor form
   - Open Q §4 updated to NonEmptyList<WorkflowEffect> vs
     NonEmptyList<OperationEffect>
   - Part 2 test-coverage list drops the "empty-LinearEffect
     monoidal-identity case" (not representable)

4. **lane2_workflow lives on BOTH Value AND Bind behaviors**, not
   just Value. β's try_register + lane2_workflow_effect_at both
   pattern-match the two variants. Authority-site section rewritten
   to show fields on both behaviors and the accessor impls. Added
   a paragraph explaining why both (Value for top-level data-decl
   roots; Bind for let-bound sub-workflows).

5. **Dag::branch_arm_of takes (&self, port, body), no root arg.**
   Port-type resolution uses the Dag's own graph state. Substrate-
   changes block, source-to-handle contract (Part 3), Worked
   Example C, Acceptance items, and STOP-AND-ESCALATE rules all
   updated to the 2-arg signature.

All five are factual fixes against live code, not stylistic changes.
The Q4 dissolution receipt and overall design contract are preserved.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- ROADMAP: DB-18 lands algebra + native Rust analysis, not full substrate
  self-inspection for workflow facts; point at Reflection boundary
- dag: mark try_register_lane2_workflow_effect as explicit scaffold API

Made-with: Cursor
briansrls added a commit that referenced this pull request Apr 18, 2026
* docs: DB-18 Part 1 — WorkflowEffect substrate carrier (Stage 2b)

Promotes Stage 2b's workflow control-flow shape from a lens-level
re-derivation to a first-class substrate carrier. Four-variant
coproduct (LinearEffect | BranchEffect | LoopEffect | ParallelEffect)
with BranchArm helper record; Q4 dissolution receipt stamped inline;
Q1–Q6 substrate-principle audit stamped inline. LinearEffect is the
only Stage 2b consumer — other variants emit C-8 fail-closed
diagnostics naming the downstream stage. Coexists orthogonally with
PR #529's CompositionVerdict (input structure vs output verdict; no
enclosing record pairs them).

Part 1 is design only; Part 2 implementation is gated on PR #529
landing (pre-start gate named in the doc).

Also adds an additive subsection to lane2-compile-time-proofs.md
§Stage 2b pointing at DB-18 — placed between Escalation and Pre-start
gate, both of which PR #529 leaves untouched — so merges without
conflict.

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

* Merge main into session/bright-bat-435

Resolves conflict in docs/lane2-compile-time-proofs.md after PR #529
(ComposedEffect reshape) landed on main. Kept the DB-18 insertion in
place, superseded the pre-#529 Pre-start gate text with #529's
"cleared" version, and kept #529's new Stage 2b / Track 17a
design-axis paragraph. Updated the DB-18 "Design:" line to reflect
that the Part 2 gate is now cleared (was forward-looking at write
time).

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

* docs: DB-18 — update WorkflowEffectConcern line-range citation

Post-PR #529 the file grew from ~569 to 673 lines (partition types,
CompositionVerdict, new fns); WorkflowEffectConcern shifted from
:565-569 to :669-673. Cite both the new and old ranges so the
historical anchor is preserved.

Addresses the single callout in the claude-review on #531.

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

* WIP: B'

* WIP: B'

* WIP: B'

* WIP: B'

* WIP: B'

* WIP: B'

* docs: DB-18 R2 — typed BoolPortRef witness, empty LinearEffect, locked authority site

Responds to two structural reviews (ChatGPT: 3 BLOCKING; codex: 2
BLOCKING) on PR #531 R1 draft.

BLOCKING (ChatGPT 1+2 / codex 1): `branch_arm_of(port, body)->Option`
and raw `condition: PortId` kept the "port is Bool-typed" invariant
at constructor-convention level — a non-Bool branch condition is a
user-reachable modeling error, not a benign absence. R2 introduces
`BoolPortRef` as a Track 9-style typed opaque handle (third Track 9
primitive after ParamRef / TransformRef from DB-9 R2.1). Sole
constructor `bool_port_of(dag, port) -> BoolPortRef?` validates the
port's declared type is Bool; no unsafe escape hatch. BranchArm's
condition field is `BoolPortRef`, not `PortId` — raw-literal
construction around a non-Bool port is a type error. Fail-closed
discipline relocated to the caller boundary: lowering emits
`Diagnostic::BranchConditionNotBool` on None per C-8, explicitly
required as a Part 2 review gate.

BLOCKING (ChatGPT 3): `LinearEffect.ops: NonEmptyList<OperationEffect>`
excluded the empty word, but `compose_effects([])` returns
`IdempotentComposition` (the monoidal identity). The asymmetry
between carrier and algebra rejected real workflow shapes (no-op
paths, empty branch arms). R2 relaxes to `List<OperationEffect>`;
the empty case is the honest monoidal identity representation.

BLOCKING (codex 2): Open Question §5 left authority site for
WorkflowEffect values undefined ("any declaration slot") — a Q5
single-authority violation. R2 locks the authority site to
user-declared `data` declarations typed `WorkflowEffect`. No new
declaration kind (per feedback_std_over_patterns — reuse `data`,
don't invent WorkflowDeclaration); no sidecar table (a WorkflowEffect
is single-declaration-local, unlike DB-9 R2.1's Cluster sidecar).
Closes R1 Open Q §5.

Non-blocking (codex): lane2-compile-time-proofs.md said PR #529 gate
was cleared while DB-18 treated it as pre-start dependency. R2 syncs
— DB-18 Pre-start gate now reads "CLEARED" with the commit SHA
preserved for record; the original gate rationale is preserved as a
rejected-alternative-style trail.

Non-blocking (ChatGPT): Acceptance item 1 tightened — locked VARIANTS
and MANDATORY fields now; additive extension fields (LoopEffect.bound
for Stage 2d, ParallelEffect.commutativity for Stage 2e) explicitly
flagged as downstream-binding refinements. Fixes the doc-coherence
gap between Open Q §3 ("Stage 2d will almost certainly need bound")
and the previous "exact payload shapes" language.

Also adds R-alt-E (raw PortId rejection) and R-alt-G (deferred-host
rejection) to the Rejected-alternatives list, preserving the design
rationale for reviewers who hit this design again.

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

* docs: DB-18 R2 propagation — two stale pre-R2 references in constraints + Pattern 3

ChatGPT review at bb08594 flagged "two different carriers described"
because R2 was mid-edit at that SHA. Most of the propagation landed
in 1ac587a, but two genuine gaps remained:

1. Constraint 3 (Bounded kernel invariant) still cited
   `LinearEffect.ops: NonEmptyList<OperationEffect>` — updated to
   `List<OperationEffect>` with an explicit "empty list is a
   well-defined leaf" clause.

2. Pattern 3 (Algebraic-form) described the carrier as "the free
   monoid on OperationEffect less the empty word" — inconsistent
   with R2's empty-word inclusion. Updated to read "the free monoid
   on OperationEffect — including the empty word, which is the
   monoidal identity the R2 relaxation explicitly preserves,"
   matching the Q1 audit and the worked examples.

The reviewer's other concerns ("Q4 table still cites PortId", "audit
Q1/Q2/Q6 still reasons about NonEmptyList", "worked examples",
"acceptance", "lane2 subsection", "authority site still open") were
already resolved by R2 at 1ac587a — their review's line citations
are against the pre-R2 WIP snapshot, not the current tip.

All remaining mentions of `PortId` in the doc are now in appropriate
historical/constructor-signature context (R1 revision history,
R-alt-E rejection record, bool_port_of's input-parameter type,
port_of's return type, Worked Example C's authoring comment).
All remaining mentions of `NonEmptyList` are in Track 9 primitive
references (the family still exists; DB-18 just doesn't consume it
post-R2) or historical R1 references.

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

* WIP: B'

* docs: DB-18 R2.2 — lock source-to-handle contract for BranchArm.condition

PR #531 ChatGPT review round 3 (at 1ac587a) accepted the R2 carrier
direction and the R2.1 propagation fix, and flagged ONE remaining
BLOCKING gap: the R2 doc locked *where* WorkflowEffect values live
(user-declared `data` typed WorkflowEffect) but not *how* a user
writes a condition expression that lowering resolves to a BoolPortRef.

Without that lock, Part 2 could invent three distinct single-authority
escape hatches: (a) name-keyed port recovery (violating
feedback_no_metadata_markers), (b) raw NodeId/PortId literals
(violating structural opacity), or (c) lowering-time synthesis of a
bool witness without a source anchor (violating
feedback_declare_facts_dont_derive). Each would reopen the Q5
single-authority question at the source boundary.

R2.2 adds §"Source-to-handle contract" under the existing Authority
Site section, locking the single legal path:

  source: ordinary Bool-typed expression (no DB-18-specific syntax)
  lowering: expression → sub-DAG → output port → bool_port_of
  fail-closed: Diagnostic::BranchConditionNotBool on None, with span
  pointing at the source expression (not the wrapping BranchArm)
  rejected: named-port lookup, raw-id literals, anchor-less synthesis

Per feedback_std_over_patterns, reuses the existing Bool-expression
surface (same form used by if/where/modifier predicates) rather than
inventing a DB-18 port-reference syntax.

Part 1 acceptance item 7 added to lock the contract. Part 2 acceptance
item 4 tightened (source-span-precise diagnostic) and new item 4b
added (single source-to-handle path with three escape hatches
explicitly rejected as Part 2 review gates).

This closes the ChatGPT review round 3 BLOCKING; the review's other
observations (R2 direction good, R2.1 propagation good, LOOP HEALTH
converging) are carried forward unchanged.

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

* WIP: B'

* WIP: B'

* WIP: B'

* WIP: B'

* docs: DB-18 — fix Workflow B type error in worked example

codex review at d7f226a (APPROVE, 0 BLOCKING) flagged one
non-blocking fix-in-PR-if-easy: Workflow B shows
`BrokenBy.first_breaker.shape: IsBreaking(AppendEffect)` but
BreakingOperation.shape is typed BreakingShape (not EffectShape),
so the outer IsBreaking wrapper is redundant and the example
wouldn't typecheck as written. Updated to show AppendEffect
directly and added a parenthetical explaining why no wrapper
is needed.

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

* WIP: B'

* WIP: B'

* WIP: B'

* WIP: B'

* WIP: B'

* docs: DB-18 — remediate to live state (β's PR #534 shipped; Part 3 deferred)

Per director: doc should describe live state, not aspirational state.
β's PR #534 (eager-fox-851) shipped DB-18 Part 2 as Rust mirrors on
the computation substrate — WorkflowEffect / BranchArm /
BranchPredicateRef in dag.rs, Dag::branch_arm_of constructor,
ValueNode.lane2_workflow authority field, workflow_idempotency::
analyze_workflow consumer. The data-declaration authoring surface
(data my_flow: WorkflowEffect = ...) is NOT shipped; it's Part 3
follow-up.

Changes:
- Authority site: rewrote §"Authority site for WorkflowEffect" from
  "user-declared data declarations typed WorkflowEffect" (aspirational)
  to "ValueNode.lane2_workflow on the computation-substrate Value
  node at the workflow root" (shipped). Explicitly names Part 3 as
  reflection + data-declaration surface work.
- Naming: renamed BoolPortRef → BranchPredicateRef throughout to
  match β's shipped Rust type. bool_port_of → Dag::branch_arm_of
  (which takes root/port/body and builds the full BranchArm rather
  than a bare predicate ref).
- Source-to-handle contract: reframed as Part 3 forward-looking
  contract. The three escape hatches (named-port lookup, raw-id
  literal, synthesis) remain explicitly rejected.
- Consumer contract: rewrote the analyzer pseudocode as Rust to
  match β's shipped workflow_idempotency::analyze_workflow signature
  (takes `(&Dag, NodeId)`, returns WorkflowIdempotencyReport with
  Linear{verdict} / Unsupported{detail} variants).
- Acceptance: split into Part 2 (shipped in #534) and Part 3
  (follow-up). Part 3 items explicitly enumerated.
- STOP-AND-ESCALATE: reframed as Part 3 dispatch rules; added a rule
  for the ValueBody/FieldValue encoding question (substrate extension
  requires escalation, not in-flight patch).
- Worked example C: rewrote in Rust using Dag::branch_arm_of +
  try_register_lane2_workflow_effect to match shipped API.
- Rejected alternatives: updated the hosting discussion — the
  adopted host is ValueNode.lane2_workflow; data-declaration surface
  reuse (not a new declaration kind) is still the Part 3 authoring
  surface.
- Cross-references: added PR #534 reference (β's shipped impl).
- lane2-compile-time-proofs.md §Stage 2b subsection: updated type
  block to show shipped Rust shape; rewrote the analyzer signature
  + authority-site prose to match live state; Part 3 follow-up
  named explicitly.

Also addresses the codex BLOCKING at d7f226a and dea9fb4 (ValueBody
substrate encoding for BoolPortRef — now obviated because the
authority site is ValueNode.lane2_workflow, not ValueBody; Part 3
explicitly names the data-declaration-surface-vs-ValueBody question
as an escalation trigger rather than an in-flight patch).

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

* WIP: B'

* WIP: B'

* WIP: B'

* WIP: B'

* docs: DB-18 — verify-against-β fixes (5 points)

Inline review at 2026-04-18T21:27:28Z flagged specific verifiable
discrepancies between my remediation and β's actual PR #534 impl.
Verified each claim against β's current head (a4dfa2e) and fixed
all five:

1. **PR #534 is OPEN, not merged.** Changed "shipped in PR #534" to
   "implemented in PR #534 (OPEN)" throughout. The doc now tracks
   #534's head SHA; "OPEN" marker becomes the merge SHA when #534
   lands.

2. **WorkflowEffect is 🟡 SCAFFOLD, not 🟢 terminal.** β's impl
   explicitly marks it as scaffold ("Four-variant workflow sum
   aligned with effects.dag; graduates to 🟢 when all four variants
   have consumers"). The doc now reflects this with the graduation
   trigger spelled out (all four variants have consumers + reflected
   into substrate.dag).

3. **LinearEffect.ops is NonEmptyList<OperationEffect>, not
   List<OperationEffect>.** β did NOT adopt R2's monoidal-identity
   relaxation; shipped NonEmptyList. The doc now reflects live:
   - Substrate-changes block shows NonEmptyList<OperationEffect>
   - Q1 audit explains why β chose the tighter carrier (no
     representation duality between "zero-ops LinearEffect" and
     "no workflow here")
   - Pattern 3 algebraic-form explains the free-semigroup vs
     free-monoid distinction (workflow input carrier is the
     semigroup; monoidal identity lives at the algebra boundary)
   - Q6 no-representation-duality argument reworked: no-workflow
     is lane2_workflow: None, NOT a zero-ops LinearEffect
   - Worked Examples A and C use NonEmptyList constructor form
   - Open Q §4 updated to NonEmptyList<WorkflowEffect> vs
     NonEmptyList<OperationEffect>
   - Part 2 test-coverage list drops the "empty-LinearEffect
     monoidal-identity case" (not representable)

4. **lane2_workflow lives on BOTH Value AND Bind behaviors**, not
   just Value. β's try_register + lane2_workflow_effect_at both
   pattern-match the two variants. Authority-site section rewritten
   to show fields on both behaviors and the accessor impls. Added
   a paragraph explaining why both (Value for top-level data-decl
   roots; Bind for let-bound sub-workflows).

5. **Dag::branch_arm_of takes (&self, port, body), no root arg.**
   Port-type resolution uses the Dag's own graph state. Substrate-
   changes block, source-to-handle contract (Part 3), Worked
   Example C, Acceptance items, and STOP-AND-ESCALATE rules all
   updated to the 2-arg signature.

All five are factual fixes against live code, not stylistic changes.
The Q4 dissolution receipt and overall design contract are preserved.

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

---------

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

Copy link
Copy Markdown
Contributor Author

⚠️ ChatGPT review abandoned — conversation returned only 469 chars after 77 minutes

The response was shorter than the expected review length. The bot will start a fresh conversation on the next push.

View conversation

@briansrls

Copy link
Copy Markdown
Contributor Author

claude-review · director-mode · β (#534) — prepare to merge once CI completes

CI currently running (ci + v3 IN_PROGRESS). Once green, you're cleared to merge — three codex rounds all resolved at prior SHA c5dfa4b, and 7aca52d self-corrected the lane2_stage_2b_db18_test::<4 tests> anchor assumption from the previous commit. Assuming 71e225e keeps CI green, nothing else on my end.

One main-branch heads-up: 36e29bc8e promoted src/v3/ROADMAP.md → root ROADMAP.md. You already touch root ROADMAP.md (+8/-4), so your diff should apply cleanly.

Thanks for the iteration. γ (#535) stays closed.

@briansrls

Copy link
Copy Markdown
Contributor Author

ChatGPT review in progress... (view conversation)

Check back in ~30 minutes for the full review.

@briansrls

Copy link
Copy Markdown
Contributor Author

Meta-Review (Loop Health)

Generated by gpt-5-4-pro

According to a document from 2026-04-18, this loop has crossed into diminishing returns. The right call is ⚖️ SHIP_WITH_DEBT. The loop did make real progress, but the remaining delta is now implementation debt that needs a follow-up PR, not another review lap.

Loop summary

I count 6 substantive review rounds in the attached history: 4 codex reviews and 2 real browser principle audits. If you count polling and failures, the log has 18 review events total, including 3 abandoned browser runs and one generic non-review fallback. I can only see about 4 reviewed revision states/pushes from the attached artifacts; the exact GitHub commit count is not recoverable from the files you gave me. Visible elapsed time is about 4 hours 8 minutes, from 17:10:26Z to 21:18:40Z on 2026-04-18.

Forward progress evidence

The loop did not stall immediately. It retired real blockers.

DB-15 is genuine forward progress. Early codex reviews flagged two real authority problems: the v3 resource port diverged from dsl/std/resources.dag, and DB-15 duplicated the mock-resource fact across predicate payload and claim-level dependency list. Later reviews explicitly say those are fixed: TestClaim.requires is now the sole authority, materialize_test_obligations projects from it, and ResourceHandle now matches the DSL authority’s labels and order. That is exactly the kind of “find a class, then collapse it to one authority” movement you want.

DB-18 also improved materially. The first browser audit blocked on BranchArm.condition: PortId as a raw-handle substrate mistake. By the end of the loop, the review burden had narrowed to receipt/classification comments and the still-open reflection boundary, not the original raw-shape blocker. The current diff reflects that change: it introduces BranchPredicateRef, keeps the Stage 2b bridge crate-private, and limits the public API to analyze_workflow rather than exporting bridge internals. That is structural progress, not cosmetic churn. See the current diff at lines 10–23, 564–589, and 1169–1372; the review history tracks the same contraction of blockers.

There are real consumers now, but only partially. Stage 2c has a real downstream path: requires flows into TestObligation. Stage 2b has a real Rust consumer plus tests. But Stage 2b still does not have the consumer that matters for the thesis: the workflow fact is not reflected into substrate for .dag lenses to consume. So principle 1 is only half satisfied.

Debt accumulation evidence

The loop is also shifting debt, not just dissolving it.

The recurring class never truly graduated: workflow/idempotency facts still live outside reflected substrate. Codex first called it a “caller-authored mirror.” The browser then called it “hidden Rust fields on generic substrate nodes.” The final codex review reduced that to “workflow facts still live only on native Value/Bind fields.” Same disease, three phrasings. That is the clearest sign that principle 3 is only partially met: the loop kept fixing local manifestations without promoting the class into a structural rule that prevents the next reappearance.

Net scaffolds also increased. The current diff carries a native-only lane2_workflow pocket, a Rust-side idempotency bridge, a fail-closed .dag idempotency stub, and a temporary v3.std.resources bridge. That is not invisible debt — which is good — but it is still debt. The loop is healthier than a silent debt shift because the compromises are named, but the substrate is still growing staging pockets faster than it is deleting them. See the current diff at lines 12–23, 221–230, 564–589, 1162–1168, and 1272–1298.

The review work is also moving up the abstraction ladder. The final codex blocker is no longer “your substrate shape is wrong”; it is “add per-type classification comments directly above WorkflowEffect and WorkflowIdempotencyReport.” That is useful discipline, but it is also the sign that the loop has mostly exhausted review value and is now repricing documentation and receipt hygiene while the main remaining debt is implementation work: substrate reflection.

There is also process debt: three browser attempts died without producing signal, and one browser output was a generic checklist rather than a real review. That burned time without changing the decision surface.

Cheating signal

The implementer is not hiding compromises. This is cheating with accounting, which is the acceptable kind.

The current diff explicitly names the Stage 2b reflection boundary, says lane2_workflow is native-only, keeps workflow_idempotency crate-private, exposes only analyze_workflow publicly, and documents the resources bridge as a bootstrap port with a dissolution trigger. That is not quiet “good enough for now” behavior; it is explicit staging debt. See the current diff at lines 10–23, 564–589, 721–730, 1162–1168, and 1272–1282.

The most recent fixes are mostly structural: typed branch-predicate carrier, sole requires authority, faithful ResourceHandle, and reduced public API surface. The only conspicuously “good enough for now” piece left is the native-only workflow staging pocket — and it is openly labeled as such.

Path to convergence

Because this should ship, the convergence path is a follow-up artifact, not another review round.

Acceptable debt to carry in this PR:

  1. the native-only lane2_workflow / try_register_lane2_workflow_effect staging pocket,
  2. the Rust-side analyze_workflow bridge while src/v3/lenses/idempotency.dag remains a fail-closed stub,
  3. the temporary v3.std.resources bridge,
  4. Stage 2c runner/execution wiring.

The follow-up must be one explicit ROADMAP active-deferral or scheduled-deletion row for the Stage 2b reflection boundary, naming the deletion trigger precisely: reflect the workflow fact through substrate plus realization wiring, then delete the native-only staging pocket as the primary authority. The ROADMAP already says it is the tracker of record; use it.

The smallest set of next actions that would justify another iteration later is straightforward: reflect the workflow carrier into substrate, wire one real lowering path to populate it, and only then rerun review. Until those land, another review round mostly just rediscovers the same reflection split in slightly different words.

Meta-verdict

⚖️ SHIP_WITH_DEBT

This loop made real progress. It did not bluff. But it has now reached the point where the remaining issue is known, named, and implementation-shaped: Stage 2b workflow facts still do not live in reflected substrate. Another review round before that follow-up lands is negative marginal value. Merge this PR with the debt tracked explicitly, then do the reflection follow-up as its own PR.


View conversation

…ection

ChatGPT meta-review SHIP_WITH_DEBT: name PR #534 staging debt under Active
deferrals with explicit dissolution trigger (reflect workflow fact, then drop
native-only pocket as primary authority).

Made-with: Cursor
@briansrls
briansrls merged commit 360661f into main Apr 18, 2026
3 checks passed
briansrls added a commit that referenced this pull request Apr 18, 2026
PR #534 shipped the R1 carrier shape (BranchPredicateRef record +
arm-level validator + NonEmptyList<OperationEffect>); DB-18 Part 1
(PR #531) locks the R2 shape (BoolPortRef typed handle + handle-level
validator + List<OperationEffect>). Names the two as dual halves of
DB-18 Part 2 alongside the existing reflection-boundary debt row.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@briansrls briansrls mentioned this pull request Apr 19, 2026
Merged
@briansrls
briansrls deleted the session/eager-fox-851 branch June 1, 2026 18:42
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