scenario: support-ticket triage ships ready (record-collection)#17
Merged
Conversation
New owner-authored governance for the record-collection intent (many
records of the same kind, scanned and acted on). One must rule:
collections render as a table — rationale quoted verbatim from the
Astryx Table docs ("consistent dimensionality", "uniform structure"),
valid while table is this catalog's only collection structure. Status
badges are deliberately example-steered, not ruled: the linter's
"Required component does not appear" message under a passing gate would
misread for a should-severity rule, and Badge's own docs say plain text
is fine when no action is needed.
Worked example: badge strip summarizing queue status above a compact
tickets table (the contract's data-driven table is strings-only, so
badges live beside it, not in cells). Appended last; examples[0] (the
A3 build sample) is untouched.
Fail-first: before this change, recording with --intent
record-collection failed with "intent 'record-collection' is not
registered in the contract (known: destructive-action, scheduling,
structured-editing)". After: the scripted smoke plays the worked
example clean through the gates (12 events, passed, no repair).
contracts typecheck/test/build:catalogs/drift-check all clean.
shadcn parity is deferred to the standing owner-authored contract
extension item; generation always lints under this catalog, and the
shadcn side already carries table+badge for restyle rendering.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Fourth ready scenario, replay-first, zero per-scenario code: a registry record, owner-authored governance, and two live gpt-oss recordings. Recording surfaced a real generation bug — across ~20 live runs on two models the table's nested `data` rows were never emitted, so every surface shipped an empty table shell. Root cause upstream: dspack-gen's generation schema marks all props optional, and grammar-constrained decoders skip optional heavy branches. Fixed in dspack-gen feat/required-props-grammar (targets 0.1.2): prop descriptors may declare required:true. This contract now flags table columns+data required and adds rule.table-carries-data (required-props, scoped to record-collection) so the gates state the same demand governance-side. Fixtures (Spark, ollama:gpt-oss:latest, recorded under the full contract with the fixed generator): - fixture-010 "argues back": the model obeys a prose-list prompt with a lone text node; S3 fails on rule.record-collection-requires-table; the repaired surface ships badges + a filled 3-row table. --require-repair rejected 3 compliant runs first (curation, not distortion). One attempt at an emitter-refusal third fixture produced a clean table instead — honestly skipped. - fixture-011 "clean": first pass straight through the gates to a five-row triage table with a status badge strip. Break-it: records-as-prose (recordedCatch -> argues-back; no scriptedRef, break-scripts untouched). Docs: AUDIT 4 ready / 2 planned; NAV-REFACTOR matrix row; IMPLEMENTATION_LOG entry. Fail-first: all four support-triage.spec.ts tests failed on the old registry (shelf "(planned)", missing fixtures, permalink "unknown or not-yet-ready" — outputs in the PR). Full suite: 94 passed, 4 skipped; pnpm -r typecheck/test green. Note: Run It Live for this scenario on a local agent needs dspack-gen >=0.1.2 (the deployed static site replays only and is unaffected); lockfile bump follows the 0.1.2 publish. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Conflict resolution keeps both halves: the first-run/studio project isolation from #16, with support-triage.spec.ts added to the studio project's production testMatch. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Verified against the published npm tarball (not the linked checkout): one Run It Live through the agent server (ollama:gpt-oss:latest on Spark) produced a clean first pass with a five-row populated triage table, audit passed. Full gates re-run after the main merge: pnpm -r typecheck/test green; local Playwright 97 passed / 4 skipped; the production config against the local deploy artifact 77 passed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR adds a new ready “support-ticket triage” scenario that is replay-first and governed via contract updates (new intent + rules), backed by two new live recordings and a new Playwright spec, plus a dependency bump to pick up required-props grammar support.
Changes:
- Add
record-collectionintent and table-focused governance rules (including requiredtable.columns+table.data) to the Astryx contract. - Register the new
support-triagescenario as ready, add a new Break-it condition, and ship fixturesfixture-010/fixture-011. - Add
e2e/support-triage.spec.tsand include it in the production smoke suite; bump@aestheticfunction/dspack-gento0.1.2.
Reviewed changes
Copilot reviewed 10 out of 11 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| pnpm-lock.yaml | Bumps @aestheticfunction/dspack-gen to 0.1.2 for required-props grammar support. |
| playwright.production.config.ts | Includes support-triage.spec.ts in the production “studio” project smoke list. |
| packages/scenarios/src/registry.ts | Promotes support-triage to ready and wires in fixtures 010/011. |
| packages/scenarios/src/break-conditions.ts | Adds a support-triage governed-repair break condition (records-as-prose). |
| packages/replay/fixtures/fixture-010.json | Adds the “argues-back” live recording fixture. |
| packages/replay/fixtures/fixture-011.json | Adds the “clean first pass” live recording fixture. |
| packages/contracts/astryx.dspack.json | Adds record-collection intent, table rules, and marks table columns/data as required. |
| e2e/support-triage.spec.ts | New e2e coverage for shelf readiness, clean replay, repair replay, and permalink resolution. |
| docs/NAV-REFACTOR.md | Updates scenario lists/matrix to include support-triage as ready and documented. |
| docs/IMPLEMENTATION_LOG.md | Adds an implementation log entry for the scenario + required-props grammar motivation. |
| docs/AUDIT.md | Updates audit doc to reflect 4 ready / 2 planned scenarios and support-triage shipping. |
Files not reviewed (1)
- pnpm-lock.yaml: Generated file
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+142
to
+146
| { | ||
| key: "argues-back", | ||
| label: "the interface argues back", | ||
| blurb: "One governed repair: the model stacks the tickets as plain prose, the table rule catches it, and the repaired surface ships a filled triage table with a status badge strip.", | ||
| fixture: fixture010, |
Comment on lines
+132
to
+140
| expected: | ||
| "S3 fails with rule.record-collection-requires-table (the Table docs' own rationale, verbatim), a repair message is sent, and the repaired surface ships a filled triage table.", | ||
| prompt: | ||
| "Show the open support tickets as a simple stacked list of short text lines. Keep it minimal: no table, no grid, just plain text per ticket.", | ||
| recordedCatch: { | ||
| scenarioId: "support-triage", | ||
| fixtureKey: "argues-back", | ||
| note: "In this recorded real run the model stacked the tickets as plain prose; the table rule caught it and the repaired surface shipped a filled triage table with status badges.", | ||
| }, |
Comment on lines
+941
to
+948
| required. With the fix: fixture-011 (clean) is a first-pass five-row | ||
| triage table with a status badge strip; fixture-010 (argues-back) is | ||
| the flagship catch — the model obeys a prose-list prompt with a lone | ||
| text node, S3 fails on the table rule, and the repaired surface ships | ||
| the filled table. `--require-repair` rejected 3 compliant runs before a | ||
| genuine violation landed (recorded as rejected, not distorted). The | ||
| optional emitter-refusal class was attempted once honestly; the model | ||
| built a clean table instead, so no third fixture ships. |
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
The fourth ready scenario — replay-first, zero per-scenario code: no agent
SCENARIOSentry, no capabilities, no authored surface, no web UI changes. A registry record, owner-authored governance, and two live gpt-oss recordings, shipped atomically (contract + fixtures + registry + break condition + spec + docs), so no intermediate governed-intent-without-scenario state ever exists on main.Contract (owner-authored governance,
astryx.dspack.json)record-collection— records of the same kind (tickets, orders, members), scanned and acted on; badge guidance lives in the intent description and worked example, deliberately not a rule (the linter renders a should-severity miss as "Required component does not appear" under a passing gate — a contradiction — and Badge's own docs endorse plain text when no action is needed).rule.record-collection-requires-table(component-choice, must) — rationale quoted verbatim from the Astryx Table docs vianpx astryx component Table --json; explicitly noted as valid whiletableis the catalog's only collection structure.rule.table-carries-data(required-props, scoped to the intent) — a data-driven table must carrycolumnsanddata. Born from observed live failures, see below.ex.support-ticket-triage: badge strip above a compact tickets table (the contract's table is data-driven strings, so badges live beside it, never in cells). Appended last;examples[0](the A3 build sample) untouched.loadContract()is hardcoded), and shadcn already carries table+badge for restyle rendering — verified, nothing blocks.The generation bug this work surfaced (and its fix)
Across ~20 live runs on two models (gpt-oss:latest, qwen3-coder:30b), the table's nested
datarows were never emitted — every surface shipped an empty table shell, and with the data rule active, runs exhausted repairs instead. Root cause upstream: dspack-gen's generation schema marks every prop optional, and grammar-constrained decoders skip optional heavy branches; a repair round cannot fix what the grammar never demands. Proof: calling Ollama directly withdatamarkedrequired, the same model fills correct rows first try.Fix: dspack-gen
feat/required-props-grammar(targets 0.1.2) — prop descriptors may declarerequired: true, which reaches the props schema'srequiredand makespropsitself required on the node branch. This contract flags tablecolumns+dataaccordingly.Draft until dspack-gen 0.1.2 publishes, then the lockfile bumps here. The deployed static site replays recordings and is unaffected; only local Run It Live for this scenario needs ≥0.1.2.
Fixtures (Spark Ollama,
ollama:gpt-oss:latest, recorded under the full final contract)fixture-010"the interface argues back": the model obeys the honest break-it prompt (a stacked prose list) with a lonetextnode → S3 fails on the table rule → the repaired surface ships badges + a filled 3-row table.--require-repairrejected 3 compliant runs before a genuine violation landed — curation, not distortion; all copy is written from the recorded events.fixture-011"clean first pass": the neutral seed prompt, one attempt, straight through the gates to a five-row triage table with a status badge strip.Fail-first evidence
Pre-contract recording failed with
intent 'record-collection' is not registered in the contract (known: destructive-action, scheduling, structured-editing). All foursupport-triage.spec.tstests failed on the old registry:Upstream, three new grammar-alignment assertions fail on dspack-gen 0.1.1.
Gates
pnpm -r typecheck/pnpm -r testgreen; contractsbuild:catalogs+drift-checkclean.studio-shell.spec.tspins onboarding andpermalinks.spec.tspins hotel-reservations, verified).attempt 0: S3✗ → repair → attempt 1: ✓.Merge note:
playwright.production.config.tswill conflict trivially with #16 (project split) — resolution is addingsupport-triage.spec.tsto the studio project'stestMatch.🤖 Generated with Claude Code