@skill-harness/cli 0.26.2 → 0.28.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 (2) hide show
  1. package/dist/decision.js +98 -9
  2. package/package.json +4 -4
package/dist/decision.js CHANGED
@@ -716,6 +716,88 @@ async function writePreparedExport({ prepared, directory }) {
716
716
  return { directory, trainingEligible: prepared.metadata.trainingEligible, files: Object.fromEntries(Object.entries(prepared.files).map(([name, s]) => [name, { sha256: learningDigest(s), bytes: Buffer.byteLength(s) }])) };
717
717
  }
718
718
 
719
+ // experiments/decision-shadow/workflow-fixtures.mjs
720
+ var WORKFLOW_ORACLE_VERSION = "workflow-convergence-oracles-v1";
721
+ function evaluateWorkflowFixture(family, facts) {
722
+ if (!facts || typeof facts !== "object" || Array.isArray(facts)) return false;
723
+ switch (family) {
724
+ case "workspace":
725
+ return typeof facts.requestedRoot === "string" && facts.requestedRoot.startsWith("/") && facts.observedRoot === facts.requestedRoot && facts.pathsObserved === true && Array.isArray(facts.requiredPaths) && facts.requiredPaths.length > 0 && Array.isArray(facts.readablePaths) && facts.requiredPaths.every((path) => typeof path === "string" && path.startsWith(facts.requestedRoot + "/") && facts.readablePaths.includes(path));
726
+ case "identity":
727
+ return facts.expected?.algorithm === "principal-candidate-v1" && facts.actual?.algorithm === facts.expected.algorithm && typeof facts.expected.id === "string" && /^[a-f0-9]{64}$/.test(facts.expected.id) && facts.actual.id === facts.expected.id;
728
+ case "evidence":
729
+ return facts.candidateMatched === true && facts.referencesVerified === true && Array.isArray(facts.obligations) && facts.obligations.some((item) => item?.due === "review") && facts.obligations.every((item) => item && ["review", "finish"].includes(item.due) && typeof item.id === "string" && item.id.length > 0 && (item.due === "review" ? item.state === "verified" : ["pending", "verified"].includes(item.state)));
730
+ case "routing": {
731
+ if (facts.observationComplete !== true || !Array.isArray(facts.productFindings) || !Array.isArray(facts.dueEvidenceGaps)) return false;
732
+ const expected = facts.productFindings.length ? "build" : facts.dueEvidenceGaps.length ? "evidence" : "git-ops";
733
+ return facts.next === expected;
734
+ }
735
+ case "reuse":
736
+ return facts.operation === "static-inspection" && facts.purpose === "same-check" && facts.previous?.state === "complete" && facts.previous.outputsVerified === true && typeof facts.currentInput === "string" && /^[a-f0-9]{64}$/.test(facts.currentInput) && facts.previous.input === facts.currentInput && facts.previous.environment === facts.currentEnvironment && typeof facts.currentEnvironment === "string" && facts.currentEnvironment.length > 0;
737
+ default:
738
+ throw new TypeError("Unknown workflow fixture family");
739
+ }
740
+ }
741
+ function workflowFixtureFamilies() {
742
+ const root = "/fixture/pilot", hash3 = "a".repeat(64);
743
+ const workspace = {
744
+ requestedRoot: root,
745
+ observedRoot: root,
746
+ pathsObserved: true,
747
+ requiredPaths: [`${root}/report.md`],
748
+ readablePaths: [`${root}/report.md`]
749
+ };
750
+ const identity2 = {
751
+ expected: { algorithm: "principal-candidate-v1", id: hash3 },
752
+ actual: { algorithm: "principal-candidate-v1", id: hash3 }
753
+ };
754
+ const evidence = {
755
+ candidateMatched: true,
756
+ referencesVerified: true,
757
+ obligations: [{ id: "behavior-check", due: "review", state: "verified" }, { id: "archive", due: "finish", state: "pending" }]
758
+ };
759
+ const routing = { observationComplete: true, productFindings: [], dueEvidenceGaps: ["missing-report"], next: "evidence" };
760
+ const reuse = {
761
+ operation: "static-inspection",
762
+ purpose: "same-check",
763
+ currentInput: hash3,
764
+ currentEnvironment: "node-fixture-v1",
765
+ previous: { state: "complete", input: hash3, environment: "node-fixture-v1", outputsVerified: true }
766
+ };
767
+ return [
768
+ {
769
+ family: "workspace",
770
+ split: "train",
771
+ question: "Do these explicitly observed roots and readable absolute paths establish access to the requested fixture workspace? Missing access evidence is not a permission-denial finding.",
772
+ facts: [workspace, { ...workspace, observedRoot: "/fixture/original" }, { ...workspace, readablePaths: [] }, { ...workspace, pathsObserved: false }]
773
+ },
774
+ {
775
+ family: "identity",
776
+ split: "train",
777
+ question: "Do these records contain matching full candidate IDs under the same principal-candidate-v1 algorithm? A diff hash or another algorithm is not an interchangeable candidate ID.",
778
+ facts: [identity2, { ...identity2, actual: { algorithm: "git-diff-sha256", id: hash3 } }, { ...identity2, actual: { algorithm: "principal-candidate-v1", id: "b".repeat(64) } }, { expected: { algorithm: "principal-candidate-v1", id: "aaaaaaa" }, actual: { algorithm: "principal-candidate-v1", id: "aaaaaaa" } }]
779
+ },
780
+ {
781
+ family: "evidence",
782
+ split: "validation",
783
+ question: "Are all review-due obligations verified by matching-candidate, verified references? Finish-due obligations may remain pending; a new runtime replay is not required by this fixture rule.",
784
+ facts: [evidence, { ...evidence, obligations: [{ id: "behavior-check", due: "review", state: "pending" }] }, { ...evidence, candidateMatched: false }, { ...evidence, referencesVerified: false }]
785
+ },
786
+ {
787
+ family: "routing",
788
+ split: "test",
789
+ question: "Does Next follow this explicit routing rule: observed product findings -> build, otherwise due evidence gaps -> evidence, otherwise git-ops? Incomplete observations cannot establish routing.",
790
+ facts: [routing, { ...routing, next: "build" }, { ...routing, productFindings: ["blocked-api-shutdown"], next: "build" }, { ...routing, observationComplete: false }]
791
+ },
792
+ {
793
+ family: "reuse",
794
+ split: "test",
795
+ question: "May this completed static inspection be reused for the same check, unchanged input/environment and verified outputs? A new independent review or live runtime observation requires a distinct operation.",
796
+ facts: [reuse, { ...reuse, previous: { ...reuse.previous, outputsVerified: false } }, { ...reuse, purpose: "independent-review" }, { ...reuse, operation: "live-database-observation" }]
797
+ }
798
+ ];
799
+ }
800
+
719
801
  // experiments/decision-shadow/learning-fixtures.mjs
720
802
  var FIXTURE_ORACLE_VERSION = "mechanical-fixture-oracles-v1";
721
803
  var COMMIT = /^[a-f0-9]{40}$/;
@@ -756,9 +838,10 @@ function evaluateFixture(family, e) {
756
838
  throw new TypeError("Unknown fixture oracle family");
757
839
  }
758
840
  }
759
- function createLearningFixtures({ recordedAt = "2026-10-08T00:00:00.000Z" } = {}) {
841
+ function createLearningFixtures({ recordedAt = "2026-10-08T00:00:00.000Z", set = "mechanical" } = {}) {
842
+ if (!["mechanical", "workflow"].includes(set)) throw new TypeError("Fixture set must be mechanical or workflow");
760
843
  const commit = "a".repeat(40), complete = Object.fromEntries(PHASES.map((p) => [p, "complete"]));
761
- const families = [
844
+ const mechanicalFamilies = [
762
845
  { family: "candidate", split: "train", question: "Do the records establish one exact full 40-character lowercase Git candidate shared by candidate, tested and reviewed?", facts: [{ candidate: commit, tested: commit, reviewed: commit }, { candidate: commit, tested: "b".repeat(40), reviewed: commit }, { candidate: commit, tested: commit, reviewed: "c".repeat(40) }, { candidate: "aaaaaaa", tested: "aaaaaaa", reviewed: "aaaaaaa" }] },
763
846
  { family: "phases", split: "train", question: "Do the latest snapshots cover exactly every required step with all five named phases complete? Earlier complete phases cannot fill later omissions.", facts: [{ required: ["step-1"], records: [{ step: "step-1", phases: complete }] }, { required: ["step-1", "step-2"], records: [{ step: "step-1", phases: complete }] }, { required: ["step-1"], records: [{ step: "step-1", phases: complete }, { step: "step-extra", phases: complete }] }, { required: ["step-1"], records: [{ step: "step-1", phases: complete }, { step: "step-1", phases: { ...complete, verified: "unknown" } }] }] },
764
847
  { family: "findings", split: "train", question: "Does the observed finding history establish that every finding identity (step, source, id) has an explicit latest verified disposition? Omission never clears an earlier finding.", facts: [{ historyObserved: true, records: [{ step: "step-1", findings: [] }] }, { historyObserved: true, records: [{ step: "step-1", findings: [{ id: "f1", source: "report-a", status: "open" }] }, { step: "step-1", findings: [{ id: "f1", source: "report-a", status: "verified" }] }] }, { historyObserved: true, records: [{ step: "step-1", findings: [{ id: "f1", source: "report-a", status: "open" }] }, { step: "step-1", findings: [] }] }, { historyObserved: true, records: [{ step: "step-1", findings: [{ id: "f1", source: "report-a", status: "open" }] }, { step: "step-1", findings: [{ id: "f1", source: "report-b", status: "verified" }] }] }] },
@@ -766,15 +849,20 @@ function createLearningFixtures({ recordedAt = "2026-10-08T00:00:00.000Z" } = {}
766
849
  { family: "report", split: "test", question: "Does this structured requested delivery comply with the selected saved-full-report/five-line-summary protocol, including its explicit unavailable-persistence blocked exception?", facts: [{ protocol: "saved-full-report-and-five-line-summary", persistence: "available", requestedDelivery: "saved-full-report-and-five-line-summary", reportReference: "required" }, { protocol: "saved-full-report-and-five-line-summary", persistence: "available", requestedDelivery: "complete-report-only-in-final", reportReference: "forbidden" }, { protocol: "saved-full-report-and-five-line-summary", persistence: "available", requestedDelivery: "saved-full-report-and-five-line-summary", reportReference: "omitted" }, { protocol: "saved-full-report-and-five-line-summary", persistence: "unavailable", requestedDelivery: "blocked-no-invented-report", reportReference: "not-saved" }] },
767
850
  { family: "cleanup", split: "test", question: "Do these records establish settled cleanup with a matching execution identity and reapedAll true? This does not establish task success or approval.", facts: [{ executionId: "exec:one", state: "settled", receipt: { state: "settled", executionId: "exec:one", reapedAll: true } }, { executionId: "exec:one", state: "settled", receipt: null }, { executionId: "exec:one", state: "settled", receipt: { state: "settled", executionId: "exec:other", reapedAll: true } }, { executionId: "exec:one", state: "settled", receipt: { state: "settled", executionId: "exec:one", reapedAll: false } }] }
768
851
  ];
852
+ const families = set === "workflow" ? workflowFixtureFamilies() : mechanicalFamilies;
853
+ const evaluate = set === "workflow" ? evaluateWorkflowFixture : evaluateFixture;
854
+ const oracleVersion = set === "workflow" ? WORKFLOW_ORACLE_VERSION : FIXTURE_ORACLE_VERSION;
855
+ const experimentId = set === "workflow" ? "workflow-convergence-20-v1" : "mechanical-fixtures-24-v1";
856
+ const scope = set === "workflow" ? "20 synthetic workflow convergence cases; no observed sessions or model-quality claims." : "24 synthetic mechanical plumbing cases; no observed examples or model-quality claims.";
769
857
  const caseDocument = { schema: 1, cases: families.flatMap((f) => f.facts.map((facts, i) => {
770
858
  const input = JSON.stringify(facts);
771
- return { id: `fixture-${f.family}-${i + 1}`, input, question: f.question, provenance: "synthetic", source: { sha256: learningDigest(input), recordId: `${FIXTURE_ORACLE_VERSION}/${f.family}/${i + 1}` }, visibility: "public" };
859
+ return { id: `fixture-${f.family}-${i + 1}`, input, question: f.question, provenance: "synthetic", source: { sha256: learningDigest(input), recordId: `${oracleVersion}/${f.family}/${i + 1}` }, visibility: "public" };
772
860
  })) };
773
861
  const cases = parseCases(caseDocument), receipts = [], labels = [], entries = [];
774
862
  for (const c of cases) {
775
863
  const family = c.id.split("-")[1], f = families.find((f2) => f2.family === family);
776
- const value = evaluateFixture(family, JSON.parse(c.input));
777
- const receipt = { schema: 1, kind: "independent-decision-label", caseId: c.id, caseHash: c.hash, source: c.source, value, labelKind: "test", actor: FIXTURE_ORACLE_VERSION, independent: true, recordedAt, method: { kind: "deterministic-test", id: family, version: FIXTURE_ORACLE_VERSION + ":" + learningDigest(evaluateFixture.toString()) } };
864
+ const value = evaluate(family, JSON.parse(c.input));
865
+ const receipt = { schema: 1, kind: "independent-decision-label", caseId: c.id, caseHash: c.hash, source: c.source, value, labelKind: "test", actor: oracleVersion, independent: true, recordedAt, method: { kind: "deterministic-test", id: family, version: oracleVersion + ":" + learningDigest(evaluate.toString()) } };
778
866
  const bytes = JSON.stringify(receipt, null, 2) + "\n";
779
867
  receipts.push({ caseId: c.id, filename: c.id + "-label.json", bytes, sha256: learningDigest(bytes) });
780
868
  labels.push({ caseId: c.id, caseHash: c.hash, value, kind: "test", actor: receipt.actor, evidenceSha256: learningDigest(bytes), independent: true });
@@ -782,8 +870,8 @@ function createLearningFixtures({ recordedAt = "2026-10-08T00:00:00.000Z" } = {}
782
870
  }
783
871
  const labelDocument = { schema: 1, labels };
784
872
  parseLabels(labelDocument, cases);
785
- const manifest = parseExperiment(cases, { schema: 1, kind: "decision-learning-experiment", id: "mechanical-fixtures-24-v1", frozenAt: recordedAt, entries });
786
- return { caseDocument, labelDocument, manifest, labelReceipts: receipts, fixtureOnly: true, trainingEligible: false, scope: "24 synthetic mechanical plumbing cases; no observed examples or model-quality claims." };
873
+ const manifest = parseExperiment(cases, { schema: 1, kind: "decision-learning-experiment", id: experimentId, frozenAt: recordedAt, entries });
874
+ return { caseDocument, labelDocument, manifest, labelReceipts: receipts, fixtureOnly: true, trainingEligible: false, scope };
787
875
  }
788
876
 
789
877
  // experiments/decision-shadow/research.mjs
@@ -1228,13 +1316,14 @@ var RESEARCH_OPTIONS = {
1228
1316
  fixtures: ["out"]
1229
1317
  };
1230
1318
  var RESEARCH_OPTIONAL = {
1319
+ fixtures: ["set"],
1231
1320
  "run-pi": ["pi-node", "auth-path", "timeout-ms"]
1232
1321
  };
1233
1322
  async function researchCommand(command, flags, helpers, options) {
1234
1323
  const { cases, jsonFile: jsonFile2, textFile: textFile2, writeNew: writeNew2, readLegacyRun } = helpers;
1235
1324
  const { emit = console.log, piRunner } = options;
1236
1325
  if (command === "fixtures") {
1237
- const f = createLearningFixtures({});
1326
+ const f = createLearningFixtures({ set: flags.set ?? "mechanical" });
1238
1327
  const dir = resolve(flags.out);
1239
1328
  await mkdir2(dir, { mode: 448 });
1240
1329
  const refs = [];
@@ -2118,7 +2207,7 @@ var help = `Decision research workflow (Node >=20; qualified Pi execution needs
2118
2207
  score --cases FILE --labels FILE --run RESULT.jsonl [--run RESULT2.jsonl]
2119
2208
  corpus --cases FILE --labels FILE --out NEW.json
2120
2209
  verify-sources --cases FILE --sources SELECTION.json --evidence-root DIR --out NEW.json
2121
- fixtures --out NEW_DIR
2210
+ fixtures --out NEW_DIR [--set mechanical|workflow]
2122
2211
  validate-experiment --cases FILE --experiment FILE
2123
2212
  preview-pi --cases FILE --experiment FILE --model openai-codex:MODEL --thinking LEVEL --split test
2124
2213
  run-pi --cases FILE --experiment FILE --model openai-codex:MODEL --thinking LEVEL --split test --pi-package DIR --out NEW.jsonl --allow-subscription [--pi-node PATH] [--auth-path FILE] [--timeout-ms N]
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@skill-harness/cli",
3
- "version": "0.26.2",
4
- "description": "skill-harness CLI \u2014 run, grade, review, and lint agent-skill scenarios on the pi harness",
3
+ "version": "0.28.0",
4
+ "description": "skill-harness CLI — run, grade, review, and lint agent-skill scenarios on the pi harness",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "main": "./dist/cli.js",
@@ -44,7 +44,7 @@
44
44
  "prepack": "node ../../scripts/release-pack.mjs --guard"
45
45
  },
46
46
  "dependencies": {
47
- "@skill-harness/core": "0.26.2",
48
- "@skill-harness/adapters": "0.26.2"
47
+ "@skill-harness/core": "0.28.0",
48
+ "@skill-harness/adapters": "0.28.0"
49
49
  }
50
50
  }