Skip to content

run_json: report the recorded failure when the internal report sink is refused - #534

Closed
luisleo526 wants to merge 6 commits into
mainfrom
fix/report-sink-typed-failure
Closed

luisleo526 wants to merge 6 commits into
mainfrom
fix/report-sink-typed-failure

Conversation

@luisleo526

Copy link
Copy Markdown
Collaborator

What

On the default (unjailed) route run_json.py installs an internal report sink. When the engine refused it, for example because the scratch arena descriptor, size or profile flag in the environment was unusable, the host reported engine_invariant ("the fresh strategy refused its report sink") and dropped the typed failure the engine had recorded.

_ReportSink.__init__ now reads the recorded failure (before the strategy state is freed) and reports it: report_sink_failed with reason arena_backing for an unusable arena. If the engine recorded nothing, the previous message and code are kept. No success path changes. The sibling jailed-admission and descriptor-sink paths already worked this way.

Tests

tests/test_run_json_sink_failure_details.py runs the real run_json.py against a real strategy module on the default route, with the arena environment set per row, and expects exit 1, one LF-terminated line and report_sink_failed with {"reason":"arena_backing"}:

  • bad descriptor number, missing / empty / zero / negative / non-decimal / overflowing byte count, invalid jailed flag;
  • descriptor 5 being a pipe, closed, a memfd with no seals, and a memfd with only some seals (the last two on Linux);
  • a double whose setter fails with a recorded failure that the free call clears: the failure must be read before the free.

Controls that must hold before and after: the default route with no arena environment, a fully sealed memfd, and three doubles where the engine recorded nothing (the old line is kept byte for byte).

On the tests-only commit the twelve real-module rows and the free-order row fail with observed code='engine_invariant'; the controls pass. With the fix all pass. docker/run_json_codes_test.py gains the catalogue entry for the new re-raise.

Compatibility

A host that matched engine_invariant for a refused internal sink on the default route now sees report_sink_failed / arena_backing for the cases above. The 1.8.0 changelog says so.

🤖 Generated with Claude Code

luisleo526 and others added 4 commits October 26, 2026 21:05
…ailure

The default route installs an internal report sink. When the engine refuses
it (an unusable scratch arena descriptor, size or profile flag in the
environment, or a descriptor that is not a sealed memfd), the host reports a
generic internal inconsistency instead of the typed failure the engine
recorded. These rows run the real run_json.py against a real strategy module
with the arena environment set up per row, and expect report_sink_failed with
reason arena_backing:

- bad descriptor number, missing / empty / zero / negative / non-decimal /
  overflowing byte count, and an invalid jailed flag;
- descriptor 5 being a pipe, closed, a memfd with no seals, and a memfd with
  only some of the seals;
- a double whose setter fails with a recorded typed failure that is cleared
  when the strategy is freed: the failure must be read before the free.

Controls that must hold before and after: the default route with no arena
environment, a fully sealed memfd, and three doubles where the engine
recorded nothing (the generic internal-inconsistency line is kept).

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…rt sink is refused

When the engine refuses the internal report sink on the default route (for
example because the scratch arena descriptor, size or profile flag in the
environment is unusable), the host reported a generic internal
inconsistency and dropped the typed failure the engine had recorded. It now
reads that record, before the strategy state is freed, and reports it
(report_sink_failed with reason arena_backing for an unusable arena). When
the engine recorded nothing, the previous message is kept. No success path
changes.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
assert actual["diagnostics"]["scratch_arena_bytes"] == size
assert 0 <= actual["diagnostics"]["scratch_arena_high_water_bytes"] <= size
for name in ARENA_DIAGNOSTICS:
assert reference["diagnostics"].pop(name) == 0
@luisleo526
luisleo526 marked this pull request as ready for review October 26, 2026 19:09
luisleo526 added a commit that referenced this pull request Oct 26, 2026
…iagnostics, dependency-scratch storage fault code (#536)

## What

One change set that carries four reviewed pull requests together, so the release gate and the parity sweep run once on the exact tree that ships. Each of the four was reviewed and green on its own; this branch is the first, #536, plus merges of the other three. No line was rewritten.

- #533 Report a storage fault on the dependency scratch as scratch_io_failed
- #534 run_json: report the recorded failure when the internal report sink is refused
- #535 entrypoint: forward an opt-in report descriptor to run_json
- #536 run_json: report the run profile and jailed arena admission in the diagnostics

### Dependency scratch fault (from #533)

A read, write or resize fault on the scratch that holds waiting requests now ends the run as `scratch_io_failed`, a non-retryable `resource` failure, instead of `engine_invariant`, as the exit-scratch consumer already did for the same storage status. A failure the storage layer had already recorded keeps its own code. The run-failure catalog is unchanged. The change is in `require_dependency_scratch` (`src/native_execution_consumer.cpp`) and adds no net lines, so the file keeps its line count and the documentation line anchors stay valid.

### Typed failure for a refused internal report sink (from #534)

On the default route `run_json.py` installs an internal report sink. When the engine refused it, for example because the scratch arena descriptor, size or profile flag in the environment was unusable, the host reported `engine_invariant` and dropped the failure the engine had recorded. It now reads that record before the strategy state is freed and reports it (`report_sink_failed` with reason `arena_backing` for an unusable arena). If the engine recorded nothing, the previous message and code are kept. No success path changes.

### Opt-in report descriptor in the container entrypoint (from #535)

`PINEFORGE_REPORT_SINK_FD=<n>` makes the entrypoint pass `--report-sink-fd <n>` to `run_json.py`, for a caller that opened the descriptor for the report records itself. The value is forwarded unchanged, an empty value as an empty argument, and `run_json.py` stays the only validator (a number below 3, 5, a phase descriptor and a non-number are refused with its usual text and exit 2, mapped to 4 by the entrypoint). With the variable unset nothing changes.

### Run profile in the report diagnostics (from #536)

Every executed success report carries two additive `diagnostics` keys: `run_profile` (`"jailed"` or `"unjailed"`, fixed when the run starts) and `scratch_arena_jailed` (true only when the jailed profile ran and the native scratch arena admission succeeded). The flag comes from the actual admission result, never from the environment. It records the checked profile and the admission; it is not an independent check of operating-system confinement. Failure, capability and validate-only documents, and every existing key and its order, are unchanged. The replay converter keeps both keys. Python only: no native export, no C++ change.

## Tests

Each change keeps the tests it was reviewed with, committed before the fix so each fails on the unchanged tree for the stated reason: the dependency-scratch fault rows (`engine_invariant` observed), the unjailed sink refusal rows (twelve real-module rows and a free-order double), the entrypoint forwarding rows (the logged argument vector lacks the option), and the profile rows (both keys absent). The faults in the first group are injected through a backing double; no claim is made that a real disk `EFBIG` reaches that helper.

## Compatibility

- A host that matched `engine_invariant` for a dependency-scratch storage fault now sees `scratch_io_failed` (class `resource`, not retryable).
- A host that matched `engine_invariant` for a refused internal report sink on the default route now sees `report_sink_failed` / `arena_backing` for the cases above.
- Two additive keys in the success report `diagnostics` object; one opt-in environment variable. The 1.8.0 changelog describes all four.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
@luisleo526

Copy link
Copy Markdown
Collaborator Author

Superseded by #536, which carries this change together with the other report-sink changes in one merge (squash 4c113fc).

@luisleo526 luisleo526 closed this Oct 26, 2026
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