mirror of
https://github.com/AgentSeal/codeburn.git
synced 2026-08-02 12:55:04 +00:00
Redesign the three token-waste detectors as pure @codeburn/core detectors that
consume an ObservationEnvelope of fingerprinted resource refs (never raw paths):
- Envelope schema 0.2.0: CallObservation gains optional resourceReads /
resourceEdits (ResourceRef = {resourceId 16-hex, resourceClass}). claude/codex
toObservations fingerprint toolSequence file paths into these; raw paths never
cross the boundary. Keep observation-0.1.0.json frozen; emit 0.2.0.
- Host privacy key (D1): sync keystore under the codeburn config dir, random
32-byte key generated on first use, stable across runs, never emitted.
- core/detectors: junk-reads, duplicate-reads, context-bloat — pure, zero
fs/env, each emitting Finding[] with confidence(basis), machine-readable
evidence, algorithmVersion.
- optimize.ts delegates the three to core, mapping Finding -> WasteFinding;
display strings, fix payloads and trend stay host-derived.
Numbers parity: all optimize tests pass unchanged; frozen-corpus optimize JSON
(3552 sessions) is byte-identical PRE vs POST.
100 lines
4.1 KiB
TypeScript
100 lines
4.1 KiB
TypeScript
import { readFileSync } from 'node:fs'
|
|
import { dirname, resolve } from 'node:path'
|
|
import { fileURLToPath } from 'node:url'
|
|
|
|
import Ajv from 'ajv'
|
|
import { describe, expect, it } from 'vitest'
|
|
|
|
import { Finding } from '../src/contracts.js'
|
|
import { ObservationEnvelope } from '../src/observations.js'
|
|
|
|
const here = dirname(fileURLToPath(import.meta.url))
|
|
const pkgRoot = resolve(here, '..')
|
|
|
|
function readJson(rel: string): unknown {
|
|
return JSON.parse(readFileSync(resolve(pkgRoot, rel), 'utf8'))
|
|
}
|
|
|
|
const goldenEnvelope = readJson('tests/fixtures/golden-envelope.json')
|
|
const goldenFinding = readJson('tests/fixtures/golden-finding.json')
|
|
const observationSchema = readJson('schemas/observation-0.2.0.json') as object
|
|
const findingSchema = readJson('schemas/finding-0.1.0.json') as object
|
|
|
|
// strict:false so unknown string formats (date-time) are ignored rather than
|
|
// erroring — we validate structure/shape, not RFC date grammar (zod already
|
|
// enforces the timestamp format on the runtime side).
|
|
const ajv = new Ajv({ strict: false, allErrors: true })
|
|
// date-time is a semantic annotation here; zod enforces the timestamp format at
|
|
// runtime, so we register it as always-valid to keep ajv from warning.
|
|
ajv.addFormat('date-time', true)
|
|
const validateEnvelope = ajv.compile(observationSchema)
|
|
const validateFinding = ajv.compile(findingSchema)
|
|
|
|
/**
|
|
* THREE-WAY AGREEMENT.
|
|
*
|
|
* For each golden fixture we assert all three views of the contract accept it:
|
|
* 1. TypeScript types — this file imports the inferred types and compiles.
|
|
* 2. zod validator — the runtime source-of-truth.
|
|
* 3. JSON Schema — the checked-in artifact, validated with ajv.
|
|
*
|
|
* We use ajv (a real JSON Schema validator, already in the tree) rather than a
|
|
* structural comparison because only real validation proves the emitted schema
|
|
* actually ACCEPTS conforming data — a structural diff would only prove the two
|
|
* shapes look alike, not that the schema is usable by an external consumer.
|
|
*/
|
|
describe('three-way agreement: golden envelope', () => {
|
|
it('zod accepts it', () => {
|
|
const parsed = ObservationEnvelope.safeParse(goldenEnvelope)
|
|
expect(parsed.success, JSON.stringify((parsed as { error?: unknown }).error)).toBe(true)
|
|
})
|
|
|
|
it('JSON Schema (ajv) accepts it', () => {
|
|
const ok = validateEnvelope(goldenEnvelope)
|
|
expect(ok, JSON.stringify(validateEnvelope.errors)).toBe(true)
|
|
})
|
|
})
|
|
|
|
describe('three-way agreement: golden finding', () => {
|
|
it('zod accepts it', () => {
|
|
const parsed = Finding.safeParse(goldenFinding)
|
|
expect(parsed.success, JSON.stringify((parsed as { error?: unknown }).error)).toBe(true)
|
|
})
|
|
|
|
it('JSON Schema (ajv) accepts it', () => {
|
|
const ok = validateFinding(goldenFinding)
|
|
expect(ok, JSON.stringify(validateFinding.errors)).toBe(true)
|
|
})
|
|
})
|
|
|
|
describe('strictness / structural minimization', () => {
|
|
it('zod rejects an unknown top-level field', () => {
|
|
expect(ObservationEnvelope.safeParse({ ...(goldenEnvelope as object), title: 'x' }).success).toBe(false)
|
|
})
|
|
|
|
it('JSON Schema also rejects an unknown top-level field (additionalProperties:false)', () => {
|
|
expect(validateEnvelope({ ...(goldenEnvelope as object), title: 'x' })).toBe(false)
|
|
})
|
|
|
|
it('rejects a call whose measuredCostUSD is set without a measured costBasis', () => {
|
|
const env = structuredClone(goldenEnvelope) as {
|
|
sessions: { calls: Array<Record<string, unknown>> }[]
|
|
}
|
|
env.sessions[0].calls[1].measuredCostUSD = 0.99 // this call is 'estimated'
|
|
expect(ObservationEnvelope.safeParse(env).success).toBe(false)
|
|
})
|
|
|
|
it('rejects a toolNames entry that carries arguments', () => {
|
|
const env = structuredClone(goldenEnvelope) as {
|
|
sessions: { calls: Array<Record<string, unknown>> }[]
|
|
}
|
|
env.sessions[0].calls[0].toolNames = ['Bash(rm -rf /)']
|
|
expect(ObservationEnvelope.safeParse(env).success).toBe(false)
|
|
})
|
|
|
|
it('rejects a non-fingerprint sessionRef', () => {
|
|
const env = structuredClone(goldenEnvelope) as { sessions: Array<Record<string, unknown>> }
|
|
env.sessions[0].sessionRef = 'raw-session-id-not-a-hash'
|
|
expect(ObservationEnvelope.safeParse(env).success).toBe(false)
|
|
})
|
|
})
|