@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.
- package/LICENSE +21 -0
- package/README.md +263 -2
- package/container/Containerfile +24 -0
- package/container/README.md +29 -0
- package/dist/adapters/opencode/events.d.ts +90 -0
- package/dist/adapters/opencode/events.js +213 -0
- package/dist/adapters/opencode/runtime.d.ts +87 -0
- package/dist/adapters/opencode/runtime.js +136 -0
- package/dist/adapters/podman/container.d.ts +62 -0
- package/dist/adapters/podman/container.js +119 -0
- package/dist/adapters/podman/image.d.ts +50 -0
- package/dist/adapters/podman/image.js +124 -0
- package/dist/adapters/process.d.ts +38 -0
- package/dist/adapters/process.js +71 -0
- package/dist/campaign/assess.d.ts +45 -0
- package/dist/campaign/assess.js +208 -0
- package/dist/campaign/close.d.ts +17 -0
- package/dist/campaign/close.js +112 -0
- package/dist/campaign/evidence-guard.d.ts +24 -0
- package/dist/campaign/evidence-guard.js +49 -0
- package/dist/campaign/freeze.d.ts +94 -0
- package/dist/campaign/freeze.js +297 -0
- package/dist/campaign/inventory.d.ts +62 -0
- package/dist/campaign/inventory.js +192 -0
- package/dist/campaign/judge.d.ts +209 -0
- package/dist/campaign/judge.js +1285 -0
- package/dist/campaign/ledger.d.ts +64 -0
- package/dist/campaign/ledger.js +149 -0
- package/dist/campaign/prepare.d.ts +129 -0
- package/dist/campaign/prepare.js +483 -0
- package/dist/campaign/primary.d.ts +96 -0
- package/dist/campaign/primary.js +189 -0
- package/dist/campaign/run.d.ts +151 -0
- package/dist/campaign/run.js +562 -0
- package/dist/campaign/status.d.ts +76 -0
- package/dist/campaign/status.js +237 -0
- package/dist/cli/otc.d.ts +51 -0
- package/dist/cli/otc.js +435 -0
- package/dist/contracts/canonical.d.ts +21 -0
- package/dist/contracts/canonical.js +44 -0
- package/dist/contracts/types.d.ts +261 -0
- package/dist/contracts/types.js +9 -0
- package/dist/contracts/validate.d.ts +72 -0
- package/dist/contracts/validate.js +1021 -0
- package/dist/contracts/version.d.ts +44 -0
- package/dist/contracts/version.js +147 -0
- package/dist/evaluators/docs/analysis/accounting.d.ts +140 -0
- package/dist/evaluators/docs/analysis/accounting.js +237 -0
- package/dist/evaluators/docs/analysis/blinding.d.ts +40 -0
- package/dist/evaluators/docs/analysis/blinding.js +80 -0
- package/dist/evaluators/docs/analysis/bounds.d.ts +40 -0
- package/dist/evaluators/docs/analysis/bounds.js +31 -0
- package/dist/evaluators/docs/analysis/budget.d.ts +137 -0
- package/dist/evaluators/docs/analysis/budget.js +305 -0
- package/dist/evaluators/docs/analysis/closure.d.ts +175 -0
- package/dist/evaluators/docs/analysis/closure.js +251 -0
- package/dist/evaluators/docs/analysis/decision.d.ts +59 -0
- package/dist/evaluators/docs/analysis/decision.js +161 -0
- package/dist/evaluators/docs/analysis/freeze.d.ts +201 -0
- package/dist/evaluators/docs/analysis/freeze.js +395 -0
- package/dist/evaluators/docs/analysis/guardrails.d.ts +51 -0
- package/dist/evaluators/docs/analysis/guardrails.js +194 -0
- package/dist/evaluators/docs/analysis/journal.d.ts +150 -0
- package/dist/evaluators/docs/analysis/journal.js +475 -0
- package/dist/evaluators/docs/analysis/ledger.d.ts +78 -0
- package/dist/evaluators/docs/analysis/ledger.js +145 -0
- package/dist/evaluators/docs/analysis/measures.d.ts +302 -0
- package/dist/evaluators/docs/analysis/measures.js +1069 -0
- package/dist/evaluators/docs/analysis/model.d.ts +236 -0
- package/dist/evaluators/docs/analysis/model.js +740 -0
- package/dist/evaluators/docs/analysis/pairing.d.ts +68 -0
- package/dist/evaluators/docs/analysis/pairing.js +95 -0
- package/dist/evaluators/docs/analysis/records.d.ts +229 -0
- package/dist/evaluators/docs/analysis/records.js +935 -0
- package/dist/evaluators/docs/analysis/recovery.d.ts +225 -0
- package/dist/evaluators/docs/analysis/recovery.js +507 -0
- package/dist/evaluators/docs/analysis/render.d.ts +235 -0
- package/dist/evaluators/docs/analysis/render.js +566 -0
- package/dist/evaluators/docs/analysis/replay.d.ts +15 -0
- package/dist/evaluators/docs/analysis/replay.js +1272 -0
- package/dist/evaluators/docs/analysis/screening/inventory.d.ts +136 -0
- package/dist/evaluators/docs/analysis/screening/inventory.js +307 -0
- package/dist/evaluators/docs/analysis/screening/noteVersion.d.ts +15 -0
- package/dist/evaluators/docs/analysis/screening/noteVersion.js +46 -0
- package/dist/evaluators/docs/analysis/screening/reconcile.d.ts +38 -0
- package/dist/evaluators/docs/analysis/screening/reconcile.js +40 -0
- package/dist/evaluators/docs/analysis/screening/rules.d.ts +163 -0
- package/dist/evaluators/docs/analysis/screening/rules.js +330 -0
- package/dist/evaluators/docs/analysis/screening/scan.d.ts +72 -0
- package/dist/evaluators/docs/analysis/screening/scan.js +362 -0
- package/dist/evaluators/docs/analysis/selector.d.ts +68 -0
- package/dist/evaluators/docs/analysis/selector.js +123 -0
- package/dist/evaluators/docs/assess.d.ts +129 -0
- package/dist/evaluators/docs/assess.js +433 -0
- package/dist/evaluators/docs/campaign.d.ts +133 -0
- package/dist/evaluators/docs/campaign.js +222 -0
- package/dist/evaluators/docs/index.d.ts +25 -0
- package/dist/evaluators/docs/index.js +25 -0
- package/dist/evaluators/docs/journal.d.ts +150 -0
- package/dist/evaluators/docs/journal.js +475 -0
- package/dist/evaluators/docs/scoring.d.ts +367 -0
- package/dist/evaluators/docs/scoring.js +662 -0
- package/dist/evaluators/skills/assess.d.ts +94 -0
- package/dist/evaluators/skills/assess.js +207 -0
- package/dist/index.d.ts +46 -0
- package/dist/index.js +29 -0
- package/dist/legacy/prod014.d.ts +86 -0
- package/dist/legacy/prod014.js +209 -0
- package/dist/reporting/report.d.ts +79 -0
- package/dist/reporting/report.js +242 -0
- package/docs/architecture.md +106 -0
- package/docs/capability-matrix.md +84 -0
- package/docs/offline-rehearsal.md +75 -0
- package/docs/parity-record.md +81 -0
- package/docs/pr-description.md +103 -0
- package/docs/schema.md +97 -0
- package/examples/skills/README.md +13 -0
- package/examples/skills/campaign.json +73 -0
- package/examples/skills/skills/otel-helper/SKILL.md +10 -0
- package/examples/skills/suite.json +29 -0
- 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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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
|
+
}
|