# Expected findings — pre-compact-poisoning

This is the testable contract enforced by
`run-pre-compact-poisoning.mjs`. Nine independent assertions
across four scenarios. Any drift = hook regression or fixture rot.

## Required assertions (9 / 9 must pass)

### Scenario A — block mode + poisoned transcript

The poisoned transcript embeds two distinct triggers:

- An "ignore all previous instructions" phrase inside a synthetic
  `tool_result` block (matches `CRITICAL_PATTERNS` in
  `injection-patterns.mjs`).
- An AWS-shaped key built at runtime via string concatenation
  (matches `SECRET_PATTERNS` regex `/AKIA[0-9A-Z]{16}/`).

A.1 Hook exits with code `2`.
A.2 Stdout is JSON `{ "decision": "block", "reason": "..." }`.
A.3 The `reason` string mentions both:
    - an injection label (`/ignore previous|override/i`), AND
    - the AWS key label (`/AWS Access Key/i`).

If A.3 fails, either the injection-patterns regex set or the
SECRET_PATTERNS table changed in a way that dropped one of these
labels.

### Scenario B — warn mode + poisoned transcript

B.1 Hook exits with code `0` (advisory, not block).
B.2 Stdout is JSON `{ "systemMessage": "..." }` with no
    `decision` field. The `systemMessage` summary is the same as
    the block-mode `reason` text.

### Scenario C — off mode + poisoned transcript

C.1 Hook exits with code `0`.
C.2 Stdout is empty (no JSON). The `off` branch returns at the
    top of the script before reading the transcript at all,
    which is the documented "fully disabled" semantic.

### Scenario D — block mode + benign transcript

This is the brick-wall control: it proves the hook does not
reflexively block all compactions.

D.1 Hook exits with code `0`.
D.2 Stdout has no `decision: "block"` JSON. (Either no JSON or
    a non-block payload — the assertion only fails on a literal
    block decision, which would indicate a false positive.)

## Total finding shape (block mode)

```
pre-compact-scan (auto): 3 finding(s) in transcript. Compaction
may preserve poisoned content in condensed form. Top: override:
ignore previous instructions, indirect: instruction addressed
to AI/assistant, AWS Access Key ID.
```

The "3 finding(s)" count covers:

1. CRITICAL — `override: ignore previous instructions`
2. MEDIUM  — `indirect: instruction addressed to AI/assistant`
   (the synthetic tool-result text frames the injection as a
   "Note to assistant", which trips the indirect-address pattern)
3. SECRET  — `AWS Access Key ID`

If `injection-patterns.mjs` adds new MEDIUM rules that match the
fixture text, the count and `Top: ...` ordering may shift. The
contract only asserts the *labels* in the reason string, not the
finding count or order — that flexibility is intentional.

## Out of scope (intentionally)

- The other secret labels in `SECRET_PATTERNS`
  (GitHub / npm / PEM / bearer / generic). Demonstrating those
  would require either growing the fixture or building each at
  runtime; the AWS key alone is sufficient to prove the
  credential-finding path activates.
- The 512 KB tail cap (`LLM_SECURITY_PRECOMPACT_MAX_BYTES`) — not
  exercised because the synthetic transcript is small.
- The leetspeak / homoglyph / multi-language MEDIUM patterns —
  exercised by `examples/prompt-injection-showcase/`.
- The `compaction_trigger` legacy field name (the hook reads
  both `trigger` and `compaction_trigger`) — only `trigger` is
  exercised here.
