Skip to content

Prove basis-bound validated patch application - #24

Merged
flyingrobots merged 48 commits into
mainfrom
task/11-basis-bound-patch-application
Aug 1, 2026
Merged

Prove basis-bound validated patch application#24
flyingrobots merged 48 commits into
mainfrom
task/11-basis-bound-patch-application

Conversation

@flyingrobots

@flyingrobots flyingrobots commented Jul 30, 2026

Copy link
Copy Markdown
Owner

Proves basis-bound validated patch application end to end: Edict declares the request, Echo durably coordinates it, and only the capability-rooted adapter touches the workspace.

Producer pin

producers.lock.json pins the exact producers this branch is proven against:

producer commit
Edict df80f92ad6242c6da31a64224666fd37aa43b0d0
Echo c354d531679861fb7bbd52ab7b7703807909ab86

Every build boundary calls tests/producer-lock.sh and refuses a checkout that is not those commits. CI reads the same file. The compatible pair was previously an operator convention, which is why an advance in either producer looked like a repository failure rather than a pin change.

What changed

  • Advances both vendored closures to Edict df80f92a, which replaces the sentinel workspace.patch.* and workspace.snapshot.* digests with the exact identities of vendored edict.external-action-resource/v1 artifacts, supplied through externalActionResources.
  • Acquires writer epochs through Echo's FilesystemWalStore::acquire_fresh_writer_epoch against Echo c354d531, replacing a static epoch identity that could not fence overlapping or restarted hosts. Against the advanced pin the previous code did not start at all.
  • Adds observationExceedsFileBudget and replacementExceedsRequestBudget as distinct obstructions, so a replacement the encoded request cannot carry is refused for a stated reason instead of as a malformed request.
  • Binds the retained evidence to witnessed bytes throughout: content digests compared against the bytes, and bases compared by a two-route derivation rather than to each other.
  • Adds CI. Four witnesses had never executed on GitHub.

Removed

tests/lib/check-resource-identities.sh and its test. That guard had become a second Edict parser written in shell and living in this repository. Edict owns canonical resource construction, identity derivation, closure validation, and rejection of malformed, missing, substituted, and sentinel resources; the build corroborates every artifact byte-for-byte and invokes that validator. Hello Echo corroborates Edict artifacts; it does not partially reparse Edict source.

Blockers

None. Both producer dependencies have merged: Edict #180 landed as flyingrobots/edict#181, Echo #714 landed as flyingrobots/echo#715.

Deferred

#26 — the observation host does not project enough basis and evidence fields to bind a settlement to the observed bytes. An observability gap rather than a behaviour defect, and a new capability rather than a repair.

Closes #11
Closes #23
Closes #25

@flyingrobots

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@flyingrobots

Copy link
Copy Markdown
Owner Author

@codex please review

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@flyingrobots, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 50 seconds

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 6a1933f4-8b01-4793-87da-6e23caa8156c

📥 Commits

Reviewing files that changed from the base of the PR and between f74e3fb and 415cbec.

📒 Files selected for processing (15)
  • .github/workflows/ci.yml
  • .gitignore
  • CHANGELOG.md
  • README.md
  • patch-host/src/main.rs
  • patch/vendor/workspace-patch/SOURCE.md
  • producers.lock.json
  • tests/build.sh
  • tests/effect-build.sh
  • tests/effect-runtime.sh
  • tests/lib/writer-epoch-assertions.sh
  • tests/patch-build.sh
  • tests/patch-runtime.sh
  • tests/producer-lock.sh
  • tests/writer-epoch-assertions.sh

Summary by CodeRabbit

  • New Features

    • Added validated workspace patch application with basis, authority, writable-path, budget, and reconciliation checks.
    • Added a command-line patch host supporting request, inspection, claim, apply, retry, replay, and recovery workflows.
    • Added writer-epoch fencing, chained write evidence, and commit-digest reporting.
    • Added packaged configuration and verification artifacts for patch and workspace observation workflows.
  • Documentation

    • Expanded guidance on validation guarantees, authority boundaries, recovery, retries, and effect handling.
    • Marked the basis-bound write roadmap phase complete.
  • Tests

    • Added coverage for schema identity guards, build validation, refusals, crash recovery, replay safety, boundary conditions, and stress scenarios.

Walkthrough

Adds a compiler-authored, basis-bound workspace patch application. Edict emits the request, Echo coordinates durable admission and settlement, the adapter performs bounded writes, and tests cover recovery, refusal, retry, replay, reconciliation, artifact integrity, writer epochs, and stress.

Changes

Validated patch execution

Layer / File(s) Summary
Patch contract and compiled artifacts
patch/src/apply-validated-patch.edict, edict.patch.application.json, edict.effect.application.json, patch/vendor/workspace-patch/*, effect/vendor/workspace-snapshot/*, patch-host/Cargo.toml.template, tests/patch-build.sh, tests/effect-build.sh
Defines applyValidated, wires application and lawpack artifacts, pins resource identities, and builds the patch host against generated artifacts.
Writer-epoch fencing and reporting
effect-host/src/main.rs, tests/effect-runtime.sh
Write phases acquire fresh persisted epochs and report chain metadata. Read-only phases remain epoch-free.
Patch host admission and execution flow
patch-host/src/main.rs, tests/patch-run.sh
Parses requests, validates inputs, records and claims actions, applies and reconciles patches, handles retries, and emits structured reports.
Runtime witness and project documentation
tests/patch-runtime.sh, tests/lib/*, tests/resource-identity-guard.sh, tests/writer-epoch-assertions.sh, README.md, docs/roadmap.md, CHANGELOG.md
Documents and tests ordering, basis and path enforcement, refusal modes, replay, reconciliation, artifact substitution, writer-epoch chains, identity guards, stress cases, and evidence checks.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related issues

  • flyingrobots/edict#178 — Covers the compiled basis-bound patch request and its build validation.

Possibly related PRs

Poem

A basis binds each bounded write,
The WAL records every flight.
Epochs chain and refusals stand,
Retries leave the world unchanged.
Echo settles, tests inspect—
No hidden filesystem effects.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 37.04% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The implementation and tests address the linked issue's validation, authority, settlement, reconciliation, restart, replay, and no-mutation requirements [#11].
Out of Scope Changes check ✅ Passed The schema updates, writer-epoch changes, host fixes, build guards, and runtime tests support the stated validated patch application objectives.
Title check ✅ Passed The title clearly and concisely describes the main change: proving basis-bound validated patch application.
Description check ✅ Passed The description directly explains the end-to-end proof, producer pins, durable coordination, validation, settlement, replay, and workspace authority boundaries.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch

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.

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

Actionable comments posted: 8

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@patch-host/src/main.rs`:
- Around line 660-716: Update open_write_store so writer-epoch acquisition uses
a fresh, explicitly sourced epoch and persisted prior-epoch linkage rather than
the static epoch_id(), fixed identity digests, and previous_epoch_id: None.
Reuse the FilesystemWalStore/WAL contract to load the prior epoch and derive or
validate unique monotonic epoch, fencing, process, host, and lease data; if
acquire_writer_epoch already guarantees this, make that guarantee explicit at
the call site or through the store API.

In `@patch/src/apply-validated-patch.edict`:
- Around line 23-32: Replace the placeholder digests in the
apply-validated-patch bindings for workspace.patch.input@1,
workspace.patch.settlement@1, and workspace.patch.reconcile@1 with the actual
SHA-256 digests of vendored schema artifacts. Ensure the build rejects any
unresolved schema references and does not permit sentinel digest values.

In `@README.md`:
- Around line 215-216: Update the README claim about compiler-artifact
substitution boundaries to match the behavior actually covered by
tests/patch-runtime.sh: either narrow it to the request and claim phases, or add
an inspect/replay recovery substitution case before retaining the
recovery-boundary claim. Prefer the minimal prose correction unless the missing
recovery test is intentionally implemented.

In `@tests/patch-runtime.sh`:
- Around line 515-517: Update the mutated-core setup around mutated_core so it
explicitly verifies that the mutation changed the artifact: capture the original
byte 0, apply the NUL substitution, and assert the original value was non-NUL
before proceeding. Keep the existing cp and dd flow, but make the fixture fail
if the substitution would be a no-op.
- Around line 410-415: Add a post-refusal assertion for the symlink case in
tests/patch-runtime.sh, checking that $patch_root/symlink/outside.txt remains
unchanged from its pre-patch content. Keep the existing stale-basis,
unauthorized, and ci-workflow assertions intact, and verify the symlink escape
target rather than only the refusal status.
- Around line 619-628: The retained-evidence leak check around the grep command
must search recursively from $patch_root, including the project root, rather
than relying on the single-depth "$patch_root"/*/*.json glob. Mirror the error
handling used by tests/runtime.sh so grep exit status 2 or any search error
fails the check, while exit status 1 still represents a clean no-match result.
- Around line 473-484: Update the settlement-size boundary comment to describe
validating the host floor rather than the exact encoded result size. In the
boundary-probe logic around boundary_result_bytes and boundary_floor, explicitly
validate that jq returned a present numeric canonicalResultByteCount before
applying the 1024 minimum, and fail with a clear named assertion if it is
missing or invalid instead of allowing set -e or arithmetic errors.
- Around line 254-266: Strengthen the tampered aperture assertion in the apply
test by reading tampered-report.json and asserting the exact aperture-mismatch
obstruction string emitted by the host, rather than relying only on run_phase’s
nonzero exit status. Keep the existing recovery inspection and secret-content
checks unchanged, and match the typed obstruction format used by the other
negative cases.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: f0b7f94c-3e6f-4cba-990b-0a8e2fb35c6b

📥 Commits

Reviewing files that changed from the base of the PR and between 66060e1 and d8fcafd.

📒 Files selected for processing (20)
  • CHANGELOG.md
  • README.md
  • docs/roadmap.md
  • edict.patch.application.json
  • patch-host/Cargo.toml.template
  • patch-host/src/main.rs
  • patch/src/apply-validated-patch.edict
  • patch/vendor/workspace-patch/SOURCE.md
  • patch/vendor/workspace-patch/adapter.cbor
  • patch/vendor/workspace-patch/adapter.sha256
  • patch/vendor/workspace-patch/exports.cbor
  • patch/vendor/workspace-patch/exports.sha256
  • patch/vendor/workspace-patch/manifest.cbor
  • patch/vendor/workspace-patch/manifest.sha256
  • patch/vendor/workspace-patch/request-profile-configuration.cbor
  • patch/vendor/workspace-patch/request-profile-configuration.sha256
  • tests/patch-build-request.jsonl
  • tests/patch-build.sh
  • tests/patch-run.sh
  • tests/patch-runtime.sh
📜 Review details
🧰 Additional context used
🪛 LanguageTool
README.md

[grammar] ~175-~175: Ensure spelling is correct
Context: ...ld corroborates the exact Edict source, lawpack closure, digest sidecars, Core artifact...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🔇 Additional comments (33)
CHANGELOG.md (1)

30-34: LGTM!

README.md (3)

163-192: LGTM!


193-214: LGTM!


220-223: LGTM!

docs/roadmap.md (1)

135-156: LGTM!

tests/patch-runtime.sh (8)

1-38: LGTM!


40-109: LGTM!


111-160: LGTM!


162-204: LGTM!


206-233: LGTM!


268-348: LGTM!


417-471: LGTM!


573-617: LGTM!

patch/src/apply-validated-patch.edict (1)

1-18: LGTM!

edict.patch.application.json (1)

1-21: LGTM!

patch/vendor/workspace-patch/adapter.cbor (1)

1-1: LGTM!

patch/vendor/workspace-patch/adapter.sha256 (1)

1-1: LGTM!

patch/vendor/workspace-patch/exports.cbor (1)

1-1: LGTM!

patch/vendor/workspace-patch/exports.sha256 (1)

1-1: LGTM!

patch/vendor/workspace-patch/manifest.cbor (1)

1-1: LGTM!

patch/vendor/workspace-patch/manifest.sha256 (1)

1-1: LGTM!

patch/vendor/workspace-patch/request-profile-configuration.cbor (1)

1-1: LGTM!

patch/vendor/workspace-patch/request-profile-configuration.sha256 (1)

1-2: LGTM!

patch/vendor/workspace-patch/SOURCE.md (1)

1-27: LGTM!

patch-host/Cargo.toml.template (1)

1-17: LGTM!

tests/patch-build-request.jsonl (1)

1-1: LGTM!

tests/patch-build.sh (1)

1-86: LGTM!

tests/patch-run.sh (1)

1-42: LGTM!

patch-host/src/main.rs (5)

36-59: LGTM!


80-206: LGTM!


224-400: LGTM!


402-487: LGTM!


208-222: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Non-exhaustive error classification risks silent misclassification.

Unlike settlement_kind() below (which exhaustively matches all ExternalActionSettlementKindV1 variants and would fail to compile on additions), this OR-pattern silently falls through new EdictExternalActionAdmissionErrorV1 variants into "requestRejected". If a future compiler-artifact-level error variant is added upstream, it won't be classified as "compilerArtifactRejected" and this PR's careful distinction between compiler-authored defects and request-level rejections quietly degrades.

♻️ Suggested defensive pattern
 fn is_compiler_artifact_rejection(error: &EdictExternalActionAdmissionErrorV1) -> bool {
-    matches!(
-        error,
-        EdictExternalActionAdmissionErrorV1::Canonical(_)
-            | EdictExternalActionAdmissionErrorV1::ArtifactShape
-            | EdictExternalActionAdmissionErrorV1::RequestCardinality
-            | EdictExternalActionAdmissionErrorV1::CallableStepsPresent
-            | EdictExternalActionAdmissionErrorV1::MissingSemanticClosure
-            | EdictExternalActionAdmissionErrorV1::CoreDigestMismatch
-            | EdictExternalActionAdmissionErrorV1::TargetDerivationMismatch
-            | EdictExternalActionAdmissionErrorV1::CapabilityClosureMismatch
-            | EdictExternalActionAdmissionErrorV1::UnsupportedExpression
-            | EdictExternalActionAdmissionErrorV1::InvalidDigest
-    )
+    match error {
+        EdictExternalActionAdmissionErrorV1::Canonical(_)
+        | EdictExternalActionAdmissionErrorV1::ArtifactShape
+        | EdictExternalActionAdmissionErrorV1::RequestCardinality
+        | EdictExternalActionAdmissionErrorV1::CallableStepsPresent
+        | EdictExternalActionAdmissionErrorV1::MissingSemanticClosure
+        | EdictExternalActionAdmissionErrorV1::CoreDigestMismatch
+        | EdictExternalActionAdmissionErrorV1::TargetDerivationMismatch
+        | EdictExternalActionAdmissionErrorV1::CapabilityClosureMismatch
+        | EdictExternalActionAdmissionErrorV1::UnsupportedExpression
+        | EdictExternalActionAdmissionErrorV1::InvalidDigest => true,
+        // Force a compile error when warp_core adds a new variant, so the
+        // classification here is revisited rather than silently defaulted.
+        EdictExternalActionAdmissionErrorV1::RequestLevelVariant(_) => false,
+    }
 }

If EdictExternalActionAdmissionErrorV1 is #[non_exhaustive], exhaustive matching isn't possible from this crate — in that case, please confirm this classification list is still complete against the current variant set.

			> Likely an incorrect or invalid review comment.

Comment thread patch-host/src/main.rs Outdated
Comment thread patch/src/apply-validated-patch.edict Outdated
Comment thread README.md Outdated
Comment thread tests/patch-runtime.sh Outdated
Comment thread tests/patch-runtime.sh
Comment thread tests/patch-runtime.sh Outdated
Comment thread tests/patch-runtime.sh
Comment thread tests/patch-runtime.sh Outdated
@flyingrobots flyingrobots added the blocked Blocked by an explicit dependency label Jul 30, 2026
Both witnesses accepted the two defects CodeRabbit found on #24.

The build boundaries compared the vendored closure without checking that any
external-action schema identity resolved, so the all-9/8/7 and all-b/c/d
sentinel digests in the compiler sources passed. The build now refuses a
malformed or sentinel identity, and requires each vendored resource sidecar
identity to appear in the compiler source it claims to bind.

The runtime boundaries never observed the writer epoch, so a static epoch
identity with no predecessor linkage was indistinguishable from a durably
fenced one. The golden paths now require a fresh epoch per write phase, exact
predecessor and final-commit-digest linkage, a strictly advancing start LSN,
no epoch on read-only phases, no epoch reused across the ordered path, and a
persisted ledger and writer lease that stay bounded.
Advances both vendored closures to Edict df80f92a, which owns canonical
edict.external-action-resource/v1 artifacts for the request input, settlement,
and reconciliation identities and pins their exact digests in the generator's
own source.

Hello Echo byte-corroborates those artifacts rather than manufacturing them:
the three new artifacts and their generator-owned identity sidecars are
vendored per closure, and both application configs supply the exact artifact
paths through externalActionResources so Edict recomputes and validates the
complete closure before publishing Core or Target IR.

Replacing the sentinel identities locally would have invented the compiler
contract in the consumer, which is why #24 deferred this to Edict #180.

Closed #180 upstream.
Both hosts hard-coded one writer epoch: a static epoch id, fixed fencing,
process, host, and lease digests, and previous_epoch_id: None, re-acquired
identically on every phase. Each phase is a separate process, so that handed
the same fencing identity to restarted and overlapping hosts and defeated the
point of epoch fencing for durable settlement.

Echo c354d531 now persists the writer-epoch ledger across restarts and exposes
FilesystemWalStore::acquire_fresh_writer_epoch, which takes the filesystem
writer lease, rereads the ledger, closes an epoch left by a terminated
process, and derives the successor from that predecessor's identity and final
commit digest. Both hosts now call it and bind the acquired epoch into every
transaction context instead of a constant.

Hello Echo derives no epoch identity and reuses no fencing token. Write phases
project the acquired epoch chain into their reports so the invariant is
observable; read-only phases take no lease and report none.

Against the advanced Echo pin the previous code no longer even starts: the
persisted ledger rejects re-acquiring the static epoch with
WriterEpochAlreadyActive.
Updates the vendored closure provenance to the Edict commit that owns the
canonical external-action resources, states that the sidecars carry
generator-owned resource identities rather than digests of the enclosing
files, and records that the build refuses unresolved or sentinel identities.

Documents writer-epoch fencing as producer-owned in both witness sections and
adds the new witness guarantees to the changelog.
@flyingrobots

Copy link
Copy Markdown
Owner Author

@coderabbitai review

Both remaining threads were blocked on producer work that has since merged: Edict #180 landed as #181 (df80f92a) and Echo #714 landed as #715 (c354d531). This head re-vendors the canonical external-action schema artifacts and switches both hosts to Echo's fresh-writer-epoch contract. Evidence, including mutation tests for the new guards, is in the two thread replies.

Note the widened scope: the workspace.snapshot@1 closure and effect-host carried the identical two defects, and advancing the shared producer pin breaks them, so they are fixed here as well.

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown

@flyingrobots I will review the updated scope. I will verify the re-vendored artifacts, the fresh-writer-epoch contract in both hosts, and the new guard coverage.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@patch-host/src/main.rs`:
- Around line 210-218: Document the intentional precedence directly beside the
budget checks in the surrounding input-validation flow: when both before and
replacement exceed max_file_bytes, observationExceedsFileBudget must be returned
first. Keep the existing check order and error mappings unchanged, and make the
comment explicit so future reordering does not alter the contract covered by
tests/patch-runtime.sh.

In `@README.md`:
- Around line 183-185: Add a command -v xxd >/dev/null prerequisite check in
tests/patch-runtime.sh alongside the existing jq and b3sum checks, ensuring the
script fails early when xxd is unavailable before the hex_digest path runs.

In `@tests/lib/check-resource-identities.sh`:
- Around line 42-98: Update names_slot and the declaring-line handling in
declared_identity so the digest search is limited to the current coordinate’s
clause: truncate text at the next coordinate before calling digest_in,
preventing a successor slot on the same line from donating its digest. Adjust
names_slot to recognize the coordinate occurrence that belongs to the clause
rather than only the first occurrence in the line, while preserving the existing
boundary checks. Add a single-line successor-adoption case to
tests/resource-identity-guard.sh.

In `@tests/lib/writer-epoch-assertions.sh`:
- Around line 42-54: Strengthen assert_chained_writer_epoch in
tests/lib/writer-epoch-assertions.sh:42-54 by requiring the predecessor
writerEpoch, its epochId and startedAtLsn, and both predecessor epochId and
wal.lastCommitDigest to match the expected 64-character hexadecimal format. In
tests/writer-epoch-assertions.sh:147-158, add mutation cases deleting each
predecessor writerEpoch field and a case deleting the predecessor writerEpoch
while setting the successor previousEpochId to null; require all cases to be
rejected.

In `@tests/patch-runtime.sh`:
- Around line 610-682: Extend the resulting-basis coverage around the existing
first/second settlement flow with a reconciled or ambiguous case where the
observed workspace bytes differ from the declared replacement. Assert that the
subsequent request’s requestBasis matches the basis derived from the actual
observed post-state, but differs from a resultingBasis derived over the declared
replacement; retain the existing positive and non-constant assertions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 869528b1-33a1-4312-9c5f-27865644bbcb

📥 Commits

Reviewing files that changed from the base of the PR and between 18e3a1c and f74e3fb.

📒 Files selected for processing (8)
  • CHANGELOG.md
  • README.md
  • patch-host/src/main.rs
  • tests/lib/check-resource-identities.sh
  • tests/lib/writer-epoch-assertions.sh
  • tests/patch-runtime.sh
  • tests/resource-identity-guard.sh
  • tests/writer-epoch-assertions.sh
📜 Review details
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2026-07-30T11:29:25.786Z
Learnt from: flyingrobots
Repo: flyingrobots/hello-echo PR: 24
File: patch-host/src/main.rs:660-716
Timestamp: 2026-07-30T11:29:25.786Z
Learning: In patch-host (e.g., patch-host/src/main.rs and related Rust code), treat durable writer-epoch acquisition as producer-owned by Echo (per Echo `#714`). Since FilesystemWalStore::open does not reconstruct epoch state, do not add any consumer-side prior-epoch reconstruction or synthetic fencing. Ensure the producer/patch-host provides durable writer-epoch acquisition and closure evidence, supports recovery/replay, implements fresh fencing, maintains monotonic LSN progression, and rejects/ignores stale or overlapping writers. Code implementing (or relying on) any consumer-side epoch reconstruction/fencing should be considered blocked until the Echo `#714` durable-epoch contract is satisfied.

Applied to files:

  • patch-host/src/main.rs
🔇 Additional comments (10)
patch-host/src/main.rs (2)

105-107: LGTM!

Also applies to: 186-198


513-519: 🗄️ Data Integrity & Integration

Verify the commit-count witness reads the WAL across retry_phase.

commitCountBefore and commitCountAfter both come from the same store.read_commits().len(). If FilesystemWalStore::read_commits returns an in-memory snapshot loaded in open_read_store, the two values are constrained by the read lifecycle rather than observing WAL growth. Make read_commits reread the durable log, or open a fresh store for commitCountAfter, depending on the intended model.

CHANGELOG.md (1)

43-43: LGTM!

Also applies to: 54-56

README.md (1)

200-216: LGTM!

Also applies to: 249-252, 267-272

tests/lib/check-resource-identities.sh (1)

131-140: LGTM!

Also applies to: 179-179

tests/lib/writer-epoch-assertions.sh (1)

16-31: LGTM!

Also applies to: 55-69

tests/patch-runtime.sh (2)

8-10: LGTM!

Also applies to: 43-52, 159-165, 400-405, 607-608, 857-864


684-714: LGTM!

tests/resource-identity-guard.sh (1)

220-245: LGTM!

Also applies to: 246-262, 264-293, 295-309

tests/writer-epoch-assertions.sh (1)

103-110: LGTM!

Also applies to: 134-145

Comment thread patch-host/src/main.rs
Comment thread README.md
Comment thread tests/lib/check-resource-identities.sh Outdated
Comment thread tests/lib/writer-epoch-assertions.sh
Comment thread tests/patch-runtime.sh

@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: f74e3fbf66

ℹ️ 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 thread tests/lib/writer-epoch-assertions.sh
Comment thread tests/patch-runtime.sh
Comment thread tests/patch-runtime.sh
Five findings from CodeRabbit, the substantive one being a same-line variant of
the successor-adoption evasion.

declared_identity stopped a following slot from donating its digest across
lines but not on one line. A source packing two clauses onto a single line let
the successor digest satisfy the slot that declared none, invisible whenever
the two vendored identities match. The tail after a coordinate, and each later
line, are now truncated at the next coordinate before a digest is taken.

The chained-epoch assertion required every successor field to exist and read
the predecessor without the same requirement, so a predecessor carrying no
writerEpoch yielded null and a successor reporting previousEpochId null
satisfied the linkage by null == null. The mutation loop only damaged the
successor and never reached it. Both sides are now required, with seven
predecessor mutants and the explicit null-to-null case.

The README named xxd as a prerequisite that the script never checked, so a
missing xxd surfaced as a shell error inside hex_digest rather than as a
missing tool.

The two budget checks have a precedence the witness pins: an input oversized in
both reports observationExceedsFileBudget. That is now stated beside the checks
so a reorder cannot silently retarget it.

The resulting-basis proof had no negative direction. In every success case the
declared replacement and the observed post-state are the same bytes, so a
producer deriving evidence from the request rather than the observation passed.
The outcomeUnknown family is the only place they differ; the evidence is now
required to vary with the observed bytes and not with the declared replacement.

While adding that, found beforeContentDigest carries the observed bytes rather
than the declared pre-state in an outcomeUnknown settlement. Echo is correct;
my assertion was wrong. The reading is now pinned, since the field name does
not convey it.

Reported by CodeRabbit.
@flyingrobots

Copy link
Copy Markdown
Owner Author

Round 7 — five, and a producer contract I had misread

Head 1c7cf27d00eba927b82890a17b653d952e02d433. All five fixed in 1c7cf27.

Finding Fix
Major — same-line successor adoption in declared_identity tail truncated at the next coordinate before extracting
Predecessor report trusted in the chain assertion (null == null) both sides required; 7 predecessor mutants + the explicit null-to-null case
xxd documented as a prerequisite but never checked added beside jq and b3sum
Budget-check precedence was a silent contract stated beside the checks
Resulting-basis proof had no negative direction pinned against the outcomeUnknown family

The Major one is the third variant of one evasion

Round 5 closed the commented-coordinate route. Round 6 closed the across-lines route. This is the same evasion packed onto one line:

input schema workspace.patch.input@1 settlement schema workspace.patch.settlement@1 digest "<settlement>"

It fails closed today only because the two vendored identities differ; make them equal and the slot with no pin passes. Your read that it is currently latent rather than exploitable is right, and it is fixed regardless.

The negative-direction finding was the sharpest

You were right that every success case has the declared replacement and the observed post-state as identical bytes, so nothing distinguished a producer deriving evidence from the request. outcomeUnknown is the only family where they differ, and Echo wraps the observed basis in a further domain-separated hash — recomputing that would rebuild producer logic in the consumer. So the property is pinned instead:

same request, observed observedOne  -> b7b60807…
same request, observed observedTwo  -> 7352f9c7…   differs with the observation
different request, observed observedOne -> b7b60807…   unchanged by the request

A producer deriving evidence from the declared replacement fails the first; one ignoring the observation fails the second.

And a correction to myself

Writing that case, my assertion that beforeContentDigest holds the declared pre-state failed. It does not: in an outcomeUnknown settlement it carries the digest of the observed bytes, because the reconciler has no witnessed pre-state to report. Echo is correct at validated_workspace_patch.rs:455; I had misread the field from its name. That reading is now pinned by the test, since the name does not convey it.

Trend

round 1 2 3 4 5 6 7
findings 5 8 4 4 4 3 5

33 total. Round 7 went back up, and three of the five were in guards I had already hardened twice. I am not going to claim a trend from this.

State at 1c7cf27d00eba927b82890a17b653d952e02d433

resource-identity-guard      PASS      writer-epoch-assertions      PASS
runtime                      PASS      effect-runtime               PASS
patch-runtime                PASS      build-cleans-output          PASS

fmt and clippy -D warnings clean. Worktree clean.

@codex review please

@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: 1c7cf27d00

ℹ️ 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 thread tests/patch-runtime.sh Outdated
Comment thread tests/patch-runtime.sh
Comment thread tests/patch-runtime.sh
Three findings, all about assertions that could not observe what they claimed.

The evidence probes varied the worldline while claiming to vary only the
observation or the replacement, so neither dependency was isolated: a producer
deriving evidence from the worldline byte alone would have produced the same
A/B/A pattern and satisfied both comparisons. The worldline is now held
constant across all three.

Reconciliation and uncertainty settlements come from write entrypoints distinct
from apply and settle, and only their posture was checked. A regression
reusing or mislinking an epoch during crash reconciliation would have passed
both reconciliation outcomes despite the stated every-write-phase guarantee.
Both are now required to chain to their claim.

A retry reporting a null writer epoch proves nothing, because the phase
supplies that null itself and taking an epoch changes the persisted ledger
without changing the commit count. Both witnesses now snapshot the ledger bytes
before the exact and conflicting retries and require them unchanged. Confirmed
the check discriminates: a read phase leaves the ledger untouched and a write
phase changes it.

Reported by Codex.
@flyingrobots

Copy link
Copy Markdown
Owner Author

Round 8 — three, all about assertions that could not see what they claimed

Head 52f50a74a139e4cfb11b0a20db488652293cc408. All three fixed in 52f50a7.

First, a correction: I reported to my operator that round 8 had come back silent. It had not — your review landed at 13:54:29 and my watcher started at 13:55:00. I missed it by 31 seconds and blamed the tooling. The findings were sitting there the whole time.

Finding Fix
Evidence probes varied the worldline while claiming to isolate the observation worldline held constant across all three
Reconciliation and uncertainty write entrypoints had no epoch evidence both required to chain to their claim
Retry checks could not show retry took no epoch ledger bytes snapshotted and required unchanged

The retry one is the sharpest thing found in eight rounds

retry_phase passes None to report itself, so writerEpoch == null in a retry report is the phase describing itself, not evidence. And acquiring an epoch changes writer-epochs.ecwal without changing the commit count, so the commit-count assertions could not see it either. A regression that opened the write store during retry would have satisfied every assertion in that block while mutating retained fencing state.

Both witnesses now snapshot the ledger before the exact and conflicting retries and require it unchanged. Verified it discriminates rather than assuming:

read phase  -> ledger unchanged   (correct)
write phase -> ledger changed     (assertion bites)

The worldline confound was mine, one round old

I added those probes in round 7 specifically to fix a different failure to isolate, and introduced a confound while doing it: worldlines 97, 98, 99 across the three cases. A producer keying off worldline parity alone would have produced A/B/A and passed both comparisons. The property I was proving was undermined by the variable I varied to prove it.

Trend

round 1 2 3 4 5 6 7 8
findings 5 8 4 4 4 3 5 3

36 total. Rounds 6-8 are 3, 5, 3 — flat, not converging.

State at 52f50a74a139e4cfb11b0a20db488652293cc408

resource-identity-guard      PASS      writer-epoch-assertions      PASS
runtime                      PASS      effect-runtime               PASS
patch-runtime                PASS      build-cleans-output          PASS

fmt and clippy -D warnings clean. Worktree clean.

@codex review please — and the question from round 3 is still open, now with more weight behind it: is tests/lib/check-resource-identities.sh worth keeping at all? Both build scripts already cmp the compiler source byte-for-byte against the Edict fixture. Every evasion found in rounds 5, 6, and 7 is unreachable unless the producer itself ships a bad source, and in that case the cmp passes by definition. That guard has now absorbed three rounds of findings defending a scenario the byte-exact comparison may already cover. Deleting it is a defensible answer and I would rather hear it than harden it a fourth time.

@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: 52f50a74a1

ℹ️ 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 thread tests/patch-runtime.sh
The reconciliation assertion checked only that externalEvidenceDigest,
evidence, and resultingBasis agreed with one another, and the two-route basis
probe added earlier exercised the apply path alone.
ValidatedWorkspacePatchReconcilerV1 is a separate implementation, so a
regression confined to it could have emitted one arbitrary value for all three
fields and passed.

Extracts the two-route derivation into request_basis_for and applies it to the
reconciled settlement, in both directions: the retained basis must equal one
derived over the observed post-state and must differ from one over the
pre-state.

Reported by Codex.
@flyingrobots

Copy link
Copy Markdown
Owner Author

Round 9 — one

Head 1ec9b2fa867b31dc08e377d55bb12dce7cca5161. Fixed in 1ec9b2f.

The reconciled success assertion checked only that its three evidence fields agreed with each other, and the two-route probe I added in round 6 exercised the apply path alone. ValidatedWorkspacePatchReconcilerV1 is a separate implementation, so a regression confined to it could emit one arbitrary value for all three and pass.

Extracted the two-route derivation into request_basis_for and applied it to the reconciled settlement in both directions:

reconciled resultingBasis        = 7894fcb0…
basis over observed 'after'      = 7894fcb0…   equal
basis over pre-state 'before'    = 5f17a365…   differs

The negative half matters: without it the equality could be satisfied by a reconciler retaining the wrong bytes.

Trend

round 1 2 3 4 5 6 7 8 9
findings 5 8 4 4 4 3 5 3 1

37 total. First single-finding round.

State at 1ec9b2fa867b31dc08e377d55bb12dce7cca5161

resource-identity-guard      PASS      writer-epoch-assertions      PASS
runtime                      PASS      effect-runtime               PASS
patch-runtime                PASS      build-cleans-output          PASS

fmt and clippy -D warnings clean. Worktree clean.

@codex review please

Third and final time asking, because it has now shaped four rounds of this review: should tests/lib/check-resource-identities.sh exist? Rounds 5, 6, and 7 each found an evasion in it. Every one required the producer to ship a malformed source — and both build scripts already cmp that source byte-for-byte against the Edict fixture, so in that scenario the comparison passes by construction and the guard is the only thing looking. If that reasoning is wrong I would like to know why; if it is right, deleting the guard removes the largest single source of findings in this PR and I will do that instead of hardening it a fourth time. A direct yes or no is more useful to me here than another finding.

@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: 1ec9b2fa86

ℹ️ 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 thread tests/patch-runtime.sh Outdated
The reconciled case runs on worldline 82 while its two basis probes ran on 91
and 92, so neither comparison isolated the byte input: a producer deriving a
basis from request context could map 82 and 91 to one value and 92 to another
and satisfy both assertions while ignoring the bytes.

This is the same confound Codex reported at round eight for the evidence
probes. I fixed those and then reintroduced it one round later in the probes
added for the reconciled binding. The two-patch chain carried it too, on
worldlines 95 and 96, where its negative assertion could be met by the
worldline difference rather than the byte difference; that was not reported.

Every basis and evidence comparison now varies only the path and the bytes.
The contract is stated at request_basis_for, since the worldline parameter is
the affordance that keeps producing this.

Reported by Codex.
@flyingrobots

Copy link
Copy Markdown
Owner Author

Round 10 — one, and it is my mistake for the third time

Head 978bf0260ec649308ee0cbc028e878dcd822e242. Fixed in 978bf02.

You reported this exact confound at round 8 for the evidence probes. I fixed those, then reintroduced it one round later in the probes I added for the reconciled binding: the reconciled case runs on worldline 82, its probes ran on 91 and 92. Same class, same file, one round apart.

While fixing it I audited every other comparison of this shape and found a third instance you did not report: the two-patch chain used worldlines 95 and 96, so its negative assertion — that the two settlements do not share a basis — could be satisfied by the worldline difference rather than the byte difference. Also fixed.

Every basis and evidence comparison now varies only the path and the bytes:

comparison worldlines
reconciled case and both probes 82, 82, 82
two-patch chain 95, 95
evidence probes 97, 97, 97

And they still discriminate: first.resultingBasis == second.requestBasis == ec35981a… while second.resultingBasis == 21f5e9d3….

The worldline parameter on request_basis_for is the affordance that keeps producing this, so its contract is now stated at the definition rather than left to whoever adds the next probe.

Trend

round 1 2 3 4 5 6 7 8 9 10
findings 5 8 4 4 4 3 5 3 1 1

38 total. Two consecutive single-finding rounds, and both were confined to test scaffolding I added during this review rather than to the change under review. Nothing since round 5 has touched shipped behaviour except the observation-budget split.

State at 978bf0260ec649308ee0cbc028e878dcd822e242

resource-identity-guard      PASS      writer-epoch-assertions      PASS
runtime                      PASS      effect-runtime               PASS
patch-runtime                PASS      build-cleans-output          PASS

fmt and clippy -D warnings clean. Worktree clean.

@codex review please

I have now asked three times whether tests/lib/check-resource-identities.sh should exist, and received a finding each time instead of an answer. I am going to stop asking and put the reasoning on the record for whoever decides: that guard produced findings in rounds 5, 6 and 7; each required the producer to ship a malformed compiler source; and both build scripts already cmp that source byte-for-byte against the Edict fixture, which passes by construction in exactly that scenario. If it survives, it should be because someone can name a failure it catches that the comparison does not.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🚀

Reviewed commit: 978bf0260e

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

@flyingrobots

Copy link
Copy Markdown
Owner Author

Self-audit while round 11 is pending

Codex's last review was 14:24:46 and this head has been up ~25 minutes with no response and no 👍, so round 11 is genuinely outstanding rather than clean. CodeRabbit is rate limited again. Recording that plainly because I misread a 31-second gap as silence two rounds ago.

Rather than idle I swept tests/effect-runtime.sh against the failure classes these ten rounds established. Result, in order of how much it worried me:

class observation witness
shape instead of value clean — binds observation.files == [{path, bytesHex}] exactly
self-consistent evidence not applicable — no such assertions exist
missing-field / null covered by the shared epoch assertions
confounded variables checked; no basis comparisons to confound
remaining shape-only 3, all coordinator-generated opaque identities

The one real thing it turned up is filed as #26 rather than fixed here: the observation witness cannot bind a settlement to its basis evidence, because effect-host does not project those fields at all. jq '.settlement | keys' returns five keys; basisDigest, externalEvidenceDigest, and schemaAdmissionEvidenceDigest are simply absent, so there is nothing for a test to check. A regression retaining a basis over the wrong bytes would pass.

That is a gap in what the witness can see, not a defect in shipped behaviour, and closing it means adding report fields to effect-host — a new capability, not a repair. This PR is 45 commits past its original scope and I flagged unbounded growth as a risk two rounds ago, so I am not going to do it here on my own initiative.

No code change in this update. Head is unchanged at 978bf02.

The resource identity guard had become a second Edict parser: 197 lines of
shell and awk covering declaration syntax, comments, coordinates, same-line
clauses, sidecar terminators, digest grammar, and placeholder recognition, plus
328 lines of tests for that parser. It produced findings in three consecutive
review rounds, each an evasion of its own parsing rather than a defect in the
change under review.

Every evasion required the producer to ship a malformed compiler source, and in
exactly that case the build already compares that source byte-for-byte against
the generator-owned fixture. Edict #181 owns canonical resource construction,
identity derivation, closure validation, and rejection of malformed, missing,
substituted, and sentinel resources, and the build invokes that validator
through the public application build. Betting an awk script preserves Edict
semantics more faithfully than Edict is not defense in depth.

Hello Echo corroborates Edict artifacts and invokes Edict's validator. It does
not partially reparse Edict source. If the public build can accept a malformed
fixture, that is an Edict defect.

The writer-epoch assertions stay: they witness dynamic Echo behaviour, which is
the consumer's responsibility.

Adds producers.lock.json with the exact Edict and Echo commits, enforced at
every build boundary. The compatible pair was an operator convention, so a
consumer on a stale producer was undetectable; verified the check rejects one.

Adds a CI workflow that reads the lock, checks the producers out at those
commits, and runs the complete witness gate with shell syntax, formatting, and
strict clippy. Four witnesses have never run on GitHub, which is why real
producer-pin failures were invisible on the pull request page.
Without the secret, actions/checkout reports 'Input required and not supplied:
token', which does not say which token, why it is needed, or what to create.
A preflight step now names the secret, the two repositories, and the scope.
I assumed the producer repositories were private and required a
PRODUCER_READ_TOKEN secret, which does not exist, so the first CI run failed at
checkout before running a single witness. Both repositories are public and
actions/checkout needs no cross-repository token to read them.

The pinned commit remains the whole contract: a moved branch cannot change what
a run proves.
@flyingrobots

Copy link
Copy Markdown
Owner Author

Simplified, pinned, and under CI

Head 26fbb2037436daafc95c4b52f18a871868918677.

Removed the consumer-owned Edict parser

tests/lib/check-resource-identities.sh (197 lines) and tests/resource-identity-guard.sh (328 lines) are deleted, with both call sites and every documentation claim that rested on them.

That guard had become a second Edict parser written in shell and living in this repository: declaration syntax, comments, coordinates, same-line clauses, sidecar terminators, digest grammar, placeholder recognition. It produced findings in three consecutive review rounds, and every one was an evasion of its own parsing rather than a defect in the change under review. Every evasion also required the producer to ship a malformed source — the exact case where the build already compares that source byte-for-byte against the generator-owned fixture.

Edict #181 owns canonical resource construction, identity derivation, closure validation, and rejection of malformed, missing, substituted, and sentinel resources. The build invokes that validator through the public application build. Betting an awk script preserves Edict semantics more faithfully than Edict is not defense in depth.

The rule is now written into the README and SOURCE.md:

Hello Echo corroborates Edict artifacts and invokes Edict's validator. It does not partially reparse Edict source.

If the public build can accept a malformed fixture, that is an Edict defect.

The writer-epoch assertions stay. Those witness dynamic Echo behaviour, which is the consumer's responsibility.

Pinned the producer pair

producers.lock.json records the exact commits, and tests/producer-lock.sh enforces it at every build boundary:

producer commit
Edict df80f92ad6242c6da31a64224666fd37aa43b0d0
Echo c354d531679861fb7bbd52ab7b7703807909ab86

The compatible pair was previously an operator convention, which is precisely why advancing it looked like a repository failure rather than a pin change. Verified the check rejects a stale checkout:

edict checkout is 9f1a11e0… but producers.lock.json pins df80f92a…

Added CI

Reads the lock, checks Edict and Echo out at those commits, and runs the complete gate: all four witnesses, the hermetic assertion test, shell syntax, fmt --check, and strict clippy on both generated hosts. On pull requests and pushes to main.

A correction: the first CI run failed because I assumed the producer repositories were private and required a PRODUCER_READ_TOKEN secret that does not exist. Both are public and actions/checkout needs no cross-repository token. That was my error, not a missing credential, and it cost one red run.

Local gate at this head

writer-epoch-assertions      PASS      runtime                      PASS
effect-runtime               PASS      patch-runtime                PASS
build-cleans-output          PASS

fmt --check and clippy -D warnings clean on both hosts. Worktree clean.

Merge gate

… CI green on this exact head        ← running now, first real execution
✓ zero unresolved review threads
✓ one independent current-head review with no P0-P2 findings   (round 10)
✓ worktree clean
✓ producer lock matches the vendored closure

P3-P5 findings go to backlog unless they expose a false semantic claim. Merging on the expected head SHA so the branch cannot move under the decision.

The observation witness links the producer checkouts and re-runs the build
through those links, to prove a relative producer path is canonicalized before
it reaches the generated Cargo manifest. It linked the path as given.

A symlink target is resolved against the directory holding the link, so a
relative producer path linked from a nested build directory dangles. Every
caller had passed absolute paths, so the probe had never been exercised the way
it was written to be. CI passes relative paths and the witness failed at once:

    fatal: cannot change to '.build/effect-tests/relative-repos/edict'

The link path stays relative, which is what the probe exercises. The target is
now resolved. Verified every witness passes under both relative and absolute
producer paths.

Found by CI on its first successful execution.
The oversize-replacement and oversize-observation cases build bodies at the
file budget, which is 131,072 hex characters. Linux caps a single argument at
MAX_ARG_STRLEN, which is exactly 131,072 bytes, so passing one through --arg
fails with 'Argument list too long'. macOS permits far larger arguments, so
these cases had passed on every developer run and could not fail there.

make_case now writes both bodies to files beside the case and reads them with
--rawfile, which has no such limit. The generated request is unchanged, which
the suite's digest and golden bindings confirm.

Only the patch witness builds bodies of that size; the observation witness uses
short literals, so it is left alone rather than changed speculatively.

Found by CI.
@flyingrobots

Copy link
Copy Markdown
Owner Author

@codex review please

Final bounded review. Head e14abd38d27eb76f49c0efe98c31a0f08fe03d40. This is the last round before merge, so please scope accordingly:

  • Report P0–P2 only. P3–P5 go to backlog unless they expose a false semantic claim in documentation or a test name.
  • Not wanted: spelling, optional refactors, extract-a-shared-crate suggestions, or probability-of-10⁻⁷⁶ cases.
  • The delta since your last review (1ec9b2f) is what needs eyes. Five commits, and none of them have been reviewed.

What changed since 1ec9b2f

Deleted tests/lib/check-resource-identities.sh and tests/resource-identity-guard.sh — 525 lines. This answers the question I asked you three times. The guard had become a second Edict parser living in the consumer, and it generated findings in rounds 5, 6 and 7, every one an evasion of its own parsing. Edict #181 owns closure validation and the build invokes it; the build also compares the source and every artifact byte-for-byte against the generator-owned fixture. If you think deleting it removed real coverage the byte-exact comparison does not provide, that is a P2 and I want to hear it.

Added producers.lock.json and tests/producer-lock.sh, enforced at all three build boundaries. Verified it rejects a stale checkout.

Added CI that reads the lock, checks Edict and Echo out at the pinned commits, and runs the full gate.

Two fixes CI found on its first two executions, both environmental and both invisible to every prior local run and to all eleven review rounds:

  1. The relative-producer-path probe linked the checkout as given, so a relative EDICT_REPO produced a dangling symlink. It had never been exercised the way it was written, because every caller passed absolute paths.
  2. make_case passed a 131,072-character hex body through jq --arg. That is exactly Linux's MAX_ARG_STRLEN; macOS permits far more, so the oversize cases could not fail on a developer machine. Now carried via --rawfile.

Gate state

✓ CI green on e14abd3          run 30707985816, first green run in this repository
✓ zero unresolved threads      0 of 45
✓ worktree clean
✓ producer lock matches the vendored closure
… one bounded current-head review   ← this

Local: five witnesses pass under both relative and absolute producer paths; fmt --check and clippy -D warnings clean on both hosts.

If this comes back with no P0–P2, I merge on that exact SHA.

@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: e14abd38d2

ℹ️ 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 thread tests/producer-lock.sh
The lock compared rev-parse HEAD, which reports the pinned commit regardless of
uncommitted changes. All three build boundaries then compile the modified Edict
and Echo sources, so a run could claim the pinned producer pair while testing
different code.

The vendored artifact comparisons do not cover this. They check the fixtures,
not the compiler and host crates the build uses.

Verified in both directions: a clean checkout at the pinned commit passes, and
a checkout at that commit with one modified file is refused by path.

Reported by Codex.
@flyingrobots

Copy link
Copy Markdown
Owner Author

Final review: one P2, fixed

Head 415cbec. Fixed in 415cbec.

Valid, and it was a false semantic claim rather than a hardening suggestion, which is exactly the bar I asked you to hold to. rev-parse HEAD reports the pinned commit regardless of uncommitted changes, so a run could claim the pinned producer pair while all three build boundaries compiled modified Edict and Echo sources. Your point that the vendored comparisons do not cover this is the part that makes it a P2: they check the fixtures, not the compiler and host crates the build actually uses.

Proved the mechanism in an isolated repository rather than dirtying the real producer checkouts:

pinned HEAD          = 6080cc6ec8d2
after modifying a tracked file:
  rev-parse HEAD     = 6080cc6ec8d2   <- unchanged
  status --porcelain = [ M src.rs ]

The lock now also requires a clean worktree. Verified both directions: the pinned checkouts pass, and a checkout at the pinned commit with one modified file is refused by path.

On the deletion

You did not object to removing check-resource-identities.sh, having now seen the head without it. I asked specifically whether it removed coverage the byte-exact comparison does not provide, and treated the absence of that objection as the answer. Recording that here so the decision is attributable rather than inferred silently.

Gate

… CI green on 415cbec          re-running
✓ zero unresolved threads
✓ one bounded current-head review, one P2, fixed
✓ worktree clean
✓ producer lock matches the vendored closure, and now proves it

Five witnesses pass locally under relative and absolute producer paths. Merging on the exact SHA once CI is green on it.

@flyingrobots
flyingrobots merged commit f435b5e into main Aug 1, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

blocked Blocked by an explicit dependency

Projects

None yet

Development

Successfully merging this pull request may close these issues.

No CI runs the witness suites Track validated patch producer dependencies Prove basis-bound validated patch application

1 participant