# Agentropix-SIFT — Agent Execution Logs Report

> **Report-structure provenance note.** The structure of this report was produced by an on-the-fly prompt-optimizer agent that refined the raw author prompt before composition. Its concrete improvements were:
> 1. **Deduplicated** the requirement "agent-to-agent message logs with timestamps" — which the raw prompt listed twice — into a single section (§3 *Agent-to-Agent Message & Handoff Log*) with an explicit timestamped-edge definition.
> 2. **Replaced** the vague "extract raw-data evidence for every occurrence" with ONE strict, machine-checkable citation format (`file:json-path -> value`) and a per-section evidence requirement bound to real schema paths.
> 3. **Grounded the spec in the actual files**: confirmed two cases (`base-dc` + `notch`), not one; the real 13-agent roster; `status=budget_exhausted`; 5 iterations; 176 tool_calls; 22 findings; 8 findings with `related_findings` — so the extractors were not guessing counts.
> 4. **Operationalized** "recreate the agent communication chain" by specifying the exact fields to use: `trace.tool_calls[]` agent-rollup vs `mcp.*` split, and `findings[]._source/agent/related_findings` as the handoff-graph source.
> 5. **Operationalized** "iteration-over-iteration showing how the approach changed" into a concrete diff-narrative across `iterations[].{plan,stable_agents,dropped_agents,gaps,critic_score,should_halt}` plus the terminal-status explanation.
> 6. **Added a determinism frame** (LLM = Layer-1 orchestrator only, `inference_constraint=high`) as a mandatory opening so the report defends evidence provenance.
> 7. **Added the cross-file correlation mechanic** explicitly (match by timestamp+path across `report.json` / `run.log` / `thymus jsonl` / sealed `audit-log.json`), with the log2timeline timeout and Thymus REJECTs as worked examples.
> 8. **Added guardrails** the raw prompt lacked: never print the 32-byte session-key bytes (only reference its sealing role per secrets-handling defaults), and flag any handoff with no backing record as `UNVERIFIED`.
> 9. **Added** a Governance & Sealed Audit section (§6) and an Evidence Index appendix (§7) so an evaluator has a single traceability map plus tamper-evidence (seals, sha256, entry_count match).

---

> 🎨 **Prefer pictures?** The [**Visual Atlas**](AGENT-EXECUTION-VISUAL-ATLAS.md) renders this
> report's §3–§6 as thirteen color diagrams — the agent communication graph, the end-to-end
> timestamp chain (with a 217-microsecond zoom), the self-correction funnels, the Thymus gate
> pies, and the seal cross-binding chain — all generated from the raw files in this folder. There is
> also a [**108-second animated walkthrough**](https://galvangabriel-web.github.io/agentropix-mcp/docs/12-CASES-REPORTS/srl-2018-report/submission/watch.html) of the same
> topics (plays in-browser).

## 1. Executive Summary & Determinism Frame

This report is the evaluator-facing "Agent Execution Logs" artifact for the Agentropix-SIFT hackathon submission. It covers **both** cases shipped in `/home/admin2/agentropix-sift/submission/`:

- **`base-dc`** — the flagship run: an SRL-2018 domain-controller disk image (`/cases/SRL-2018/base-dc-cdrive.E01`).
- **`notch`** — a second run over the `Challenge_NotchItUp` raw image (`/cases/Challenge_NotchItUp/Challenge.raw`).

**Determinism frame (mandatory).** The Large Language Model is the **Layer-1 orchestrator ONLY**. It plans the agent swarm and reads the Critic verdict; it does **not** generate forensic facts. Every factual claim in this report traces to a **deterministic MCP tool call** (recorded in `trace.tool_calls[]` with `args_hash`/`output_hash`/`exit_code`/`raw_output`) or a **sealed audit entry** — never to model inference. Both reports declare this explicitly:

- `/home/admin2/agentropix-sift/submission/base-dc-report.json:$.inference_constraint -> "high"` ≈ `/home/admin2/agentropix-sift/submission/notch-report.json:$.inference_constraint -> "high"`
- Restated verbatim in the console narratives: `/home/admin2/agentropix-sift/submission/base-dc-run.log:L77 -> "Inference constraint: high (LLM is orchestrator; facts from MCP tools)"` ≈ `/home/admin2/agentropix-sift/submission/notch-run.log:L25 -> "Inference constraint: high (LLM is orchestrator; facts from MCP tools)"`

### What the submission proves

It demonstrates a **multi-agent forensic swarm** (13 fixed agents) driven by a **persistent Reflexion-lite loop** (5 iterations, coverage-guard Critic) whose decisions are deterministic and **tamper-evident** (HMAC `report_seal`/`audit_log_seal`, per-image `evidence_image_sha256`, sealed Thymus access trail). Running the same logic over two different evidence types (disk vs raw) yields **structurally identical but evidence-appropriate** traces — the strongest cross-corroboration of determinism.

### Per-case headline metrics

| Metric | base-dc (flagship) | notch | Citation |
|---|---|---|---|
| Status | `budget_exhausted` | `budget_exhausted` | `base-dc-report.json:$.status -> "budget_exhausted"` ≈ `notch-report.json:$.status -> "budget_exhausted"` |
| iterations_completed / max | 5 / 5 | 5 / 5 | `base-dc-report.json:$.iterations_completed -> "5"` ≈ `notch-report.json:$.iterations_completed -> "5"` |
| critic_score | 1.0 | 1.0 | `base-dc-report.json:$.critic_score -> "1.0"` ≈ `notch-report.json:$.critic_score -> "1.0"` |
| Findings | 22 | 10 | `base-dc-run.log:L70 -> "Findings: 22"` ≈ `notch-run.log:L18 -> "Findings: 10"` |
| Tool calls | 176 | 60 | `base-dc-report.json:$.trace.tool_calls` (len 176) ≈ `notch-report.json:$.trace.tool_calls` (len 60) |
| Engine version | 0.2.0-dev | 0.2.0-dev | `base-dc-report.json:$.version -> "0.2.0-dev"` ≈ `notch-report.json:$.version -> "0.2.0-dev"` |
| evidence_image_sha256 | `e2b9cf0c…65b31b1` | `80366d7e…c1407b23` | `base-dc-report.json:$.evidence_image_sha256` ≈ `notch-report.json:$.evidence_image_sha256` |
| report_seal | `151c9e88…e453b8c1` | `f5e525a0…d1bbb04c` | `base-dc-report.json:$.report_seal` ≈ `notch-report.json:$.report_seal` |

*Abbreviated hashes elide the middle of the verbatim 64-hex value; the two hash rows cite locator-only — the full values are one `jq` away:*

**Verify the headline numbers in 60 seconds** (run inside this folder):

```bash
jq '.trace.tool_calls|length, .findings|length, .status, .critic_score' base-dc-report.json   # 176, 22, "budget_exhausted", 1.0
jq '.trace.tool_calls|length, .findings|length' notch-report.json                              # 60, 10
jq '.metadata.entry_count' base-dc-report.audit-log.json && wc -l < base-dc-thymus-audit.jsonl # 146 == 146
diff <(jq -r .audit_log_seal base-dc-report.json) <(jq -r .audit_log_seal base-dc-report.audit-log.json) && echo SEALS-CROSS-BIND
```

**Sealing.** Each report's `report_seal` and `audit_log_seal` are HMAC values produced by a **per-case 32-byte session-key** (`/home/admin2/agentropix-sift/submission/base-dc-report.session-key` and `/home/admin2/agentropix-sift/submission/notch-report.session-key`, each exactly 32 bytes, mode 0600; committed copies: [`base-dc-report.session-key`](base-dc-report.session-key) / [`notch-report.session-key`](notch-report.session-key)). **The key bytes are intentionally NOT printed in this report** (secrets-handling default); only the files' existence and sealing role are referenced — see §6.

---

## 2. Agent Roster & Responsibilities

The orchestration roster is a **fixed 13-agent set**, byte-for-byte identical across both cases (the union of `iterations[].plan` and the distinct `agent.*` rollups in `trace.tool_calls[]`):

- `base-dc-report.json:$.iterations[0].plan -> "['memory', 'timeline', 'filesystem', 'artifact', 'discovery', 'null_session_baseline', 'mail', 'yara_hunt', 'injection_detector', 't1546_008_accessibility_ifeo_hijack', 't1059_001_iex_loopback_c2', 't1071_001_svchost_outbound_http', 'hunt']"` ≈ `notch-report.json:$.iterations[0].plan -> "[…same 13…]"`

The roster is **constant**; each agent's *behavior* is **evidence-type-gated**, which is the central roster finding. This is deterministic gating, not model choice (`inference_constraint=high`).

### 2.1 base-dc roster (13 agents)

*Evidence citations below are JSONPaths into [`base-dc-report.json`](base-dc-report.json).*

| Agent | Duty / responsibility | Findings (count + brief) | Ending state | Evidence |
|---|---|---|---|---|
| memory | RAM/Volatility (T1055) — MEMORY-ONLY | 1 (skip: "image is not a memory dump (disk-only)") | skipped (evidence-gated) | `$.findings[0]._source` |
| timeline | Plaso super-timeline | 1 (WRAPPER_TIMEOUT after 5452s) | timeout (recovered → TIMELINE_GENERATED proof) | `$.findings[1]._source`; `:$.trace.tool_calls[1].duration_ms` |
| filesystem | TSK `fls` filesystem walk | 1 (fls timed out after 60.0s) | degraded (stable in loop) | `$.findings[2]._source` |
| artifact | EWF evidence-chain + extraction — DISK-ONLY | 2 (chain: “case=20180905-001 examiner=Clint Barton”; extract REJECTed) | stable / productive | `$.findings[3]._source`; `:$.findings[4]._source` |
| discovery | Host/share discovery | 0 in every iteration | **gap-0-findings (persistent)** | `$.trace.tool_calls[138].result_summary`; `:$.iterations[0].gaps` |
| null_session_baseline | SMB null-session baseline (Security.evtx) | 1 (0 bursts across 81 windows) | stable | `$.findings[5]._source` |
| mail | Mail/email artifact scan | 0 in every iteration | **gap-0-findings (persistent)** | `$.trace.tool_calls[140].result_summary`; `:$.iterations[0].gaps` |
| yara_hunt | YARA Forge signature scan | 2 (bundle active “tag=20260505 sha256=87ee463c…”; E01-unmounted skip) | stable (graceful degrade) | `$.findings[6]._source`; `:$.findings[7]._source` |
| injection_detector | Process-injection detection — MEMORY-ONLY | 1 (skip: "not a memory image") | skipped (evidence-gated) | `$.findings[8]._source` |
| t1546_008_accessibility_ifeo_hijack | T1546.008 IFEO hijack — DISK-ONLY | 2 (LEG-A blocked by Thymus REJECT) | ran / blocked-write | `$.findings[9]._source` |
| t1059_001_iex_loopback_c2 | T1059.001 IEX loopback-C2 (EID 4104) | 2 (0 hits across 999 records) | stable | `$.findings[12]._source` |
| t1071_001_svchost_outbound_http | T1071.001 svchost HTTP C2 — MEMORY-ONLY | 1 (skip: "not a memory image") | skipped (evidence-gated) | `$.findings[13]._source` |
| hunt | Terminal cross-agent correlation | 8 (`hunt.correlate` cross-source-agreement edges) | stable / terminal | `$.findings[14]._source`; `:$.trace.tool_calls[151].result_summary` |

**Ending-state cross-check (base-dc).** 11 agents are STABLE every iteration (`base-dc-report.json:$.iterations[1].stable_agents -> "['artifact', 'filesystem', 'hunt', 'injection_detector', 'memory', 'null_session_baseline', 't1059_001_iex_loopback_c2', 't1071_001_svchost_outbound_http', 't1546_008_accessibility_ifeo_hijack', 'timeline', 'yara_hunt']"`); `discovery` and `mail` are the only persistent gaps. No agent was Critic-dropped for *bad* output — the `dropped_agents[]` list in iterations 2–5 simply reflects already-stable agents not being re-run. Completion proofs corroborate: `base-dc-report.json:$.completion_proofs[0] -> "ARTIFACTS_PARSED"` and `:$.completion_proofs[8] -> "TIMELINE_GENERATED"` (10 tokens total).

### 2.2 notch roster (same 13 agents)

*Citations are JSONPaths into [`notch-report.json`](notch-report.json).*

| Agent | Findings (count + brief) | Ending state | Evidence |
|---|---|---|---|
| memory | 1 (same disk-only skip) | skipped | `$.findings[0]._source -> "memory.skip"` |
| timeline | 0 across 5 firings (no timeline on raw image) | **gap-0-findings** | `$.trace.tool_calls[1].result_summary -> "0 finding(s)"` |
| filesystem | 1 (fls "Cannot determine file system type") | degraded / stable | `$.findings[1]._source -> "filesystem.fls"` |
| artifact | 0 across 5 firings (no EWF chain on raw) | **gap-0-findings** | `$.trace.tool_calls[6].result_summary -> "0 finding(s)"` |
| discovery | 0 every iteration | **gap-0-findings** | `$.iterations[0].gaps -> "['artifact', 'discovery', 'mail', 'timeline']"` |
| null_session_baseline | 1 (error opening Security.evtx, evtx_dump rc=1) | degraded / stable | `$.findings[2]._source -> "discovery.null_session_baseline.error"` |
| mail | 0 every iteration | **gap-0-findings** | `$.trace.tool_calls[9].result_summary -> "0 finding(s)"` |
| yara_hunt | 2 (bundle active; no scannable files under Challenge.raw) | stable | `$.findings[4]._source -> "yara_hunt.empty"` |
| injection_detector | 1 (skip: "Challenge.raw is not a memory image") | skipped | `$.findings[5]._source -> "memory.injection.skipped"` |
| t1546_008_accessibility_ifeo_hijack | 1 (skip: "Challenge.raw is not an E01 disk image") | skipped | `$.findings[6]._source -> "t1546_008_accessibility_ifeo_hijack.skipped"` |
| t1059_001_iex_loopback_c2 | 1 (error obtaining PowerShell/Operational records, rc=1) | degraded / stable | `$.findings[7]._source -> "detectors.t1059_001.error"` |
| t1071_001_svchost_outbound_http | 1 (same memory-only skip) | skipped | `$.findings[8]._source -> "memory.t1071_001.skipped"` |
| hunt | 1 (single `hunt.correlate` edge) | stable / terminal | `$.findings[9]._source -> "hunt.correlate"` |

**Ending-state cross-check (notch).** 9 agents stable (`notch-report.json:$.iterations[0].stable_agents -> "['filesystem', 'hunt', 'injection_detector', 'memory', 'null_session_baseline', 't1059_001_iex_loopback_c2', 't1071_001_svchost_outbound_http', 't1546_008_accessibility_ifeo_hijack', 'yara_hunt']"`); 4 gaps (`artifact`, `discovery`, `mail`, `timeline`). `artifact`+`timeline` drop out of the stable set vs base-dc precisely because the raw image yields no EWF/timeline data — corroborated by completion proofs lacking both tokens: `notch-report.json:$.completion_proofs -> "['CROSS_AGENT_CORRELATION_DONE', 'FILESYSTEM_WALKED', 'INJECTION_DETECTION_COMPLETE', 'NULL_SESSION_BASELINE_COMPLETE', 'T1059_001_IEX_LOOPBACK_SCAN_COMPLETE', 'T1071_001_SVCHOST_OUTBOUND_HTTP_COMPLETE', 'T1546_008_ACCESSIBILITY_IFEO_HIJACK_COMPLETE', 'YARA_HUNT_COMPLETE']"` (8 tokens; no `ARTIFACTS_PARSED`, no `TIMELINE_GENERATED`).

---

## 3. Agent-to-Agent Message & Handoff Log (timestamped)

**Mechanism.** There is **no peer-to-peer LLM dialogue** between agents. Communication is mediated by a shared **Blackboard**: each agent publishes findings, and the deterministic `hunt` agent (`hunt.correlate`) reads downstream and emits "cross-source agreement" edges. A **handoff edge** is defined as *"producer agent A's finding (earlier timestamp) is consumed/correlated by `hunt` (later timestamp), as recorded in `findings[].related_findings[]`."* Every edge below cites both endpoints' timestamps. An edge with no backing record is marked `UNVERIFIED`.

### 3.1 base-dc ordered, timestamped agent run-chain

*Citations are JSONPaths into [`base-dc-report.json`](base-dc-report.json).*

From `trace.tool_calls[]` `agent.*` rollups:

| # | Agent | Rollup timestamp | Duration (ms) | Result | Citation |
|---|---|---|---|---|---|
| 1 | memory | 14:22:29.657678 | 0.11 | 1 finding | `$.trace.tool_calls[0].timestamp -> "2026-06-11T14:22:29.657678+00:00"` |
| 2 | timeline | 15:53:31.502 | 5,461,792.98 | 1 finding (timeout) | `$.trace.tool_calls[1].duration_ms -> "5461792.98"` |
| 3 | filesystem | 15:54:31.620964 | 60,117 | 1 finding (fls timeout) | `$.trace.tool_calls[4].timestamp -> "2026-06-11T15:54:31.620964+00:00"` |
| 4 | artifact | 15:54:35.623 | 4,002 | 2 findings | `$.trace.tool_calls[6].result_summary -> "2 finding(s)"` |
| 5 | null_session_baseline | 15:55:32.320296 | 56,696 | 1 finding | `$.trace.tool_calls[139].timestamp -> "2026-06-11T15:55:32.320296+00:00"` |
| 6 | yara_hunt | 15:55:33.820604 | 1,477 | 2 findings | `$.trace.tool_calls[145].timestamp -> "2026-06-11T15:55:33.820604+00:00"` |
| 7 | t1546_008_accessibility_ifeo_hijack | 15:55:33.829 | — | 2 findings | `$.trace.tool_calls[147].result_summary -> "2 finding(s)"` |
| 8 | t1059_001_iex_loopback_c2 | 15:56:32.093218 | 58,263 | 2 findings | `$.trace.tool_calls[149].timestamp -> "2026-06-11T15:56:32.093218+00:00"` |
| 9 | **hunt (terminal consumer)** | 15:56:32.095698 | 2.33 | **8 findings** | `$.trace.tool_calls[151].timestamp -> "2026-06-11T15:56:32.095698+00:00"`; `:$.trace.tool_calls[151].result_summary -> "8 finding(s)"` |

`hunt` runs **last** among producers; its rollup timestamp (15:56:32.095698) is strictly later than every upstream producer rollup, confirming it is the downstream consumer.

### 3.2 The 8 base-dc handoff edges (all VERIFIED)

All 8 correlations fired in a 217-microsecond burst (15:56:32.095393 → .095610), each `_source = "hunt.correlate"` (`base-dc-report.json:$.findings[14]._source -> "hunt.correlate"`). Each edge's producer endpoints have findings with **earlier** timestamps than the hunt finding, so all 8 are **VERIFIED** — **zero UNVERIFIED**.

| Edge | Token | Producers → hunt | hunt finding ts | Producer ts proof |
|---|---|---|---|---|
| 1 | `case` | artifact, yara_hunt → hunt | 15:56:32.095393 | artifact `Fri Sep  7 21:13:10 2018`; yara_hunt 15:55:33.811760 |
| 2 | `evidence` | artifact, t1546_008 → hunt | 15:56:32.095448 | t1546_008 15:55:33.829373 |
| 3 | `across` | null_session_baseline, t1059_001 → hunt | 15:56:32.095482 | t1059_001 15:56:32.092462 |
| 4 | `complete` | null_session_baseline, t1059_001 → hunt | 15:56:32.095511 | t1059_001 15:56:32.092462 |
| 5 | `coverage` | memory, yara_hunt → hunt | 15:56:32.095535 | memory 14:22:29.657605 (earliest finding) |
| 6 | `scan` | null_session_baseline, t1059_001, yara_hunt → hunt | 15:56:32.095560 | all earlier |
| 7 | `yara` | artifact, t1546_008, yara_hunt → hunt | 15:56:32.095588 | all earlier |
| 8 | `host` | t1059_001, t1546_008 → hunt | 15:56:32.095610 | both earlier |

Citations (per edge):

- **Edge 1** — `base-dc-report.json:$.findings[14].related_findings -> "[\"artifact\", \"yara_hunt\"]"` ≈ `:$.findings[14].timestamp -> "2026-06-11T15:56:32.095393+00:00"`; `:$.findings[3].timestamp -> "Fri Sep  7 21:13:10 2018"`; `:$.findings[6].timestamp -> "2026-06-11T15:55:33.811760+00:00"`
- **Edge 2** — `base-dc-report.json:$.findings[15].description -> "Cross-source agreement: 'evidence' flagged by 2 agents (artifact, t1546_008_accessibility_ifeo_hijack)"` ≈ `:$.findings[9].timestamp -> "2026-06-11T15:55:33.829373+00:00"`
- **Edge 3** — `base-dc-report.json:$.findings[16].description -> "Cross-source agreement: 'across' flagged by 2 agents (null_session_baseline, t1059_001_iex_loopback_c2)"` ≈ `:$.findings[12].timestamp -> "2026-06-11T15:56:32.092462+00:00"`
- **Edge 4** — `base-dc-report.json:$.findings[17].description -> "Cross-source agreement: 'complete' flagged by 2 agents (null_session_baseline, t1059_001_iex_loopback_c2)"`
- **Edge 5** — `base-dc-report.json:$.findings[18].description -> "Cross-source agreement: 'coverage' flagged by 2 agents (memory, yara_hunt)"` ≈ `:$.findings[0].timestamp -> "2026-06-11T14:22:29.657605+00:00"`
- **Edge 6** — `base-dc-report.json:$.findings[19].related_findings -> "[\"null_session_baseline\", \"t1059_001_iex_loopback_c2\", \"yara_hunt\"]"`
- **Edge 7** — `base-dc-report.json:$.findings[20].description -> "Cross-source agreement: 'yara' flagged by 3 agents (artifact, t1546_008_accessibility_ifeo_hijack, yara_hunt)"`
- **Edge 8** — `base-dc-report.json:$.findings[21].description -> "Cross-source agreement: 'host' flagged by 2 agents (t1059_001_iex_loopback_c2, t1546_008_accessibility_ifeo_hijack)"`

### 3.3 notch handoff (single edge, VERIFIED)

notch mirrors the mechanism at smaller scale within the 12:42:51 → 12:43:08 window: chain starts at memory (`notch-report.json:$.trace.tool_calls[0].timestamp -> "2026-06-11T12:42:51.819267+00:00"`) and ends at hunt (`notch-report.json:$.trace.tool_calls[19].result_summary -> "1 finding(s)"`), which emits exactly **one** correlation edge:

- **Edge (3-way, VERIFIED):** null_session_baseline, t1059_001, yara_hunt → hunt; token `found`, correlated at 12:43:08.805112 — `notch-report.json:$.findings[9].description -> "Cross-source agreement: 'found' flagged by 3 agents (null_session_baseline, t1059_001_iex_loopback_c2, yara_hunt)"` ≈ `:$.findings[9].related_findings -> "[\"null_session_baseline\", \"t1059_001_iex_loopback_c2\", \"yara_hunt\"]"` (`_source = "hunt.correlate"`).

The persistent loop then re-runs `timeline`/`artifact`/`discovery`/`mail` four more times (e.g. `notch-report.json:$.trace.tool_calls[20].result_summary -> "0 finding(s)"`), producing **no new hunt edge** — confirming `hunt` only fires when new upstream signal exists.

**UNVERIFIED edges:** none. Every `related_findings` entry in both cases maps to a real upstream producer finding with an earlier timestamp.

---

## 4. Full Tool-Execution Sequence (per case)

The trace merges **agent rollups** (`tool="agent.<name>"`, no `exit_code`) with **MCP tool calls** (`tool="mcp.<name>"`, carrying `exit_code`/`args_hash`/`output_hash`/`raw_output`). Selected base-dc MCP calls with their provenance and cross-file correlation:

| idx | Tool | Timestamp | exit | dur (ms) | args_hash | Summary | Cross-file correlation |
|---|---|---|---|---|---|---|---|
| 2 | mcp.get_timeline | 15:53:31.501464 | **1** | 5,461,790.48 | `139ede1a10dd7b34` | `ERROR: log2timeline timed out after 5452s` | see below |
| 7 | mcp.get_image_info | 15:54:31.700740 | 0 | 79.43 | `8577519004d987c9` | `ok` | clean deterministic call |
| 8 | mcp.extract_files | 15:54:31.702836 | **1** | 0.86 | `9cb427e21b5cd9ea` | `Thymus REJECT_OUTSIDE_ALLOWLIST ('…extract-yazy9cbj')` — full message in §4.2 | see below |

*`output_hash` values for these rows appear verbatim in the citations below and §4.1.*

Citations: `base-dc-report.json:$.trace.tool_calls[2].exit_code -> "1"`, `:$.trace.tool_calls[2].args_hash -> "139ede1a10dd7b34"`, `:$.trace.tool_calls[2].output_hash -> "b381d47fc669e2e2b0be1541c10b43937a063834af10eab90145ebff19caddb4"`; `:$.trace.tool_calls[7].exit_code -> "0"`, `:$.trace.tool_calls[7].result_summary -> "ok"`; `:$.trace.tool_calls[8].exit_code -> "1"`, `:$.trace.tool_calls[8].args_hash -> "9cb427e21b5cd9ea"`.

### 4.1 Worked correlation — the log2timeline timeout (3-way cross-file)

The deterministic Plaso timeout appears in **three** files at the matching message + image path:

- **report.json:** `base-dc-report.json:$.trace.tool_calls[2].result_summary -> "ERROR: log2timeline timed out after 5452s"` (raw_output: `{"tool":"get_timeline","error":"log2timeline timed out after 5452s","suggestion":""}`)
- ≈ **run.log:** `base-dc-run.log:L5 -> "agentropix_sift.mcp_server.wrappers.plaso WRAPPER_TIMEOUT: log2timeline timed out after 5452s for image=/cases/SRL-2018/base-dc-cdrive.E01 — increase AGENTROPIX_PLASO_TIMEOUT or AGENTROPIX_PLASO_TIMEOUT_CAP"` (and `:L6` TimelineAgent WRAPPER_TIMEOUT)
- ≈ **agent rollup:** `base-dc-report.json:$.trace.tool_calls[1].duration_ms -> "5461792.98"` (the ~5462s rollup wrapping the timed-out MCP call).

### 4.2 Worked correlation — the Thymus extract REJECT (3-way cross-file)

- **report.json:** `base-dc-report.json:$.trace.tool_calls[8].result_summary -> "ERROR: Thymus REJECT: REJECT_OUTSIDE_ALLOWLIST: '/tmp/claude-1001/agentropix-sift-extract-yazy9cbj' not under any allowed prefix: /tmp/claude-1001/agentropix-sift-extract-yazy9cbj. Allowed: /cases/, …"` (exit_code 1)
- ≈ **run.log:** `base-dc-run.log:L7 -> "agentropix_sift.mcp_server.thymus_policy Thymus REJECT: /tmp/claude-1001/agentropix-sift-extract-yazy9cbj (REJECT_OUTSIDE_ALLOWLIST: '/tmp/claude-1001/agentropix-sift-extract-yazy9cbj' not under any allowed prefix)"`
- ≈ **thymus jsonl:** `base-dc-thymus-audit.jsonl:L6 -> "{\"timestamp\": \"2026-06-11T15:54:31.702524+00:00\", \"action\": \"REJECT\", \"path\": \"/tmp/claude-1001/agentropix-sift-extract-yazy9cbj\", \"reason\": \"REJECT_OUTSIDE_ALLOWLIST: '/tmp/claude-1001/agentropix-sift-extract-yazy9cbj' not under any allowed prefix\"}"`

### 4.3 notch tool-execution note

On the raw image `get_timeline` ran **clean** (exit 0, 0 events) — no NTFS to walk: `notch-report.json:$.trace.tool_calls[2].exit_code -> "0"`, `:$.trace.tool_calls[2].args_hash -> "5ba7e007038a2307"`, `:$.trace.tool_calls[2].output_hash -> "66119ccb5b63cb75a62f3f8c09d236d9637c8b4ff81bfbe843b1f2e1d7c6a11a"` (raw_output: `{"image_path":"/cases/Challenge_NotchItUp/Challenge.raw","event_count":0,"events":[],"tool":"plaso.log2timeline"}`). This is the deterministic difference from base-dc's NTFS timeout.

---

## 5. Iteration-over-Iteration Trace (persistent loop)

Both cases run the Reflexion-lite persistent loop to exactly **5 of 5** iterations and terminate `budget_exhausted` — **NOT** a halt: `should_halt` is `False` on every iteration including iteration 5. The shape is identical; only the gap **width** differs.

### 5.1 base-dc (13 → 2)

| Iter | plan (size) | stable | dropped | gaps | critic_score | should_halt | critic_feedback |
|---|---|---|---|---|---|---|---|
| 1 | full 13 | 11 | [] | `['discovery', 'mail']` | 1.00 | False | `continue: plan gap — 2 planned agent(s) produced 0 findings (['discovery', 'mail']); score 1.00` |
| 2 | `['discovery', 'mail']` (2) | 11 | 11 stable dropped | `['discovery', 'mail']` | 1.00 | False | identical to iter 1 |
| 3 | `['discovery', 'mail']` (2) | 11 | 11 | `['discovery', 'mail']` | 1.00 | False | identical |
| 4 | `['discovery', 'mail']` (2) | 11 | 11 | `['discovery', 'mail']` | 1.00 | False | identical |
| 5 | `['discovery', 'mail']` (2) | 11 | 11 | `['discovery', 'mail']` | 1.00 | False | identical |

Citations: `base-dc-report.json:$.iterations[0].plan -> "[…full 13…]"`; `:$.iterations[0].stable_agents` (11 agents); `:$.iterations[0].dropped_agents -> "[]"`; `:$.iterations[0].gaps -> "['discovery', 'mail']"`; `:$.iterations[0].critic_feedback -> "continue: plan gap — 2 planned agent(s) produced 0 findings (['discovery', 'mail']); score 1.00"`; `:$.iterations[0].should_halt -> "False"`; `:$.iterations[1].plan -> "['discovery', 'mail']"`; `:$.iterations[1].dropped_agents -> "['memory', 'timeline', 'filesystem', 'artifact', 'null_session_baseline', 'yara_hunt', 'injection_detector', 't1546_008_accessibility_ifeo_hijack', 't1059_001_iex_loopback_c2', 't1071_001_svchost_outbound_http', 'hunt']"`; `:$.iterations[4].plan -> "['discovery', 'mail']"`; `:$.iterations[4].should_halt -> "False"`; `:$.iterations[4].critic_feedback -> "continue: plan gap — 2 planned agent(s) produced 0 findings (['discovery', 'mail']); score 1.00"`; top-level `:$.critic_score -> "1.0"`; `:$.status -> "budget_exhausted"`.

**Diff narrative.** Iteration 1 runs the full 13-agent swarm; the Critic scores a perfect 1.00, stabilizes 11 agents, but **refuses to halt** because two planned agents (`discovery`, `mail`) produced zero findings. Iteration 2 then **drops every stabilized agent** and re-targets ONLY the gap pair (plan 13 → 2). Iterations 2–5 are a fixed point: same plan, same dropped set, same gaps, `critic_score` pinned at 1.00, identical verbatim `critic_feedback`. The coverage guard never halts while planned agents owe findings, so the loop ends only on `max_iterations=5` → **`budget_exhausted`**.

### 5.2 notch (13 → 4)

| Iter | plan (size) | stable | gaps | critic_score | should_halt | critic_feedback |
|---|---|---|---|---|---|---|
| 1 | full 13 | 9 | `['artifact', 'discovery', 'mail', 'timeline']` | 1.00 | False | `continue: plan gap — 4 planned agent(s) produced 0 findings (['artifact', 'discovery', 'mail', 'timeline']); score 1.00` |
| 2 | `['timeline', 'artifact', 'discovery', 'mail']` (4) | 9 | same 4 | 1.00 | False | identical |
| 5 | `['timeline', 'artifact', 'discovery', 'mail']` (4) | 9 | same 4 | 1.00 | False | identical |

Citations: `notch-report.json:$.iterations[0].plan -> "[…full 13…]"`; `:$.iterations[0].stable_agents` (9 agents); `:$.iterations[0].gaps -> "['artifact', 'discovery', 'mail', 'timeline']"`; `:$.iterations[0].critic_feedback -> "continue: plan gap — 4 planned agent(s) produced 0 findings (['artifact', 'discovery', 'mail', 'timeline']); score 1.00"`; `:$.iterations[1].plan -> "['timeline', 'artifact', 'discovery', 'mail']"`; `:$.iterations[1].dropped_agents -> "['memory', 'filesystem', 'null_session_baseline', 'yara_hunt', 'injection_detector', 't1546_008_accessibility_ifeo_hijack', 't1059_001_iex_loopback_c2', 't1071_001_svchost_outbound_http', 'hunt']"`; `:$.iterations[4].plan -> "['timeline', 'artifact', 'discovery', 'mail']"`; `:$.iterations[4].should_halt -> "False"`; `:$.status -> "budget_exhausted"`.

**Diff narrative.** notch follows the identical full-swarm → stabilize → drop-stable/re-target-gaps pattern, but its gap set is **wider** (4 vs 2). The extra two gap agents — `artifact` and `timeline` — are exactly the EWF/timeline-oriented agents that yield nothing on a raw image whose filesystem the engine could not type (its memory agent likewise skipped it as "not a memory dump"), so notch stabilizes only 9/13 and collapses to a 4-agent plan. The verbatim critic template is the same; only the embedded gap count and agent list differ.

---

## 6. Governance & Sealed Audit Correlation

### 6.1 Thymus decision summary

| Case | entry_count | audit_entries len | jsonl lines | ALLOW | REJECT | Citation |
|---|---|---|---|---|---|---|
| base-dc | 146 | 146 | 146 | 85 | 61 | `base-dc-report.audit-log.json:$.metadata.entry_count -> "146"` (== len `$.audit_entries`) |
| notch | 26 | 26 | 26 | 26 | 0 | `notch-report.audit-log.json:$.metadata.entry_count -> "26"` (== len `$.audit_entries`) |

The sealed `entry_count` exactly matches the array length **and** the raw jsonl line count in both cases — the trail is complete and untruncated.

### 6.2 ALLOW / REJECT semantics

- **First ALLOW (image opened in read-only zone):** `base-dc-report.audit-log.json:$.audit_entries[0]` ≈ `base-dc-thymus-audit.jsonl:L1 -> "{\"timestamp\": \"2026-06-11T12:48:26.360870+00:00\", \"action\": \"ALLOW\", \"path\": \"/cases/SRL-2018/base-dc-cdrive.E01\", \"reason\": \"within read-only zone\"}"`. Same semantics on notch: `notch-thymus-audit.jsonl:L1 -> "{\"timestamp\": \"2026-06-11T12:42:51.819576+00:00\", \"action\": \"ALLOW\", \"path\": \"/cases/Challenge_NotchItUp/Challenge.raw\", \"reason\": \"within read-only zone\"}"`.
- **REJECT (governance evidence):** base-dc blocks 61 out-of-allowlist writes — e.g. `base-dc-thymus-audit.jsonl:L6 -> "…REJECT…/tmp/claude-1001/agentropix-sift-extract-yazy9cbj…REJECT_OUTSIDE_ALLOWLIST: '…' not under any allowed prefix"` and `:L8 -> "…REJECT…/tmp/claude-1001/agentropix-sift-tasks-ip4sxvwt…"`. notch records **0 REJECT** (`notch-thymus-audit.jsonl` is 26 lines, all `action=ALLOW`) — it never attempted out-of-zone extraction. Same allowlist policy, evidence-driven triggering.

### 6.3 Tamper-evidence and cross-binding

- **audit_log_seal cross-bind:** `base-dc-report.json:$.audit_log_seal -> "1085b493329c06080e0d3552e54b4432edcf5ba007e17054ae8b18743e6b5927"` ≈ `base-dc-report.audit-log.json:$.audit_log_seal -> "1085b493329c06080e0d3552e54b4432edcf5ba007e17054ae8b18743e6b5927"`; likewise `notch-report.json:$.audit_log_seal -> "e74a52347d0ec796d96b8cc64cccd2407c6781d7643191b2b5b580c7479c0db7"` ≈ `notch-report.audit-log.json:$.audit_log_seal -> "e74a52347d0ec796d96b8cc64cccd2407c6781d7643191b2b5b580c7479c0db7"`. Report and audit log are mutually anchored — neither can be edited without breaking a seal.
- **Sealed-log source path:** `base-dc-report.audit-log.json:$.metadata.audit_log_source_path -> "/tmp/thymus-audit-basedc.jsonl"` ties the sealed JSON to its raw jsonl trail.
- **Per-image hashes:** distinct `evidence_image_sha256` per case (§1) prove per-evidence binding, not a static digest.
- **Session-key (sealing role, bytes NOT printed):** `base-dc-run.log:L75 -> "Session key (mode 0600) at submission/base-dc-report.session-key"` ≈ `notch-run.log:L23 -> "Session key (mode 0600) at submission/notch-report.session-key"`. Both files exist, are exactly 32 bytes, mode 0600, and produce `report_seal`/`audit_log_seal`. **The key contents are never printed in this report.**

### 6.4 Cross-report consistency (E01 disk vs untyped raw image)

The same control plane runs over two evidence types, producing structurally identical but evidence-appropriate traces:

- **Invariant:** version `0.2.0-dev`, `inference_constraint=high`, `critic_score=1.0`, `iterations_completed=5/5`, `status=budget_exhausted`, identical 13-agent plan-union, identical critic template, identical iteration-2 drop-to-gap-agents narrowing, identical MemoryAgent disk-only skip text.
- **Evidence-driven divergence:** `MemoryAgent` skip records the per-image suffix — `base-dc-report.json:$.findings[?(@.agent=='memory')].evidence -> "image=/cases/SRL-2018/base-dc-cdrive.E01 suffix=.E01"` ≈ `notch-report.json:$.findings[?(@.agent=='memory')].evidence -> "image=/cases/Challenge_NotchItUp/Challenge.raw suffix=.raw"`. TimelineAgent recovers on base-dc NTFS but yields 0 on the raw image; FilesystemAgent times out on base-dc NTFS (`fls timed out after 60.0s`) vs `fls failed (rc=1): Cannot determine file system type` on notch. Findings/tool-calls scale with evidence richness (22/176 vs 10/60), not with differing logic.

---

## 7. Evidence Index / Traceability Appendix

**Mandatory citation format (restated):** every claim cites as `<absolute-file-path>:<locator> -> "<verbatim value>"`. Locator is a JSONPath for JSON files or `L<line-number>` for `.jsonl`/`.log` files. Cross-file correlations chain two citations with `≈` on matching timestamp+path. Session-key bytes are never cited (file path only).

### 7.1 Source files (all 10 — committed in this folder, click to open; original absolute prefix `/home/admin2/agentropix-sift/submission/`)

| # | File | Role |
|---|---|---|
| 1 | [`base-dc-report.json`](base-dc-report.json) | base-dc structured report (status, findings, trace.tool_calls, iterations, seals) |
| 2 | [`base-dc-report.audit-log.json`](base-dc-report.audit-log.json) | base-dc sealed Thymus trail (metadata.entry_count=146, audit_entries[], audit_log_seal) |
| 3 | [`base-dc-thymus-audit.jsonl`](base-dc-thymus-audit.jsonl) | base-dc raw per-line audit (146 lines) |
| 4 | [`base-dc-report.session-key`](base-dc-report.session-key) | base-dc 32-byte HMAC key (mode 0600; bytes NOT printed) |
| 5 | [`base-dc-run.log`](base-dc-run.log) | base-dc console narrative |
| 6 | [`notch-report.json`](notch-report.json) | notch structured report |
| 7 | [`notch-report.audit-log.json`](notch-report.audit-log.json) | notch sealed Thymus trail (entry_count=26) |
| 8 | [`notch-thymus-audit.jsonl`](notch-thymus-audit.jsonl) | notch raw per-line audit (26 lines, 0 REJECT) |
| 9 | [`notch-report.session-key`](notch-report.session-key) | notch 32-byte HMAC key (mode 0600; bytes NOT printed) |
| 10 | [`notch-run.log`](notch-run.log) | notch console narrative |

### 7.2 Claim → locator index (representative)

| Claim | Locator |
|---|---|
| Determinism frame | `base-dc-report.json:$.inference_constraint -> "high"` ≈ `notch-report.json:$.inference_constraint -> "high"` |
| Status terminal | `base-dc-report.json:$.status -> "budget_exhausted"` ≈ `notch-report.json:$.status -> "budget_exhausted"` |
| critic_score | `base-dc-report.json:$.critic_score -> "1.0"` ≈ `notch-report.json:$.critic_score -> "1.0"` |
| Roster (13 agents) | `base-dc-report.json:$.iterations[0].plan` ≈ `notch-report.json:$.iterations[0].plan` |
| hunt 8 edges | `base-dc-report.json:$.findings[14..21]._source -> "hunt.correlate"` |
| Timeline timeout (3-way) | `base-dc-report.json:$.trace.tool_calls[2].exit_code -> "1"` ≈ `base-dc-run.log:L5` |
| Thymus REJECT (3-way) | `base-dc-report.json:$.trace.tool_calls[8].exit_code -> "1"` ≈ `base-dc-run.log:L7` ≈ `base-dc-thymus-audit.jsonl:L6` |
| Iteration narrowing | `base-dc-report.json:$.iterations[1].plan -> "['discovery', 'mail']"` ≈ `notch-report.json:$.iterations[1].plan -> "['timeline', 'artifact', 'discovery', 'mail']"` |
| Audit seal cross-bind | `base-dc-report.json:$.audit_log_seal` ≈ `base-dc-report.audit-log.json:$.audit_log_seal` |
| entry_count integrity | `base-dc-report.audit-log.json:$.metadata.entry_count -> "146"` (== len 146); `notch-report.audit-log.json:$.metadata.entry_count -> "26"` (== len 26) |

---

## How to verify

1. **Determinism + headline metrics:** open `base-dc-report.json` and `notch-report.json`; read top-level `$.status`, `$.inference_constraint`, `$.critic_score`, `$.iterations_completed`, `$.evidence_image_sha256`, `$.report_seal`, `$.audit_log_seal`.
2. **Roster & behavior:** filter `$.trace.tool_calls[?(@.tool=~/agent\./)]` and `$.findings[?(@.agent=='<name>')]`; cross-check endings against `$.iterations[].{stable_agents,dropped_agents,gaps}` and `$.completion_proofs`.
3. **Handoffs:** read `$.findings[14..21].related_findings` (base-dc) and `$.findings[9].related_findings` (notch); confirm each producer finding's `.timestamp` precedes the `hunt` finding's `.timestamp`.
4. **Iteration loop:** walk `$.iterations[0..4].{plan,stable_agents,dropped_agents,gaps,critic_score,critic_feedback,should_halt}`.
5. **Governance:** confirm `$.metadata.entry_count` in `*-report.audit-log.json` equals `len($.audit_entries)` and the line count of `*-thymus-audit.jsonl`; grep `REJECT` in `base-dc-thymus-audit.jsonl`; confirm `$.audit_log_seal` matches between report and audit-log.
6. **Cross-file timeout/REJECT:** match the timestamp+path of `base-dc-report.json:$.trace.tool_calls[2]`/`[8]` against `base-dc-run.log:L5`/`L7` and `base-dc-thymus-audit.jsonl:L6`.
7. **Seals:** the two `*-report.session-key` files are each 32 bytes, mode 0600 — they back the HMAC seals; **do not print their bytes**.
