Self-contained scenario documents (v1): parse, validate, verify, execute exampleBot:greetbot - #1
Merged
Conversation
…ocuments (v1) Implements https://chatwright.dev/formats/scenario-document/v1 in runtime-ts, ported from runtime-go's scenario/ package (chatwright.dev/runtime v0.4.0): parsing, structural validation, secret/credential-URL/executable-string rejection, independent journal verification (byte-identical detail strings), and a narrow Build path that executes the exampleBot:greetbot cross-runtime conformance fixture end to end. Behavioural parity with runtime-go: same accept/reject outcomes, same rule codes and JSON pointers, same verify-detail prefix/join. Bot-transport support is the declared mirror image (runtime-ts refuses "http" by name, accepts "iframe"; runtime-go is the reverse) per docs/runtime-parity.md. Cassette playback has no engine in either language yet on the TS side, so parity for the shared fixture rests on the resolved run description and verdict, not a shared cassette file — the greetbot exampleBot's cassette provider is satisfied by substituting the repository's own deterministic GreetbotProvider policy, recorded as an override, never silently presented as "the cassette ran". failurePolicy/ceiling validate identically but are not enforced at execution time (a pre-existing runtime-ts run-package gap). Every gap is named in docs/runtime-parity.md's new "Part 3" section. 52 new tests: 36 parse/validate parity cases ported from validate_test.go + parse_security_test.go, 6 byte-parity verify cases ported from verify_test.go, and 10 conformance cases against a byte-identical copy of runtime-go's own testdata/greetbot-language-onboarding.json fixture. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SkkrXdtf8mU2GRo2hHsHT1 Signed-off-by: Alexander Trakhimenok <alex@trakhimenok.com>
There was a problem hiding this comment.
Pull request overview
This PR adds first-class support in runtime-ts for the self-contained scenario-document/v1 format: parsing + security scans, structural validation, independent journal verification, and a narrowly-scoped Build path that can execute the shipped exampleBot:greetbot conformance fixture.
Changes:
- Introduces
scenario-document/v1types plus parse/shape/secret scans and structural validation that emit machine-readable issues. - Adds byte-parity independent journal verification (
verify) with conformance tests against the shared greetbot fixture. - Implements a constrained Build path that executes documents only when
bot.exampleBot === "greetbot", recording provider overrides when substituting for cassette replay.
Reviewed changes
Copilot reviewed 20 out of 20 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/testkit/greetbot-bot.ts | Updates documentation to reflect greetbot’s role as the shipped exampleBot:greetbot transport. |
| src/scenario/testdata/greetbot-language-onboarding.json | Adds the shared, portable scenario-document greetbot fixture. |
| src/scenario/issues.ts | Adds Issue/Report types and rejection error rendering for parser/validator findings. |
| src/scenario/index.ts | Re-exports new scenario-document modules from the scenario package surface. |
| src/scenario/greetbot.ts | Aligns greetbot success-criteria prose with the portable document fixture. |
| src/scenario/examplebots.ts | Adds an exampleBot registry and factory for greetbot. |
| src/scenario/document.ts | Defines the scenario-document/v1 TypeScript shape and vocabulary types. |
| src/scenario/document-verify.ts | Implements compiled verify evaluation with ordered expectations and pinned formatting. |
| src/scenario/document-verify.test.ts | Adds byte-parity tests for verify evaluation and pinned string constants. |
| src/scenario/document-validate.ts | Adds structural validation rules, including runtime-ts’s transport support divergence. |
| src/scenario/document-shape.ts | Adds forbidden-member scan and unknown-member rejection via a shape-aware walk. |
| src/scenario/document-secrets.ts | Adds secret shape checking, secretRef cross-checks, and credential-bearing URL rejection. |
| src/scenario/document-parse.ts | Implements the pure, in-memory parse pipeline (scans → decode → validate). |
| src/scenario/document-parse.test.ts | Adds parity/security/AC coverage for parsing and validation behaviour. |
| src/scenario/document-json.ts | Adds shared JSON-walk helpers (sorted keys, JSON pointer escaping, object guard). |
| src/scenario/document-fidelity.ts | Adds fidelity vocabulary validation and deterministic resolution heuristics. |
| src/scenario/document-conformance.test.ts | Adds end-to-end conformance: parse → build → execute → verify for the greetbot fixture. |
| src/scenario/document-build.ts | Builds an executable Run for the greetbot exampleBot and records provider overrides. |
| README.md | Updates feature matrix to reflect partial support for portable scenario documents. |
| docs/runtime-parity.md | Documents scenario-document/v1 support, intentional deviations, and declared parity gaps. |
Comments suppressed due to low confidence (1)
src/scenario/issues.ts:91
- This doc comment says ScenarioRejectionError.message never includes any document-supplied value, but it concatenates Issue.message (which can include document values like an unknown member name or unsupported capability). Adjust the comment to match the actual guarantee (e.g. never echoing secret/credential values).
* The error thrown (returned as `RejectionError` in Go) for a {@link Report}
* with at least one `SeverityError` issue. `message` renders every
* error-severity issue as `"<pointer>: <code>: <message>"`, one per line —
* never the report's warnings, and never any document-supplied value.
*/
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+4
to
+8
| * JSON pointer and the rule id and never echoes the offending value (the | ||
| * format README's own rule); {@link Issue.message} is written by this | ||
| * package's own code at each call site, never built by interpolating a | ||
| * document-supplied value, so that requirement holds by construction rather | ||
| * than by scrubbing after the fact. |
Comment on lines
+304
to
+306
| const inputsRaw = v["inputs"]; | ||
| return { name: reqStr(v["name"], fail), inputs: isJsonObject(inputsRaw) ? (inputsRaw as Record<string, unknown>) : undefined }; | ||
| } |
Comment on lines
+168
to
+170
| if (c.field === "edited") { | ||
| result = entry.version > 0 === c.bool; | ||
| } else { |
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.
Summary
Implements
https://chatwright.dev/formats/scenario-document/v1inruntime-ts, ported behaviourally fromruntime-go'sscenario/package (chatwright.dev/runtimev0.4.0):document-parse.ts,document-shape.ts,document-secrets.ts) — pure, in-memory, no I/O. Rejects inline secrets, credential-bearing URLs, executable-string members, unknown members, unsupported format/schema/capabilities. Never echoes an offending value; every rejection names a JSON pointer + rule id.document-validate.ts,document-fidelity.ts) — structural rules (budgets, chat/actor references, provider shape, fidelity/environment/data-sensitivity vocabulary, regex subset). One deliberate, declared divergence: bot-transport support is the exact mirror image of Go's (runtime-tsrefuses"http"by name, accepts"iframe";runtime-gois the reverse) — both satisfyunsupported-transport-is-refused-by-name.document-verify.ts) — the independent journal re-check, ported byte-for-byte:UNMET_PREFIXand the"; "join are pinned constants, tested for literal string equality against the Go source.document-build.ts,examplebots.ts) — maps a validated document onto an executableRun, narrowly scoped tobot.exampleBot === "greetbot"(the one transport-neutral cross-runtime fixture). Everything else is refused by name, not approximated.Cassette decision
runtime-tshas no cassette engine (Go's key issha256(providerConfig + "\x00" + json.Marshal(prompt))— Go struct field order, not reproducible without inventing a canonical-JSON contract). Chose model-free parity: the shared greetbot document'scassette+replayprovider is satisfied by substituting the repository's own deterministicGreetbotProviderpolicy, recorded inBuiltScenario.providerOverrides— never silently presented as "the cassette ran". Every other provider kind is refused by name.Gaps (declared, not papered over)
Recorded in
docs/runtime-parity.md's new "Part 3" section:DisallowUnknownFieldsequivalent) — reports every violation vs Go's first-only; same accept/reject outcome.url-addressed bots (http/iframe) validate but Build refuses them by name — not wired in this task.failurePolicy/ceilingvalidate identically but are not enforced at execution time — pre-existingruntime-tsrunpackage gap (predates this PR), soceiling-trip-attributes-run-and-partis unmet in TS today.platformIdentity.firstNamedoesn't carrybot.nameinruntime-ts(fixedTelegramCodecidentity) — pre-existingSessionproperty, not scenario-document-specific.modelproviders not wired (secret resolution viaprocess.envwould only suit a Node host;SecretResolver/EnvOnlySecretResolverare exported ready for a follow-up).Test plan
npm run typecheck— clean (see CI)npm test— 187/187 passing (52 new: 36 parse/validate parity cases ported fromvalidate_test.go/parse_security_test.go, 6 byte-parity verify cases ported fromverify_test.go, 10 conformance cases against a byte-identical copy ofruntime-go's owntestdata/greetbot-language-onboarding.json)runtime-go/scenario/testdata/greetbot-language-onboarding.jsonat copy timescripts/vendor-runtime.mjs) — picking this up needs a separate SHA bump in Studio, not done here.🤖 Generated with Claude Code
https://claude.ai/code/session_01SkkrXdtf8mU2GRo2hHsHT1