@bjoernrochel/sevro 0.1.0-rc.7 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (230) hide show
  1. package/CONTRIBUTING.md +22 -6
  2. package/README.md +5 -1
  3. package/docs/README.md +19 -15
  4. package/docs/architecture.md +138 -17
  5. package/docs/assets/README.md +5 -4
  6. package/docs/builtin-graders-v1.md +17 -3
  7. package/docs/codex-host.md +155 -49
  8. package/docs/contracts.md +2 -1
  9. package/docs/creating-cases.md +9 -2
  10. package/docs/development-cli.md +187 -19
  11. package/docs/documentation-quality.md +12 -2
  12. package/docs/evaluation-workflow.md +110 -0
  13. package/docs/extension-protocol-v1.md +26 -3
  14. package/docs/getting-started.md +23 -2
  15. package/docs/guide-evaluation.md +33 -1
  16. package/docs/identity-v1.md +25 -2
  17. package/docs/installing.md +11 -11
  18. package/docs/native-controls.md +76 -5
  19. package/docs/native-hosts.md +196 -7
  20. package/docs/reading-results.md +102 -0
  21. package/docs/recipes.md +489 -0
  22. package/docs/releases.md +31 -12
  23. package/docs/report-v1.md +29 -1
  24. package/docs/results-v1.md +141 -4
  25. package/docs/run-settings-v1.md +147 -0
  26. package/docs/runtime-v1.md +307 -18
  27. package/docs/troubleshooting.md +12 -12
  28. package/docs/typescript-quality.md +89 -26
  29. package/examples/recipes/candidate.ts +26 -0
  30. package/examples/recipes/cases/coding-change.json +21 -0
  31. package/examples/recipes/cases/host-tools.json +21 -0
  32. package/examples/recipes/cases/mise.json +21 -0
  33. package/examples/recipes/cases/node.json +21 -0
  34. package/examples/recipes/cases/python-uv.json +21 -0
  35. package/examples/recipes/cases/semantic.json +17 -0
  36. package/examples/recipes/coding-change/check.ts +23 -0
  37. package/examples/recipes/extension-artifacts.ts +65 -0
  38. package/examples/recipes/extension-methods.ts +174 -0
  39. package/examples/recipes/extension.ts +37 -0
  40. package/examples/recipes/repository-skill/.agents/skills/startup-guide/SKILL.md +7 -0
  41. package/examples/recipes/repository-skill/.claude/skills/startup-guide/SKILL.md +7 -0
  42. package/examples/recipes/repository-skill/README.md +3 -0
  43. package/examples/recipes/run-inputs.ts +188 -0
  44. package/examples/recipes/run-options.ts +131 -0
  45. package/examples/recipes/run.ts +63 -0
  46. package/examples/recipes/runtime/empty.json +1 -0
  47. package/examples/recipes/runtime/goals.json +1 -0
  48. package/examples/recipes/runtime/mise.json +6 -0
  49. package/examples/recipes/runtime/path.json +1 -0
  50. package/examples/recipes/runtime/plugins.json +4 -0
  51. package/examples/recipes/runtime/python-uv.json +10 -0
  52. package/examples/recipes/semantic-judge.ts +29 -0
  53. package/examples/templates/coding/adapter.ts +17 -0
  54. package/examples/templates/coding/case.json +15 -0
  55. package/examples/templates/repository-skill/.agents/skills/startup-guide/SKILL.md +6 -0
  56. package/examples/templates/repository-skill/extension-data.ts +64 -0
  57. package/examples/templates/repository-skill/extension.ts +31 -0
  58. package/examples/templates/response/adapter.ts +12 -0
  59. package/examples/templates/response/case.json +13 -0
  60. package/package.json +11 -5
  61. package/schemas/report-v1.schema.json +9 -0
  62. package/schemas/run-evidence-v1.schema.json +2 -1
  63. package/schemas/runtime-v1.schema.json +63 -7
  64. package/src/advisory.ts +2 -2
  65. package/src/candidate-artifacts.ts +206 -0
  66. package/src/candidate-codex-state.ts +185 -0
  67. package/src/case-validation.ts +73 -50
  68. package/src/cli/cancellation.ts +20 -0
  69. package/src/cli/command.ts +87 -0
  70. package/src/cli/completion.ts +15 -0
  71. package/src/cli/demo.ts +60 -0
  72. package/src/cli/evaluation.ts +55 -0
  73. package/src/cli/extension.ts +79 -0
  74. package/src/cli/help.ts +198 -0
  75. package/src/cli/hosts.ts +55 -0
  76. package/src/cli/init-settings.ts +30 -0
  77. package/src/cli/init.ts +111 -0
  78. package/src/{cli-invocation.ts → cli/invocation.ts} +214 -218
  79. package/src/cli/loading.ts +151 -0
  80. package/src/cli/preparation.ts +154 -0
  81. package/src/cli/presentation.ts +93 -0
  82. package/src/cli/progress.ts +6 -0
  83. package/src/cli/run-manifest.ts +128 -0
  84. package/src/cli/run-options.ts +403 -0
  85. package/src/cli/run-overrides.ts +69 -0
  86. package/src/cli/settings.ts +154 -0
  87. package/src/cli/summary.ts +43 -0
  88. package/src/cli.ts +13 -591
  89. package/src/contract-types.ts +94 -0
  90. package/src/engine.ts +13 -5
  91. package/src/evaluation/advisory.ts +150 -0
  92. package/src/{evaluation-check-plan.ts → evaluation/check-plan.ts} +103 -67
  93. package/src/evaluation/collection.ts +88 -0
  94. package/src/evaluation/context.ts +10 -0
  95. package/src/{evaluation-grading-host.ts → evaluation/grading-host.ts} +5 -5
  96. package/src/evaluation/grading.ts +235 -0
  97. package/src/{evaluation-invocation-plan.ts → evaluation/invocation-plan.ts} +147 -58
  98. package/src/{evaluation-plan.ts → evaluation/plan.ts} +208 -141
  99. package/src/evaluation/progress.ts +19 -0
  100. package/src/evaluation/resources.ts +241 -0
  101. package/src/evaluation/run.ts +206 -0
  102. package/src/{evaluation-semantic.ts → evaluation/semantic.ts} +133 -115
  103. package/src/evaluation/trial.ts +800 -0
  104. package/src/evaluation/types.ts +207 -0
  105. package/src/evidence/host-results.ts +85 -0
  106. package/src/evidence/retained-artifacts.ts +18 -0
  107. package/src/evidence/trial-writer.ts +203 -0
  108. package/src/evidence/usage.ts +17 -0
  109. package/src/extension-client.ts +34 -96
  110. package/src/extension-session.ts +24 -48
  111. package/src/{advisory-fixture.ts → fixtures/advisory.ts} +15 -76
  112. package/src/{fixture-bin.ts → fixtures/bin.ts} +1 -1
  113. package/src/{fixture-cleanup.ts → fixtures/cleanup.ts} +13 -7
  114. package/src/fixtures/create.ts +183 -0
  115. package/src/{generated-fixture.ts → fixtures/generated.ts} +10 -53
  116. package/src/{git-hooks.ts → fixtures/git-hooks.ts} +1 -1
  117. package/src/{preparation.ts → fixtures/preparation.ts} +6 -16
  118. package/src/{repository-fixture.ts → fixtures/repository.ts} +30 -64
  119. package/src/{fixture-setup.ts → fixtures/setup.ts} +33 -51
  120. package/src/{fixture-tools.ts → fixtures/tools.ts} +1 -1
  121. package/src/fixtures/workspace-state.ts +212 -0
  122. package/src/generated/cli-result.d.cts +2 -1
  123. package/src/generated/extension.d.cts +2 -1
  124. package/src/generated/report.cjs +1 -1
  125. package/src/generated/report.d.cts +2 -1
  126. package/src/generated/run-evidence.cjs +1 -1
  127. package/src/generated/run-evidence.d.cts +2 -1
  128. package/src/generated/runtime.cjs +1 -1
  129. package/src/generated/runtime.d.cts +2 -1
  130. package/src/git.ts +93 -0
  131. package/src/graders/git-head.ts +29 -84
  132. package/src/graders/shell.ts +57 -68
  133. package/src/host-diagnostic-redaction.ts +52 -0
  134. package/src/host-diagnostics.ts +331 -0
  135. package/src/hosts/claude-continuation.ts +57 -40
  136. package/src/hosts/claude-credential.ts +160 -63
  137. package/src/hosts/claude-events.ts +34 -18
  138. package/src/hosts/claude-keychain.ts +78 -21
  139. package/src/hosts/claude-login-recovery.ts +85 -0
  140. package/src/hosts/claude-login-refresh.ts +178 -0
  141. package/src/hosts/claude-login-store.ts +162 -0
  142. package/src/hosts/claude-login.ts +107 -0
  143. package/src/hosts/claude-native-goal.ts +15 -12
  144. package/src/hosts/claude-process.ts +10 -50
  145. package/src/hosts/claude-refresh-lock.ts +81 -0
  146. package/src/hosts/claude-repository-invocation.ts +9 -34
  147. package/src/hosts/claude-settings.ts +91 -14
  148. package/src/hosts/claude-skill-activations.ts +169 -0
  149. package/src/hosts/claude-state.ts +46 -16
  150. package/src/hosts/claude-tool-calls.ts +13 -10
  151. package/src/hosts/claude.ts +122 -73
  152. package/src/hosts/codex-app-server.ts +78 -52
  153. package/src/hosts/codex-configuration.ts +5 -10
  154. package/src/hosts/codex-exec-controls.ts +516 -0
  155. package/src/hosts/codex-marketplace.ts +12 -10
  156. package/src/hosts/codex-native-calls.ts +15 -122
  157. package/src/hosts/codex-process.ts +10 -54
  158. package/src/hosts/codex-profile.ts +50 -21
  159. package/src/hosts/codex-session-files.ts +1 -9
  160. package/src/hosts/codex-state.ts +141 -19
  161. package/src/hosts/codex.ts +221 -179
  162. package/src/hosts/isolation-common.ts +15 -11
  163. package/src/hosts/isolation-roots.ts +13 -20
  164. package/src/hosts/linux-sandbox.ts +27 -3
  165. package/src/hosts/mac-sandbox.ts +111 -18
  166. package/src/hosts/native-controls.ts +72 -22
  167. package/src/hosts/native-request.ts +55 -0
  168. package/src/hosts/process-lifecycle.ts +221 -45
  169. package/src/hosts/protected-worktrees.ts +1 -1
  170. package/src/hosts/skill-hook-catalog.ts +131 -0
  171. package/src/hosts/skill-hook-handler.ts +374 -0
  172. package/src/hosts/skill-hooks.ts +577 -0
  173. package/src/instrumentation.ts +7 -8
  174. package/src/native-transcript-state.ts +22 -3
  175. package/src/native-transcript-view.ts +52 -13
  176. package/src/native-transcripts.ts +34 -15
  177. package/src/paths.ts +22 -0
  178. package/src/process-lifecycle.ts +150 -0
  179. package/src/provenance.ts +27 -7
  180. package/src/prune.ts +300 -0
  181. package/src/report.ts +63 -36
  182. package/src/resolved-case.ts +1 -1
  183. package/src/results-lock.ts +42 -0
  184. package/src/results.ts +14 -18
  185. package/src/run-owner.ts +4 -4
  186. package/src/{runtime-config.ts → runtime/config.ts} +182 -44
  187. package/src/{runtime-discovery-apple.ts → runtime/discovery-apple.ts} +3 -3
  188. package/src/{runtime-discovery-providers.ts → runtime/discovery-providers.ts} +3 -9
  189. package/src/{runtime-discovery.ts → runtime/discovery.ts} +4 -4
  190. package/src/{runtime-hook-processes.ts → runtime/hook-processes.ts} +1 -1
  191. package/src/{runtime-hooks.ts → runtime/hooks.ts} +33 -22
  192. package/src/{runtime-paths.ts → runtime/paths.ts} +6 -18
  193. package/src/{runtime-query.ts → runtime/query.ts} +5 -5
  194. package/src/{runtime-seeds.ts → runtime/seeds.ts} +3 -3
  195. package/src/runtime/shared-cache.ts +130 -0
  196. package/src/{runtime-state.ts → runtime/state.ts} +28 -11
  197. package/src/schema-types.ts +73 -161
  198. package/src/shell.ts +4 -0
  199. package/src/transcript-retention.ts +146 -0
  200. package/src/value-guards.ts +22 -0
  201. package/docs/assets/sevro-logo.png +0 -0
  202. package/docs/assets/sevro-symbol.svg +0 -1
  203. package/docs/assets/sevro-wordmark.svg +0 -1
  204. package/docs/cache-ownership-validation.md +0 -49
  205. package/docs/claude-continuation-validation.md +0 -47
  206. package/docs/concurrency-validation.md +0 -86
  207. package/docs/configuration-root-validation.md +0 -70
  208. package/docs/engine-slice.md +0 -114
  209. package/docs/evidence/runtime-startup-30/README.md +0 -185
  210. package/docs/evidence/runtime-startup-30/after.json +0 -319
  211. package/docs/evidence/runtime-startup-30/baseline-timeout.jsonl +0 -2
  212. package/docs/evidence/runtime-startup-30/before.json +0 -403
  213. package/docs/evidence/runtime-startup-30/consumer-smoke.json +0 -63
  214. package/docs/evidence/runtime-startup-30/full-policy-diagnostic.jsonl +0 -3
  215. package/docs/evidence/runtime-startup-30/minimal-policy-diagnostic.jsonl +0 -3
  216. package/docs/evidence/runtime-startup-30/plugin-template-assessment.json +0 -20
  217. package/docs/evidence/runtime-startup-30/seedless-timeout.jsonl +0 -2
  218. package/docs/evidence.md +0 -17
  219. package/docs/fixture-cleanup-validation.md +0 -70
  220. package/docs/guide-verification.json +0 -239
  221. package/src/evaluation-context.ts +0 -257
  222. package/src/evaluation-fixture.ts +0 -313
  223. package/src/evaluation-run.ts +0 -198
  224. package/src/evaluation-trial.ts +0 -946
  225. package/src/evaluation-types.ts +0 -149
  226. package/src/hosts/claude-nested-skills.ts +0 -272
  227. package/src/hosts/codex-command-output.ts +0 -510
  228. package/src/hosts/codex-executor-command.ts +0 -291
  229. package/src/hosts/codex-skill-reads.ts +0 -609
  230. /package/src/{trial-scheduler.ts → evaluation/scheduler.ts} +0 -0
@@ -1,6 +1,7 @@
1
1
  # TypeScript quality
2
2
 
3
- `bun run check:typescript` is the local and CI acceptance command. It performs a
3
+ `bun run check:typescript` is the complete local acceptance command. CI runs the
4
+ same checks in parallel phases. The complete command performs a
4
5
  frozen dependency installation, checks the complete source inventory, verifies
5
6
  generated schemas, documentation contracts, formatting, type-aware lint and typing,
6
7
  checks the Codex guide evaluation in dry mode and runs documentation examples, exercises coverage
@@ -14,13 +15,44 @@ schemas, documentation contracts, formatting, lint and types without the full in
14
15
  or package installation. Install dependencies with `bun install --frozen-lockfile`
15
16
  first. Quality dependencies are development dependencies pinned in `bun.lock`.
16
17
 
18
+ After frozen installation, `bun run check:typescript --phase <name>` runs one
19
+ phase and checks the complete source inventory. Each phase reports its elapsed
20
+ time, including when it fails. The complete command runs these same phases in
21
+ order; `--fast` runs only `static` without installing dependencies.
22
+
23
+ | Phase | Checks |
24
+ | --------- | --------------------------------------------------------------------------------------------- |
25
+ | `static` | Schema freshness, documentation contracts, formatting, type-aware lint and typing |
26
+ | `docs` | All guide dry runs, tutorial examples and recipe checks |
27
+ | `tests` | Coverage integrity controls, the complete instrumented Bun suite and both coverage thresholds |
28
+ | `package` | Packed-package installation and installed CLI workflows |
29
+
30
+ Use the same prerequisites as the corresponding complete-gate checks. A single
31
+ phase is partial evidence; full acceptance requires all four on the same candidate.
32
+ CI assigns every test file to exactly one of four duration-balanced shards, then
33
+ enforces both coverage thresholds on their combined counters over the complete
34
+ production inventory. Local `--phase tests` and the complete command still run
35
+ the suite in one process. See the [shard commands](../scripts/coverage/README.md#test-shards)
36
+ for reproducing the CI split.
37
+
17
38
  ## Source dispositions
18
39
 
19
- [`typescript-sources.json`](../typescript-sources.json) explicitly lists every
20
- TypeScript and JavaScript source. The guard refuses new, missing or duplicated
21
- files and declarations outside the configured TypeScript project. Changing a
22
- source disposition requires review; adding a glob is not a substitute for the
23
- inventory. The coverage baseline includes the platform-reachable subset of
40
+ The [inventory guard](../scripts/typescript-inventory.ts) derives every TypeScript
41
+ and JavaScript source disposition from Git paths, following the
42
+ [maintainer decision in #143](https://github.com/BjRo/sevro/issues/143#issuecomment-6382662399).
43
+ It classifies `git ls-files --cached --others --exclude-standard -z` output using
44
+ the checked path rules, so supported additions and staged renames or deletions
45
+ need no manual manifest edit. Non-ignored untracked sources are included in local
46
+ candidate checks; committed CI checkouts use their tracked paths. Declarations
47
+ take precedence over directory roles, and only the validators in the shared
48
+ [schema catalog](../scripts/schema-catalog.ts) receive the generated disposition.
49
+ Unknown or ambiguous paths, missing tracked files, an incomplete generated set,
50
+ and sources outside lint or typing are refused. An independent filesystem audit
51
+ also refuses ignored authored sources rather than silently dropping them.
52
+ Changing a disposition rule still requires review. Isolated coverage snapshots
53
+ index their copied candidate independently, preserving additions and deletions
54
+ without changing the source checkout's index.
55
+ The coverage baseline includes the platform-reachable subset of
24
56
  `inventory.production`; `scripts/coverage/platform-scope.ts` names each excluded
25
57
  host-specific file. Every production file remains inventoried, typed and linted,
26
58
  and the opposite platform measures it. Current instrumentation supports authored
@@ -29,14 +61,14 @@ validators in `inventory.generated` are excluded from instrumentation and the
29
61
  coverage denominator. New `.tsx`, `.mts`, `.cts` or JavaScript
30
62
  production requires an explicit instrumentation and quality-policy change first.
31
63
 
32
- | Inventory | Lint and typing | Test and coverage disposition |
33
- | -------------- | ---------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
34
- | `production` | Type-aware strict lint and strict compiler | All platform-reachable authored executable `src` statements and branch outcomes, including unimported files in that scope |
35
- | `generated` | CommonJS syntax parsing, exact `schemas:check` freshness, and checked `.d.cts` boundary declarations | Real Bun schema tests; the five declared generated validators are outside the coverage denominator |
36
- | `declarations` | Type-aware lint and compiler | Declaration-only files have no executable counters |
37
- | `tooling` | Same type-aware lint and strict compiler; `.mjs`/`.cjs` use `checkJs` and explicit JSDoc contracts | Durable public gate probes, schema freshness, release and installed-package tests; outside the runner-production denominator |
38
- | `tests` | Same type-aware lint and strict compiler, including fixtures | Real Bun filesystem/process/protocol tests; outside the production denominator |
39
- | `examples` | Same type-aware lint and strict compiler | Packaged demonstration host adapters are synthetic consumer fixtures, exercised by installed-package validation; they implement no runner behavior and are outside the runner-production denominator |
64
+ | Inventory | Lint and typing | Test and coverage disposition |
65
+ | -------------- | ---------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
66
+ | `production` | Type-aware strict lint and strict compiler | All platform-reachable authored executable `src` statements and branch outcomes, including unimported files in that scope |
67
+ | `generated` | CommonJS syntax parsing, exact `schemas:check` freshness, and checked `.d.cts` boundary declarations | Real Bun schema tests; the five declared generated validators are outside the coverage denominator |
68
+ | `declarations` | Type-aware lint and compiler | Declaration-only files have no executable counters |
69
+ | `tooling` | Same type-aware lint and strict compiler; `.mjs`/`.cjs` use `checkJs` and explicit JSDoc contracts | Durable public gate probes, schema freshness, release and installed-package tests; outside the runner-production denominator |
70
+ | `tests` | Same type-aware lint and strict compiler, including fixtures | Real Bun filesystem/process/protocol tests; outside the production denominator |
71
+ | `examples` | Same type-aware lint and strict compiler | Packaged recipe adapters, extensions and evaluator checks are consumer examples, exercised by tutorial and installed-package validation; they implement no runner behavior and are outside the runner-production denominator |
40
72
 
41
73
  Generated AJV CommonJS is deliberately not rewritten to conform to authored-code
42
74
  rules. An earlier audit of the original four generated files using ESLint's
@@ -45,8 +77,8 @@ recommended JavaScript rules found 879 redeclarations, 281 useless assignments,
45
77
  generation freshness, typed public boundaries and runtime behavior remain checked.
46
78
  Generated compiler output is excluded from coverage because the coverage target
47
79
  measures authored runner behavior. This applies only to
48
- `src/generated/cli-result.cjs`, `extension.cjs`, `report.cjs`, `run-evidence.cjs` and `runtime.cjs`
49
- as explicitly declared in the inventory. This disposition does not apply to authored CommonJS, including
80
+ the exact validator paths derived from the [schema catalog](../scripts/schema-catalog.ts),
81
+ which also drives schema generation. This disposition does not apply to authored CommonJS, including
50
82
  the coverage capture hook.
51
83
 
52
84
  ## Types, functions and review
@@ -120,7 +152,7 @@ macOS `sandbox-exec`, Keychain and Apple metadata modules are excluded; on macOS
120
152
  `bubblewrap` module is excluded. Shared isolation and credential code is counted
121
153
  on both.
122
154
 
123
- Apple developer-tool discovery lives in `src/runtime-discovery-apple.ts`, which
155
+ Apple developer-tool discovery lives in `src/runtime/discovery-apple.ts`, which
124
156
  the public loader imports only under its explicit Darwin guard. Linux excludes
125
157
  that exact native module; macOS measures it, and both platforms still typecheck
126
158
  and lint it. The provider lookup, bounded query and read-root safeguards remain
@@ -146,12 +178,24 @@ summary, process ledger, Bun test log, unmodified source snapshot, LCOV and HTML
146
178
  production files, branch exclusions, and exact projected counts.
147
179
  `coverage-raw.json` and the raw summary preserve all validated counters before
148
180
  platform projection.
181
+ CI also retains `.quality/shards/<index>/` with each shard's source snapshot,
182
+ process ledger, test log, raw and portable counters, and manifest. Process IDs
183
+ remain scoped to their original shard. Aggregation verifies candidate contents,
184
+ workflow run, platform, architecture, Bun version, instrumentation
185
+ layout, and exact test assignments before combining counters with normalized
186
+ source paths. Missing, repeated, incomplete, or mismatched shards fail the gate.
187
+ Failed-job reruns can reuse successful shards from earlier attempts of the same
188
+ workflow run; manifests retain each attempt, and rerun shards replace only their
189
+ own artifacts. Another candidate or workflow run cannot supply missing evidence.
149
190
  `--coverage [Bun test arguments]` is a diagnostic measurement; it still enforces
150
191
  both thresholds and writes `.quality/coverage-targeted`, keeping it distinct from
151
192
  the full-suite evidence. Sparse runs are expected to fail the full denominator.
152
193
 
153
194
  The durable public probes are `--probe snapshot`, `--probe scope`, `--probe branch`,
154
195
  `--probe missing-reports`, `--probe integrity`, and `--probe source-map`.
196
+ `tests/scripts/typescript-quality.test.ts` invokes each probe once during the suite;
197
+ the phase entrypoint and shard merge do not rerun them. The commands remain
198
+ available for targeted diagnostics.
155
199
  The scope probe records a zero-seeded baseline and merged real process report
156
200
  under `.quality/coverage-scope`; it verifies that ordinary generated CommonJS
157
201
  validation still runs. Branch and missing-report controls
@@ -167,21 +211,40 @@ Codex 0.160.1 and Claude Code 2.1.284 for deterministic native hook tests. Hoste
167
211
  CI targets `ubuntu-24.04` x64 with Bun 1.3.13 and Node 24.13.0. Its first
168
212
  successful hosted run is required before describing that environment as validated.
169
213
  GitHub documents the [Ubuntu runner labels](https://docs.github.com/en/actions/reference/runners/github-hosted-runners).
170
- Ubuntu installs `bubblewrap` and `socat` and runs the same native isolation cases;
171
- local macOS runs retain `sandbox-exec`. Windows remains unverified.
214
+ Ubuntu installs `bubblewrap`, `ripgrep`, and `socat` and runs the same native isolation cases;
215
+ local macOS runs retain `sandbox-exec`. The macOS quality and coverage gate is
216
+ local-only; the hosted workflow does not run a macOS gate. Windows remains unverified.
217
+
218
+ Ordinary changes and explicit workflow dispatches run the four canonical phases
219
+ concurrently. The tests phase consists of four shard jobs followed by a combined
220
+ coverage job. Each job installs locked dependencies; native host tools are
221
+ installed for `docs` and the test shards, and UV and mise for `docs`. Each shard
222
+ retains its evidence, and the combined `tests` job uploads `.quality` even when
223
+ coverage fails. Shards do not cancel their siblings on failure. A new commit to
224
+ the same pull request cancels its superseded workflow run; main pushes and manual
225
+ runs have independent concurrency groups. The stable aggregate `TypeScript quality`
226
+ status requires every phase and all four shards to succeed. A missing, cancelled,
227
+ skipped or failed phase or shard cannot turn that status green.
172
228
 
173
- Ordinary changes and explicit workflow dispatches invoke the canonical gate,
174
- upload `.quality` even when coverage fails, and publish the stable aggregate
175
- `TypeScript quality` status. A cancelled or failed gate cannot turn that status
176
- green.
229
+ CI caches Bun's package downloads by Bun version, OS, architecture and `bun.lock`;
230
+ every job still performs frozen installation. The docs job and test shards also
231
+ cache the installed Codex and Claude packages by OS, architecture and the setup
232
+ script's content hash, which includes both pinned versions and their installation
233
+ layout. There are no fallback keys for native tools. On a hit, setup copies the
234
+ cached packages into a fresh temporary tool root and verifies both native versions
235
+ before publishing their paths. The cache never contains credentials or replaces
236
+ the isolation prerequisites or required checks.
177
237
 
178
238
  Release metadata changes follow [the release workflow](releases.md) and reuse a
179
239
  successful full gate from an ancestor on `main`, with identical code,
180
240
  dependencies, schemas, tests, and other quality inputs. The selector permits
181
241
  only version/distribution-tag changes and its exact release-file inventory.
182
- It verifies the original full-gate step, so a reused release run cannot serve
183
- as fresh test evidence. Release CI runs `check:typescript --fast`, records the
184
- tested source and CI run URL, and skips the test/coverage job. The aggregate
242
+ It verifies each original phase job and its gate step, including every shard and
243
+ the combined tests job for sharded runs, or a historical complete gate job and
244
+ step. A partial matrix or reused release run cannot serve as fresh test evidence.
245
+ Duplicate phase or shard jobs are ambiguous and refused. Release CI
246
+ runs `check:typescript --fast`, records the tested source and CI run URL, and
247
+ skips all four full-gate phase jobs. The aggregate
185
248
  status requires both verified prior evidence and successful static checks;
186
249
  missing, cancelled, failed, or ambiguous evidence cannot establish a pass.
187
250
  Reused results remain attributed to their original run rather than being
@@ -0,0 +1,26 @@
1
+ import { readFile, writeFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+
4
+ /** Trusted, deterministic participant for exercising the recipe's real graders. */
5
+ const host = {
6
+ id: "recipes.deterministic",
7
+ model: "fixture-editor",
8
+ effort: "none",
9
+ async run({ workspace }: { workspace: string }) {
10
+ const task = await readFile(join(workspace, "README.md"), "utf8");
11
+ const failing = process.env.SEVRO_RECIPE_FAIL === "1";
12
+ if (task.includes("Fix add")) {
13
+ await writeFile(
14
+ join(workspace, "math.cjs"),
15
+ `exports.add = (a, b) => a ${failing ? "-" : "+"} b;\n`,
16
+ );
17
+ }
18
+ return {
19
+ finalMessage: failing
20
+ ? "Start with npm start on port 9000."
21
+ : "Run bun run dev on port 3000.",
22
+ complete: true,
23
+ };
24
+ },
25
+ };
26
+ export default host;
@@ -0,0 +1,21 @@
1
+ {
2
+ "id": "recipe-coding-change",
3
+ "prompt": "Fix add in math.cjs so it adds its two numeric arguments. Do not modify README.md. Explain how to start the application.",
4
+ "fixture": {
5
+ "files": {
6
+ "README.md": "Fix add in math.cjs. Start the application with bun run dev on port 3000.\n",
7
+ "math.cjs": "exports.add = (a, b) => a - b;\n"
8
+ }
9
+ },
10
+ "checks": [
11
+ {
12
+ "id": "addition",
13
+ "grader": "sevro.shell",
14
+ "configuration": {
15
+ "run": "{{evaluator-test}}",
16
+ "expectExact": "coding checks passed"
17
+ }
18
+ }
19
+ ],
20
+ "requiredEvidence": []
21
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "id": "recipe-host-tools",
3
+ "prompt": "Read app.env with rg, then explain the application's start command and port.",
4
+ "fixture": {
5
+ "files": {
6
+ "README.md": "Start with bun run dev on port 3000.\n",
7
+ "app.env": "port=3000\n"
8
+ }
9
+ },
10
+ "checks": [
11
+ {
12
+ "id": "rg-access",
13
+ "grader": "sevro.shell",
14
+ "configuration": {
15
+ "run": "rg '^port=3000$' app.env",
16
+ "expectExact": "port=3000"
17
+ }
18
+ }
19
+ ],
20
+ "requiredEvidence": []
21
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "id": "recipe-mise",
3
+ "prompt": "Use node from PATH to read app.json, then explain the application's start command and port. The operator has resolved mise's existing node@24.13.0 installation; do not download anything.",
4
+ "fixture": {
5
+ "files": {
6
+ "README.md": "Start with bun run dev on port 3000.\n",
7
+ "app.json": "{\"port\":3000}\n"
8
+ }
9
+ },
10
+ "checks": [
11
+ {
12
+ "id": "mise-node-access",
13
+ "grader": "sevro.shell",
14
+ "configuration": {
15
+ "run": "node -e 'if (process.version !== \"v24.13.0\") process.exit(1); const a = require(\"./app.json\"); if (a.port !== 3000) process.exit(1); console.log(a.port)'",
16
+ "expectExact": "3000"
17
+ }
18
+ }
19
+ ],
20
+ "requiredEvidence": []
21
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "id": "recipe-node",
3
+ "prompt": "Read app.json with Node and explain the application's start command and port.",
4
+ "fixture": {
5
+ "files": {
6
+ "README.md": "Start with bun run dev on port 3000.\n",
7
+ "app.json": "{\"port\":3000}\n"
8
+ }
9
+ },
10
+ "checks": [
11
+ {
12
+ "id": "node-access",
13
+ "grader": "sevro.shell",
14
+ "configuration": {
15
+ "run": "node -e 'const a = require(\"./app.json\"); if (a.port !== 3000) process.exit(1); console.log(a.port)'",
16
+ "expectExact": "3000"
17
+ }
18
+ }
19
+ ],
20
+ "requiredEvidence": []
21
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "id": "recipe-python-uv",
3
+ "prompt": "Use uv run --offline --no-project --python \"$RECIPE_PYTHON\" python to read app.json, then explain the application's start command and port.",
4
+ "fixture": {
5
+ "files": {
6
+ "README.md": "Start with bun run dev on port 3000.\n",
7
+ "app.json": "{\"port\":3000}\n"
8
+ }
9
+ },
10
+ "checks": [
11
+ {
12
+ "id": "uv-python-access",
13
+ "grader": "sevro.shell",
14
+ "configuration": {
15
+ "run": "uv run --offline --no-project --python \"$RECIPE_PYTHON\" python -c 'import json; p=json.load(open(\"app.json\"))[\"port\"]; assert p == 3000; print(p)'",
16
+ "expectExact": "3000"
17
+ }
18
+ }
19
+ ],
20
+ "requiredEvidence": []
21
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "id": "recipe-semantic",
3
+ "prompt": "Read README.md and explain how to start the application, including its port.",
4
+ "fixture": {
5
+ "files": { "README.md": "Start with bun run dev on port 3000.\n" }
6
+ },
7
+ "checks": [
8
+ {
9
+ "id": "startup-meaning",
10
+ "grader": "sevro.semantic",
11
+ "configuration": {
12
+ "proposition": "The response tells the reader to run bun run dev and says that the application listens on port 3000."
13
+ }
14
+ }
15
+ ],
16
+ "requiredEvidence": []
17
+ }
@@ -0,0 +1,23 @@
1
+ import assert from "node:assert/strict";
2
+ import { join } from "node:path";
3
+ import { pathToFileURL } from "node:url";
4
+
5
+ interface Addition {
6
+ add: (left: number, right: number) => number;
7
+ }
8
+ function isAddition(value: unknown): value is Addition {
9
+ return (
10
+ typeof value === "object" &&
11
+ value !== null &&
12
+ "add" in value &&
13
+ typeof value.add === "function"
14
+ );
15
+ }
16
+ const candidate: unknown = await import(
17
+ pathToFileURL(join(process.cwd(), "math.cjs")).href
18
+ );
19
+ assert.ok(isAddition(candidate), "Candidate must export add");
20
+ assert.equal(candidate.add(2, 3), 5);
21
+ assert.equal(candidate.add(-2, 3), 1);
22
+ assert.equal(candidate.add(0, 0), 0);
23
+ console.log("coding checks passed");
@@ -0,0 +1,65 @@
1
+ import { createHash } from "node:crypto";
2
+ import { readFile } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+
5
+ export function artifact(relativePath: string, content: string) {
6
+ return {
7
+ id: `recipes.${createHash("sha256").update(relativePath).digest("hex").slice(0, 16)}`,
8
+ relativePath,
9
+ sha256: createHash("sha256").update(content).digest("hex"),
10
+ contentBase64: Buffer.from(content).toString("base64"),
11
+ gitExclude: true,
12
+ };
13
+ }
14
+
15
+ export async function repositoryArtifacts(host: string) {
16
+ const directory = host === "claude" ? ".claude" : ".agents";
17
+ const relative = `${directory}/skills/startup-guide/SKILL.md`;
18
+ const body = await readFile(
19
+ join(import.meta.dir, "repository-skill", relative),
20
+ "utf8",
21
+ );
22
+ return [artifact(relative, body)];
23
+ }
24
+
25
+ export function pluginArtifacts() {
26
+ const files = {
27
+ "marketplace/.claude-plugin/marketplace.json": JSON.stringify({
28
+ name: "recipes",
29
+ owner: { name: "Sevro examples" },
30
+ plugins: [
31
+ { name: "startup", source: "./plugin", description: "Startup recipe" },
32
+ ],
33
+ }),
34
+ "marketplace/plugin/.claude-plugin/plugin.json": JSON.stringify({
35
+ name: "startup",
36
+ version: "1.0.0",
37
+ }),
38
+ "marketplace/plugin/.codex-plugin/plugin.json": JSON.stringify({
39
+ name: "startup",
40
+ version: "1.0.0",
41
+ skills: "./skills/",
42
+ }),
43
+ "marketplace/plugin/skills/startup-guide/SKILL.md":
44
+ "---\nname: startup-guide\ndescription: Explain application startup.\n---\nRead README.md. Explain its command and port.\n",
45
+ "marketplace/plugin/hooks/hooks.json": JSON.stringify({
46
+ hooks: {
47
+ SessionStart: [
48
+ {
49
+ hooks: [
50
+ {
51
+ type: "command",
52
+ command: '/bin/sh "${CLAUDE_PLUGIN_ROOT}/hooks/start.sh"',
53
+ },
54
+ ],
55
+ },
56
+ ],
57
+ },
58
+ }),
59
+ "marketplace/plugin/hooks/start.sh":
60
+ "#!/bin/sh\nprintf 'hook ran\\n' > \"$PWD/hook-ran.txt\"\n",
61
+ };
62
+ return Object.entries(files).map(([path, content]) =>
63
+ artifact(path, content),
64
+ );
65
+ }
@@ -0,0 +1,174 @@
1
+ import {
2
+ artifact,
3
+ pluginArtifacts,
4
+ repositoryArtifacts,
5
+ } from "./extension-artifacts";
6
+
7
+ export function record(value: unknown): Record<string, unknown> {
8
+ if (value === null || typeof value !== "object" || Array.isArray(value))
9
+ throw new Error("Expected a JSON object");
10
+ return value as Record<string, unknown>;
11
+ }
12
+
13
+ export function describe(mode: string, host: string) {
14
+ const capabilities: Record<string, string[]> = {
15
+ lifecycle: [],
16
+ "repository-skill": [`sevro.${host}.repository-invocation`],
17
+ plugins:
18
+ host === "codex"
19
+ ? ["sevro.codex.plugin-marketplace", "sevro.codex.explicit-invocation"]
20
+ : ["sevro.claude.plugin-dirs", "sevro.claude.explicit-invocation"],
21
+ };
22
+ const requiredCapabilities = capabilities[mode];
23
+ if (!requiredCapabilities) throw new Error("Unknown extension recipe");
24
+ return {
25
+ extension: { id: "recipes.startup", version: "1.0.0" },
26
+ protocols: ["sevro.extension.v1"],
27
+ requiredCapabilities,
28
+ optionalCapabilities: [],
29
+ graders: ["recipes.startup"],
30
+ taskVerdictPolicies: [],
31
+ };
32
+ }
33
+
34
+ function requiredInvocation(mode: string, host: string): string[] {
35
+ if (mode !== "repository-skill") return [];
36
+ return [
37
+ host === "codex"
38
+ ? "sevro.codex.explicit-invocation"
39
+ : "sevro.claude.repository-invocation",
40
+ ];
41
+ }
42
+
43
+ export function resolveCase(mode: string, host: string, fail: boolean) {
44
+ const invocation = mode === "lifecycle" ? "" : "{{sevro.skill_invocation}} ";
45
+ const prompt = fail
46
+ ? "Deliberate negative control: return only wrong."
47
+ : "Read README.md. Explain the application's start command and port. Do not change files.";
48
+ const requiredEvidence = requiredInvocation(mode, host);
49
+ return {
50
+ cases: [
51
+ {
52
+ id: "recipe-extension",
53
+ prompt: invocation + prompt,
54
+ fixture: {
55
+ kind: "generated",
56
+ commits: [
57
+ {
58
+ message: "chore: initialize sample",
59
+ files: { "README.md": "Run bun run dev on port 3000.\n" },
60
+ },
61
+ ],
62
+ },
63
+ checks: [
64
+ {
65
+ id: "startup-command",
66
+ grader: "sevro.regex",
67
+ configuration: { pattern: "bun run dev" },
68
+ },
69
+ {
70
+ id: "prepared-input",
71
+ grader: "sevro.shell",
72
+ configuration: {
73
+ run: "test \"$(cat startup.txt)\" = 'prepared startup data'",
74
+ },
75
+ },
76
+ ...(mode === "plugins"
77
+ ? [
78
+ {
79
+ id: "plugin-hook",
80
+ grader: "sevro.shell",
81
+ configuration: {
82
+ run: "cat hook-ran.txt",
83
+ expectExact: "hook ran",
84
+ },
85
+ },
86
+ ]
87
+ : []),
88
+ {
89
+ id: "recipes.startup.answer",
90
+ grader: "recipes.startup",
91
+ configuration: {},
92
+ },
93
+ ],
94
+ requiredEvidence,
95
+ extensionData: { "recipes.startup": { mode, host } },
96
+ },
97
+ ],
98
+ };
99
+ }
100
+
101
+ export async function prepare(mode: string, host: string) {
102
+ const base = {
103
+ artifacts: [artifact("startup.txt", "prepared startup data\n")],
104
+ requestedInstrumentation: [],
105
+ extensionData: { "recipes.startup": { mode, host } },
106
+ };
107
+ switch (mode) {
108
+ case "repository-skill":
109
+ return {
110
+ ...base,
111
+ artifacts: [...base.artifacts, ...(await repositoryArtifacts(host))],
112
+ ...(host === "codex"
113
+ ? { codexRepositorySkillInvocation: { skillName: "startup-guide" } }
114
+ : {
115
+ claudeRepositorySkillInvocation: { skillName: "startup-guide" },
116
+ }),
117
+ };
118
+ case "plugins":
119
+ return {
120
+ ...base,
121
+ artifacts: [...base.artifacts, ...pluginArtifacts()],
122
+ ...(host === "codex"
123
+ ? {
124
+ codexMarketplace: {
125
+ artifactRoot: "marketplace",
126
+ marketplaceName: "recipes",
127
+ pluginNames: ["startup"],
128
+ },
129
+ codexSkillInvocation: {
130
+ pluginName: "startup",
131
+ skillName: "startup-guide",
132
+ },
133
+ }
134
+ : {
135
+ claudePluginDirs: { artifactRoots: ["marketplace/plugin"] },
136
+ claudeSkillInvocation: {
137
+ pluginName: "startup",
138
+ skillName: "startup-guide",
139
+ },
140
+ }),
141
+ };
142
+ default:
143
+ return base;
144
+ }
145
+ }
146
+
147
+ function answerStatus(observation: Record<string, unknown>) {
148
+ if (observation.completeness !== "complete") return "unavailable";
149
+ const text = record(observation.data).text;
150
+ return typeof text === "string" && /\b3000\b/.test(text)
151
+ ? "passed"
152
+ : "failed";
153
+ }
154
+
155
+ export function evaluate(params: Record<string, unknown>) {
156
+ if (!Array.isArray(params.observations))
157
+ throw new Error("Missing observations");
158
+ const observations: unknown[] = params.observations;
159
+ const observation = observations
160
+ .map(record)
161
+ .find((item) => item.id === "sevro.observation.final-message");
162
+ if (!observation) throw new Error("Missing final-message observation");
163
+ const status = answerStatus(observation);
164
+ return {
165
+ checks: [
166
+ {
167
+ id: "recipes.startup.answer",
168
+ status,
169
+ evidenceRefs: ["sevro.observation.final-message"],
170
+ },
171
+ ],
172
+ metrics: [],
173
+ };
174
+ }
@@ -0,0 +1,37 @@
1
+ import {
2
+ describe,
3
+ evaluate,
4
+ prepare,
5
+ record,
6
+ resolveCase,
7
+ } from "./extension-methods";
8
+
9
+ const mode = process.argv[2] ?? "lifecycle";
10
+ const host = process.argv[3] ?? "deterministic";
11
+ const fail = process.argv[4] === "fail";
12
+ const request = record(JSON.parse(await Bun.stdin.text()) as unknown);
13
+
14
+ async function result() {
15
+ switch (request.method) {
16
+ case "describe":
17
+ return describe(mode, host);
18
+ case "resolve":
19
+ return resolveCase(mode, host, fail);
20
+ case "prepare":
21
+ return await prepare(mode, host);
22
+ case "evaluate":
23
+ return evaluate(record(request.params));
24
+ default:
25
+ throw new Error("Unsupported extension method");
26
+ }
27
+ }
28
+
29
+ // One request per process; stdout contains only the matching protocol response.
30
+ process.stdout.write(
31
+ JSON.stringify({
32
+ protocol: request.protocol,
33
+ id: request.id,
34
+ method: request.method,
35
+ result: await result(),
36
+ }),
37
+ );
@@ -0,0 +1,7 @@
1
+ ---
2
+ name: startup-guide
3
+ description: Explain how to start this sample application and identify its port.
4
+ ---
5
+
6
+ Read README.md. Explain the exact start command and port from that file.
7
+ Do not change files. Do not invent a different command or port.
@@ -0,0 +1,7 @@
1
+ ---
2
+ name: startup-guide
3
+ description: Explain how to start this sample application and identify its port.
4
+ ---
5
+
6
+ Read README.md. Explain the exact start command and port from that file.
7
+ Do not change files. Do not invent a different command or port.