fix: validate stored records at the public boundary (v0.1.3) - #8
Merged
Merged
Conversation
…l library workflow
The package root exported isRecord as a DecisionRecord type guard, but it only
detected record-shaped envelopes: isRecord({ response: null }) returned true,
so a consumer could treat a malformed envelope as a validated record. Split the
two jobs. isRecord is now a plain detector; readRecord is the boundary that
returns a checked DecisionRecord or throws. gate, replay, and extractResponse
route through it; behavior on malformed input is unchanged (the same errors,
from the same validator), only the advertised contract is honest. Tests pin
both: detection without a validity claim, and validation through readRecord.
Add examples/library/record-and-gate.mts, the complete recorded workflow with
public imports only: one shared SDK client, SIGINT/SIGTERM abort, elapsed-time
measurement, client errors that never reach policy, a record that stores hashes
of state and questions plus model identity and latency (never the supplied
text), then offline policy over readRecord + evaluatePolicy. Verified against
the stub: says_nothing at 0.6, deny exit 3, record carries stub:jev-latest and
no claim text, replay re-emits it. README library section rebuilt around it;
examples README documents how to run it.
Contributor
Author
|
@astra please review this PR (slow role). Focus areas:
Blocking: any path where malformed or ambiguous input can still reach |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
isRecordwas exported as aDecisionRecordtype guard but only detected record-shaped envelopes:isRecord({ response: null })returnedtrue, so a library consumer could treat a malformed envelope as a validated record. Split detection from validation:isRecord(input): boolean— envelope detector only.readRecord(input): DecisionRecord— new validated boundary; returns a checked record or throws.gate,replay,extractResponseroute throughreadRecord. Behavior on malformed input is unchanged (same validator, same errors); only the advertised contract is honest.Plus
examples/library/record-and-gate.mts: the complete recorded workflow with public imports only — one shared SDK client, SIGINT/SIGTERM abort, elapsed-time measurement, client errors that never reach policy, a record storing hashes of state/questions plus model identity and latency (never the supplied text), then offline policy overreadRecord+evaluatePolicy. Verified against the stub:says_nothingat 0.6 → deny exit 3, record carriesstub:jev-latest, no claim text, replay re-emits it. README library section rebuilt around it.Verification
tsc --noEmitclean.readRecord, plus the existing malformed-envelope and bare-response coverage.npm pack --dry-run: 93 files / 60.4 kB, example included viaexamples/.Version bumped to 0.1.3. After merge: tag + release → publish workflow stages it → maintainer approves with 2FA.