fix: pre-dispatch rejection of unloggable args, mutation-proof event copies, proto-safe bindings (Codex round 1)
Three findings from the PR-4 convergence round: (A) A root-undefined binding argument passed normalization untouched, so the sub-call DISPATCHED and only then failed the tool/code-dispatch append (Session.append rejects undefined event data) — a sub-call executed with no log record, violating the nothing-executes-unlogged contract. And the tool received the SAME object later handed to the append, so a tool mutating its args desynced the logged record from what was dispatched (or re-poisoned the append). jsonNormalizeArgs now rejects undefined up front with a model-correctable message and returns TWO independent parses of the canonical JSON text: the tool gets one, the event logs the sibling — identical by construction, mutation-proof. (B) The bridge built its bindings record with plain-object assignment, so a registered tool named __proto__ hit the prototype setter and silently vanished (the runtime host resolves binding names as own properties). The record is now null-prototype with defineProperty, mirroring the worker-side namespace build. (B) The header-pin sanity assertions ran only inside NON-pinning scenarios, so a class consisting solely of its pinning scenario (the two Code Mode classes) would accept a re-recorded pin carrying several headers or a header-delta. A fixtures meta-test now asserts every pinning fixture directly.
This commit is contained in:
@@ -408,6 +408,20 @@ describe('snapshot fixtures', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('every pinning fixture carries exactly one request/header and no deltas', async () => {
|
||||
// The live uniformity guard runs only in NON-pinning scenarios, so a
|
||||
// class made of just its pinning scenario (the Code Mode classes) would
|
||||
// otherwise accept a re-recorded pin with several headers or a mid-run
|
||||
// header-delta — shapes the pin design cannot represent. Assert the
|
||||
// committed pins directly.
|
||||
for (const scenario of pinningByClass.values()) {
|
||||
const fixture = await readFile(join(SNAPSHOTS_DIR, scenario.name, 'session.jsonl'), 'utf8')
|
||||
const headers = normalizedHeaders(fixture, fixtureContext(fixture))
|
||||
expect(headers.length, `${scenario.name}: a pinning fixture must carry exactly one request/header`).toBe(1)
|
||||
expect(headerDeltaCount(fixture), `${scenario.name}: a pinning fixture must carry no request/header-delta`).toBe(0)
|
||||
}
|
||||
})
|
||||
|
||||
it('committed fixtures carry request-header content ONLY in the pinning scenario', async () => {
|
||||
// The whole point of the pin: a system-prompt or tool-schema change must
|
||||
// churn exactly one committed line. A non-pinning fixture that carries the
|
||||
|
||||
Reference in New Issue
Block a user