@markstanden/opencode-test-container 0.0.0-stage → 0.1.1

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 (123) 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/release-0.1.0.md +52 -0
  117. package/docs/releasing.md +82 -0
  118. package/docs/schema.md +97 -0
  119. package/examples/skills/README.md +13 -0
  120. package/examples/skills/campaign.json +73 -0
  121. package/examples/skills/skills/otel-helper/SKILL.md +10 -0
  122. package/examples/skills/suite.json +29 -0
  123. package/package.json +45 -5
@@ -0,0 +1,64 @@
1
+ /** The finite ledger event names the lifecycle understands. */
2
+ export declare const LEDGER_EVENTS: readonly ["trial-start", "trial-end", "primary-check", "assessment", "judge-attempt", "judge-selection", "close"];
3
+ /** One ledger event name. */
4
+ export type LedgerEventName = (typeof LEDGER_EVENTS)[number];
5
+ /** An append-only lifecycle event. */
6
+ export interface LedgerEvent {
7
+ /** The schema version value. */
8
+ schemaVersion: 1;
9
+ /** The event value. */
10
+ event: LedgerEventName;
11
+ /** The at value. */
12
+ at: string;
13
+ /** The campaign id value. */
14
+ campaignId: string;
15
+ [field: string]: unknown;
16
+ }
17
+ /** The readable ledger state: absence, open, closed or malformed. */
18
+ export interface LedgerRead {
19
+ /** The state value. */
20
+ state: "absent" | "open" | "closed" | "malformed";
21
+ /** The events value. */
22
+ events: LedgerEvent[];
23
+ /** The problems value. */
24
+ problems: string[];
25
+ /** The closed at value. */
26
+ closedAt: string | null;
27
+ }
28
+ /** Path of the append-only attempts ledger. */
29
+ export declare function ledgerPath(root: string): string;
30
+ /**
31
+ * Parse ledger text without trusting it. Missing is an explicit state, and a
32
+ * malformed line poisons the ledger: no later event is credited.
33
+ * @param text The exact ledger bytes, or undefined when absent.
34
+ * @returns The parse result.
35
+ */
36
+ export declare function parseLedger(text: string | undefined): LedgerRead;
37
+ /**
38
+ * Read the ledger from a campaign root.
39
+ * @param root The campaign root.
40
+ * @returns The parse result.
41
+ */
42
+ export declare function readLedger(root: string): LedgerRead;
43
+ /**
44
+ * Append one event. The caller owns the decision that the ledger is open;
45
+ * this helper only writes a complete line.
46
+ * @param root The campaign root.
47
+ * @param event The event without the stamp.
48
+ * @param at The event time; defaults to now.
49
+ * @returns The written event.
50
+ */
51
+ export declare function appendLedgerEvent(root: string, event: {
52
+ event: LedgerEventName;
53
+ campaignId: string;
54
+ } & Record<string, unknown>, at?: string): LedgerEvent;
55
+ /** Count the trial launches recorded in a ledger. */
56
+ export declare function trialStarts(events: readonly LedgerEvent[]): LedgerEvent[];
57
+ /** Count the durable assessments recorded in a ledger. */
58
+ export declare function assessmentEvents(events: readonly LedgerEvent[]): LedgerEvent[];
59
+ /** The slot ids that have started, in order. */
60
+ export declare function startedSlots(events: readonly LedgerEvent[]): string[];
61
+ /** Why a launch is refused by ledger/budget state, or null when permitted. */
62
+ export declare function launchRefusal(read: LedgerRead, budget: {
63
+ trials: number;
64
+ }): string | null;
@@ -0,0 +1,149 @@
1
+ import { appendFileSync, existsSync, mkdirSync, readFileSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import { isRecord } from "../contracts/validate.js";
4
+ /** The finite ledger event names the lifecycle understands. */
5
+ export const LEDGER_EVENTS = [
6
+ "trial-start",
7
+ "trial-end",
8
+ "primary-check",
9
+ "assessment",
10
+ "judge-attempt",
11
+ "judge-selection",
12
+ "close",
13
+ ];
14
+ /** Path of the append-only attempts ledger. */
15
+ export function ledgerPath(root) {
16
+ return join(root, "evidence", "ATTEMPTS.jsonl");
17
+ }
18
+ /** Whether a value names a known event. */
19
+ function isEventName(value) {
20
+ return (typeof value === "string" &&
21
+ LEDGER_EVENTS.includes(value));
22
+ }
23
+ /** Parse one non-blank ledger line into an event, or a problem. */
24
+ function parseEventLine(line, index) {
25
+ let parsed;
26
+ try {
27
+ parsed = JSON.parse(line);
28
+ }
29
+ catch {
30
+ return { ok: false, problem: `line ${index + 1} is not JSON` };
31
+ }
32
+ if (!isRecord(parsed)) {
33
+ return { ok: false, problem: `line ${index + 1} is not an object` };
34
+ }
35
+ if (parsed.schemaVersion !== 1) {
36
+ return {
37
+ ok: false,
38
+ problem: `line ${index + 1} has an unsupported schemaVersion`,
39
+ };
40
+ }
41
+ if (!isEventName(parsed.event)) {
42
+ return { ok: false, problem: `line ${index + 1} has an unknown event` };
43
+ }
44
+ if (typeof parsed.at !== "string" ||
45
+ typeof parsed.campaignId !== "string") {
46
+ return {
47
+ ok: false,
48
+ problem: `line ${index + 1} lacks at/campaignId`,
49
+ };
50
+ }
51
+ return { ok: true, event: parsed };
52
+ }
53
+ /**
54
+ * Parse ledger text without trusting it. Missing is an explicit state, and a
55
+ * malformed line poisons the ledger: no later event is credited.
56
+ * @param text The exact ledger bytes, or undefined when absent.
57
+ * @returns The parse result.
58
+ */
59
+ export function parseLedger(text) {
60
+ if (text === undefined) {
61
+ return { state: "absent", events: [], problems: [], closedAt: null };
62
+ }
63
+ const events = [];
64
+ const problems = [];
65
+ for (const [index, line] of text.split("\n").entries()) {
66
+ if (line.trim() === "")
67
+ continue;
68
+ const parsed = parseEventLine(line, index);
69
+ if (parsed.ok)
70
+ events.push(parsed.event);
71
+ else
72
+ problems.push(parsed.problem);
73
+ }
74
+ if (problems.length > 0) {
75
+ return { state: "malformed", events, problems, closedAt: null };
76
+ }
77
+ const closure = events.filter((event) => event.event === "close").at(-1);
78
+ return {
79
+ state: closure === undefined ? "open" : "closed",
80
+ events,
81
+ problems: [],
82
+ closedAt: closure?.at ?? null,
83
+ };
84
+ }
85
+ /**
86
+ * Read the ledger from a campaign root.
87
+ * @param root The campaign root.
88
+ * @returns The parse result.
89
+ */
90
+ export function readLedger(root) {
91
+ const path = ledgerPath(root);
92
+ if (!existsSync(path))
93
+ return parseLedger(undefined);
94
+ try {
95
+ return parseLedger(readFileSync(path, "utf8"));
96
+ }
97
+ catch (error) {
98
+ return {
99
+ state: "malformed",
100
+ events: [],
101
+ problems: [
102
+ `ledger is unreadable: ${error instanceof Error ? error.message : String(error)}`,
103
+ ],
104
+ closedAt: null,
105
+ };
106
+ }
107
+ }
108
+ /**
109
+ * Append one event. The caller owns the decision that the ledger is open;
110
+ * this helper only writes a complete line.
111
+ * @param root The campaign root.
112
+ * @param event The event without the stamp.
113
+ * @param at The event time; defaults to now.
114
+ * @returns The written event.
115
+ */
116
+ export function appendLedgerEvent(root, event, at = new Date().toISOString()) {
117
+ const path = ledgerPath(root);
118
+ mkdirSync(dirname(path), { recursive: true });
119
+ const written = { schemaVersion: 1, ...event, at };
120
+ appendFileSync(path, `${JSON.stringify(written)}\n`, "utf8");
121
+ return written;
122
+ }
123
+ /** Count the trial launches recorded in a ledger. */
124
+ export function trialStarts(events) {
125
+ return events.filter((event) => event.event === "trial-start");
126
+ }
127
+ /** Count the durable assessments recorded in a ledger. */
128
+ export function assessmentEvents(events) {
129
+ return events.filter((event) => event.event === "assessment");
130
+ }
131
+ /** The slot ids that have started, in order. */
132
+ export function startedSlots(events) {
133
+ return trialStarts(events)
134
+ .map((event) => typeof event.slotId === "string" ? event.slotId : null)
135
+ .filter((slot) => slot !== null);
136
+ }
137
+ /** Why a launch is refused by ledger/budget state, or null when permitted. */
138
+ export function launchRefusal(read, budget) {
139
+ if (read.state === "malformed") {
140
+ return `attempt ledger is malformed: ${read.problems.join("; ")}`;
141
+ }
142
+ if (read.state === "closed") {
143
+ return "campaign is closed; no further dispatch is permitted";
144
+ }
145
+ if (trialStarts(read.events).length >= budget.trials) {
146
+ return `trial budget exhausted (${budget.trials})`;
147
+ }
148
+ return null;
149
+ }
@@ -0,0 +1,129 @@
1
+ import type { Campaign, CampaignBundle, Suite } from "../contracts/types.ts";
2
+ import { type Inventory } from "./inventory.ts";
3
+ import { type ToolIdentity } from "../contracts/version.ts";
4
+ /** A staged arm: every source path is now a root-relative payload. */
5
+ export interface StagedArm {
6
+ /** The arm id value. */
7
+ armId: string;
8
+ /** The skills value. */
9
+ skills: Array<{
10
+ id: string;
11
+ path: string;
12
+ }>;
13
+ /** The plugins value. */
14
+ plugins: Array<{
15
+ id: string;
16
+ path: string;
17
+ options?: unknown;
18
+ }>;
19
+ /** The instructions value. */
20
+ instructions: Array<{
21
+ id: string;
22
+ path: string;
23
+ as: string;
24
+ }>;
25
+ /** The inventory value. */
26
+ inventory: Inventory;
27
+ }
28
+ /** One prepared case: prompt and pristine workspace only. */
29
+ export interface PreparedCase {
30
+ /** The case id value. */
31
+ caseId: string;
32
+ /** The prompt sha256 value. */
33
+ promptSha256: string;
34
+ /** The workspace value. */
35
+ workspace: Inventory;
36
+ /** The staged product primary check, when the case declares one. */
37
+ primaryCheck?: {
38
+ id: string;
39
+ /** Root-relative staged directory. */
40
+ directory: string;
41
+ script: string;
42
+ timeoutMs: number;
43
+ inventory: Inventory;
44
+ };
45
+ }
46
+ /** The immutable record of everything preparation staged. */
47
+ export interface PreparedManifest {
48
+ /** The schema version value. */
49
+ schemaVersion: 1;
50
+ /** The campaign id value. */
51
+ campaignId: string;
52
+ /** The suite id value. */
53
+ suiteId: string;
54
+ /** The campaign sha256 value. */
55
+ campaignSha256: string;
56
+ /** The suite sha256 value. */
57
+ suiteSha256: string;
58
+ /** The suite canonical sha256 value. */
59
+ suiteCanonicalSha256: string;
60
+ /** The schedule sha256 value. */
61
+ scheduleSha256: string;
62
+ /** The subjects sha256 value. */
63
+ subjectsSha256: string;
64
+ /** The workspaces sha256 value. */
65
+ workspacesSha256: string;
66
+ /** The prompts sha256 value. */
67
+ promptsSha256: string;
68
+ /** The product primary-check payloads sha256; empty object when none. */
69
+ primaryChecksSha256: string;
70
+ /** The artefact that prepared this manifest. */
71
+ tool: ToolIdentity;
72
+ /** The suite value. */
73
+ suite: Suite;
74
+ /** The campaign value. */
75
+ campaign: Campaign;
76
+ /** The subjects value. */
77
+ subjects: StagedArm[];
78
+ /** The cases value. */
79
+ cases: PreparedCase[];
80
+ }
81
+ /** The staging result, or the problems that prevented it. */
82
+ export type PrepareResult = {
83
+ ok: true;
84
+ root: string;
85
+ manifest: PreparedManifest;
86
+ } | {
87
+ ok: false;
88
+ problems: string[];
89
+ };
90
+ /**
91
+ * A campaign with source paths erased: identities are staged content, not the
92
+ * developer's checkout paths.
93
+ * @param bundle The validated campaign and suite.
94
+ * @returns The normalised campaign stored in the manifest.
95
+ */
96
+ export declare function normaliseCampaign(bundle: CampaignBundle): Campaign;
97
+ /** The default plugin members staged for the runtime. */
98
+ export declare const DEFAULT_PLUGIN_INCLUDE: readonly ["package.json", "package-lock.json", "src", "dist", "node_modules", "CONVENTION.md", "README.md", "LICENSE"];
99
+ /**
100
+ * Validate a campaign, then stage an immutable root: suite, prompts, pristine
101
+ * workspaces and subject payloads. Truth never enters a workspace.
102
+ * @param campaignPath The campaign document.
103
+ * @param root The fresh campaign root.
104
+ * @returns The prepared manifest, or actionable problems.
105
+ */
106
+ export declare function prepareCampaign(campaignPath: string, root: string): PrepareResult;
107
+ /** The hash of a prepared manifest's canonical content. */
108
+ export declare function manifestSha256(manifest: PreparedManifest): string;
109
+ /** Read the prepared manifest. */
110
+ export declare function loadPreparedManifest(root: string): {
111
+ ok: true;
112
+ manifest: PreparedManifest;
113
+ } | {
114
+ ok: false;
115
+ problem: string;
116
+ };
117
+ /**
118
+ * Recheck every staged payload against the prepared manifest.
119
+ * @param root The campaign root.
120
+ * @param manifest The prepared manifest.
121
+ * @returns Differences; empty means the payload is intact.
122
+ */
123
+ export declare function preparedDifferences(root: string, manifest: PreparedManifest): string[];
124
+ /** The canonical text of a prepared manifest, for receipts and displays. */
125
+ export declare function manifestText(manifest: PreparedManifest): string;
126
+ /** A human-readable staging summary. */
127
+ export declare function prepareSummary(manifest: PreparedManifest): string;
128
+ /** A path's basename, used in diagnostics without exposing an absolute path. */
129
+ export declare function displayPath(path: string): string;