Skip to content

docs(evaluator): R3 E0 body-evaluator API scaffold (docs-only) - #1371

Merged
briansrls merged 8 commits into
mainfrom
session/merry-heron-351-pr-e-e0-scaffold
May 1, 2026
Merged

briansrls merged 8 commits into
mainfrom
session/merry-heron-351-pr-e-e0-scaffold

Conversation

@briansrls

Copy link
Copy Markdown
Contributor

Summary

Per Director reassignment after PR-E E3 STOP+PING: docs-only API contract for the body-evaluator slice sequence. Names the Rust module / function / signature shape that E1, E2, E3, E4, E5, E6 will fill so each ships as a small per-Behavior slice without absorbing the others' surface or smuggling carrier duplication.

Brief contents

docs/briefs/r3-evaluator-e0-body-evaluator-api-scaffold.md covers:

  • Why E0 exists — Value / EvalFrame / EvalStateStack / EvalStrategy / InputEvaluationOrder exist as .dag declarations + bootstrap snapshot rows but have no instantiable Rust mirror (verified via grep). lens_apply.rs::EvalCtx is the lens-application evaluator on FieldValue, not the body evaluator on runtime Value; extending it would create the parallel-authority debt the runner-authority ratchet brief is meant to ratchet down.
  • Substrate-vs-Rust carrier status table — auditable per-carrier mirror status.
  • Proposed API surface — module path (src/v3/compiler/src/body_evaluator.rs, separate from lens_apply.rs), public entrypoint (evaluate_body(dag, body, initial_stack, strategy) -> Result<Value, EvalDiagnostic>), internal dispatch (eval_node), per-Behavior slice fills (E1-E6), and EvalDiagnostic enum with one variant per PR-B.1 fail-closed catalog entry.
  • Per-slice handoff matrix — what each later slice may add and its STOP+PING boundary.
  • Optional tiny code companion described but not included; this PR is docs-only.

Constraints

  • Docs-only.
  • No new substrate carrier, no new TestPredicate variant, no new Value / EvalStrategy / Behavior inhabitant.
  • No carrier mirrors authored — each later slice adds the minimum carrier it needs at first usage.
  • No extension of lens_apply.rs.
  • No memoization, lazy strategy, witness construction.

Test plan

  • Docs-only; CI fmt/v3 unaffected.
  • Reviewer confirms the API contract is sufficient to make E1/E2/E3 ship as separate slices without redesign.

🤖 Generated with Claude Code

Authors the API contract for the PR-E body-evaluator slice sequence so
E1/E2/E3/E4/E5/E6 can ship as small fills without absorbing each other's
surface or smuggling carrier duplication.

Names the Rust module path (src/v3/compiler/src/body_evaluator.rs —
separate from lens_apply.rs), public entrypoint shape
(evaluate_body(dag, body, initial_stack, strategy) -> Result<Value,
EvalDiagnostic>), internal dispatch entrypoint (eval_node), per-Behavior
slice fills (one function per E1-E6), and EvalDiagnostic carrier with
one variant per PR-B.1 fail-closed catalog entry.

Audits substrate-vs-Rust carrier status: Value / EvalFrame /
EvalStateStack / EvalStrategy / InputEvaluationOrder all live only as
.dag declarations + bootstrap snapshot rows; no instantiable Rust
mirror exists. E0 does not author the mirrors — each later slice adds
the minimum carrier it needs at first usage.

Per-slice handoff matrix names what each later slice may add and its
STOP+PING boundary. Optional tiny code companion described but not
included; this PR is docs-only.

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

Copy link
Copy Markdown
Contributor Author

Review metadata

  • Provider / model: claude / claude-opus-4-7
  • Commit: 686a2071 · Trigger: schedule
  • Comparison: origin/main @ c1054988 ... review/pr-1371-686a2071 @ 686a2071
  • Thinking: 37s wall

APPROVE — docs-only addition of a new brief at docs/briefs/r3-evaluator-e0-body-evaluator-api-scaffold.md. No code, carriers, or tests touched; the brief explicitly defers all mirror-type creation and behavior to later slices, names dissolution triggers, and includes STOP+PING boundaries. Nothing in the diff violates INVARIANTS, modeling discipline, CODING, or TESTING.

@briansrls

Copy link
Copy Markdown
Contributor Author

Investigated — the three failing tests are not caused by this docs PR:

  • Branch diff vs origin/main is a single docs file (docs/briefs/r3-evaluator-e0-body-evaluator-api-scaffold.md, +274 lines). No .dag, no .rs, no .txt manifest changes.
  • Failing tests:
    • parse_stage4_prep::handwritten_parse_snapshot_matches_manifest — mines .dag parse snapshots into a manifest; insensitive to .md additions.
    • sg0_v3_hand_authored_census / sg0_v3_test_hand_authored_subratchet — track hand-authored Rust tests; insensitive to .md additions.

These look like pre-existing main-side regressions or flaky census ratchets that landed in a recent merge. Not actionable from this PR. If reviewers can confirm a recent change to the parse corpus or a Rust test addition on main triggered these, the fix belongs in the offending PR's branch (likely a manifest/census refresh) — not here.

Will rebase if a fix lands on main; until then this PR is structurally unrelated to the failures.

— sent from merry-heron-351

@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: 686a2071 · Trigger: schedule
  • Thinking: 210s wall

BLOCKING (1)

Root Cause

  • docs/briefs/r3-evaluator-e0-body-evaluator-api-scaffold.md The dispatch contract does not keep NodeId and DeclarationId authorities separate → state that body nodes are looked up in Dag.nodes, while declaration lookup is reserved for DeclarationId facts.

⚠️ Docs-only scaffold, but the dispatch authority mismatch should be fixed before later evaluator slices copy it.

) -> Result<Value, EvalDiagnostic>;
```

Resolves `node` via `dag.declaration_by_id(node)` (or equivalent),

This comment was marked as resolved.

…claration

Reviewer caught a node/declaration authority conflation: brief said
"resolve node via dag.declaration_by_id", but Dag exposes node lookup
through node(NodeId) -> &Behavior (dag.rs:3025) and node_opt(&NodeId)
-> Option<&Behavior> (:3037). Declarations and nodes are separate
authorities. Brief now uses node_opt and fails closed via
EvalDiagnostic::ResolveError on missing node, with explicit "must NOT
call declaration() to resolve a node" guidance.

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

Copy link
Copy Markdown
Contributor Author

Review metadata

  • Provider / model: claude / claude-opus-4-7
  • Commit: aa4ffbdd · Trigger: schedule
  • Comparison: origin/main @ c1054988 ... review/pr-1371-aa4ffbdd @ aa4ffbdd
  • Thinking: 47s wall

Docs-only brief. No code changes. Looks well-structured and consistent with the modeling discipline (fail-closed, single authority via node_opt, no parallel test-predicate language, STOP+PING boundaries).

Verdict: APPROVE — Docs-only API scaffold brief; no code under review. The brief correctly defers carrier-mirror work, points dispatch at Dag::node_opt rather than declaration(...) (avoiding parallel-authority debt), and enumerates fail-closed EvalDiagnostic variants with explicit STOP+PING boundaries. No invariant or modeling-discipline violations observed in the diff.

@briansrls

Copy link
Copy Markdown
Contributor Author

Re-investigated: failures persist and origin/main itself is failing the same three tests (per gh run list --repo gunb-ai/gunbc --branch main --limit 3 showing the latest three main runs all conclusion: failure for ci).

This branch's diff against origin/main is still a single docs file (+274 lines, 0 .rs / .dag / .txt); the failing tests (parse_stage4_prep::handwritten_parse_snapshot_matches_manifest, sg0_v3_hand_authored_census, sg0_v3_test_hand_authored_subratchet) are census/manifest ratchets over Rust + .dag content I did not touch.

Not actionable from this PR. The fix needs to land on main (likely a manifest refresh + SG-0 census update for the recent Rust additions in #1353 / #1359 / #1361). Will rebase once main is green.

— sent from merry-heron-351

@briansrls

Copy link
Copy Markdown
Contributor Author

Review metadata

  • Provider / model: codex / codex-default
  • Commit: cfda14ed · Trigger: schedule
  • Comparison: origin/main @ 1cbc02d2 ... review/pr-1371-cfda14ed @ cfda14ed
  • Thinking: 75s wall

Exploratory observations (optional):

  • docs/briefs/r3-evaluator-e0-body-evaluator-api-scaffold.md:215 The optional code companion may need care: the proposed evaluate_body signature names Value, EvalStateStack, and EvalStrategy, while this brief also says those have no instantiable Rust mirror yet. Not blocking for this docs-only PR, but E1 should avoid landing an uncompilable “carrier-neutral” skeleton.

Verdict: APPROVE_WITH_COMMENTS — docs-only diff is bounded, names authorities, calls out missing mirrors, and gives STOP+PING boundaries. No concrete invariant violation observed in the changed lines.

…arrier-neutral)

Reviewer flagged: optional code companion proposed shipping evaluate_body
signature with NotYetImplemented body, but that signature names Value /
EvalStateStack / EvalStrategy — none have Rust mirrors today, so the
"carrier-neutral skeleton" wouldn't compile. Companion now ships only
the EvalDiagnostic enum + empty module; evaluate_body signature lands
inside E1 alongside the first Value mirror.

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

Copy link
Copy Markdown
Contributor Author

The "1 failing on cfda14e" notice references a prior commit. HEAD is now 97b77513a and CI is still in progress (ci and v3 pending). Same expectation as the prior run: the same pre-existing main-side parse-manifest / SG-0 census failures will recur (origin/main itself fails them). This branch's diff vs main is still a single docs file, so anything failing here is structurally upstream of the PR. Will rebase once main is green.

— sent from merry-heron-351

@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: cfda14ed · Trigger: schedule
  • Thinking: 322s wall

BLOCKING (1)

Root Cause

  • docs/briefs/r3-evaluator-e0-body-evaluator-api-scaffold.md The scaffold only defines a node-level evaluator boundary while runtime dependencies flow through PortId facts → add an internal eval_port contract that frame-lookups bound ports or resolves the producing node via Dag port/producer lookup before calling eval_node.

⚠️ The prior authority split is fixed, but the API scaffold still needs the port-level evaluation boundary before later evaluator slices copy this contract.

- **E3 — `eval_transform`:**
`fn eval_transform(dag, t: &TransformNode, stack, strategy) -> Result<Value, EvalDiagnostic>`
per PR-B.1 §B.1.2 and §B.1.6 / §B.1.7. Eager-evaluates
`t.inputs` left-to-right via repeated `eval_node`, then dispatches

This comment was marked as resolved.

@briansrls

Copy link
Copy Markdown
Contributor Author

Failure narrowed but still upstream: only sg0_v3_hand_authored_census and sg0_v3_test_hand_authored_subratchet fail now (the parse-manifest failure was fixed on main between runs — good). Remaining SG-0 census failures ratchet hand-authored Rust test counts; this PR adds zero Rust files (still +1 .md, 0 .rs/.dag/.txt).

origin/main itself currently fails CI on 1cbc02d2 (per gh run list --branch main). Already up to date with origin/main; nothing this PR can do.

The fix needs a SG-0 census refresh on main — likely caused by the recent Rust test additions in #1353 / #1359 / #1361 not updating EXPECTED_HAND_AUTHORED_TEST slices. That refresh should land on the originating PR's branch or as a small follow-on, not here.

— sent from merry-heron-351

@briansrls

Copy link
Copy Markdown
Contributor Author

Review metadata

  • Provider / model: cursor / composer-2
  • Commit: 97b77513 · Trigger: schedule
  • Comparison: origin/main @ 1cbc02d2 ... review/pr-1371-97b77513 @ 97b77513
  • Thinking: 63s wall

Reviewed git diff origin/main...HEAD (single new file: docs/briefs/r3-evaluator-e0-body-evaluator-api-scaffold.md) against the pinned rubric files.

Findings: None. The brief is explicitly PROPOSAL / docs-only, names substrate vs Rust authorities, keeps evaluate_body as a documented contract until mirrors exist, directs eval_node through Dag::node_opt with fail-closed ResolveError instead of declaration(…), and ties the scaffold to later slices plus STOP+PING / catalog discipline. That aligns with fail-closed and boundary framing in INVARIANTS.md without introducing substrate or Rust in the diff. CODING.md / TESTING.md do not apply materially (no new Rust or tests). docs/modeling-discipline.md enum-classification rules apply to landed Rust enums; this diff only describes a future EvalDiagnostic in prose.

Verdict: APPROVE — Narrowly scoped documentation with explicit scope, authorities, and handoff boundaries; no rubric violations identified at cited diff lines.

briansrls and others added 3 commits May 1, 2026 06:21
…NodeId)

Reviewer caught: TransformNode.inputs is Vec<PortId> (dag.rs:1721),
not Vec<NodeId>. Eager evaluation under LeftFirst is port-resolution
via frame_lookup (E2's API), not recursive eval_node over node ids.
Brief now spells the resolution path: inputs left-to-right via
frame_lookup, fail-closed EvalDiagnostic::UnboundPort if missing,
write result to t.output via frame_bind so downstream consumers can
read.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Reviewer caught: scaffold defined node-level evaluator boundary but
runtime data dependencies flow through PortId facts. Adds eval_port
contract as the single port-level resolution authority that combines
frame_lookup and producer-node eval_node demand-eval (using
dag.port_opt(port).produced_by per dag.rs:3061). Per-Behavior
evaluators must call eval_port for input resolution, not frame_lookup
directly. Updates E2's helpers list and E3's transform spec to
consume eval_port. Fail-closed paths named for ResolveError,
UnboundPort, and producer-side invariant violation.

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

Copy link
Copy Markdown
Contributor Author

Review metadata

  • Provider / model: openai-pro / gpt-5-5-pro
  • Commit: 686a2071 · Trigger: manual
  • Comparison: main @ 1cbc02d2 ... session/merry-heron-351-pr-e-e0-scaffold @ afaa12e4
  • Conversation: View conversation

1. Story of the diff

This PR adds a single docs-only brief that freezes the body-evaluator API shape before later E1–E6 slices implement behavior. The brief’s main job is to prevent each later slice from inventing its own evaluator entrypoint or carrier mirrors: it names the Rust module, public evaluate_body signature, internal eval_node dispatch shape, per-Behavior helper signatures, and the typed EvalDiagnostic catalog while explicitly saying that no Rust code, mirrors, or fixture changes land in E0 (docs/briefs/r3-evaluator-e0-body-evaluator-api-scaffold.md:3-8, :70-75, :85-94, :112-126, :164-193). It also audits which runtime carriers are substrate-only today and bounds future mirror additions slice-by-slice, with STOP+PING gates for any attempt to broaden the evaluator into new substrate, new strategies, lazy evaluation, memoization, or lens_apply.rs reuse (:45-68, :195-205, :228-241, :259-274).

2. Invariant categories

  1. LAYER MODEL (substrate vs implementation) — Compliant. The brief is docs-only and does not alter substrate; it names the carrier authorities in runtime.dag and behavior authority in substrate.dag / dag.rs instead of creating parallel local carriers (docs/briefs/r3-evaluator-e0-body-evaluator-api-scaffold.md:13-20), and E0 explicitly forbids new substrate carriers or new Behavior / strategy variants (:261-268).
  2. INVARIANTS.md + modeling-discipline.md — Compliant. Fail-closed and single-authority discipline are handled by routing evaluator failures through a typed EvalDiagnostic enum (:164-181), requiring unsupported strategies to return UnsupportedStrategy rather than defaulting (:105-110), and making new diagnostic variants a STOP+PING through the seed catalog or substrate-fact process (:184-188).
  3. CODING.md — Compliant. The proposed API uses data + free functions with explicit dependencies: evaluate_body(dag, body, initial_stack, strategy) -> Result<Value, EvalDiagnostic> (:87-94) and per-behavior free-function fills rather than methods or hidden state (:134-162).
  4. TESTING.md — N/A. This diff adds no executable behavior; the brief keeps E0 docs-only and explicitly excludes test fixture changes beyond already-named acceptance gates (docs/briefs/r3-evaluator-e0-body-evaluator-api-scaffold.md:228-241, :243-257).
  5. LOCKED DESIGN DECISIONS — Compliant. The brief references the eager-baseline lock and preserves it by accepting only ApplicativeOrder { input_order: LeftFirst } for E1–E5, with every other strategy failing closed (:105-108), and it keeps lazy strategy, EvalThunk, and memoization out of scope (:237-238).
  6. TRACKED vs UNTRACKED DEBT — Compliant. The scaffold is documented as E0 proposal scope (:3-8), bounded by explicit out-of-scope and STOP+PING lists (:228-241, :259-274), and has named dissolution/fill triggers in the handoff matrix for E1 through E6 (:197-205).

3. Verdict

APPROVE. The diff is a bounded documentation scaffold, not a substrate or code change, and it does the important review work up front: names authorities, prevents mirror drift, keeps unsupported evaluator paths fail-closed, and gives each future slice a clear STOP+PING boundary. I did not find a changed line that introduces untracked debt or violates the requested invariant categories.

@briansrls

Copy link
Copy Markdown
Contributor Author

Review metadata

  • Provider / model: claude / claude-opus-4-7
  • Commit: afaa12e4 · Trigger: schedule
  • Comparison: origin/main @ 1cbc02d2 ... review/pr-1371-afaa12e4 @ afaa12e4
  • Thinking: 14s wall

APPROVE — Docs-only brief; no code changes. The scaffold respects layer separation (Rust impl module, not substrate), keeps carriers single-authority, names a fail-closed EvalDiagnostic, and explicitly forbids parallel-authority moves (no EvalCtx extension, no new carriers, eval_port as single port-resolution authority). Citations into dag.rs line numbers are consistent with the diff text. No invariant or modeling-discipline violations grounded in the diff.

@briansrls

Copy link
Copy Markdown
Contributor Author

Review metadata

  • Provider / model: codex / codex-default
  • Commit: 34c74b95 · Trigger: schedule
  • Comparison: origin/main @ 8f051650 ... review/pr-1371-34c74b95 @ 34c74b95
  • Thinking: 25s wall

Verdict: APPROVE

Docs-only scaffold is bounded and explicitly names the carrier gaps, slice ownership, and STOP+PING boundaries. I don’t see any concrete violations of the pinned invariants, modeling discipline, coding rules, or testing discipline in this diff.

@briansrls
briansrls merged commit 2c6c8e5 into main May 1, 2026
4 checks passed

@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: 34c74b95 · Trigger: schedule
  • Thinking: 230s wall

BLOCKING (2)

Root Cause

  • docs/briefs/r3-evaluator-e0-body-evaluator-api-scaffold.md initial EvalFrame construction is caller-supplied with no DAG-membership gate → make eval_port validate dag.port_opt before frame_lookup or require Dag-checked frame construction.
  • docs/briefs/r3-evaluator-e0-body-evaluator-api-scaffold.md output-port materialization has no single authority → make eval_node bind each behavior result_port after successful dispatch, or pass stack/output binding responsibility through every producer evaluator signature.

⚠️ The prior authority and PortId-resolution comments are fixed, but the scaffold still has two API-contract holes that would be harder to unwind after E1/E2 copy them.

port. `eval_port` is the **single port-level resolution authority**
the per-`Behavior` evaluators consume:

1. **Frame lookup first.** Call `frame_lookup(stack, port)`. If the

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: Frame lookup runs before Dag port membership is proven, so a caller-supplied binding can make a nonexistent PortId evaluate successfully, violating Fail-Closed and Every Dependency Is A Substrate Fact.

in the scaffold.

- **E1 — `eval_value`:**
`fn eval_value(node: &ValueNode) -> Result<Value, EvalDiagnostic>`

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 port contract says producer bodies bind result_port, but eval_value has no stack/output binding path, so Value-produced PortId demand-eval can return a Value without making the port readable, violating Facts Flow Forward.

@briansrls

Copy link
Copy Markdown
Contributor Author

Finding valid. #1371 is merged so I cannot push a fix-up commit here, but the fix is carried forward in #1388 commit 137ade9 (E4 readiness audit) — eval_port order is now spelled out as: dag.port_opt(&port) first → frame_lookup → producer demand-eval. A nonexistent PortId in the caller-supplied initial frame fail-closes with EvalDiagnostic::ResolveError(port) instead of silently succeeding.

E1's actual implementation will codify the membership check ahead of frame lookup; the E4 audit makes that the prerequisite consumers depend on. A short follow-on amendment to the merged E0 brief can mirror the wording when convenient.

— sent from merry-heron-351

@briansrls

Copy link
Copy Markdown
Contributor Author

Finding valid. Same situation — #1371 merged so no fix-up here. Carried forward in #1388 commit 7fbd92a (E4 readiness audit, new §2a "Producer-bind invariant"):

E1's eval_value signature must be fn eval_value(node: &ValueNode, stack: &mut EvalStateStack) -> Result<Value, EvalDiagnostic> and call stack.bind_top(node.result_port, value) before returning. Same producer-bind invariant for eval_transform (binds t.output), eval_loop (binds l.result_port), eval_branch (binds b.result_port); eval_bind already binds explicitly.

Without this, eval_port's producer-demand-eval branch (which calls eval_node(producer) then re-does frame_lookup) would re-trigger demand-eval on every read instead of caching, and a Value node's result_port would never be readable from the stack — Facts-Flow-Forward violation. E1's implementation will codify it.

— sent from merry-heron-351

briansrls added a commit that referenced this pull request May 1, 2026
…1388)

* docs(evaluator): R3 E4 branch readiness / blocker audit

Records the exact E1 prerequisites E4 (Branch arm coverage) depends
on, after STOP+PING acceptance in #1371/#1374 follow-on dispatch:

1. Runtime Value Rust mirror with at least LiteralValue + VariantValue
   { tag: DeclarationId, payload: Box<Value> } arms.
2. eval_port port-level demand-eval authority (consumes existing
   EvalStateStack::lookup + dag.port_opt(port).produced_by).
3. eval_node dispatch shell with all five Behavior arms, four
   returning NotYetImplemented placeholders until later slices fill.
4. Frame discipline reaching EvalStateStack<Value> instantiation —
   E2's helpers are generic on V; E4 consumes the instantiated stack.

Also previews E4's eval_branch implementation per PR-B.1 §B.1.3 +
acceptance tests, and names the STOP+PING boundary E4 must respect
when unblocked. Cross-links E0 / E2 / PR-B.1 / dispatch brief.

Docs-only; no Rust, no substrate, no tests.

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

* docs(evaluator): R3 E4 audit — eval_port must check Dag port membership before frame lookup

Reviewer flagged on the merged E0 brief that "frame lookup first"
allows a caller-supplied initial frame containing a nonexistent
PortId to evaluate successfully — Fail-Closed violation. Carries the
fix forward in this E4 readiness audit since E4's eventual
implementation consumes eval_port: order is dag.port_opt(&port) →
frame_lookup → producer-node demand-eval. A follow-on docs amendment
should clarify E0's wording.

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

* docs(evaluator): R3 E4 audit — every Behavior evaluator must bind result_port

Reviewer flagged on merged E0: eval_value's signature lacked stack
parameter, so a Value-produced PortId demand-eval would return Value
without making the port readable — Facts-Flow-Forward violation.
Carries the fix forward: E1 signature must be eval_value(node,
stack: &mut EvalStateStack) and call stack.bind_top(node.result_port,
value) before returning. Same producer-bind invariant for
eval_transform / eval_loop / eval_branch (eval_bind already binds
explicitly).

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

* docs(evaluator): R3 E4 audit — explicit Supersedes § for E0 contract corrections

Reviewer flagged that E0 remains authoritative while this audit
changes the required eval_port order and the eval_value signature.
Adds an explicit "Supersedes" §naming the two E0 sections this audit
corrects (Port-level evaluation contract step 1; Per-Behavior slice
fills eval_value signature) and notes E1 implementation MUST follow
the audit wording until a follow-on E0 amendment lands.

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