Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 55 additions & 1 deletion docs/evidence/issue-62/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,47 @@ URLs, headers, credentials, prompts, tool arguments, or wire identifiers.
validates the canonical manifest schema and fails closed on mutation,
deletion, loss, missing fingerprints, or route/catalog mismatch.

#### Planner v2 contract

The manifest's `planner` object is an exact four-field envelope:

```json
{
"inputs": {
"provider": "official",
"model": "gpt-5.6-sol",
"protocol": "responses",
"cli_version": "0.146.0",
"cli_package_sha256": "<sha256>",
"candidate_sha": "<candidate sha>",
"catalog_digest": "<sha256>",
"route_digest": "<sha256>"
},
"core_plan": {
"status": "complete",
"items": [
{
"id": "core-message",
"type": "message",
"disposition": "preserved",
"evidence_ref": "artifact.json#core.message"
}
]
},
"hosted_only_items": [],
"unknown_tagged_items": []
}
```

`core_plan.status` is explicitly `complete` or `partial`. Every item in all
three lists has exactly `id`, `type`, `disposition`, and `evidence_ref`; IDs
are globally unique and each list is deterministically sorted. Dispositions
use the vocabulary above, and `evidence_ref` is a non-empty relative
`artifact#pointer` reference (no URLs, backslashes, or parent-directory
segments). Planner inputs are bound to the candidate identity and the official
Responses route before a live child starts. A synthetic fixture may carry only
`partial`/`Unqualified` planner evidence and can never qualify the issue.

For Codex CLI `0.146.0`, the package metadata does not include `gitHead`.
Evidence may use `cli_source_commit: null` with
`cli_source_commit_status: not_published_by_registry`; a fabricated SHA is
Expand All @@ -276,6 +317,12 @@ Run two independent instances around an isolated Gateway process:
isolated client -> pre sidecar -> isolated Gateway -> post sidecar -> upstream
```

Codex CLI may issue a model-refresh `GET /models` before the Responses controls.
The sidecar forwards that discovery GET with the same bounded timeout and
response cap, but never persists its path, headers, or body; only the required
POST Responses controls create evidence records. A discovery failure therefore
fails the client request without fabricating a core capture.

The two commands require distinct output directories and a shared, isolated
32-byte-or-longer HMAC key file. The operator must replace every angle-bracket
token only after the live window identifies the exact candidate, isolated
Expand All @@ -290,6 +337,7 @@ py -3.13 scripts/capture_issue_62_live_evidence.py `
--forward-base-url http://127.0.0.1:<ISOLATED_GATEWAY_PORT> `
--output-dir <ISOLATED_OUTPUT_ROOT>\pre `
--hmac-key-file <ISOLATED_HMAC_KEY_FILE> `
--run-nonce <FRESH_32_HEX_RUN_NONCE> `
--max-request-bytes <AUTHORIZED_REQUEST_CAP> `
--max-response-bytes <AUTHORIZED_RESPONSE_CAP> `
--connect-timeout-seconds <AUTHORIZED_CONNECT_TIMEOUT> `
Expand All @@ -304,6 +352,7 @@ py -3.13 scripts/capture_issue_62_live_evidence.py `
--forward-base-url <AUTHORIZED_UPSTREAM_BASE_URL> `
--output-dir <ISOLATED_OUTPUT_ROOT>\post `
--hmac-key-file <ISOLATED_HMAC_KEY_FILE> `
--run-nonce <SAME_FRESH_32_HEX_RUN_NONCE> `
--max-request-bytes <AUTHORIZED_REQUEST_CAP> `
--max-response-bytes <AUTHORIZED_RESPONSE_CAP> `
--connect-timeout-seconds <AUTHORIZED_CONNECT_TIMEOUT> `
Expand All @@ -317,7 +366,12 @@ terminal classifications, or a fixed incomplete failure code. URLs, paths,
headers, credentials, key material, raw bodies, prompt/tool content, wire
identifiers, and exception text are never artifact fields. Overflow, timeout,
cancellation, forwarding failure, incomplete SSE framing, and server lifecycle
failure cannot produce a complete record and leave no `.partial` artifact.
failure cannot produce a complete record and leave no `.partial` artifact. The
operator supplies one fresh 32-hex run nonce to both hops for each window. It
is included in the correlation HMAC context and the producer HMAC over each
canonical record (excluding the producer-HMAC field); post accepts each token
only once for that run. A producer snapshot is read back immediately and
checked again at shutdown, so a rewritten record fails closed.

The focused tests use loopback fake servers only:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
- Create: `tests/test_issue_62_live_evidence_sidecar.py`

**Interfaces:**
- Produces: `SidecarConfig`, `validate_config(config)`, `write_capture_record(output_dir, hop, record)`, and `main(argv=None) -> int`.
- Produces: `SidecarConfig`, `validate_config(config)`, `write_capture_record(output_dir, hop, record, capture_key=..., correlation_token=...)`, and `main(argv=None) -> int`.
- Consumes: only Python standard-library modules and a pre-existing HMAC key file.

- [x] **Step 1: Write failing activation and artifact tests**
Expand All @@ -43,7 +43,13 @@ def test_config_rejects_non_loopback(host, base_config):
sidecar.validate_config(dataclasses.replace(base_config, listen_host=host))

def test_atomic_record_is_sanitized_and_leaves_no_partial(tmp_path):
record_path = sidecar.write_capture_record(tmp_path, "pre", SAFE_RECORD)
record_path = sidecar.write_capture_record(
tmp_path,
"pre",
SAFE_RECORD,
capture_key=KEY,
correlation_token=CORRELATION_TOKEN,
)
assert json.loads(record_path.read_text(encoding="utf-8"))["schema"] == (
"codexhub.issue62.live-evidence-lane.v1"
)
Expand Down
Loading