Skip to content

Commit 189df62

Browse files
committed
Establish a repeated compaction evidence baseline
1 parent 6ea5969 commit 189df62

7 files changed

Lines changed: 857 additions & 0 deletions

File tree

evals/compaction/README.md

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
# Primary compaction mechanics baseline
2+
3+
This is the frozen, offline component baseline for the primary compaction
4+
replacement. It is **not** a complete TUI/exec, permission-isolation, attachment
5+
security, or live model-quality evaluation. The bounded integration-harness
6+
scope has Greybeard approval; production `src/` remains unchanged.
7+
8+
## Reproduce
9+
10+
```bash
11+
bun test ./evals/compaction/metrics.test.ts ./tests/integration/compaction-baseline.test.ts
12+
```
13+
14+
The serial integration test uses the existing `openIntegrationSession`,
15+
`runUntilDone`, and `closeIntegrationSession`. Optional test-only wiring registers
16+
`createSessionPruningCompactor` with `createModelSummarizer`, and supplies the
17+
normal `buildCompactionContinuationMessage()` delivery callback. The actual
18+
primary director, governor, reactor, toolset, and optimized git-backed store run.
19+
No test calls the compactor directly, rewrites history, or uses a substitute
20+
compaction implementation. Existing harness callers remain unchanged.
21+
22+
The harness defaults to `permissiveAuthorize`; this fixture also bypasses tool
23+
permission prompts in its temporary workspace. **It proves no authorization,
24+
approval-resume, or permission-isolation property.** No production permission or
25+
repeat guard is changed. There is no production hook, second host, network
26+
server, or paid provider request.
27+
28+
## Frozen protocol
29+
30+
- Product revision: `6ea596945657f3a0d3af5bfb0277b94af19e1589`, package `0.3.18`.
31+
- Research revision `b92dad53` is not the baseline. No version bump is included.
32+
- Protocol/model script: `primary-component-mechanics-v1` in the integration test.
33+
- External inference: `@intx/inference-testing` `0.3.0`, Anthropic wire format,
34+
source/model `anthropic:claude-integration` / `claude-integration`.
35+
- Vendored Interchange base: `0205b07b64d03f0fec2e4be3593c764070a9ba8a`, with
36+
repository-local patches recorded in `docs/VENDORING.md` and the patch ledger.
37+
- Runtime of the captured sample: Bun `1.3.14`, Darwin arm64.
38+
- Production policy: six recent turns; production anchor and no-op rules;
39+
model summary limit 4,000 characters and deterministic factory limit 2,500.
40+
- Trigger schedule: primary inference calls **19, 31, 43** report **synthetic**
41+
input 200,000; other explicitly scripted setup/growth calls report synthetic
42+
input 100 and output 1. Zero cache/thinking fields are synthetic wire fields,
43+
not measurements. Evidence-response wire frames use harness defaults, not
44+
measured provider usage. Production thresholds and hysteresis are unchanged.
45+
- Each phase adds ten distinct user/assistant audit-item exchanges, then a
46+
distinct real `read_file` call. The governor intercepts the post-tool infer and
47+
resumes through the same agent's contentless inbound channel.
48+
- Time bounds: 30 seconds per send; 120 seconds for the positive fixture.
49+
50+
Git blob identities freeze the uncommitted harness additions without inventing
51+
a commit revision. Recompute with `git hash-object` on these paths:
52+
53+
- `evals/compaction/fixtures.ts`: `67f1fdfb45272464efc62111c1c7525ed067264b`
54+
- `evals/compaction/metrics.ts`: `3cc4efadb5dad1f8078b6db412287b0ab5c25e8f`
55+
- `tests/integration/compaction-baseline.test.ts`: `abed36b077cb16e30bc5015852144bb6bb7fb5b2`
56+
- Original captured-run evaluator: `80a627549e0e009ca7c3079e26875baf3407e8e9`
57+
- `tests/integration/harness.ts`: `4567ac03433b70f1eed4f3238e2a3b5e1f5b4557`
58+
59+
The fixture module deterministically generates the exact input bytes: an early
60+
constraint, a later corrected decision, a failing `bun diagnose.ts` with decisive
61+
output after 250 preamble lines, and an oversized diagnostic with the decisive
62+
value after 1,500 lines. A full read and a targeted middle-line read exercise
63+
real tools. Before growth, the test verifies that all four facts reached
64+
persisted history. Generated workspace files contain no grader expectations.
65+
66+
## Evidence and scoring
67+
68+
The summarizer responder extracts only evidence markers in the **actual excerpt
69+
received from the production summarizer**. It never reads the original fixture
70+
or discarded turns. The primary response matcher selects an answer only when
71+
its exact set of source/value/id triples is present in the actual wire request,
72+
independent of their order. A real-agent reversed-order regression recovers all
73+
four facts without weakening source/value matching.
74+
All 16 subsets include an explicit all-missing response. A separate real-agent
75+
negative test supplies no evidence and verifies that fixture answers do not
76+
appear. These controlled responders measure transport/loss, not model judgment.
77+
78+
`metrics.ts` scores exact source and value, separately from artifact completion.
79+
Its tests reject altered artifacts, wrong sources/answers, absent evidence,
80+
repeated work, requested-only folds, no-ops, and missing continuation. Denominators
81+
remain four required facts per observation; the failed recovery task is retained.
82+
83+
At complete `runUntilDone` boundaries the fixture reads and validates the small
84+
`turns.jsonl` directly, without an in-flight `store.load()` or recovery read. A
85+
qualifying fold requires changed persisted SHA-256 bytes, fewer persisted turns,
86+
an additional production compacted-context marker, a new summarizer invocation,
87+
and primary continuation inference. Requests alone cannot qualify. This proves
88+
persisted replacement in a completed run, not crash atomicity or restart recovery.
89+
90+
The work counters derive from actual tool start/done events. Failed shell calls
91+
include the production `exit code <nonzero>\n` content prefix, even without
92+
`isError`. A regression executes `exit 7` twice through real tools and observes
93+
two failures and one repeated failed attempt. The three fixed
94+
phase-end reads are labelled verification by their frozen call IDs, not by a
95+
model-provided excuse. Other repeated reads/searches, repeated failed attempts,
96+
and duplicated edits are distinct metrics. No search or edit is prescribed here;
97+
zero repetition is not evidence of capable live problem-solving.
98+
99+
## Captured outcome
100+
101+
`results/baseline.json` retains one successful mechanics run, including all three
102+
observations, persisted hashes, phase latencies, and the failed recovery result.
103+
104+
- Mechanics task qualification: **1/1**; persisted folds **3/3**.
105+
- Persisted turn counts: **36 → 8**, **28 → 10**, **30 → 12**.
106+
- Continuation primary calls: **20, 32, 44**.
107+
- Required-fact recovery after each fold: **1/4**; full-recovery tasks **0/1**.
108+
- Only the initial constraint survives. The corrected decision, failed-command
109+
evidence, and decisive oversized-output fact are lost from the primary reply.
110+
- Three actual summarizer calls; six tool calls; three verification reads;
111+
zero observed repeated reads/searches, repeated failed attempts, or duplicated edits.
112+
- Captured phase latencies: approximately **757, 796, 801 ms**. They include the
113+
tool call, folding/persistence, continuation and reply, not compaction alone.
114+
- Positive fixture duration: approximately **12.12 seconds**, including setup and growth.
115+
116+
Primary/summarizer token totals, real cache reads/writes, monetary cost,
117+
compaction-only latency, and live completion quality are **unavailable**, not
118+
zero. Persisted hashes include runtime timestamps and legitimately vary between
119+
runs; the frozen source hashes identify the repeatable protocol.
120+
121+
The test characterizes the observed baseline loss; passing tests do not mean
122+
factual recovery passes. Replacement comparison must reuse these fixture bytes,
123+
trigger schedule, budgets, and exact-source grader. Keep this captured result
124+
unchanged and report improved recovery separately rather than weakening the
125+
grade or excluding the baseline failure.
126+
127+
## Remaining scope
128+
129+
Real TUI/exec host continuity, workflow/controller state, approvals, worker/task
130+
ownership, attachments, concurrent incoming messages, recovery, and finalization
131+
belong to Unit 6 and the Unit 8 cross-surface matrix. Archive exactness and
132+
security belong to Units 2–4. Live quality and spend-approved token/cache/cost
133+
comparison belong to Unit 8. These requirements moved; they were not removed.
134+
135+
## Verification
136+
137+
The focused command above passes (9 tests, 47 assertions with the evaluator
138+
regressions; the original captured run has 7 tests and 44 assertions). Results
139+
retain the original sample and record corrected-evaluator verification separately;
140+
fixture bytes, trigger schedule, and the observed 1/4 baseline recovery are unchanged.
141+
Required regression and repository gates:
142+
143+
```bash
144+
bun test ./src/agent/compaction.test.ts ./src/context-compactor.test.ts ./src/session/runtime-assembly.test.ts ./src/session/optimized-context-store.test.ts ./tests/unit/compactor-pairing.test.ts
145+
bun run typecheck
146+
bun run build
147+
bun run test
148+
bun run check
149+
```
150+
151+
No commit or release action is part of this fixture.

evals/compaction/fixtures.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import type { Evidence } from "./metrics.js";
2+
3+
export const BASELINE = {
4+
productRevision: "6ea596945657f3a0d3af5bfb0277b94af19e1589",
5+
packageVersion: "0.3.18",
6+
protocol: "primary-component-mechanics-v1",
7+
model: "claude-integration",
8+
provider: "anthropic",
9+
folds: 3,
10+
growthTurnsPerFold: 10,
11+
syntheticLowInput: 100,
12+
syntheticTriggerInput: 200000,
13+
outputTokens: 1,
14+
keepRecentTurns: 6,
15+
summaryMaxChars: 4000,
16+
wallTimeoutMs: 30000,
17+
} as const;
18+
19+
export const REQUIRED_EVIDENCE: readonly Evidence[] = [
20+
{ id: "constraint", source: "operator:initial", value: "no-schema-change" },
21+
{ id: "decision", source: "operator:correction", value: "west-not-east" },
22+
{ id: "failure", source: "command:diagnose", value: "unsupported-format-7" },
23+
{ id: "decisive", source: "file:diagnostic.log:middle", value: "route-cobalt" },
24+
];
25+
26+
export function evidenceText(facts: readonly Evidence[]): string {
27+
return facts.map((fact) => `[[evidence:${fact.id}|${fact.source}|${fact.value}]]`).join("\n");
28+
}
29+
30+
export const INITIAL =
31+
"Audit the deployment. Preserve this constraint: " + evidenceText(REQUIRED_EVIDENCE.slice(0, 1));
32+
export const CORRECTION =
33+
"Correction: target west instead of east. " + evidenceText(REQUIRED_EVIDENCE.slice(1, 2));
34+
export const FAILED_OUTPUT =
35+
"Diagnostic preamble.\n".repeat(250) + evidenceText(REQUIRED_EVIDENCE.slice(2, 3));
36+
export const OVERSIZED_OUTPUT =
37+
"Unrelated diagnostic row.\n".repeat(1500) +
38+
evidenceText(REQUIRED_EVIDENCE.slice(3)) +
39+
"\nUnrelated trailing row.".repeat(1500);

evals/compaction/metrics.test.ts

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import { describe, expect, test } from "bun:test";
2+
import { type } from "arktype";
3+
import {
4+
grade,
5+
Measurement,
6+
qualifyingFold,
7+
recoverEvidence,
8+
repeatedWork,
9+
type Fold,
10+
} from "./metrics.js";
11+
12+
const fact = { id: "region", source: "operator:correction", value: "west" };
13+
const folds: Fold[] = [10, 20, 30].map((call) => ({
14+
requestedAtCall: call,
15+
beforeHash: `before-${call}`,
16+
afterHash: `after-${call}`,
17+
beforeTurns: 20,
18+
afterTurns: 10,
19+
persisted: true,
20+
continuedAtCall: call + 1,
21+
}));
22+
const baseline = {
23+
expected: [fact],
24+
recovered: [fact],
25+
expectedArtifact: "west\n",
26+
artifact: "west\n",
27+
folds,
28+
trace: [],
29+
};
30+
31+
describe("compaction baseline grading", () => {
32+
test("requires evidence actually visible to the scripted responder", () => {
33+
expect(recoverEvidence("[[evidence:region|operator:correction|west]]")).toEqual([fact]);
34+
expect(recoverEvidence("The region was mentioned earlier.")).toEqual([]);
35+
expect(
36+
grade({ ...baseline, recovered: recoverEvidence("evidence removed") }).factualRecovery,
37+
).toBe(false);
38+
});
39+
40+
test("rejects wrong values, sources, and altered artifacts independently", () => {
41+
expect(grade(baseline).passed).toBe(true);
42+
expect(grade({ ...baseline, recovered: [{ ...fact, value: "east" }] }).passed).toBe(false);
43+
expect(grade({ ...baseline, recovered: [{ ...fact, source: "invented" }] }).passed).toBe(false);
44+
const altered = grade({ ...baseline, artifact: "east\n" });
45+
expect(altered.completion).toBe(false);
46+
expect(altered.factualRecovery).toBe(true);
47+
});
48+
49+
test("keeps failed fold denominators and rejects requests, no-ops, and missing continuation", () => {
50+
expect(grade({ ...baseline, folds: [] }).qualifying).toBe(false);
51+
for (const fold of folds) {
52+
expect(qualifyingFold({ ...fold, persisted: false })).toBe(false);
53+
expect(qualifyingFold({ ...fold, afterHash: fold.beforeHash })).toBe(false);
54+
expect(qualifyingFold({ ...fold, continuedAtCall: null })).toBe(false);
55+
expect(qualifyingFold({ ...fold, afterTurns: fold.beforeTurns })).toBe(false);
56+
}
57+
expect(grade({ ...baseline, recovered: [] }).requiredFacts).toBe(1);
58+
});
59+
60+
test("counts repeated work separately from legitimate scheduled verification", () => {
61+
const read = {
62+
name: "read_file",
63+
argumentsKey: "a",
64+
outcome: "success",
65+
purpose: "action",
66+
} as const;
67+
const search = { ...read, name: "grep" };
68+
const failure = { ...read, name: "run_shell", outcome: "failure" } as const;
69+
const edit = { ...read, name: "edit_file" };
70+
const trace = [
71+
read,
72+
read,
73+
search,
74+
search,
75+
failure,
76+
failure,
77+
edit,
78+
edit,
79+
{ ...read, purpose: "verification" } as const,
80+
];
81+
expect(repeatedWork(trace)).toEqual({
82+
repeatedReads: 1,
83+
repeatedSearches: 1,
84+
repeatedFailedAttempts: 1,
85+
duplicatedEdits: 1,
86+
verificationCalls: 1,
87+
});
88+
expect(grade({ ...baseline, trace }).passed).toBe(false);
89+
});
90+
91+
test("missing usage is unavailable, not zero or an unlabelled estimate", () => {
92+
expect(Measurement({ status: "unavailable", reason: "offline" }) instanceof type.errors).toBe(
93+
false,
94+
);
95+
expect(
96+
Measurement({ status: "reported", value: -1, unit: "tokens" }) instanceof type.errors,
97+
).toBe(true);
98+
expect(Measurement({ value: 0, unit: "tokens" }) instanceof type.errors).toBe(true);
99+
expect(
100+
Measurement({ status: "synthetic", value: 200000, unit: "trigger tokens" }) instanceof
101+
type.errors,
102+
).toBe(false);
103+
});
104+
});

0 commit comments

Comments
 (0)