Skip to content

feat(provider): judge authored effect programs in the lowerer and verifier - #935

Open
flyingrobots wants to merge 1 commit into
feat/effect-program-proofsfrom
feat/provider-effect-program-judgment
Open

flyingrobots wants to merge 1 commit into
feat/effect-program-proofsfrom
feat/provider-effect-program-judgment

Conversation

@flyingrobots

Copy link
Copy Markdown
Owner

Summary

Until now only Echo's runtime judged an authored effect program. The provider lowerer and verifier now each judge one themselves, sharing no code with the runtime or with each other. This meets the ruling's requirement that the lowerer and verifier independently establish that the executable respects its contract.

Each component establishes:

  • the program's binding to its own Core, Target IR and exports;
  • the pinned lawpack, adapter, target profile and intrinsic catalog that authorize each effect;
  • each effect's runtime ABI, write class and failure contract, through the intrinsic-scoped failure table (identifier → runtime tag), with exactly one declared obstruction per failure arm and a matching authority class;
  • node-for-node Core and Target IR correspondence, in execution order;
  • the body's types, proven ranges and declared budget under that component's own cost model. Input constraints dominate the body, let caps carry forward, earlier conjuncts prove later ones, and a require proves nothing.

Where it runs:

  • Lowerer: encode_effect_publication judges before serializing and refuses unsafe programs with UnsupportedProgram. judge_effect_program exposes the same judgment.
  • Verifier: effect_package::verify_effect_package also binds every identity the package claims, its operation, its output type and its footprint contract to the program it carries (InvalidBinding).

Evidence

warp-core generates a verdict corpus from its own effect-package builders, asserts the runtime's verdict on every entry, and pins the bytes under crates/echo-edict-provider-lowerer/tests/fixtures/effect-programs. The lowerer and verifier tests embed the same bytes and must reach the same verdicts.

Entry Verdict Refusing stage (lowerer and verifier)
read-control, ensure-node-control, slice-proven accept —
slice-unproven, slice-other-operand, slice-require-only refuse body proof
forged-node-order refuse correspondence
wrong-intrinsic-input-type refuse intrinsic ABI
undeclared-obstruction, failure-mapping-{tag-spelled,unknown,duplicate} refuse relation
over-budget, read-captured-budget refuse budget

I traced the refusing stage for every entry in both components, so each forgery is refused by the check it targets. Every refused entry other than the two budget cases carries an ample budget.

The verifier tests also cover:

  • package identities rebound to a different program;
  • another operation coordinate or output type;
  • a non-effect program kind;
  • non-canonical bytes.

Behaviour changes, please review

  • Budgets. The providers' cost models are stricter than the runtime's meter, the Edict/Echo cost-model divergence already noted on feat: verify runtime slice and subtraction operands by proven range #931. Two cases change:
    • The captured node-atom read declares 1 MiB of steps for a 1 MiB read plus scan, so the providers refuse it before installation. The runtime would refuse it only when it executes. The corpus records it as read-captured-budget.
    • The synthetic wire control declared one step, and publication now refuses that. I changed only its declared budget, to 1,024 steps, and re-emitted the two retained publication controls. No test assertion changed.
  • Type tables. Both components' Core type readers now accept an unsigned width named directly in a type table ({ kind: "U32" }), as Core and the runtime already do. This changes the components, so they are re-promoted.
  • Allowlist. I added one .ban-nondeterminism-allowlist entry for the corpus's explicit, ignored regeneration test.

Test plan

  • warp-core with native_rule_bootstrap,trusted_runtime,host_test,delta_validate (corpus verdicts, every existing suite)
  • lowerer, verifier and echo-wesley-gen suites
  • cargo clippy --workspace --all-targets -D warnings, plus warp-core with its test features
  • scripts/ban-globals.sh, scripts/ban-nondeterminism.sh, Prettier, markdownlint, pre-push gate
  • det-gates candidates promoted (follow-up commit)

…ifier

Echo's runtime was the only component that judged an authored effect
program. The provider lowerer and verifier now each judge one themselves,
sharing no code with the runtime or each other:

- binding to the program's own Core, Target IR and exports;
- the pinned lawpack, adapter, target profile and intrinsic catalog that
  authorize each effect;
- each effect's runtime ABI, write class and failure contract through the
  intrinsic-scoped failure table, with one declared obstruction per arm;
- node-for-node Core and Target IR correspondence;
- the body's types, proven ranges (input constraints dominate, let caps
  carry, a require proves nothing) and declared budget under each
  component's own cost model.

effect_publication::encode_effect_publication runs the lowerer's judgment
before serializing and refuses unsafe programs with UnsupportedProgram;
effect_publication::judge_effect_program exposes it. The verifier's
effect_package::verify_effect_package also binds every identity the package
claims, its operation, output and footprint contract to the program it
carries.

warp-core generates and pins a verdict corpus from its own fixture builders
and asserts the runtime's verdict on each entry; the lowerer and verifier
reach the same verdicts on the same bytes. The providers' cost models are
stricter than the runtime's meter, so the captured node-atom read's 1 MiB
step budget and the synthetic wire control's one-step budget are refused
before installation; the wire control now declares 1,024 steps and the
retained publication controls are re-emitted.

Both components' Core type readers now accept an unsigned width named
directly in a type table ({ kind: "U32" }), as Core and the runtime do.
@coderabbitai

coderabbitai Bot commented Oct 21, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: Repository: flyingrobots/echo/.coderabbit.yaml
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 55f40396-7f0f-490a-bdb1-0eb21086edbd

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-21T14:23:27.455787Z 0c52256 PR opened
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@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: 0c522565ef

ℹ️ 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".

Comment on lines +62 to +63
for (value, expected) in [
(get(closure, "core_identity", E)?, &judged.core_identity),

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 Bind the canonical meaning identity to the judged Core

Changing only semantic_closure.canonical_meaning_identity in an accepted corpus package to an unrelated digest still returns Ok(()): this loop checks core_identity but never checks the separate canonical meaning claim. The lowerer sets both identities to the Core digest, and warp-core/src/echo_operation.rs subsequently uses canonical_meaning_identity as the installed operation's semantic identity and carries it into receipts. Consequently, verification accepts an executable attributed to a meaning it does not implement. Require this field to equal judged.core_identity and add it to the identity-mutation tests.

Useful? React with 👍 / 👎.

Comment on lines +75 to +80
for constraint in list(get(intent, "inputConstraints")?)? {
cost = cost.plus(
self.predicate(get(constraint, "predicate")?, &scope, true, 0)?
.0,
)?;
self.proven.push(get(constraint, "predicate")?);

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 Check all input constraints before publishing their proof facts

Publishing each constraint immediately lets later input constraints use earlier ones as range proofs. For example, adding a second constraint that evaluates isUtf8(slice(text, 0, at)) after the existing at <= length(text) constraint in slice-proven is accepted by the lowerer. However, warp-core/src/edict_effects/instruction_relation.rs:51–58 checks every input constraint with an empty fact set and only then publishes their facts, so runtime preparation rejects that same program for an unproven slice. The verifier's new effect_intent loop has the same discrepancy. Audit all constraints first, then expose their predicates to the body, preserving sequential proof propagation only within an all predicate.

Useful? React with 👍 / 👎.

Comment on lines +224 to +228
if conforming && unit {
Ok(Cost {
steps: 1,
bytes: 64,
})

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 Include the mutation emission reservation in allocation costs

For mutation programs with a tight maxAllocatedBytes, the ABI charge includes only the 64-byte unit result and omits the additional 280 bytes that warp-core/src/edict_effects/frame.rs::charge_emission reserves for every emitted mutation. The verifier's ABI cost has the same omission. Rebinding ensure-node-control with a Core allocation budget of 3,978 bytes is accepted by both providers, but an allowed input containing 1,024 atom bytes consumes that budget plus the unaccounted reservation and fails during execution despite passing the static budget judgment. Include the emission reservation in both cost models and add a tight-allocation mutation witness.

Useful? React with 👍 / 👎.

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