@markstanden/opencode-test-container 0.0.0-stage → 0.1.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 (121) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +263 -2
  3. package/container/Containerfile +24 -0
  4. package/container/README.md +29 -0
  5. package/dist/adapters/opencode/events.d.ts +90 -0
  6. package/dist/adapters/opencode/events.js +213 -0
  7. package/dist/adapters/opencode/runtime.d.ts +87 -0
  8. package/dist/adapters/opencode/runtime.js +136 -0
  9. package/dist/adapters/podman/container.d.ts +62 -0
  10. package/dist/adapters/podman/container.js +119 -0
  11. package/dist/adapters/podman/image.d.ts +50 -0
  12. package/dist/adapters/podman/image.js +124 -0
  13. package/dist/adapters/process.d.ts +38 -0
  14. package/dist/adapters/process.js +71 -0
  15. package/dist/campaign/assess.d.ts +45 -0
  16. package/dist/campaign/assess.js +208 -0
  17. package/dist/campaign/close.d.ts +17 -0
  18. package/dist/campaign/close.js +112 -0
  19. package/dist/campaign/evidence-guard.d.ts +24 -0
  20. package/dist/campaign/evidence-guard.js +49 -0
  21. package/dist/campaign/freeze.d.ts +94 -0
  22. package/dist/campaign/freeze.js +297 -0
  23. package/dist/campaign/inventory.d.ts +62 -0
  24. package/dist/campaign/inventory.js +192 -0
  25. package/dist/campaign/judge.d.ts +209 -0
  26. package/dist/campaign/judge.js +1285 -0
  27. package/dist/campaign/ledger.d.ts +64 -0
  28. package/dist/campaign/ledger.js +149 -0
  29. package/dist/campaign/prepare.d.ts +129 -0
  30. package/dist/campaign/prepare.js +483 -0
  31. package/dist/campaign/primary.d.ts +96 -0
  32. package/dist/campaign/primary.js +189 -0
  33. package/dist/campaign/run.d.ts +151 -0
  34. package/dist/campaign/run.js +562 -0
  35. package/dist/campaign/status.d.ts +76 -0
  36. package/dist/campaign/status.js +237 -0
  37. package/dist/cli/otc.d.ts +51 -0
  38. package/dist/cli/otc.js +435 -0
  39. package/dist/contracts/canonical.d.ts +21 -0
  40. package/dist/contracts/canonical.js +44 -0
  41. package/dist/contracts/types.d.ts +261 -0
  42. package/dist/contracts/types.js +9 -0
  43. package/dist/contracts/validate.d.ts +72 -0
  44. package/dist/contracts/validate.js +1021 -0
  45. package/dist/contracts/version.d.ts +44 -0
  46. package/dist/contracts/version.js +147 -0
  47. package/dist/evaluators/docs/analysis/accounting.d.ts +140 -0
  48. package/dist/evaluators/docs/analysis/accounting.js +237 -0
  49. package/dist/evaluators/docs/analysis/blinding.d.ts +40 -0
  50. package/dist/evaluators/docs/analysis/blinding.js +80 -0
  51. package/dist/evaluators/docs/analysis/bounds.d.ts +40 -0
  52. package/dist/evaluators/docs/analysis/bounds.js +31 -0
  53. package/dist/evaluators/docs/analysis/budget.d.ts +137 -0
  54. package/dist/evaluators/docs/analysis/budget.js +305 -0
  55. package/dist/evaluators/docs/analysis/closure.d.ts +175 -0
  56. package/dist/evaluators/docs/analysis/closure.js +251 -0
  57. package/dist/evaluators/docs/analysis/decision.d.ts +59 -0
  58. package/dist/evaluators/docs/analysis/decision.js +161 -0
  59. package/dist/evaluators/docs/analysis/freeze.d.ts +201 -0
  60. package/dist/evaluators/docs/analysis/freeze.js +395 -0
  61. package/dist/evaluators/docs/analysis/guardrails.d.ts +51 -0
  62. package/dist/evaluators/docs/analysis/guardrails.js +194 -0
  63. package/dist/evaluators/docs/analysis/journal.d.ts +150 -0
  64. package/dist/evaluators/docs/analysis/journal.js +475 -0
  65. package/dist/evaluators/docs/analysis/ledger.d.ts +78 -0
  66. package/dist/evaluators/docs/analysis/ledger.js +145 -0
  67. package/dist/evaluators/docs/analysis/measures.d.ts +302 -0
  68. package/dist/evaluators/docs/analysis/measures.js +1069 -0
  69. package/dist/evaluators/docs/analysis/model.d.ts +236 -0
  70. package/dist/evaluators/docs/analysis/model.js +740 -0
  71. package/dist/evaluators/docs/analysis/pairing.d.ts +68 -0
  72. package/dist/evaluators/docs/analysis/pairing.js +95 -0
  73. package/dist/evaluators/docs/analysis/records.d.ts +229 -0
  74. package/dist/evaluators/docs/analysis/records.js +935 -0
  75. package/dist/evaluators/docs/analysis/recovery.d.ts +225 -0
  76. package/dist/evaluators/docs/analysis/recovery.js +507 -0
  77. package/dist/evaluators/docs/analysis/render.d.ts +235 -0
  78. package/dist/evaluators/docs/analysis/render.js +566 -0
  79. package/dist/evaluators/docs/analysis/replay.d.ts +15 -0
  80. package/dist/evaluators/docs/analysis/replay.js +1272 -0
  81. package/dist/evaluators/docs/analysis/screening/inventory.d.ts +136 -0
  82. package/dist/evaluators/docs/analysis/screening/inventory.js +307 -0
  83. package/dist/evaluators/docs/analysis/screening/noteVersion.d.ts +15 -0
  84. package/dist/evaluators/docs/analysis/screening/noteVersion.js +46 -0
  85. package/dist/evaluators/docs/analysis/screening/reconcile.d.ts +38 -0
  86. package/dist/evaluators/docs/analysis/screening/reconcile.js +40 -0
  87. package/dist/evaluators/docs/analysis/screening/rules.d.ts +163 -0
  88. package/dist/evaluators/docs/analysis/screening/rules.js +330 -0
  89. package/dist/evaluators/docs/analysis/screening/scan.d.ts +72 -0
  90. package/dist/evaluators/docs/analysis/screening/scan.js +362 -0
  91. package/dist/evaluators/docs/analysis/selector.d.ts +68 -0
  92. package/dist/evaluators/docs/analysis/selector.js +123 -0
  93. package/dist/evaluators/docs/assess.d.ts +129 -0
  94. package/dist/evaluators/docs/assess.js +433 -0
  95. package/dist/evaluators/docs/campaign.d.ts +133 -0
  96. package/dist/evaluators/docs/campaign.js +222 -0
  97. package/dist/evaluators/docs/index.d.ts +25 -0
  98. package/dist/evaluators/docs/index.js +25 -0
  99. package/dist/evaluators/docs/journal.d.ts +150 -0
  100. package/dist/evaluators/docs/journal.js +475 -0
  101. package/dist/evaluators/docs/scoring.d.ts +367 -0
  102. package/dist/evaluators/docs/scoring.js +662 -0
  103. package/dist/evaluators/skills/assess.d.ts +94 -0
  104. package/dist/evaluators/skills/assess.js +207 -0
  105. package/dist/index.d.ts +46 -0
  106. package/dist/index.js +29 -0
  107. package/dist/legacy/prod014.d.ts +86 -0
  108. package/dist/legacy/prod014.js +209 -0
  109. package/dist/reporting/report.d.ts +79 -0
  110. package/dist/reporting/report.js +242 -0
  111. package/docs/architecture.md +106 -0
  112. package/docs/capability-matrix.md +84 -0
  113. package/docs/offline-rehearsal.md +75 -0
  114. package/docs/parity-record.md +81 -0
  115. package/docs/pr-description.md +103 -0
  116. package/docs/schema.md +97 -0
  117. package/examples/skills/README.md +13 -0
  118. package/examples/skills/campaign.json +73 -0
  119. package/examples/skills/skills/otel-helper/SKILL.md +10 -0
  120. package/examples/skills/suite.json +29 -0
  121. package/package.json +45 -5
@@ -0,0 +1,81 @@
1
+ # Recorded-evidence parity record
2
+
3
+ Run 2026-11-15 during reconciliation. No model calls; both analysers read the
4
+ same recorded evidence.
5
+
6
+ ## Committed mini corpus
7
+
8
+ Fixture: `tests/fixtures/docs-parity` (4 trials, copied from the recorded
9
+ confirmation campaign).
10
+
11
+ Command (OTC):
12
+
13
+ ```sh
14
+ node dist/cli/otc.js replay \
15
+ --manifest tests/fixtures/docs-parity/manifest.json \
16
+ --evidence-root tests/fixtures/docs-parity/evidence \
17
+ --out /tmp/opencode/mini-new
18
+ ```
19
+
20
+ Original command (docs @ extraction base):
21
+
22
+ ```sh
23
+ node tests/fixtures/placement-harness/analysis/analyse.mts \
24
+ --campaign tests/fixtures/analysis/manifests/mini.json \
25
+ --evidence-root /tmp/opencode/mini-evidence \
26
+ --out /tmp/opencode/mini-old
27
+ ```
28
+
29
+ Result: `analysis.json`, `REPORT.md` and `SCREENING.json` deep-equal;
30
+ `analysis-receipt.json` equal after removing `renderedAt`. This is enforced by
31
+ `tests/contract/docs-parity.test.ts` in `npm test`.
32
+
33
+ ## Recorded confirmation corpus
34
+
35
+ Evidence: `/home/mark/tmp/recording-confirm-20261110` (40 trials, 20 pairs).
36
+ Result: all three artefacts byte-identical; receipt differs only in
37
+ `renderedAt`. Decision `inconclusive`; totals 40 planned/completed; utility
38
+ 20; bounds delta -13..7.
39
+
40
+ Rerun 2026-11-15 at OTC head `4de97a0` against the original analyser at docs
41
+ `76ed331`: `analysis.json`, `REPORT.md`, `SCREENING.json` and the receipt
42
+ (apart from `renderedAt`) were byte-identical.
43
+
44
+ ## Recorded screening corpus
45
+
46
+ Evidence: `/home/mark/tmp/docs-desc-fresh-20261110` (48 trials, six arms).
47
+ Result: all three artefacts byte-identical; decision `not-defined`; utility
48
+ 24; 6 screening findings.
49
+
50
+ Rerun 2026-11-15 at OTC head `4de97a0` against the original analyser at docs
51
+ `76ed331`: all three artefacts and the receipt (apart from `renderedAt`) were
52
+ byte-identical.
53
+
54
+ ## Fresh lifecycle rehearsal
55
+
56
+ `npm run rehearse:fresh-docs` (2026-11-15, real Podman image
57
+ `sha256:58505eac…`, OpenCode 2.2.6, scripted loopback provider) ran four docs
58
+ slots and a separate exhausted-budget root:
59
+
60
+ | Slot | Primary | Outcome | Selected attempt | Judge requests |
61
+ | --------------- | ------- | ------- | ---------------- | -------------- |
62
+ | useful-write | pass | pass | attempt-001 | 1 |
63
+ | gate-blocked | pass | pass | attempt-001 | 1 |
64
+ | primary-failure | fail | fail | attempt-001 | 1 |
65
+ | recovery | — | pass | attempt-002 | 2 |
66
+
67
+ The recovery's two attempts recorded the same `packetSha256`; the exhausted
68
+ root refused the second dispatch after one claim and wrote no selection.
69
+ Closure, analysis and post-close refusals held. Scripted chat requests: 6
70
+ trial + 5 judge + 1 exhausted; readiness probes: 0; paid provider calls: 0.
71
+
72
+ ## Limits
73
+
74
+ - The two larger corpora are ~56 MB and are not committed; the committed mini
75
+ corpus enforces the same pipeline offline.
76
+ - Retrieval measures require a manifest measurement declaration. The recorded
77
+ manifests declare none, so retrieval-specific parity is covered by the
78
+ ported measures contract tests, not by a recorded retrieval campaign.
79
+ - The frozen published `ANALYSIS-confirmation.json` predates the current
80
+ analysis schema and is not a byte target. The comparison is original
81
+ analyser at the extraction base versus the OTC port on the same evidence.
@@ -0,0 +1,103 @@
1
+ # PR — extract OTC: an isolated test sandbox for OpenCode plugins, skills and instructions
2
+
3
+ ## Summary
4
+
5
+ Adds `@markstanden/opencode-test-container` (`otc`) as a new package:
6
+
7
+ - versioned suite/campaign/subject/runtime contracts with discriminated
8
+ `skill-trigger` and `docs-task` cases;
9
+ - immutable preparation, freeze and delivery checks (resolved image ID, CLI
10
+ version, subject, prompt and product-checker hashes);
11
+ - explicit scheduled execution through Podman and the real OpenCode CLI;
12
+ - product-supplied primary checks executed in their own no-network container,
13
+ never mounted into the trial;
14
+ - an arm-blind docs judge using the established prompt/parse/validate/failure
15
+ classification, with one registered `json-syntax` recovery from identical
16
+ packet bytes and durable per-slot selection;
17
+ - append-only lifecycle ledger, deterministic assessment, idempotent closure,
18
+ read-only status and derived reports;
19
+ - the established docs analysis implementation (recording credit, utility and
20
+ opportunity counts, retrieval, preservation, guardrails, pairing/bounds,
21
+ screening, decision and reporting) with its ported contract suite;
22
+ - deterministic skill-activation evaluator;
23
+ - emitted-JavaScript `otc` bin proven through `npm pack` in a temporary
24
+ consumer;
25
+ - offline real-adapter rehearsals for both the skill/docs baseline and the
26
+ complete fresh docs lifecycle;
27
+ - explicit legacy readers for frozen prod014 schedule/manifest records.
28
+
29
+ ## Design
30
+
31
+ `docs/architecture.md` records the source-to-destination ownership manifest and
32
+ the subject/runtime/suite/campaign/assessment split. `docs/schema.md` records
33
+ the glossary and migration table. Core imports only contracts and adapters; it
34
+ never imports a docs checkout or assumes A/B, eight pairs, a judge or a
35
+ provider. Docs semantics live only in `src/evaluators/docs`.
36
+
37
+ A checker is trusted product data. OTC accepts, stages and executes it; the
38
+ docs repository still owns the case data, truth and checker bytes. The trial
39
+ cannot read the checker, and the judge cannot read the campaign or arm
40
+ identities.
41
+
42
+ ## Evidence-guarantee repair pass
43
+
44
+ Four independently reproduced blockers are closed:
45
+
46
+ - one checked-selection reader is shared by judge, status, closure and report;
47
+ it re-derives attempt identity, packet identity, policy hash, judgement
48
+ SHA-256, outcome/utility and the judged mutation state. A readable `{}` is
49
+ not a judgement and cannot permit closure;
50
+ - judge and recovery claims now share `<root>/evidence`; a recovery settles both
51
+ claims by dispatch outcome, and an interrupted dispatch leaves visible
52
+ `spawned` claims that block closure;
53
+ - the judge's standing permission is read from the pristine prepared workspace,
54
+ never the trial's live `AGENTS.md`; A/B instruction canaries are absent from
55
+ both packets;
56
+ - the checked reader hashes the actual slot and attempt packet bytes, compares
57
+ them with registered identities and with the packet reconstructed from
58
+ current evidence, and reports missing/changed packets across judge, status,
59
+ closure and report;
60
+ - the executing tool identity includes an artifact hash derived from the
61
+ executing module's own `src`/`dist` location and is compared with the freeze
62
+ before `run`, `judge`, `delivery-check` or `status` trusts it; source
63
+ execution beside a build and installed JavaScript execution each fingerprint
64
+ the right tree. Read-only historical replay remains independent of the
65
+ freeze.
66
+
67
+ ## Proof
68
+
69
+ - `npm ci`, `npm test` (552 tests, including packaged CLI), `npm run
70
+ typecheck`, `defined comply` all pass.
71
+ - Coverage populations, all measured on this head:
72
+ - migrated docs analysis keeps the inherited 95/90/95 floors
73
+ (95.6/90.2/95.9);
74
+ - all OTC runtime has the explicit 75/55/80 ratchet (91.6/80.2/93.0);
75
+ - the genuinely new lifecycle/adapters/evaluators population has an explicit
76
+ 82/66/84 ratchet (87.8/71.3/90.0).
77
+ - Recorded-evidence parity: committed mini corpus deep-equal; recorded 40-trial
78
+ confirmation and 48-trial screening corpora byte-identical apart from the
79
+ receipt's `renderedAt`.
80
+ - `npm run rehearse:offline`: real Podman image, OpenCode 2.2.6, scripted
81
+ provider, zero paid calls.
82
+ - `npm run rehearse:fresh-docs`: primary pass/fail, useful and gate-blocked
83
+ writes, malformed-then-valid judge recovery selecting `attempt-002` with
84
+ identical packet bytes, exhausted budget refusal, checked selection,
85
+ closure, analysis and post-close refusal. 6 trial + 5 judge + 1 exhausted
86
+ scripted chat requests, 0 readiness probes, 0 paid calls.
87
+ - Installed tarball runs the complete offline docs lifecycle from an unrelated
88
+ directory with spaces, with no checkout or `tsx`/Bun dependency. The receipt
89
+ records the tarball filename, SHA-256 and npm integrity.
90
+ - The migrated `harness/run.ts` CLI is removed, so no OTC source can spawn the
91
+ docs repository's `placement-harness/harness.mts`. Only product-supplied
92
+ suite and checker bytes remain external data.
93
+
94
+ ## Limits
95
+
96
+ - The judge is a model call when dispatched; it is not deterministic evidence.
97
+ The deterministic assessment remains the criterion.
98
+ - `network: "host"` is documented honestly as host networking, not
99
+ provider-only egress.
100
+ - The two larger recorded parity corpora remain external and are not
101
+ committed; the committed mini corpus enforces the same pipeline.
102
+ - Publishing to npm and creating the remote are external steps; the repository
103
+ is ready to publish.
package/docs/schema.md ADDED
@@ -0,0 +1,97 @@
1
+ # Schema and migration table
2
+
3
+ ## Glossary
4
+
5
+ - **suite** — reusable case definitions plus one assessment kind.
6
+ - **case** — one prompt/context and its expected behaviour.
7
+ - **campaign** — one frozen scheduled execution.
8
+ - **arm** — one treatment configuration.
9
+ - **slot** — one case × arm × repetition.
10
+ - **trial** — execution of a slot.
11
+ - **assessment** — deterministic or model-based evaluation.
12
+ - **judge attempt** — one separately accounted model assessment dispatch.
13
+ - **report** — derived output outside immutable evidence.
14
+
15
+ Canonical fields: `caseId`, `campaignId`, `armId`, `slotId`, `repetitionId`,
16
+ `attemptId`. `repetitionId` is an opaque string; the legacy numeric repetition
17
+ is rendered as `r<N>` only at the adapter boundary.
18
+
19
+ ## Versioned documents
20
+
21
+ | Document | Version | Notes |
22
+ | ------------- | ------- | --------------------------------------------------------------------- |
23
+ | suite | 1 | discriminated `skill-trigger` / `docs-task`; no executable fields |
24
+ | campaign | 1 | explicit slots, runtime, budgets, assessment |
25
+ | manifest | 1 | embedded suite and normalised campaign; no source absolute paths |
26
+ | freeze | 1 | `kind: "otc-freeze"`, immutable IDs, tool identity and `freezeSha256` |
27
+ | assessment | 1 | skill or docs shape; deterministic `attemptId` |
28
+ | primary check | 1 | product checker identity, fixed interpreter and outcome |
29
+ | judgement | 1 | arm-blind judge verdict and its failure class |
30
+ | selection | 1 | per-slot chosen attempt and every attempt fact |
31
+ | report | 1 | derived, `reportSha256` over content |
32
+ | ledger event | 1 | trial, primary-check, assessment, judge and close events |
33
+
34
+ `docs-task` cases may also carry `truth` (arm-blind judge context) and `primaryCheck: { id, directory, script, timeoutMs? }`. The checker directory
35
+ is staged under `prepare/primary-checks/<caseId>`; the fixed contract is
36
+ `node /checker/<script> /live /pristine`. It is never mounted into the trial.
37
+
38
+ A campaign `assessment` may carry `judge: { model, timeoutMs? }` and
39
+ `recovery` (the narrow registration in `analysis/recovery.ts`). Judge and
40
+ recovery budgets count dispatches, not HTTP requests; `providerRequestLimit`
41
+ stays `null` unless an adapter exposes a real request count.
42
+
43
+ The prepared manifest and freeze receipt also carry
44
+ `tool: { name, version, evaluatorVersion, artifactSha256 }`. The artifact hash
45
+ covers the executing runtime tree and package manifest, derived from the
46
+ executing module's own `src`/`dist` location, so source execution beside a
47
+ build and installed JavaScript execution each fingerprint the right tree. `run`, `judge`, `delivery-check` and
48
+ `status` compare the executing identity with the frozen identity before
49
+ dispatch; read-only historical replay does not require it.
50
+
51
+ Judge and recovery claims live only under
52
+ `<root>/evidence/{judge,recovery}`. A recovery settles both of its claims
53
+ according to the dispatch outcome; an interrupted dispatch keeps the claim
54
+ `spawned` and closure refuses it. Status counts `judged`, `pending`, `failed`,
55
+ `invalid` and `unsettled` from the same checked state.
56
+
57
+ A selection is trusted only through the shared checked reader: attempt set,
58
+ packet bytes, policy hash, selected judgement SHA-256, outcome/utility and the
59
+ judged documentation state are re-derived. The slot and attempt `packet.md`
60
+ bytes are hashed and compared with the registered identity, with each other,
61
+ and with the packet reconstructed from current evidence. A readable object
62
+ alone is never a judgement and never permits closure; missing, changed or
63
+ mismatched packets and metadata are explicit integrity diagnostics in judge,
64
+ status, closure and report. Metadata-reading problems from every attempt are
65
+ retained even when other attempts remain readable, so an earlier terminal
66
+ failure never masks an inconsistent later recovery.
67
+
68
+ ## New/legacy names
69
+
70
+ | Legacy | New | Handling |
71
+ | ----------------------- | -------------------------------- | ----------------------------------------------- |
72
+ | `scenario` | `caseId` | mapped by `legacy/prod014.ts`; old id unchanged |
73
+ | `stamp` | `slotId` | the frozen stamp becomes the slot id, unchanged |
74
+ | numeric `repetition` | `repetitionId` | `r<N>` at the adapter boundary |
75
+ | `pairId` | `pairId` (legacy metadata) | retained on `LegacySlot` |
76
+ | `arm` A/B | `armId` | retained, no implicit pair policy |
77
+ | `PLACEMENT_*` | `OTC_*` or typed manifest fields | new code only; no silent alias |
78
+ | `AC_*` | typed manifest fields | new code only |
79
+ | `docs-operations.jsonl` | unchanged | genuine plugin interface |
80
+ | `OPENCODE_*` | unchanged | genuine upstream interface |
81
+
82
+ Conflicting legacy/new environment aliases are not accepted. Frozen evidence
83
+ and historical `.mjs` files are never rewritten by a rename.
84
+
85
+ ## Outcome states
86
+
87
+ - validity: `valid`, `invalid`, `unknown`, `unrun`.
88
+ - outcome: `pass`, `fail`, `unknown`.
89
+ - A missing capture or failed target load is `unknown`/`invalid`, never pass.
90
+ - Docs refused writes retain zero recording credit; attempts are reported.
91
+ - A primary checker that fails makes the criterion fail; a timeout or spawn
92
+ error is `unknown`; a declared checker with no result is `invalid`.
93
+ - Judge failure classes: `json-syntax`, `schema`, `semantic`,
94
+ `empty-or-truncated`, `transport-or-process`. Only a complete `json-syntax`
95
+ failure is recoverable, once, from identical packet bytes.
96
+ - A judge attempt is a durable dispatch, not an HTTP request; request counts
97
+ are reported separately when the adapter exposes them.
@@ -0,0 +1,13 @@
1
+ # Skill-only example
2
+
3
+ A minimal, self-contained skill bundle and suite. It needs no docs plugin and
4
+ no judge. Run it against any image containing the scripted provider route:
5
+
6
+ ```sh
7
+ otc validate --campaign examples/skills/campaign.json
8
+ ```
9
+
10
+ The campaign names `localhost/otc-sandbox:2.2.6`; build it with the arguments
11
+ in `container/README.md`, or replace the image with one you already trust.
12
+ `SCRIPTED_API_KEY` is a placeholder for the offline provider, never a real
13
+ credential.
@@ -0,0 +1,73 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "campaignId": "skill-basic-example",
4
+ "suite": {
5
+ "path": "suite.json"
6
+ },
7
+ "subjects": [
8
+ {
9
+ "armId": "skill-only",
10
+ "skills": [
11
+ {
12
+ "id": "otel-helper",
13
+ "path": "skills/otel-helper"
14
+ }
15
+ ]
16
+ }
17
+ ],
18
+ "schedule": {
19
+ "kind": "explicit",
20
+ "slots": [
21
+ {
22
+ "slotId": "s1",
23
+ "caseId": "positive-instrument",
24
+ "armId": "skill-only",
25
+ "repetitionId": "r0"
26
+ },
27
+ {
28
+ "slotId": "s2",
29
+ "caseId": "negative-unrelated",
30
+ "armId": "skill-only",
31
+ "repetitionId": "r0"
32
+ },
33
+ {
34
+ "slotId": "s3",
35
+ "caseId": "negative-near-miss",
36
+ "armId": "skill-only",
37
+ "repetitionId": "r0"
38
+ }
39
+ ]
40
+ },
41
+ "runtime": {
42
+ "image": "localhost/otc-sandbox:2.2.6",
43
+ "opencodeVersion": "2.2.6",
44
+ "provider": {
45
+ "route": "scripted",
46
+ "model": "scripted/scripted",
47
+ "overlay": {
48
+ "providers": {
49
+ "zai-coding-plan": {
50
+ "settings": {
51
+ "baseURL": "http://127.0.0.1:8799/v1"
52
+ }
53
+ },
54
+ "scripted": {
55
+ "settings": {
56
+ "baseURL": "http://127.0.0.1:8799/v1"
57
+ }
58
+ }
59
+ }
60
+ }
61
+ },
62
+ "timeoutMs": 120000,
63
+ "network": "host",
64
+ "passthroughEnv": ["SCRIPTED_API_KEY"]
65
+ },
66
+ "budgets": {
67
+ "trials": 3,
68
+ "judgeAttempts": 0
69
+ },
70
+ "assessment": {
71
+ "kind": "skill-activation"
72
+ }
73
+ }
@@ -0,0 +1,10 @@
1
+ ---
2
+ name: otel-helper
3
+ description: Use when a task asks for OpenTelemetry span, trace or metric instrumentation in a Node service.
4
+ ---
5
+
6
+ # OpenTelemetry helper
7
+
8
+ Use this skill when the task names OpenTelemetry and asks for instrumentation
9
+ of spans, traces or metrics. Prefer official `@opentelemetry/*` packages, name
10
+ spans after the operation and never invent an exporter endpoint.
@@ -0,0 +1,29 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "suiteId": "skill-basic",
4
+ "description": "Synthetic activation suite for exercising the skill evaluator offline.",
5
+ "assessment": "skill-activation",
6
+ "cases": [
7
+ {
8
+ "caseId": "positive-instrument",
9
+ "kind": "skill-trigger",
10
+ "targetSkillId": "otel-helper",
11
+ "expected": "activation",
12
+ "prompt": "Instrument the checkout service with OpenTelemetry spans."
13
+ },
14
+ {
15
+ "caseId": "negative-unrelated",
16
+ "kind": "skill-trigger",
17
+ "targetSkillId": "otel-helper",
18
+ "expected": "no-activation",
19
+ "prompt": "Rename the local variable total to subtotal."
20
+ },
21
+ {
22
+ "caseId": "negative-near-miss",
23
+ "kind": "skill-trigger",
24
+ "targetSkillId": "otel-helper",
25
+ "expected": "no-activation",
26
+ "prompt": "Add a console log around the payment call; no telemetry is needed."
27
+ }
28
+ ]
29
+ }
package/package.json CHANGED
@@ -1,6 +1,46 @@
1
1
  {
2
- "name": "@markstanden/opencode-test-container",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
2
+ "name": "@markstanden/opencode-test-container",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "An isolated test sandbox for OpenCode plugins, skills and agent instructions.",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/markstanden/opencode-test-container.git"
9
+ },
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "default": "./dist/index.js"
14
+ }
15
+ },
16
+ "bin": {
17
+ "otc": "./dist/cli/otc.js"
18
+ },
19
+ "files": [
20
+ "dist",
21
+ "container",
22
+ "examples",
23
+ "docs",
24
+ "README.md",
25
+ "LICENSE"
26
+ ],
27
+ "engines": {
28
+ "node": ">=26"
29
+ },
30
+ "scripts": {
31
+ "build": "rm -rf dist && tsc -p tsconfig.build.json",
32
+ "prepack": "npm run build",
33
+ "test": "npm run build && node --test 'tests/**/*.test.ts'",
34
+ "test:unit": "node --test 'tests/unit/**/*.test.ts'",
35
+ "test:coverage": "npm run build && node scripts/check-coverage.mjs",
36
+ "typecheck": "tsc --noEmit",
37
+ "rehearse:offline": "npm run build && node rehearsals/offline.mts",
38
+ "rehearse:fresh-docs": "npm run build && node rehearsals/fresh-docs.mts",
39
+ "rehearse:installed": "npm run build && node rehearsals/installed.mts"
40
+ },
41
+ "devDependencies": {
42
+ "@types/node": "^26.6.4",
43
+ "typescript": "^6.0.3"
44
+ },
45
+ "license": "MIT"
46
+ }