Skip to content

fix(multiharness): wire fence evidence, sentinel, and adapter-failure packaging - #1049

Draft
johnjhughes wants to merge 1 commit into
feat/harness-lane-live-verificationfrom
fix/harness-lane-2ve1-blockers
Draft

fix(multiharness): wire fence evidence, sentinel, and adapter-failure packaging#1049
johnjhughes wants to merge 1 commit into
feat/harness-lane-live-verificationfrom
fix/harness-lane-2ve1-blockers

Conversation

@johnjhughes

Copy link
Copy Markdown
Collaborator

Fixes the five merge blockers in legalforecastbench-2ve1 so stack #1042 can merge.

This is a sixth layer on top of #1045. The lower PRs stay as reviewed; the blockers live in the code they introduced, and this PR is the one that must land with them.

Blockers

B2 (the one that matters most). adapter.py published native_tools_enabled: True and server_side_web_tools_disabled: True as literals. Every parser already computed the real observation; nothing read it. A Claude run whose init lists WebSearch would still publish server_side_web_tools_disabled: true and a scored LFB row. These are real federal cases whose outcomes are one search away.

The summary now derives both booleans from the parser. A row whose transcript shows provider-executed web retrieval is refused as sandbox_denial rather than scored. The release transcript also binds stdout_sha256, so editing container-logs/*.stdout after the fact cannot survive repackaging.

B3. sentinel.py had no production caller. docs/community-submissions.md said every run wrote the token. The probe now runs once per harness before scored rows, through the adapter's own container path, and the run refuses if the token does not come back. The docs sentence now describes that probe, not a ride-along on scored prompts (which would falsify the digest-bound prompt).

B1. An adapter-raised exception (_failure_result) omitted adapter_version. Packaging then died on _require_str(..., 'adapter_version') after a paid 100-row run. Failure summaries now carry the adapter identity, and the packager takes harness identity from succeeded rows so one thin failure does not make the run unpackageable.

B4. _scan_text_content decoded as strict UTF-8 and swallowed UnicodeDecodeError. A .stdout identical but for one 0xff published ANTHROPIC_API_KEY unflagged. Decode now uses errors='replace'.

B5. Host-path redaction knew /home and /Users and missed /work/, where this repo actually lives. /work, /root, and /srv are rewritten to placeholders. The tree-wide _ABSOLUTE_PATH check is not applied to the community tree — that would also refuse /workspace/foo.

Tests

Focused (serial):

uv run pytest tests/test_multiharness_harness_lane_run.py \
  tests/test_multiharness_harness_lane_community_intake.py \
  tests/test_claude_code_container_adapter.py \
  tests/test_agy_container_adapter.py \
  tests/test_multiharness_adapter_registry.py \
  tests/test_publication_guardrails.py \
  tests/test_multiharness_harness_lane_scoring.py \
  tests/test_codex_cli_container_adapter.py \
  tests/test_kimi_container_adapter.py \
  tests/test_grok_container_adapter.py \
  tests/test_architecture.py tests/test_architecture_rules.py -q

Also uv run pyright and uv run ruff check on the touched files.

Do not merge the stack until this layer is reviewed. The lower five were already green; these holes were not in those tests.

… packaging

Five merge blockers from the adversarial review of stack #1042
(legalforecastbench-2ve1). CI was green on all five PRs; none of these is
caught by the tests that already existed.

The published native_tools_enabled / server_side_web_tools_disabled booleans
were hardcoded True. Every parser already computed the observation; nothing
read it. A Claude run whose init lists WebSearch would still publish
server_side_web_tools_disabled: true and a scored LFB row. The summary now
derives both booleans from the parser, and a row whose transcript shows
provider-executed web retrieval is refused as sandbox_denial rather than
scored. The release transcript also binds stdout_sha256 so a post-hoc edit
of container-logs/*.stdout cannot survive repackaging.

The workspace sentinel was tests-only while community-submissions.md claimed
every run wrote the token. The probe now runs once per harness before scored
rows, through the adapter's own container path, and the run refuses if the
token does not come back. The docs sentence now describes that probe.

An adapter-raised exception (proxy-bind timeout, missing evidence file)
emitted a public_summary without adapter_version, so packaging died after
the money was spent. Failure summaries now carry the adapter identity, and
the packager takes harness identity from succeeded rows so one thin failure
does not make a 100-row run unpackageable.

The publication secret scan decoded text as strict UTF-8 and swallowed
UnicodeDecodeError, so a .stdout identical but for one 0xff published
ANTHROPIC_API_KEY unflagged. It now decodes with errors=replace.

Host-path redaction knew /home and /Users and missed /work, where this repo
actually lives. /work, /root, and /srv are rewritten to placeholders; the
tree-wide absolute-path refusal is not applied, because it would also refuse
/workspace/foo.
@cursor

cursor Bot commented Sep 3, 2026

Copy link
Copy Markdown

PR Summary

High Risk
Changes scoring refusal, release transcript binding, and publication scanning on security-sensitive harness-lane paths where incorrect posture or missed secrets would let web-augmented or leaky runs publish as valid community results.

Overview
Closes merge blockers around honest published posture, tool-use proof, and fail-closed intake for the container harness lane.

Web fence and native-tools summary fields no longer ship as constants. ContainerCliAdapter parses each row’s stdout via harness_fence_observation; transcripts that show provider-side web tools available or used fail as sandbox_denial instead of scoring. Harnesses whose envelopes don’t report tools (e.g. agy) only get native_tools_enabled: true when the once-per-run sentinel proves a workspace read. Release transcripts now bind stdout_sha256 so post-hoc stdout edits break repackaging.

Sentinel probe runs once per harness (under sentinel-probe/) before scored rows/* work, through run_container, with sentinel-probe.json gating the run; docs and community intake copy describe the probe path instead of a per-row token in scored prompts.

Packaging and guardrails: adapter-exception summaries include adapter_version; results grouping takes harness identity from succeeded rows so mixed failures still package. Community scrubbing adds /work, /root, and /srv host-root placeholders. Publication secret scans decode harness streams with UTF-8 errors='replace' so a single invalid byte can’t skip a credential on .stdout.

Reviewed by Cursor Bugbot for commit ba91702. Bugbot is set up for automated code reviews on this repo. Configure here.

@cursor

cursor Bot commented Sep 3, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_4df1733c-1ce0-4202-9ef5-46169e3df2f7)

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 3, 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-09-03T03:22:42.260904Z ba91702 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.

@codecov

codecov Bot commented Sep 3, 2026

Copy link
Copy Markdown

⚠️ JUnit XML file not found

The CLI was unable to find any JUnit XML files to upload.
For more help, visit our troubleshooting guide.

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

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

"packet_sha256": request.task.task_sha256,
"prompt_sha256": prompt_sha256,
"response_sha256": output_sha256,
"stdout_sha256": release_bytes_sha256(stdout_bytes),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Validate the bound stdout digest during packaging

When a release-backed row's container log is changed after execution but before a package is built, this new field does not make the change detectable: _validate_transcript_binding() checks only request, packet, prompt, and response, while build_harness_lane_results_package() archives the current log without rehashing it against stdout_sha256. The altered stdout therefore produces a valid new package while the transcript still claims the old digest, so re-read the recorded stdout and compare it before accepting or packaging the row.

AGENTS.md reference: AGENTS.md:L24-L24

Useful? React with 👍 / 👎.

Comment on lines +399 to +402
record_path = run_root / "sentinel-probe.json"
if record_path.is_file():
self._require_proven_sentinel(record_path)
return

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Key sentinel evidence by harness identity

In a MultiHarnessRunConfig containing multiple ContainerCliAdapter instances—or a fresh run that reuses an output directory—this single unkeyed record is created by the first invocation and every later adapter accepts it solely because its verdict is proven. _observed_native_tools() reads the same file, so one harness can inherit another harness's probe and publish native_tools_enabled: true without ever passing its own sentinel; store and validate the record against the adapter/image identity for the current run.

AGENTS.md reference: AGENTS.md:L9-L9

Useful? React with 👍 / 👎.

# Adapter-raised failures and web-fence refusals may omit identity fields
# or carry a different observed posture. The harness identity is the
# succeeded rows'; failed rows still count in status and failure_class.
identity_rows = [row for row in rows if row.get("failure_class") is None]

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 Do not treat missing failure classes as successes

When incomplete_run_policy="record_failure" catches an adapter exception, _failure_result() emits no failure_class, so this new predicate places the failed summary in identity_rows and the later aggregation counts it as "none". A mixed package can consequently report one failed canonical row while reporting two rows with no failure class; tag adapter exceptions with a non-null failure class or retain canonical row status when grouping.

Useful? React with 👍 / 👎.

@ai-agent-on-behalf-of-john-hughes

Copy link
Copy Markdown
Contributor

Do not merge this PR. Do not patch it again. This stack is not mergeable.

This is the tip of the six-PR harness-lane stack (base #1040 … this B1–B5 layer). Two independent reviews of this tip each found blockers the previous missed. The review-loop cap is 3; a seventh layer will not converge.

Verified design-level failures that this layer did not (and cannot cheaply) fix:

  1. The graded model never receives the case packet. adapter.py creates the container workspace empty and passes only prompt.txt. solver_inputs.py requires source/model-packet.json present-but-not-visible — it is not staged. Every number this lane produced measured the wrong thing.
  2. Invalid/unparseable forecast is defaulted and reported as succeeded. Committed artifact community/submissions/2026/harness-lane-synthetic-pipeline-check/full-results/lfb/runs.jsonl has invalid_output: true, is_valid: false, json_decode_error, while canonical-runs.jsonl has status: succeeded, failure_class: none.
  3. Web/search fence is only on the initial argv. The tools-on agent has shell + the same executable + credentials and can re-invoke without disable flags. Credential HOME is mounted read/write.
  4. Refused hostnames are a covert exfil channel (raw denied hostnames recorded in publishable logs). CONNECT is not bound to TLS SNI / HTTP Host.

B1–B5 on this layer are incomplete or bypassed (hardcoded measurement booleans, hostname redaction misses /work/, fence flags not derived from parser observations). That is why we are stopping, not stacking.

#1040 (base) conflicts with main after #1034. Next step is a from-main rebuild, not another stack layer and not a rebase of these six branches.

Converted to draft so it cannot merge. Do not close — keep as salvage reference (egress proxy resisted 25 hostile CONNECT hosts; per-run --internal network; sidecar sole door; cap-drop ALL; no-new-privileges; read-only rootfs; digest-pinned; credential staging 0600 in a fresh 0700 HOME deleted in finally; intake workflow_dispatch only; validation before upload). Do not push more commits to these branches.

Tracked on legalforecastbench-2ve1. legalforecastbench-hf4a (container_execution mislabel) stays on trunk — do not also patch it here. Kimi (legalforecastbench-i9q8) stays parked and unregistered.

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