token-harness 0.1.2 → 0.1.3

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/token-harness.mjs CHANGED
@@ -97,18 +97,18 @@ function compareVersions(a, b) {
97
97
  return comparePrerelease(a.prerelease, b.prerelease);
98
98
  }
99
99
  function classifyVersion(observed, range) {
100
- const version2 = parseSemanticVersion(observed);
100
+ const version3 = parseSemanticVersion(observed);
101
101
  const minimum = parseSemanticVersion(range.minimum);
102
- if (version2 === null || minimum === null)
102
+ if (version3 === null || minimum === null)
103
103
  return "unparseable";
104
- if (compareVersions(version2, minimum) < 0)
104
+ if (compareVersions(version3, minimum) < 0)
105
105
  return "below-range";
106
106
  if (range.maximum === null)
107
107
  return "in-range";
108
108
  const maximum = parseSemanticVersion(range.maximum);
109
109
  if (maximum === null)
110
110
  return "unparseable";
111
- return compareVersions(version2, maximum) > 0 ? "unknown-newer" : "in-range";
111
+ return compareVersions(version3, maximum) > 0 ? "unknown-newer" : "in-range";
112
112
  }
113
113
 
114
114
  // packages/core/dist/src/domain/compatibility-rows.js
@@ -134,6 +134,19 @@ var COMPATIBILITY_ROWS = [
134
134
  configSchema: "claude-skills-directory",
135
135
  fixture: "tests/fixtures/rows/harnesstrim-claude-windows",
136
136
  verificationTier: "config-only"
137
+ },
138
+ {
139
+ harness: "codex",
140
+ harnessVersion: { minimum: "0.146.0", maximum: "0.146.0" },
141
+ provider: "harnesstrim",
142
+ providerVersion: "0.1.0",
143
+ platform: { os: "windows", wsl: false, supported: true, limitation: null },
144
+ // The same shape as the Claude skills row, and for the same reason: the install writes skill
145
+ // files rather than a document this build parses, so the schema it depends on is the directory
146
+ // layout. A separate id because the directory is `.codex/skills` and the protected paths differ.
147
+ configSchema: "codex-skills-directory",
148
+ fixture: "tests/fixtures/rows/harnesstrim-codex-windows",
149
+ verificationTier: "config-only"
137
150
  }
138
151
  ];
139
152
  function classifyRowHarnessVersion(rows, observed) {
@@ -695,14 +708,14 @@ function resolveOwnership(input) {
695
708
  });
696
709
  continue;
697
710
  }
698
- if (!provider.assignable) {
711
+ if (!provider.assignableHarnesses.has(harness.id)) {
699
712
  const observed = input.observedVersions[provider.id] ?? null;
700
713
  exclusions.push({
701
714
  scope,
702
715
  excluded: provider.id,
703
716
  retained: null,
704
717
  reason: [
705
- observed === null ? `${provider.id} implements ${capability} but no installer state this build could observe produces it in isolation` : `${provider.id} ${observed} implements ${capability} but no installer state at that version produces it in isolation`,
718
+ observed === null ? `${provider.id} implements ${capability} on ${harness.id} but no installer state this build could observe produces it there` : `${provider.id} ${observed} implements ${capability} on ${harness.id} but no installer state at that version produces it there`,
706
719
  "RFC 0003: a capability the provider has but cannot be asked for is not an assignable capability",
707
720
  "It is still detected, adopted, reconciled against the owner, and measured"
708
721
  ]
@@ -1026,21 +1039,21 @@ function validateStoredPlan(input) {
1026
1039
  detail: `the plan was computed for ${stored.projectRoot} and this invocation is in ${input.projectRoot}`
1027
1040
  });
1028
1041
  }
1029
- for (const [id, version2] of Object.entries(stored.versions.providers)) {
1042
+ for (const [id, version3] of Object.entries(stored.versions.providers)) {
1030
1043
  const current = input.versions.providers[id];
1031
- if (current !== version2) {
1044
+ if (current !== version3) {
1032
1045
  rejections.push({
1033
1046
  reason: "plan-version-drift",
1034
- detail: `${id} was ${version2 ?? "absent"} when the plan was made and is now ${current ?? "absent"}`
1047
+ detail: `${id} was ${version3 ?? "absent"} when the plan was made and is now ${current ?? "absent"}`
1035
1048
  });
1036
1049
  }
1037
1050
  }
1038
- for (const [id, version2] of Object.entries(stored.versions.harnesses)) {
1051
+ for (const [id, version3] of Object.entries(stored.versions.harnesses)) {
1039
1052
  const current = input.versions.harnesses[id];
1040
- if (current !== version2) {
1053
+ if (current !== version3) {
1041
1054
  rejections.push({
1042
1055
  reason: "plan-version-drift",
1043
- detail: `${id} was ${version2 ?? "absent"} when the plan was made and is now ${current ?? "absent"}`
1056
+ detail: `${id} was ${version3 ?? "absent"} when the plan was made and is now ${current ?? "absent"}`
1044
1057
  });
1045
1058
  }
1046
1059
  }
@@ -1327,7 +1340,7 @@ function resolveMetricsWindow(input) {
1327
1340
  var INSTALL_COMMANDS = {
1328
1341
  winget: {
1329
1342
  executable: "winget",
1330
- args: (packageName, version2) => [
1343
+ args: (packageName, version3) => [
1331
1344
  "install",
1332
1345
  "--id",
1333
1346
  packageName,
@@ -1337,16 +1350,16 @@ var INSTALL_COMMANDS = {
1337
1350
  "--silent",
1338
1351
  "--accept-package-agreements",
1339
1352
  "--accept-source-agreements",
1340
- ...version2 === null ? [] : ["--version", version2]
1353
+ ...version3 === null ? [] : ["--version", version3]
1341
1354
  ],
1342
1355
  verified: true
1343
1356
  },
1344
1357
  cargo: {
1345
1358
  executable: "cargo",
1346
- args: (packageName, version2) => [
1359
+ args: (packageName, version3) => [
1347
1360
  "install",
1348
1361
  packageName,
1349
- ...version2 === null ? [] : ["--version", version2]
1362
+ ...version3 === null ? [] : ["--version", version3]
1350
1363
  ],
1351
1364
  verified: false
1352
1365
  }
@@ -1464,8 +1477,8 @@ async function queryAvailableVersion(input) {
1464
1477
  }));
1465
1478
  return { status: "failed", version: null, destination, diagnostics };
1466
1479
  }
1467
- const version2 = recipe.parse(outcome2.stdout);
1468
- if (version2 === null) {
1480
+ const version3 = recipe.parse(outcome2.stdout);
1481
+ if (version3 === null) {
1469
1482
  diagnostics.push(diagnostic({
1470
1483
  severity: "warning",
1471
1484
  code: "version-query-unreadable",
@@ -1474,7 +1487,7 @@ async function queryAvailableVersion(input) {
1474
1487
  }));
1475
1488
  return { status: "unknown", version: null, destination, diagnostics };
1476
1489
  }
1477
- return { status: "found", version: version2, destination, diagnostics };
1490
+ return { status: "found", version: version3, destination, diagnostics };
1478
1491
  }
1479
1492
  var INVENTORY_COMMANDS = {
1480
1493
  /**
@@ -3133,10 +3146,10 @@ function parsePins(raw, path, diagnostics) {
3133
3146
  if (typeof entry !== "object" || entry === null)
3134
3147
  continue;
3135
3148
  const provider = entry.provider;
3136
- const version2 = entry.version;
3149
+ const version3 = entry.version;
3137
3150
  if (typeof provider !== "string" || provider === "")
3138
3151
  continue;
3139
- if (typeof version2 !== "string" || parseSemanticVersion(version2) === null) {
3152
+ if (typeof version3 !== "string" || parseSemanticVersion(version3) === null) {
3140
3153
  diagnostics.push(diagnostic({
3141
3154
  severity: "warning",
3142
3155
  code: "pin-version-unparseable",
@@ -3145,7 +3158,7 @@ function parsePins(raw, path, diagnostics) {
3145
3158
  }));
3146
3159
  continue;
3147
3160
  }
3148
- pins.push({ provider, version: version2 });
3161
+ pins.push({ provider, version: version3 });
3149
3162
  }
3150
3163
  return pins;
3151
3164
  }
@@ -5053,8 +5066,8 @@ async function detectVersion2(context) {
5053
5066
  };
5054
5067
  }
5055
5068
  const matched = VERSION_PATTERN.exec(outcome2.stdout);
5056
- const version2 = matched?.[1] ?? null;
5057
- if (version2 === null) {
5069
+ const version3 = matched?.[1] ?? null;
5070
+ if (version3 === null) {
5058
5071
  return {
5059
5072
  version: null,
5060
5073
  verdict: null,
@@ -5068,14 +5081,14 @@ async function detectVersion2(context) {
5068
5081
  };
5069
5082
  }
5070
5083
  return {
5071
- version: version2,
5072
- verdict: classifyVersion(version2, MANIFEST.testedVersions),
5084
+ version: version3,
5085
+ verdict: classifyVersion(version3, MANIFEST.testedVersions),
5073
5086
  evidence: [
5074
5087
  evidence({
5075
5088
  kind: "version-output",
5076
5089
  source: command.executable,
5077
5090
  path: outcome2.executablePath,
5078
- detail: `reported ${version2}`
5091
+ detail: `reported ${version3}`
5079
5092
  })
5080
5093
  ]
5081
5094
  };
@@ -5083,7 +5096,7 @@ async function detectVersion2(context) {
5083
5096
  async function detect(context) {
5084
5097
  const evidenceItems = [];
5085
5098
  const warnings = [];
5086
- const { version: version2, verdict, evidence: versionEvidence } = await detectVersion2(context);
5099
+ const { version: version3, verdict, evidence: versionEvidence } = await detectVersion2(context);
5087
5100
  evidenceItems.push(...versionEvidence);
5088
5101
  const configs = await Promise.all(MANIFEST.configFiles.map((declaration) => resolveConfig(declaration, context)));
5089
5102
  const present = configs.filter((config) => config.exists);
@@ -5106,7 +5119,7 @@ async function detect(context) {
5106
5119
  remediation: "Repair the JSON syntax, then run the command again"
5107
5120
  }));
5108
5121
  }
5109
- if (version2 === null && present.length > 0) {
5122
+ if (version3 === null && present.length > 0) {
5110
5123
  warnings.push(diagnostic({
5111
5124
  severity: "warning",
5112
5125
  code: "harness-executable-missing",
@@ -5119,16 +5132,16 @@ async function detect(context) {
5119
5132
  warnings.push(diagnostic({
5120
5133
  severity: "warning",
5121
5134
  code: "harness-version-unknown-newer",
5122
- message: `Claude Code ${String(version2)} is newer than the versions whose configuration schema Token Harness has observed (up to ${String(MANIFEST.testedVersions.maximum)})`,
5135
+ message: `Claude Code ${String(version3)} is newer than the versions whose configuration schema Token Harness has observed (up to ${String(MANIFEST.testedVersions.maximum)})`,
5123
5136
  remediation: "Check the release notes for hook-schema changes before applying a plan"
5124
5137
  }));
5125
5138
  }
5126
- const state = unparsed.length > 0 ? "broken" : configured.length > 0 && version2 !== null ? "configured" : version2 !== null || present.length > 0 ? "detected" : "absent";
5139
+ const state = unparsed.length > 0 ? "broken" : configured.length > 0 && version3 !== null ? "configured" : version3 !== null || present.length > 0 ? "detected" : "absent";
5127
5140
  const primary = present.find((config) => config.declaration.primary) ?? present[0] ?? null;
5128
5141
  return {
5129
5142
  harnessId: CLAUDE,
5130
5143
  state,
5131
- version: version2,
5144
+ version: version3,
5132
5145
  versionVerdict: verdict,
5133
5146
  configPath: primary?.path ?? null,
5134
5147
  declaredVerificationTier: MANIFEST.verificationTier,
@@ -5382,22 +5395,22 @@ async function detectVersion3(context) {
5382
5395
  })
5383
5396
  ]
5384
5397
  };
5385
- const version2 = VERSION_PATTERN2.exec(outcome2.stdout)?.[1] ?? null;
5398
+ const version3 = VERSION_PATTERN2.exec(outcome2.stdout)?.[1] ?? null;
5386
5399
  return {
5387
- version: version2,
5388
- verdict: version2 === null ? null : classifyVersion(version2, MANIFEST2.testedVersions),
5400
+ version: version3,
5401
+ verdict: version3 === null ? null : classifyVersion(version3, MANIFEST2.testedVersions),
5389
5402
  evidence: [
5390
5403
  evidence({
5391
5404
  kind: "version-output",
5392
5405
  source: "codex",
5393
5406
  path: outcome2.executablePath,
5394
- detail: version2 === null ? "reported no recognisable semantic version" : `reported ${version2}`
5407
+ detail: version3 === null ? "reported no recognisable semantic version" : `reported ${version3}`
5395
5408
  })
5396
5409
  ]
5397
5410
  };
5398
5411
  }
5399
5412
  async function detect2(context) {
5400
- const { version: version2, verdict, evidence: versionEvidence } = await detectVersion3(context);
5413
+ const { version: version3, verdict, evidence: versionEvidence } = await detectVersion3(context);
5401
5414
  const configs = await Promise.all(MANIFEST2.configFiles.map((file) => resolveConfig2(file, context)));
5402
5415
  const present = configs.filter((config) => config.exists);
5403
5416
  const hooks = configs.find((config) => config.declaration.path.endsWith("hooks.json"));
@@ -5410,7 +5423,7 @@ async function detect2(context) {
5410
5423
  path: hooks.path,
5411
5424
  remediation: "Repair the JSON syntax, then run the command again"
5412
5425
  }));
5413
- if (version2 === null && present.length > 0)
5426
+ if (version3 === null && present.length > 0)
5414
5427
  warnings.push(diagnostic({
5415
5428
  severity: "warning",
5416
5429
  code: "harness-executable-missing",
@@ -5422,15 +5435,15 @@ async function detect2(context) {
5422
5435
  warnings.push(diagnostic({
5423
5436
  severity: "warning",
5424
5437
  code: "harness-version-unknown-newer",
5425
- message: `Codex ${String(version2)} is newer than the observed hook schema`,
5438
+ message: `Codex ${String(version3)} is newer than the observed hook schema`,
5426
5439
  remediation: "Check the release notes for hook-schema changes before applying a plan"
5427
5440
  }));
5428
5441
  const configured = (hooks?.configuredPoints.length ?? 0) > 0;
5429
5442
  const primary = configs.find((config) => config.declaration.primary && config.exists) ?? present[0] ?? null;
5430
5443
  return {
5431
5444
  harnessId: CODEX,
5432
- state: hooks?.exists && !hooks.parsed ? "broken" : configured && version2 !== null ? "configured" : version2 !== null || present.length > 0 ? "detected" : "absent",
5433
- version: version2,
5445
+ state: hooks?.exists && !hooks.parsed ? "broken" : configured && version3 !== null ? "configured" : version3 !== null || present.length > 0 ? "detected" : "absent",
5446
+ version: version3,
5434
5447
  versionVerdict: verdict,
5435
5448
  configPath: primary?.path ?? null,
5436
5449
  declaredVerificationTier: MANIFEST2.verificationTier,
@@ -5547,10 +5560,231 @@ async function verify2(context) {
5547
5560
  }
5548
5561
  var codexAdapter = { manifest: MANIFEST2, detect: detect2, inspect: inspect2, verify: verify2 };
5549
5562
 
5550
- // packages/adapters/dist/src/harnesses/opencode.js
5551
- var OPENCODE = harnessId("opencode");
5563
+ // packages/adapters/dist/src/harnesses/hermes.js
5564
+ var HERMES = harnessId("hermes");
5552
5565
  var VERSION_PATTERN3 = /(\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?)/;
5566
+ var PLUGIN = "harnesstrim";
5553
5567
  var MANIFEST3 = {
5568
+ schemaVersion: MANIFEST_SCHEMA_VERSION,
5569
+ id: HERMES,
5570
+ displayName: "Hermes Agent",
5571
+ homepage: "https://hermes-agent.nousresearch.com/docs/",
5572
+ testedVersions: { minimum: "0.1.0", maximum: "0.1.0" },
5573
+ verificationTier: "config-only",
5574
+ versionCommand: { executable: "hermes", args: ["--version"] },
5575
+ interceptionPoints: [{ scopeId: "transform-tool-result", eventName: "transform_tool_result" }],
5576
+ configFiles: [
5577
+ {
5578
+ path: ".hermes/plugins/harnesstrim/plugin.yaml",
5579
+ scope: "user",
5580
+ parser: "markers",
5581
+ primary: true
5582
+ },
5583
+ {
5584
+ path: ".hermes/plugins/harnesstrim/config.json",
5585
+ scope: "user",
5586
+ parser: "json",
5587
+ primary: false
5588
+ }
5589
+ ],
5590
+ toolFamilies: [
5591
+ { id: "tool.result", platforms: ["windows", "macos", "linux"], executesShellCommands: false }
5592
+ ],
5593
+ requiresEnablement: true,
5594
+ enablementNote: "The plugin must be enabled with `hermes plugins enable harnesstrim`.",
5595
+ receiptFamily: "provider-telemetry"
5596
+ };
5597
+ async function resolveConfig3(declaration, context) {
5598
+ const path = resolveConfigPath(declaration, context);
5599
+ const stat3 = await context.fs.stat(path);
5600
+ if (stat3 === null || stat3.kind !== "file") {
5601
+ return {
5602
+ declaration,
5603
+ path,
5604
+ exists: false,
5605
+ parsed: false,
5606
+ configuredPoints: [],
5607
+ matchers: [],
5608
+ commands: []
5609
+ };
5610
+ }
5611
+ const text = new TextDecoder().decode(await context.fs.readFile(path));
5612
+ const parsed = declaration.parser === "json" ? (() => {
5613
+ try {
5614
+ return JSON.parse(text);
5615
+ } catch {
5616
+ return null;
5617
+ }
5618
+ })() : text;
5619
+ const valid = parsed !== null;
5620
+ return {
5621
+ declaration,
5622
+ path,
5623
+ exists: true,
5624
+ parsed: valid,
5625
+ configuredPoints: valid && declaration.path.endsWith("plugin.yaml") ? ["transform-tool-result"] : [],
5626
+ matchers: valid && declaration.path.endsWith("plugin.yaml") ? ["tool.result"] : [],
5627
+ commands: valid && declaration.path.endsWith("plugin.yaml") ? ["hermes plugins enable harnesstrim"] : []
5628
+ };
5629
+ }
5630
+ async function version2(context) {
5631
+ const result = await context.runner.run({
5632
+ executable: "hermes",
5633
+ args: ["--version"],
5634
+ cwd: context.projectRoot,
5635
+ timeoutMs: 2e4
5636
+ });
5637
+ if (result.failure !== null)
5638
+ return {
5639
+ value: null,
5640
+ verdict: null,
5641
+ evidence: [
5642
+ evidence({
5643
+ kind: "absence",
5644
+ source: "hermes",
5645
+ detail: `not runnable: ${result.failure.reason}`
5646
+ })
5647
+ ]
5648
+ };
5649
+ const value = VERSION_PATTERN3.exec(result.stdout)?.[1] ?? null;
5650
+ return {
5651
+ value,
5652
+ verdict: value === null ? null : classifyVersion(value, MANIFEST3.testedVersions),
5653
+ evidence: [
5654
+ evidence({
5655
+ kind: "version-output",
5656
+ source: "hermes",
5657
+ path: result.executablePath,
5658
+ detail: value ? `reported ${value}` : "reported no recognisable version"
5659
+ })
5660
+ ]
5661
+ };
5662
+ }
5663
+ async function enabled(context) {
5664
+ const result = await context.runner.run({
5665
+ executable: "hermes",
5666
+ args: ["plugins", "list"],
5667
+ cwd: context.projectRoot,
5668
+ timeoutMs: 2e4
5669
+ });
5670
+ if (result.failure !== null || result.exitCode !== 0)
5671
+ return null;
5672
+ const line = result.stdout.split("\n").find((entry) => entry.toLowerCase().includes(PLUGIN));
5673
+ return line === void 0 ? false : /enabled/i.test(line);
5674
+ }
5675
+ async function detect3(context) {
5676
+ const observed = await version2(context);
5677
+ const configs = await Promise.all(MANIFEST3.configFiles.map((file) => resolveConfig3(file, context)));
5678
+ const plugin = configs.find((file) => file.declaration.path.endsWith("plugin.yaml"));
5679
+ const isEnabled = await enabled(context);
5680
+ const warnings = [];
5681
+ if (observed.verdict === "unknown-newer")
5682
+ warnings.push(diagnostic({
5683
+ severity: "warning",
5684
+ code: "harness-version-unknown-newer",
5685
+ message: `Hermes ${observed.value} is newer than the observed plugin contract`,
5686
+ remediation: "Review the transform_tool_result plugin contract before applying changes"
5687
+ }));
5688
+ if (plugin?.exists && isEnabled === false)
5689
+ warnings.push(diagnostic({
5690
+ severity: "warning",
5691
+ code: "plugin-disabled",
5692
+ message: "HarnessTrim is installed in Hermes but is not enabled",
5693
+ path: plugin.path,
5694
+ remediation: "Run `hermes plugins enable harnesstrim`"
5695
+ }));
5696
+ const present = plugin?.exists === true;
5697
+ return {
5698
+ harnessId: HERMES,
5699
+ state: present && isEnabled === true && observed.value !== null ? "configured" : present || observed.value !== null ? "detected" : "absent",
5700
+ version: observed.value,
5701
+ versionVerdict: observed.verdict,
5702
+ configPath: plugin?.path ?? null,
5703
+ declaredVerificationTier: MANIFEST3.verificationTier,
5704
+ evidence: [
5705
+ ...observed.evidence,
5706
+ ...plugin ? [
5707
+ evidence({
5708
+ kind: "config-entry",
5709
+ source: "Hermes plugin directory",
5710
+ path: plugin.path,
5711
+ detail: plugin.exists ? "HarnessTrim plugin manifest present" : "plugin manifest absent"
5712
+ })
5713
+ ] : []
5714
+ ],
5715
+ warnings
5716
+ };
5717
+ }
5718
+ async function inspect3(context) {
5719
+ const configs = await Promise.all(MANIFEST3.configFiles.map((file) => resolveConfig3(file, context)));
5720
+ return {
5721
+ harnessId: HERMES,
5722
+ configs,
5723
+ activeToolFamilies: familiesOnThisPlatform(MANIFEST3, context.facts),
5724
+ uncoveredToolFamilies: [],
5725
+ enabled: await enabled(context),
5726
+ diagnostics: [],
5727
+ summaries: configs.filter((config) => config.configuredPoints.length > 0).map((config) => ({
5728
+ harnessId: HERMES,
5729
+ configPath: config.path,
5730
+ scope: config.declaration.scope,
5731
+ interceptionPoints: config.configuredPoints,
5732
+ matchers: config.matchers,
5733
+ commands: config.commands
5734
+ }))
5735
+ };
5736
+ }
5737
+ async function verify3(context) {
5738
+ const detection = await detect3(context);
5739
+ const inspection = await inspect3(context);
5740
+ const plugin = inspection.configs.some((config) => config.configuredPoints.length > 0);
5741
+ const checks = [
5742
+ {
5743
+ id: "executable-resolves",
5744
+ status: detection.version === null ? "fail" : "pass",
5745
+ summary: detection.version === null ? "hermes could not be run" : `Hermes ${detection.version}`,
5746
+ achievedTier: detection.version === null ? null : "presence",
5747
+ evidence: detection.evidence.filter((item) => item.kind === "version-output"),
5748
+ remediation: detection.version === null ? "Install Hermes or add it to PATH" : null
5749
+ },
5750
+ {
5751
+ id: "plugin-installed",
5752
+ status: plugin ? "pass" : "fail",
5753
+ summary: plugin ? "HarnessTrim plugin manifest is installed" : "HarnessTrim plugin manifest is missing",
5754
+ achievedTier: plugin ? "config-only" : null,
5755
+ evidence: [],
5756
+ remediation: plugin ? null : "Run `harnesstrim install hermes --apply`"
5757
+ },
5758
+ {
5759
+ id: "plugin-enabled",
5760
+ status: inspection.enabled === true ? "pass" : inspection.enabled === false ? "fail" : "not-exercised",
5761
+ summary: inspection.enabled === true ? "HarnessTrim is enabled" : inspection.enabled === false ? "HarnessTrim is installed but disabled" : "Hermes plugin state could not be read",
5762
+ achievedTier: inspection.enabled === true ? "config-only" : null,
5763
+ evidence: [],
5764
+ remediation: inspection.enabled === false ? "Run `hermes plugins enable harnesstrim`" : null
5765
+ },
5766
+ {
5767
+ id: "canary-intercepted",
5768
+ status: "not-exercised",
5769
+ summary: "Hermes exposes no independent receipt; HarnessTrim telemetry is verified by the provider adapter",
5770
+ achievedTier: null,
5771
+ evidence: [],
5772
+ remediation: null
5773
+ }
5774
+ ];
5775
+ return {
5776
+ harnessId: HERMES,
5777
+ declaredTier: MANIFEST3.verificationTier,
5778
+ achievedTier: checks.some((check) => check.achievedTier === "config-only") ? "config-only" : checks.some((check) => check.achievedTier === "presence") ? "presence" : null,
5779
+ checks
5780
+ };
5781
+ }
5782
+ var hermesAdapter = { manifest: MANIFEST3, detect: detect3, inspect: inspect3, verify: verify3 };
5783
+
5784
+ // packages/adapters/dist/src/harnesses/opencode.js
5785
+ var OPENCODE = harnessId("opencode");
5786
+ var VERSION_PATTERN4 = /(\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?)/;
5787
+ var MANIFEST4 = {
5554
5788
  schemaVersion: MANIFEST_SCHEMA_VERSION,
5555
5789
  id: OPENCODE,
5556
5790
  displayName: "OpenCode",
@@ -5650,7 +5884,7 @@ async function resolvePluginDirectory(declaration, context) {
5650
5884
  commands: modules.map((entry) => context.fs.join(path, entry))
5651
5885
  };
5652
5886
  }
5653
- async function resolveConfig3(declaration, context) {
5887
+ async function resolveConfig4(declaration, context) {
5654
5888
  if (declaration.parser === "markers")
5655
5889
  return resolvePluginDirectory(declaration, context);
5656
5890
  const path = resolveConfigPath(declaration, context);
@@ -5711,23 +5945,23 @@ async function detectVersion4(context) {
5711
5945
  })
5712
5946
  ]
5713
5947
  };
5714
- const version2 = VERSION_PATTERN3.exec(outcome2.stdout)?.[1] ?? null;
5948
+ const version3 = VERSION_PATTERN4.exec(outcome2.stdout)?.[1] ?? null;
5715
5949
  return {
5716
- version: version2,
5717
- verdict: version2 === null ? null : classifyVersion(version2, MANIFEST3.testedVersions),
5950
+ version: version3,
5951
+ verdict: version3 === null ? null : classifyVersion(version3, MANIFEST4.testedVersions),
5718
5952
  evidence: [
5719
5953
  evidence({
5720
5954
  kind: "version-output",
5721
5955
  source: "opencode",
5722
5956
  path: outcome2.executablePath,
5723
- detail: version2 === null ? "reported no recognisable semantic version" : `reported ${version2}`
5957
+ detail: version3 === null ? "reported no recognisable semantic version" : `reported ${version3}`
5724
5958
  })
5725
5959
  ]
5726
5960
  };
5727
5961
  }
5728
- async function detect3(context) {
5729
- const { version: version2, verdict, evidence: versionEvidence } = await detectVersion4(context);
5730
- const configs = await Promise.all(MANIFEST3.configFiles.map((file) => resolveConfig3(file, context)));
5962
+ async function detect4(context) {
5963
+ const { version: version3, verdict, evidence: versionEvidence } = await detectVersion4(context);
5964
+ const configs = await Promise.all(MANIFEST4.configFiles.map((file) => resolveConfig4(file, context)));
5731
5965
  const present = configs.filter((config) => config.exists);
5732
5966
  const configured = present.filter((config) => config.configuredPoints.length > 0);
5733
5967
  const warnings = [];
@@ -5743,17 +5977,17 @@ async function detect3(context) {
5743
5977
  warnings.push(diagnostic({
5744
5978
  severity: "warning",
5745
5979
  code: "harness-version-unknown-newer",
5746
- message: `OpenCode ${String(version2)} is newer than the observed configuration schema`,
5980
+ message: `OpenCode ${String(version3)} is newer than the observed configuration schema`,
5747
5981
  remediation: "Check the release notes for plugin-schema changes before applying a plan"
5748
5982
  }));
5749
5983
  const primary = present.find((config) => config.declaration.primary) ?? present[0] ?? null;
5750
5984
  return {
5751
5985
  harnessId: OPENCODE,
5752
- state: present.some((config) => !config.parsed) ? "broken" : configured.length > 0 && version2 !== null ? "configured" : version2 !== null || present.length > 0 ? "detected" : "absent",
5753
- version: version2,
5986
+ state: present.some((config) => !config.parsed) ? "broken" : configured.length > 0 && version3 !== null ? "configured" : version3 !== null || present.length > 0 ? "detected" : "absent",
5987
+ version: version3,
5754
5988
  versionVerdict: verdict,
5755
5989
  configPath: primary?.path ?? null,
5756
- declaredVerificationTier: MANIFEST3.verificationTier,
5990
+ declaredVerificationTier: MANIFEST4.verificationTier,
5757
5991
  evidence: [
5758
5992
  ...versionEvidence,
5759
5993
  ...present.map((config) => evidence({
@@ -5766,9 +6000,9 @@ async function detect3(context) {
5766
6000
  warnings
5767
6001
  };
5768
6002
  }
5769
- async function inspect3(context) {
5770
- const configs = await Promise.all(MANIFEST3.configFiles.map((file) => resolveConfig3(file, context)));
5771
- const activeToolFamilies = familiesOnThisPlatform(MANIFEST3, context.facts);
6003
+ async function inspect4(context) {
6004
+ const configs = await Promise.all(MANIFEST4.configFiles.map((file) => resolveConfig4(file, context)));
6005
+ const activeToolFamilies = familiesOnThisPlatform(MANIFEST4, context.facts);
5772
6006
  return {
5773
6007
  harnessId: OPENCODE,
5774
6008
  configs,
@@ -5786,9 +6020,9 @@ async function inspect3(context) {
5786
6020
  }))
5787
6021
  };
5788
6022
  }
5789
- async function verify3(context) {
5790
- const detection = await detect3(context);
5791
- const inspection = await inspect3(context);
6023
+ async function verify4(context) {
6024
+ const detection = await detect4(context);
6025
+ const inspection = await inspect4(context);
5792
6026
  const configured = inspection.configs.some((config) => config.configuredPoints.length > 0);
5793
6027
  const readable = inspection.configs.filter((config) => config.exists).every((config) => config.parsed);
5794
6028
  const checks = [
@@ -5827,15 +6061,226 @@ async function verify3(context) {
5827
6061
  ];
5828
6062
  return {
5829
6063
  harnessId: OPENCODE,
5830
- declaredTier: MANIFEST3.verificationTier,
6064
+ declaredTier: MANIFEST4.verificationTier,
5831
6065
  achievedTier: detection.version !== null && configured && readable ? "config-only" : detection.version !== null ? "presence" : null,
5832
6066
  checks
5833
6067
  };
5834
6068
  }
5835
- var opencodeAdapter = { manifest: MANIFEST3, detect: detect3, inspect: inspect3, verify: verify3 };
6069
+ var opencodeAdapter = { manifest: MANIFEST4, detect: detect4, inspect: inspect4, verify: verify4 };
6070
+
6071
+ // packages/adapters/dist/src/harnesses/pi.js
6072
+ var PI = harnessId("pi");
6073
+ var VERSION_PATTERN5 = /(\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?)/;
6074
+ var MANIFEST5 = {
6075
+ schemaVersion: MANIFEST_SCHEMA_VERSION,
6076
+ id: PI,
6077
+ displayName: "Pi",
6078
+ // The npm package declares no `homepage`; the repository it ships is the documented home.
6079
+ homepage: "https://github.com/earendil-works/pi",
6080
+ // Only the version actually observed — PLAN §15 item 31, "tested range of exactly the observed
6081
+ // version". `pi --version` on the machine this was written against printed `0.83.0`.
6082
+ testedVersions: { minimum: "0.83.0", maximum: "0.83.0" },
6083
+ // RFC 0007: the extension emits no receipt of its own, so nothing above `config-only` is
6084
+ // provable. The configuration is the extension module sitting in an auto-loaded directory.
6085
+ verificationTier: "config-only",
6086
+ versionCommand: { executable: "pi", args: ["--version"] },
6087
+ interceptionPoints: [{ scopeId: "tool-result", eventName: "tool_result" }],
6088
+ // `markers` because there is nothing to parse: Pi auto-loads any module in these directories,
6089
+ // and the presence of the HarnessTrim extension file is the whole configuration.
6090
+ configFiles: [
6091
+ {
6092
+ path: ".pi/agent/extensions/harnesstrim/index.ts",
6093
+ scope: "user",
6094
+ parser: "markers",
6095
+ primary: true
6096
+ },
6097
+ {
6098
+ path: ".pi/extensions/harnesstrim/harnesstrim.ts",
6099
+ scope: "project",
6100
+ parser: "markers",
6101
+ primary: false
6102
+ }
6103
+ ],
6104
+ // The extension intercepts `tool_result` and reduces text chunks — a content seam, never a
6105
+ // shell. Mirrors the OpenCode plugin API claim.
6106
+ toolFamilies: [
6107
+ { id: "tool.result", platforms: ["windows", "macos", "linux"], executesShellCommands: false }
6108
+ ],
6109
+ // There is no separate enable state: the module being present in the directory is the load
6110
+ // decision, so the adapter reads presence and nothing else.
6111
+ requiresEnablement: false,
6112
+ enablementNote: null,
6113
+ receiptFamily: "none"
6114
+ };
6115
+ async function resolveConfig5(declaration, context) {
6116
+ const path = resolveConfigPath(declaration, context);
6117
+ const stat3 = await context.fs.stat(path);
6118
+ if (stat3 === null || stat3.kind !== "file") {
6119
+ return {
6120
+ declaration,
6121
+ path,
6122
+ exists: false,
6123
+ parsed: false,
6124
+ configuredPoints: [],
6125
+ matchers: [],
6126
+ commands: []
6127
+ };
6128
+ }
6129
+ const text = new TextDecoder().decode(await context.fs.readFile(path));
6130
+ const isHarnesstrim = /harnesstrim/i.test(text);
6131
+ return {
6132
+ declaration,
6133
+ path,
6134
+ exists: true,
6135
+ parsed: isHarnesstrim,
6136
+ configuredPoints: isHarnesstrim ? ["tool-result"] : [],
6137
+ matchers: isHarnesstrim ? ["tool.result"] : [],
6138
+ // There is no enable command: Pi loads the module from the directory directly.
6139
+ commands: []
6140
+ };
6141
+ }
6142
+ async function detectVersion5(context) {
6143
+ const result = await context.runner.run({
6144
+ executable: "pi",
6145
+ args: ["--version"],
6146
+ cwd: context.projectRoot,
6147
+ timeoutMs: 2e4
6148
+ });
6149
+ if (result.failure !== null)
6150
+ return {
6151
+ value: null,
6152
+ verdict: null,
6153
+ evidence: [
6154
+ evidence({
6155
+ kind: "absence",
6156
+ source: "pi",
6157
+ detail: `not runnable: ${result.failure.reason}`
6158
+ })
6159
+ ]
6160
+ };
6161
+ const value = VERSION_PATTERN5.exec(result.stdout)?.[1] ?? null;
6162
+ return {
6163
+ value,
6164
+ verdict: value === null ? null : classifyVersion(value, MANIFEST5.testedVersions),
6165
+ evidence: [
6166
+ evidence({
6167
+ kind: "version-output",
6168
+ source: "pi",
6169
+ path: result.executablePath,
6170
+ detail: value ? `reported ${value}` : "reported no recognisable version"
6171
+ })
6172
+ ]
6173
+ };
6174
+ }
6175
+ async function detect5(context) {
6176
+ const observed = await detectVersion5(context);
6177
+ const configs = await Promise.all(MANIFEST5.configFiles.map((file) => resolveConfig5(file, context)));
6178
+ const module = configs.find((file) => file.parsed);
6179
+ const present = module !== void 0;
6180
+ const warnings = [];
6181
+ if (observed.verdict === "unknown-newer")
6182
+ warnings.push(diagnostic({
6183
+ severity: "warning",
6184
+ code: "harness-version-unknown-newer",
6185
+ message: `Pi ${observed.value} is newer than the observed extension contract`,
6186
+ remediation: "Review the tool_result extension contract before applying changes"
6187
+ }));
6188
+ return {
6189
+ harnessId: PI,
6190
+ state: present && observed.value !== null ? "configured" : present || observed.value !== null ? "detected" : "absent",
6191
+ version: observed.value,
6192
+ versionVerdict: observed.verdict,
6193
+ configPath: module?.path ?? null,
6194
+ declaredVerificationTier: MANIFEST5.verificationTier,
6195
+ evidence: [
6196
+ ...observed.evidence,
6197
+ ...module ? [
6198
+ evidence({
6199
+ kind: "config-entry",
6200
+ source: "HarnessTrim Pi extension",
6201
+ path: module.path,
6202
+ detail: "extension module present in an auto-loaded directory"
6203
+ })
6204
+ ] : []
6205
+ ],
6206
+ warnings
6207
+ };
6208
+ }
6209
+ async function inspect5(context) {
6210
+ const configs = await Promise.all(MANIFEST5.configFiles.map((file) => resolveConfig5(file, context)));
6211
+ return {
6212
+ harnessId: PI,
6213
+ configs,
6214
+ activeToolFamilies: familiesOnThisPlatform(MANIFEST5, context.facts),
6215
+ uncoveredToolFamilies: [],
6216
+ // No separate enablement exists, so nothing is reported as enabled or disabled.
6217
+ enabled: null,
6218
+ diagnostics: [],
6219
+ summaries: configs.filter((config) => config.configuredPoints.length > 0).map((config) => ({
6220
+ harnessId: PI,
6221
+ configPath: config.path,
6222
+ scope: config.declaration.scope,
6223
+ interceptionPoints: config.configuredPoints,
6224
+ matchers: config.matchers,
6225
+ commands: config.commands
6226
+ }))
6227
+ };
6228
+ }
6229
+ async function verify5(context) {
6230
+ const detection = await detect5(context);
6231
+ const inspection = await inspect5(context);
6232
+ const module = inspection.configs.some((config) => config.configuredPoints.length > 0);
6233
+ const checks = [
6234
+ {
6235
+ id: "executable-resolves",
6236
+ status: detection.version === null ? "fail" : "pass",
6237
+ summary: detection.version === null ? "pi could not be run" : `Pi ${detection.version}`,
6238
+ achievedTier: detection.version === null ? null : "presence",
6239
+ evidence: detection.evidence.filter((item) => item.kind === "version-output"),
6240
+ remediation: detection.version === null ? "Install Pi or add it to PATH" : null
6241
+ },
6242
+ {
6243
+ id: "module-installed",
6244
+ status: module ? "pass" : "fail",
6245
+ summary: module ? "HarnessTrim extension module is installed" : "No HarnessTrim extension module was found in the Pi extension directories",
6246
+ achievedTier: module ? "config-only" : null,
6247
+ evidence: [],
6248
+ remediation: module ? null : "Run `harnesstrim install pi --apply`"
6249
+ },
6250
+ {
6251
+ id: "mode-unreadable",
6252
+ status: "info",
6253
+ summary: "The effective reduction mode (HARNESSTRIM_MODE) defaults to dryrun and is not observable from the extension alone",
6254
+ achievedTier: null,
6255
+ evidence: [],
6256
+ remediation: null
6257
+ },
6258
+ {
6259
+ id: "canary-intercepted",
6260
+ status: "not-exercised",
6261
+ summary: "Pi exposes no independent receipt; HarnessTrim telemetry is verified by the provider adapter",
6262
+ achievedTier: null,
6263
+ evidence: [],
6264
+ remediation: null
6265
+ }
6266
+ ];
6267
+ return {
6268
+ harnessId: PI,
6269
+ declaredTier: MANIFEST5.verificationTier,
6270
+ achievedTier: checks.some((check) => check.achievedTier === "config-only") ? "config-only" : checks.some((check) => check.achievedTier === "presence") ? "presence" : null,
6271
+ checks
6272
+ };
6273
+ }
6274
+ var piAdapter = { manifest: MANIFEST5, detect: detect5, inspect: inspect5, verify: verify5 };
5836
6275
 
5837
6276
  // packages/adapters/dist/src/harnesses/index.js
5838
- var HARNESS_ADAPTERS = [claudeAdapter, codexAdapter, opencodeAdapter];
6277
+ var HARNESS_ADAPTERS = [
6278
+ claudeAdapter,
6279
+ codexAdapter,
6280
+ hermesAdapter,
6281
+ opencodeAdapter,
6282
+ piAdapter
6283
+ ];
5839
6284
  function listHarnessAdapters() {
5840
6285
  return HARNESS_ADAPTERS;
5841
6286
  }
@@ -5843,8 +6288,11 @@ function listHarnessAdapters() {
5843
6288
  // packages/adapters/dist/src/providers/harnesstrim.js
5844
6289
  var HARNESSTRIM2 = providerId("harnesstrim");
5845
6290
  var CLAUDE2 = harnessId("claude");
6291
+ var HERMES2 = harnessId("hermes");
5846
6292
  var CODEX2 = harnessId("codex");
5847
- var CLAUDE_ARTIFACT_DIGESTS = {
6293
+ var PI2 = harnessId("pi");
6294
+ var SKILLS_UPSTREAM = "0.0.7";
6295
+ var SKILL_ARTIFACT_DIGESTS = {
5848
6296
  "compact-handoff/SKILL.md": "sha256:0efbf35581c559359e755b204778b64a289dab4d20dadc1cba3d5b5c995b5f01",
5849
6297
  "debug-log-slim/SKILL.md": "sha256:17f5ccc34d29d7aba083444e0e4d87fd3d49916ebcb4e6544caf8c89f13f0045",
5850
6298
  "delegate-bulk/SKILL.md": "sha256:3753de2ad18271c24832e4cda63115d353620515d5d80f40e93bc07d2a7257d7",
@@ -5853,16 +6301,18 @@ var CLAUDE_ARTIFACT_DIGESTS = {
5853
6301
  "review-delta/SKILL.md": "sha256:4dfbf9d6ec08dff27b4759726536b43928370e288706fa01f5b498648e741388",
5854
6302
  "scaffold-fast/SKILL.md": "sha256:1a18d52c4d335fbd3f74e2559bc20d6346193a40eef7b85149f51f44f697d182"
5855
6303
  };
6304
+ var SKILL_ARTIFACTS = Object.keys(SKILL_ARTIFACT_DIGESTS);
5856
6305
  var OPENCODE2 = harnessId("opencode");
5857
6306
  var MODE_CARRYING_HARNESSES = /* @__PURE__ */ new Set([
5858
6307
  OPENCODE2,
5859
- harnessId("hermes"),
6308
+ HERMES2,
5860
6309
  harnessId("pi"),
5861
6310
  harnessId("omp")
5862
6311
  ]);
5863
6312
  var HOOK_COMMAND_PATTERN = /(^|[\\/\s"'])harnesstrim(\.cmd|\.exe)?([\s"']|$)/i;
5864
6313
  var PLUGIN_MODULE_PATTERN2 = /(^|[\\/])harnesstrim\.(ts|js|mjs|cjs|mts|cts)$/i;
5865
- var MANIFEST4 = {
6314
+ var PI_EXTENSION_PATTERN = /(^|[\\/])\.pi[\\/](agent[\\/])?extensions[\\/]harnesstrim[\\/]/i;
6315
+ var MANIFEST6 = {
5866
6316
  schemaVersion: MANIFEST_SCHEMA_VERSION,
5867
6317
  id: HARNESSTRIM2,
5868
6318
  displayName: "HarnessTrim",
@@ -5948,15 +6398,48 @@ var MANIFEST4 = {
5948
6398
  // names it is not read. Item 30 readmits it together with the adapter and a matrix row; the
5949
6399
  // registry assertion in `registries.test.ts` refuses a home-relative location that names a
5950
6400
  // harness the registry does not know.
5951
- locations: [".harnesstrim/metrics.jsonl"]
6401
+ locations: [".harnesstrim/metrics.jsonl", ".hermes/harnesstrim-metrics.jsonl"]
5952
6402
  },
5953
- delegatedInstallReview: {
5954
- upstreamVersion: "0.0.7",
5955
- reviewedWriteSet: [
5956
- ...Object.keys(CLAUDE_ARTIFACT_DIGESTS).map((path) => `.claude/skills/${path}`)
5957
- ],
5958
- containmentBoundary: [".claude", "CLAUDE.md"],
5959
- upstreamUninstallAvailable: true
6403
+ /**
6404
+ * One review per harness — PLAN §15 item 46b.
6405
+ *
6406
+ * Codex was observed at `0.1.0`: `harnesstrim install codex <dir> --apply --no-instructions`
6407
+ * writes the same seven skill artifacts as the Claude invocation, byte for byte — the digests
6408
+ * below are shared because the files are the same files — under `.codex/skills/` instead, and
6409
+ * skips `AGENTS.md` exactly as the Claude one skips `CLAUDE.md`. What differs is the boundary and
6410
+ * the protected paths, which is precisely why one review could not describe both.
6411
+ *
6412
+ * OpenCode is absent on purpose. Its installer writes `.opencode/plugin/harnesstrim.ts` and
6413
+ * `.opencode/package.json` *and runs an npm install*, so the containment boundary would hold a
6414
+ * `node_modules` tree: a snapshot question, not another entry in this map.
6415
+ */
6416
+ delegatedInstallReviews: {
6417
+ claude: {
6418
+ upstreamVersion: SKILLS_UPSTREAM,
6419
+ reviewedWriteSet: SKILL_ARTIFACTS.map((path) => `.claude/skills/${path}`),
6420
+ containmentBoundary: [".claude", "CLAUDE.md"],
6421
+ upstreamUninstallAvailable: true
6422
+ },
6423
+ codex: {
6424
+ upstreamVersion: SKILLS_UPSTREAM,
6425
+ reviewedWriteSet: SKILL_ARTIFACTS.map((path) => `.codex/skills/${path}`),
6426
+ containmentBoundary: [".codex", "AGENTS.md"],
6427
+ upstreamUninstallAvailable: true
6428
+ }
6429
+ }
6430
+ };
6431
+ var SKILLS_INSTALL = {
6432
+ claude: {
6433
+ directory: ".claude",
6434
+ instructions: "CLAUDE.md",
6435
+ hook: "settings.json",
6436
+ args: ["--no-hook", "--no-instructions"]
6437
+ },
6438
+ codex: {
6439
+ directory: ".codex",
6440
+ instructions: "AGENTS.md",
6441
+ hook: "hooks.json",
6442
+ args: ["--no-instructions"]
5960
6443
  }
5961
6444
  };
5962
6445
  var TESTED_UPSTREAM = "0.0.7";
@@ -6004,9 +6487,9 @@ function parseCapabilities(stdout) {
6004
6487
  if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed))
6005
6488
  return null;
6006
6489
  const record = parsed;
6007
- const version2 = record["version"];
6490
+ const version3 = record["version"];
6008
6491
  const harnesses = record["harnesses"];
6009
- if (typeof version2 !== "string" || typeof harnesses !== "object" || harnesses === null) {
6492
+ if (typeof version3 !== "string" || typeof harnesses !== "object" || harnesses === null) {
6010
6493
  return null;
6011
6494
  }
6012
6495
  const entries = {};
@@ -6016,7 +6499,7 @@ function parseCapabilities(stdout) {
6016
6499
  return null;
6017
6500
  entries[harness] = entry;
6018
6501
  }
6019
- return { version: version2, harnesses: entries };
6502
+ return { version: version3, harnesses: entries };
6020
6503
  }
6021
6504
  async function probeCapabilities(context) {
6022
6505
  const outcome2 = await context.runner.run({
@@ -6095,18 +6578,18 @@ function driftWarning(message, remediation) {
6095
6578
  function formatSurface(surface) {
6096
6579
  return `${surface.toolFamily}/${surface.interceptionPoint}`;
6097
6580
  }
6098
- function narrowable(capabilities) {
6099
- if (capabilities === null || capabilities === void 0)
6100
- return false;
6101
- const managed = MANIFEST4.harnesses.map((entry) => entry.harness);
6102
- return managed.every((harness) => {
6581
+ function assignableOn(capabilities) {
6582
+ if (capabilities === null)
6583
+ return [];
6584
+ const reviews = MANIFEST6.delegatedInstallReviews ?? {};
6585
+ return Object.keys(reviews).filter((harness) => {
6103
6586
  const observed = capabilities.harnesses[harness];
6104
- return observed !== void 0 && observed.narrowing.length > 0;
6105
- });
6587
+ return observed !== void 0 && observed.narrowing.length > 0 && writeSetStillReviewed(capabilities, harnessId(harness));
6588
+ }).map((harness) => harnessId(harness));
6106
6589
  }
6107
6590
  function writeSetStillReviewed(capabilities, harness) {
6108
- const review = MANIFEST4.delegatedInstallReview;
6109
- if (review === null || capabilities === null)
6591
+ const review = MANIFEST6.delegatedInstallReviews?.[harness];
6592
+ if (review === void 0 || capabilities === null)
6110
6593
  return false;
6111
6594
  const observed = capabilities.harnesses[harness];
6112
6595
  if (observed === void 0)
@@ -6132,35 +6615,37 @@ function compareCapabilities(manifest, capabilities) {
6132
6615
  }
6133
6616
  }
6134
6617
  }
6135
- const review = manifest.delegatedInstallReview;
6136
- if (review === null)
6137
- return warnings;
6138
- const claude = capabilities.harnesses["claude"];
6139
- if (claude === void 0)
6140
- return warnings;
6141
- const declared = claude.writeSet.map(writeSetPath);
6142
- for (const reviewed of review.reviewedWriteSet) {
6143
- if (declared.some((entry) => coveredBy(reviewed, entry)))
6144
- continue;
6145
- warnings.push(driftWarning(`the reviewed write set records ${reviewed} at ${review.upstreamVersion}, but \`harnesstrim capabilities\` (${capabilities.version}) declares nothing covering it (${declared.length === 0 ? "nothing" : declared.join(", ")})`, `Re-review the write set at the installed version before delegating another install`));
6146
- }
6147
- const boundary = review.containmentBoundary.map(writeSetPath);
6148
- for (const entry of declared) {
6149
- if (boundary.some((prefix) => coveredBy(entry, prefix)))
6618
+ for (const [harness, review] of Object.entries(manifest.delegatedInstallReviews ?? {})) {
6619
+ const observed = capabilities.harnesses[harness];
6620
+ if (observed === void 0)
6150
6621
  continue;
6151
- warnings.push(driftWarning(`\`harnesstrim capabilities\` (${capabilities.version}) declares ${entry} for claude, which sits outside the containment boundary recorded at ${review.upstreamVersion} (${boundary.join(", ")})`, "Re-review the write set: rollback restores the boundary, and a path it does not cover would survive a rollback"));
6622
+ const declared = observed.writeSet.map(writeSetPath);
6623
+ for (const reviewed of review.reviewedWriteSet) {
6624
+ if (declared.some((entry) => coveredBy(reviewed, entry)))
6625
+ continue;
6626
+ warnings.push(driftWarning(`the reviewed ${harness} write set records ${reviewed} at ${review.upstreamVersion}, but \`harnesstrim capabilities\` (${capabilities.version}) declares nothing covering it (${declared.length === 0 ? "nothing" : declared.join(", ")})`, `Re-review the write set at the installed version before delegating another install`));
6627
+ }
6628
+ const boundary = review.containmentBoundary.map(writeSetPath);
6629
+ for (const entry of declared) {
6630
+ if (boundary.some((prefix) => coveredBy(entry, prefix)))
6631
+ continue;
6632
+ warnings.push(driftWarning(`\`harnesstrim capabilities\` (${capabilities.version}) declares ${entry} for ${harness}, which sits outside the containment boundary recorded at ${review.upstreamVersion} (${boundary.join(", ")})`, "Re-review the write set: rollback restores the boundary, and a path it does not cover would survive a rollback"));
6633
+ }
6152
6634
  }
6153
6635
  return warnings;
6154
6636
  }
6155
6637
  function harnessesWiredToHarnessTrim(configs) {
6156
6638
  const wired = /* @__PURE__ */ new Set();
6157
6639
  for (const config of configs) {
6158
- if (config.commands.some((command) => identifiesCommand(command)))
6640
+ const hermesPlugin = config.harnessId === HERMES2 && (config.configPath.toLowerCase().includes(".hermes/plugins/harnesstrim") || config.commands.some((command) => /hermes plugins enable harnesstrim/i.test(command)));
6641
+ const piExtension = config.harnessId === PI2 && PI_EXTENSION_PATTERN.test(config.configPath);
6642
+ if (piExtension || hermesPlugin || config.commands.some((command) => identifiesCommand(command))) {
6159
6643
  wired.add(config.harnessId);
6644
+ }
6160
6645
  }
6161
6646
  return [...wired];
6162
6647
  }
6163
- var VERSION_PATTERN4 = /(\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?)/;
6648
+ var VERSION_PATTERN6 = /(\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?)/;
6164
6649
  async function probeExecutable(context) {
6165
6650
  const asked = await context.runner.run({
6166
6651
  executable: "harnesstrim",
@@ -6183,17 +6668,17 @@ async function probeExecutable(context) {
6183
6668
  };
6184
6669
  }
6185
6670
  if (asked.exitCode === 0) {
6186
- const version2 = VERSION_PATTERN4.exec(asked.stdout)?.[1] ?? null;
6671
+ const version3 = VERSION_PATTERN6.exec(asked.stdout)?.[1] ?? null;
6187
6672
  return {
6188
6673
  installed: true,
6189
- version: version2,
6674
+ version: version3,
6190
6675
  path: asked.executablePath,
6191
6676
  evidence: [
6192
6677
  evidence({
6193
6678
  kind: "version-output",
6194
6679
  source: "harnesstrim --version",
6195
6680
  path: asked.executablePath,
6196
- detail: version2 === null ? "reported no recognisable version" : `reported ${version2}`
6681
+ detail: version3 === null ? "reported no recognisable version" : `reported ${version3}`
6197
6682
  })
6198
6683
  ]
6199
6684
  };
@@ -6233,16 +6718,19 @@ async function probeExecutable(context) {
6233
6718
  };
6234
6719
  }
6235
6720
  function metricsLocations(context) {
6236
- return [context.fs.join(context.projectRoot, ".harnesstrim", "metrics.jsonl")];
6721
+ return [
6722
+ context.fs.join(context.projectRoot, ".harnesstrim", "metrics.jsonl"),
6723
+ context.fs.join(context.paths.home, ".hermes", "harnesstrim-metrics.jsonl")
6724
+ ];
6237
6725
  }
6238
- async function detect4(context) {
6726
+ async function detect6(context) {
6239
6727
  const probe = await probeExecutable(context);
6240
6728
  const observed = probe.installed ? await probeCapabilities(context) : null;
6241
6729
  const configured = harnessesWiredToHarnessTrim(context.harnessConfigs);
6242
6730
  const warnings = [];
6243
6731
  const evidenceItems = [...probe.evidence, ...observed?.evidence ?? []];
6244
6732
  if (observed !== null && observed.capabilities !== null) {
6245
- warnings.push(...compareCapabilities(MANIFEST4, observed.capabilities));
6733
+ warnings.push(...compareCapabilities(MANIFEST6, observed.capabilities));
6246
6734
  }
6247
6735
  for (const harness of configured) {
6248
6736
  evidenceItems.push(evidence({
@@ -6273,7 +6761,7 @@ async function detect4(context) {
6273
6761
  // cannot answer.
6274
6762
  versionVerdict: probe.version === null ? null : classifyVersion(probe.version, TESTED_VERSIONS),
6275
6763
  configuredHarnesses: configured,
6276
- unmanagedHarnessesConfigured: configured.filter((harness) => !MANIFEST4.harnesses.some((entry) => entry.harness === harness)),
6764
+ unmanagedHarnessesConfigured: configured.filter((harness) => !MANIFEST6.harnesses.some((entry) => entry.harness === harness)),
6277
6765
  // RFC 0002 §Providers may exceed the managed surface: HarnessTrim ships adapters for Hermes and
6278
6766
  // Pi as well, which Token Harness does not manage, so a wired one is reported and left alone.
6279
6767
  supportsUnmanagedHarnesses: true,
@@ -6281,10 +6769,9 @@ async function detect4(context) {
6281
6769
  // circumstantial: PLAN §11 says Token Harness never installs it, so every installation it ever
6282
6770
  // sees is the user's.
6283
6771
  managedByTokenHarness: false,
6284
- // PLAN §15 item 46. Decided by the build in front of us, not by the manifest: a build that
6285
- // declares narrowing flags for every managed harness can be asked for the narrowed states
6286
- // §6.1 recorded as unproducible, and one that cannot answer keeps the `0.0.5` verdict.
6287
- assignable: observed?.capabilities === null ? false : narrowable(observed?.capabilities),
6772
+ // PLAN §15 item 46, per harness. Decided by the build in front of us and by which harnesses
6773
+ // carry a reviewed write set: those are the two things that make the assignment producible.
6774
+ assignableHarnesses: assignableOn(observed?.capabilities ?? null),
6288
6775
  evidence: evidenceItems,
6289
6776
  warnings
6290
6777
  };
@@ -6356,7 +6843,7 @@ function toEvent(event, sourceId, ordinal, line, context) {
6356
6843
  pipelineId: null,
6357
6844
  pipelineOrder: null,
6358
6845
  toolFamily: event.tool === "" ? null : event.tool,
6359
- capability: event.harness === OPENCODE2 ? "tool.output.reduce" : "shell.output.reduce"
6846
+ capability: event.harness === OPENCODE2 || event.harness === HERMES2 || event.harness === PI2 ? "tool.output.reduce" : "shell.output.reduce"
6360
6847
  },
6361
6848
  measurement: {
6362
6849
  class: dryrun || hasUnprovenMode ? "counterfactual" : hasTokens ? "exact-local" : "estimated-local",
@@ -6389,7 +6876,7 @@ function toEvent(event, sourceId, ordinal, line, context) {
6389
6876
  source: { nativeEventId: native ? eventId : null, importedAt: context.now() }
6390
6877
  };
6391
6878
  }
6392
- async function verify4(context) {
6879
+ async function verify6(context) {
6393
6880
  const checks = [];
6394
6881
  const diagnostics = [];
6395
6882
  const probe = await probeExecutable(context);
@@ -6606,84 +7093,102 @@ async function collectMetrics(context, store) {
6606
7093
  diagnostics
6607
7094
  };
6608
7095
  }
6609
- async function plan(context, request) {
6610
- const claudeDirectory = context.fs.join(context.projectRoot, ".claude");
6611
- const skillsDirectory = context.fs.join(claudeDirectory, "skills");
6612
- const expectedArtifacts = Object.entries(CLAUDE_ARTIFACT_DIGESTS).map(([path, digest]) => ({
6613
- path: context.fs.join(skillsDirectory, ...path.split("/")),
7096
+ function skillArtifacts(context, harness) {
7097
+ const install = SKILLS_INSTALL[harness];
7098
+ if (install === void 0)
7099
+ return [];
7100
+ const skills = context.fs.join(context.projectRoot, install.directory, "skills");
7101
+ return Object.entries(SKILL_ARTIFACT_DIGESTS).map(([path, digest]) => ({
7102
+ path: context.fs.join(skills, ...path.split("/")),
6614
7103
  digest
6615
7104
  }));
6616
- if (!request.harnesses.some((harness) => harness.id === CLAUDE2)) {
7105
+ }
7106
+ async function plan(context, request) {
7107
+ const reviews = MANIFEST6.delegatedInstallReviews ?? {};
7108
+ const targets = request.harnesses.map((harness) => harness.id).filter((harness) => reviews[harness] !== void 0 && SKILLS_INSTALL[harness] !== void 0);
7109
+ if (targets.length === 0) {
6617
7110
  return { providerId: HARNESSTRIM2, desiredState: request.desiredState, actions: [] };
6618
7111
  }
6619
7112
  if (request.desiredState === "absent") {
6620
- const actions = expectedArtifacts.map((artifact) => ({
7113
+ const actions2 = targets.flatMap((harness) => skillArtifacts(context, harness).map((artifact) => ({
6621
7114
  kind: "remove-owned-change",
6622
- id: `harnesstrim-claude-skill-remove-${digestText(artifact.path).slice(7, 15)}`,
7115
+ id: `harnesstrim-${harness}-skill-remove-${digestText(artifact.path).slice(7, 15)}`,
6623
7116
  riskClass: "reversible",
6624
7117
  requiresNetwork: false,
6625
7118
  requiresElevation: false,
6626
7119
  affectedPaths: [artifact.path],
6627
7120
  affectedProcesses: [],
6628
- preconditions: ["the HarnessTrim skill still matches the reviewed 0.0.7 artifact"],
7121
+ preconditions: [
7122
+ `the HarnessTrim skill still matches the reviewed ${SKILLS_UPSTREAM} artifact`
7123
+ ],
6629
7124
  postconditions: ["the owned HarnessTrim skill is absent"],
6630
7125
  rollbackData: "file-snapshot",
6631
- explanation: `Remove the owned HarnessTrim Claude skill ${context.fs.basename(context.fs.dirname(artifact.path))}`,
7126
+ explanation: `Remove the owned HarnessTrim ${harness} skill ${context.fs.basename(context.fs.dirname(artifact.path))}`,
6632
7127
  path: artifact.path,
6633
- reverses: `harnesstrim-claude-skills-${digestText(context.projectRoot).slice(7, 15)}`,
6634
- target: { kind: "owned-file", path: artifact.path, digest: artifact.digest, mode: null }
6635
- }));
6636
- return { providerId: HARNESSTRIM2, desiredState: "absent", actions };
7128
+ reverses: `harnesstrim-${harness}-skills-${digestText(context.projectRoot).slice(7, 15)}`,
7129
+ target: {
7130
+ kind: "owned-file",
7131
+ path: artifact.path,
7132
+ digest: artifact.digest,
7133
+ mode: null
7134
+ }
7135
+ })));
7136
+ return { providerId: HARNESSTRIM2, desiredState: "absent", actions: actions2 };
6637
7137
  }
6638
7138
  const installed = await probeExecutable(context);
6639
7139
  if (!installed.installed) {
6640
7140
  return { providerId: HARNESSTRIM2, desiredState: "configured", actions: [] };
6641
7141
  }
6642
7142
  const observed = await probeCapabilities(context);
6643
- const reviewed = observed.capabilities === null ? installed.version === TESTED_UPSTREAM : writeSetStillReviewed(observed.capabilities, CLAUDE2);
6644
- if (!reviewed) {
6645
- return { providerId: HARNESSTRIM2, desiredState: "configured", actions: [] };
7143
+ const actions = [];
7144
+ for (const harness of targets) {
7145
+ const review = reviews[harness];
7146
+ const install = SKILLS_INSTALL[harness];
7147
+ if (review === void 0 || install === void 0)
7148
+ continue;
7149
+ const reviewed = observed.capabilities === null ? installed.version === review.upstreamVersion : writeSetStillReviewed(observed.capabilities, harnessId(harness));
7150
+ if (!reviewed)
7151
+ continue;
7152
+ const expectedArtifacts = skillArtifacts(context, harness);
7153
+ actions.push({
7154
+ kind: "delegated-provider-install",
7155
+ id: `harnesstrim-${harness}-skills-${digestText(context.projectRoot).slice(7, 15)}`,
7156
+ riskClass: "delegated",
7157
+ requiresNetwork: false,
7158
+ requiresElevation: false,
7159
+ affectedPaths: expectedArtifacts.map((artifact) => artifact.path),
7160
+ affectedProcesses: ["harnesstrim"],
7161
+ preconditions: [
7162
+ "harnesstrim is installed, on PATH, and declares the reviewed write set",
7163
+ `the reviewed installer writes only the declared ${harness} skill artifacts`
7164
+ ],
7165
+ postconditions: [
7166
+ `HarnessTrim ${harness} skills match the reviewed ${review.upstreamVersion} artifacts`,
7167
+ "no HarnessTrim hook or reduce-pipe instruction was added"
7168
+ ],
7169
+ rollbackData: "directory-snapshot",
7170
+ explanation: `Install HarnessTrim ${harness} skills without a hook or reduce-pipe instruction`,
7171
+ executable: "harnesstrim",
7172
+ args: ["install", harness, context.projectRoot, "--apply", ...install.args],
7173
+ containmentBoundary: review.containmentBoundary.map((path) => context.fs.join(context.projectRoot, path)),
7174
+ expectedArtifacts,
7175
+ // The two files each installer reports skipping. Named here because they must not appear.
7176
+ protectedPaths: [
7177
+ context.fs.join(context.projectRoot, install.directory, install.hook),
7178
+ context.fs.join(context.projectRoot, install.instructions)
7179
+ ],
7180
+ rollbackStrategy: "restore-snapshot",
7181
+ snapshotSizeCapBytes: 1048576,
7182
+ upstreamUninstallAvailable: review.upstreamUninstallAvailable
7183
+ });
6646
7184
  }
6647
- const review = MANIFEST4.delegatedInstallReview;
6648
- if (review === null)
6649
- throw new Error("HarnessTrim delegated-install review is missing");
6650
- const action = {
6651
- kind: "delegated-provider-install",
6652
- id: `harnesstrim-claude-skills-${digestText(context.projectRoot).slice(7, 15)}`,
6653
- riskClass: "delegated",
6654
- requiresNetwork: false,
6655
- requiresElevation: false,
6656
- affectedPaths: expectedArtifacts.map((artifact) => artifact.path),
6657
- affectedProcesses: ["harnesstrim"],
6658
- preconditions: [
6659
- "harnesstrim 0.0.7 is installed and on PATH",
6660
- "the reviewed installer writes only the declared Claude skill artifacts"
6661
- ],
6662
- postconditions: [
6663
- "HarnessTrim Claude skills match the reviewed 0.0.7 artifacts",
6664
- "no HarnessTrim hook or reduce-pipe instruction was added"
6665
- ],
6666
- rollbackData: "directory-snapshot",
6667
- explanation: "Install HarnessTrim Claude skills without a hook or reduce-pipe instruction",
6668
- executable: "harnesstrim",
6669
- args: ["install", "claude", context.projectRoot, "--apply", "--no-hook", "--no-instructions"],
6670
- containmentBoundary: review.containmentBoundary.map((path) => context.fs.join(context.projectRoot, path)),
6671
- expectedArtifacts,
6672
- protectedPaths: [
6673
- context.fs.join(claudeDirectory, "settings.json"),
6674
- context.fs.join(context.projectRoot, "CLAUDE.md")
6675
- ],
6676
- rollbackStrategy: "restore-snapshot",
6677
- snapshotSizeCapBytes: 1048576,
6678
- upstreamUninstallAvailable: review.upstreamUninstallAvailable
6679
- };
6680
- return { providerId: HARNESSTRIM2, desiredState: "configured", actions: [action] };
7185
+ return { providerId: HARNESSTRIM2, desiredState: "configured", actions };
6681
7186
  }
6682
7187
  var harnesstrimAdapter = {
6683
- manifest: MANIFEST4,
6684
- detect: detect4,
7188
+ manifest: MANIFEST6,
7189
+ detect: detect6,
6685
7190
  identifiesCommand,
6686
- verify: verify4,
7191
+ verify: verify6,
6687
7192
  collectMetrics,
6688
7193
  plan,
6689
7194
  plansWithoutOwnership: true
@@ -6885,7 +7390,7 @@ function buildRtkPlan(input) {
6885
7390
  var RTK2 = providerId("rtk");
6886
7391
  var CLAUDE3 = harnessId("claude");
6887
7392
  var OPENCODE3 = harnessId("opencode");
6888
- var MANIFEST5 = {
7393
+ var MANIFEST7 = {
6889
7394
  schemaVersion: MANIFEST_SCHEMA_VERSION,
6890
7395
  id: RTK2,
6891
7396
  displayName: "RTK",
@@ -7045,10 +7550,10 @@ var MANIFEST5 = {
7045
7550
  // location differs per platform; `rtkDatabasePath` is the single derivation.
7046
7551
  locations: ["<user data directory>/rtk/history.db"]
7047
7552
  },
7048
- delegatedInstallReview: null
7553
+ delegatedInstallReviews: null
7049
7554
  };
7050
7555
  var TESTED_VERSIONS2 = { minimum: "0.40.0", maximum: "0.44.0" };
7051
- var VERSION_PATTERN5 = /(\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?)/;
7556
+ var VERSION_PATTERN7 = /(\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?)/;
7052
7557
  var HOOK_COMMAND_PATTERN2 = /(^|[\\/\s"'])rtk(\.exe)?([\s"']|$)/i;
7053
7558
  var PLUGIN_MODULE_PATTERN3 = /(^|[\\/])rtk\.(ts|js|mjs|cjs|mts|cts)$/i;
7054
7559
  function isRecord4(value) {
@@ -7120,17 +7625,17 @@ async function readVersion(context) {
7120
7625
  ]
7121
7626
  };
7122
7627
  }
7123
- const version2 = VERSION_PATTERN5.exec(outcome2.stdout)?.[1] ?? null;
7628
+ const version3 = VERSION_PATTERN7.exec(outcome2.stdout)?.[1] ?? null;
7124
7629
  return {
7125
- version: version2,
7126
- verdict: version2 === null ? null : classifyVersion(version2, TESTED_VERSIONS2),
7630
+ version: version3,
7631
+ verdict: version3 === null ? null : classifyVersion(version3, TESTED_VERSIONS2),
7127
7632
  executable: outcome2.executablePath,
7128
7633
  evidence: [
7129
7634
  evidence({
7130
7635
  kind: "version-output",
7131
7636
  source: "rtk --version",
7132
7637
  path: outcome2.executablePath,
7133
- detail: version2 === null ? "reported no recognisable version" : `reported ${version2}`
7638
+ detail: version3 === null ? "reported no recognisable version" : `reported ${version3}`
7134
7639
  })
7135
7640
  ]
7136
7641
  };
@@ -7166,11 +7671,11 @@ async function readAnalytics(context) {
7166
7671
  ]
7167
7672
  };
7168
7673
  }
7169
- async function detect5(context) {
7674
+ async function detect7(context) {
7170
7675
  const evidenceItems = [];
7171
7676
  const warnings = [];
7172
- const version2 = await readVersion(context);
7173
- evidenceItems.push(...version2.evidence);
7677
+ const version3 = await readVersion(context);
7678
+ evidenceItems.push(...version3.evidence);
7174
7679
  const configured = harnessesWiredToRtk(context.harnessConfigs);
7175
7680
  for (const harness of configured) {
7176
7681
  evidenceItems.push(evidence({
@@ -7180,15 +7685,15 @@ async function detect5(context) {
7180
7685
  detail: "names rtk in a hook command"
7181
7686
  }));
7182
7687
  }
7183
- if (version2.verdict === "unknown-newer") {
7688
+ if (version3.verdict === "unknown-newer") {
7184
7689
  warnings.push(diagnostic({
7185
7690
  severity: "warning",
7186
7691
  code: "provider-version-unknown-newer",
7187
- message: `RTK ${String(version2.version)} is newer than the versions Token Harness has observed (up to ${TESTED_VERSIONS2.maximum})`,
7692
+ message: `RTK ${String(version3.version)} is newer than the versions Token Harness has observed (up to ${TESTED_VERSIONS2.maximum})`,
7188
7693
  remediation: "Check the release notes for analytics or hook changes before applying a plan"
7189
7694
  }));
7190
7695
  }
7191
- const state = version2.version === null ? configured.length > 0 ? "broken" : "absent" : configured.length > 0 ? "configured" : "installed";
7696
+ const state = version3.version === null ? configured.length > 0 ? "broken" : "absent" : configured.length > 0 ? "configured" : "installed";
7192
7697
  if (state === "broken") {
7193
7698
  warnings.push(diagnostic({
7194
7699
  severity: "error",
@@ -7201,12 +7706,12 @@ async function detect5(context) {
7201
7706
  return {
7202
7707
  providerId: RTK2,
7203
7708
  state,
7204
- version: version2.version,
7205
- executable: version2.executable,
7709
+ version: version3.version,
7710
+ executable: version3.executable,
7206
7711
  // Detection reports what it saw; which channel installed it is an installation-stage
7207
7712
  // question and guessing it from a path would be a guess.
7208
7713
  installationChannel: null,
7209
- versionVerdict: version2.verdict,
7714
+ versionVerdict: version3.verdict,
7210
7715
  configuredHarnesses: configured,
7211
7716
  unmanagedHarnessesConfigured: [],
7212
7717
  /**
@@ -7227,29 +7732,35 @@ async function detect5(context) {
7227
7732
  // every installation it finds is the user's.
7228
7733
  managedByTokenHarness: false,
7229
7734
  /**
7230
- * Always, and independent of the observed version.
7735
+ * Claude Code alone, and independent of the observed version.
7736
+ *
7737
+ * RTK's assignment is produced by this build writing the hook itself — `rtk-plan.ts` appends the
7738
+ * entry — so nothing about the installed `rtk` decides whether the state is reachable. What does
7739
+ * decide it is which harness schema that builder knows, and it knows one: `HOOK_LIST_HARNESSES`
7740
+ * holds `claude` and the builder refuses anything else, because a `{matcher, hooks:[…]}` object
7741
+ * at `hooks.<event>` is Claude Code's schema and nothing else's.
7231
7742
  *
7232
- * RTK's assignment is produced by this build writing the hook itself — `rtk-plan.ts` appends
7233
- * the entry — so nothing about the installed `rtk` decides whether the state is reachable.
7234
- * That is the difference from HarnessTrim, whose assignment is produced by *its* installer and
7235
- * therefore depends on which flags that installer has.
7743
+ * RTK claims OpenCode too, from spike 9.1, and that claim is real — detected, adopted, verified
7744
+ * and measured there. It is simply not *written* there: its OpenCode integration is a plugin
7745
+ * module `rtk init -g --opencode` installs globally, which this build has no action for. Saying
7746
+ * so here is what stops the resolver assigning a scope nothing can produce.
7236
7747
  */
7237
- assignable: true,
7748
+ assignableHarnesses: [CLAUDE3],
7238
7749
  evidence: evidenceItems,
7239
7750
  warnings
7240
7751
  };
7241
7752
  }
7242
- async function verify5(context) {
7753
+ async function verify7(context) {
7243
7754
  const checks = [];
7244
7755
  const diagnostics = [];
7245
- const version2 = await readVersion(context);
7756
+ const version3 = await readVersion(context);
7246
7757
  checks.push({
7247
7758
  id: "executable-resolves",
7248
- status: version2.version === null ? "fail" : "pass",
7249
- summary: version2.version === null ? "rtk could not be run" : `rtk ${version2.version}`,
7250
- achievedTier: version2.version === null ? null : "presence",
7251
- evidence: version2.evidence,
7252
- remediation: version2.version === null ? "Install RTK, or add it to PATH" : null
7759
+ status: version3.version === null ? "fail" : "pass",
7760
+ summary: version3.version === null ? "rtk could not be run" : `rtk ${version3.version}`,
7761
+ achievedTier: version3.version === null ? null : "presence",
7762
+ evidence: version3.evidence,
7763
+ remediation: version3.version === null ? "Install RTK, or add it to PATH" : null
7253
7764
  });
7254
7765
  const configured = harnessesWiredToRtk(context.harnessConfigs);
7255
7766
  checks.push({
@@ -7549,19 +8060,19 @@ function identifiesCommand2(command) {
7549
8060
  return HOOK_COMMAND_PATTERN2.test(command) || PLUGIN_MODULE_PATTERN3.test(command);
7550
8061
  }
7551
8062
  async function plan2(context, request) {
7552
- const version2 = await readVersion(context);
8063
+ const version3 = await readVersion(context);
7553
8064
  return buildRtkPlan({
7554
8065
  context,
7555
8066
  request,
7556
- installed: version2.version !== null,
8067
+ installed: version3.version !== null,
7557
8068
  identifiesCommand: identifiesCommand2,
7558
- installationChannels: MANIFEST5.installationChannels
8069
+ installationChannels: MANIFEST7.installationChannels
7559
8070
  });
7560
8071
  }
7561
8072
  var rtkAdapter = {
7562
- manifest: MANIFEST5,
7563
- detect: detect5,
7564
- verify: verify5,
8073
+ manifest: MANIFEST7,
8074
+ detect: detect7,
8075
+ verify: verify7,
7565
8076
  collectMetrics: collectMetrics2,
7566
8077
  identifiesCommand: identifiesCommand2,
7567
8078
  plan: plan2
@@ -7613,13 +8124,13 @@ async function computePlan(context) {
7613
8124
  for (const adapter of providerAdapters) {
7614
8125
  const detection = await adapter.detect(providerContext);
7615
8126
  versions.providers[adapter.manifest.id] = detection.version;
7616
- assignable.set(adapter.manifest.id, detection.assignable);
8127
+ assignable.set(adapter.manifest.id, new Set(detection.assignableHarnesses));
7617
8128
  }
7618
8129
  }
7619
8130
  const providers = providerAdapters.map((adapter) => ({
7620
8131
  id: adapter.manifest.id,
7621
8132
  capabilities: adapter.manifest.capabilities,
7622
- assignable: assignable.get(adapter.manifest.id) ?? false
8133
+ assignableHarnesses: assignable.get(adapter.manifest.id) ?? /* @__PURE__ */ new Set()
7623
8134
  }));
7624
8135
  const resolution = resolveOwnership({
7625
8136
  profile,
@@ -7736,9 +8247,10 @@ async function computePlan(context) {
7736
8247
  remediation: null
7737
8248
  }));
7738
8249
  }
8250
+ const refusalIsTheOutcome = actions.length === 0;
7739
8251
  for (const entry of blocked) {
7740
8252
  diagnostics.push(diagnostic({
7741
- severity: "error",
8253
+ severity: refusalIsTheOutcome ? "error" : "warning",
7742
8254
  code: "managed-mutation-blocked",
7743
8255
  message: `plan refuses managed mutation of ${entry.harness} by ${entry.provider}${entry.providerVersion !== null ? ` at ${entry.providerVersion}` : ""}: ${entry.missing}`,
7744
8256
  remediation: "Add a compatibility row whose fixture proves this combination, or configure this integration by hand"
@@ -7777,7 +8289,11 @@ async function runPlan(context) {
7777
8289
  const computed = await computePlan(context);
7778
8290
  const persisted = await persist(context, computed);
7779
8291
  const report = { ...computed.report, persisted };
7780
- const exitCode = computed.blocked.length > 0 ? EXIT_CODES["unsupported-environment"] : report.conflicts.length > 0 ? EXIT_CODES["blocked-by-conflict"] : EXIT_CODES.ok;
8292
+ const exitCode = planExitCode({
8293
+ blocked: computed.blocked.length,
8294
+ actions: report.actions.length,
8295
+ conflicts: report.conflicts.length
8296
+ });
7781
8297
  return commandResult({
7782
8298
  command: "plan",
7783
8299
  exitCode,
@@ -7785,6 +8301,13 @@ async function runPlan(context) {
7785
8301
  diagnostics: computed.diagnostics
7786
8302
  });
7787
8303
  }
8304
+ function planExitCode(counts) {
8305
+ if (counts.blocked > 0 && counts.actions === 0)
8306
+ return EXIT_CODES["unsupported-environment"];
8307
+ if (counts.conflicts > 0)
8308
+ return EXIT_CODES["blocked-by-conflict"];
8309
+ return EXIT_CODES.ok;
8310
+ }
7788
8311
 
7789
8312
  // apps/cli/dist/src/commands/apply.js
7790
8313
  var PLANS_DIRECTORY = "plans";
@@ -7848,7 +8371,7 @@ async function runApply(context) {
7848
8371
  const diagnostics = [];
7849
8372
  const computed = await computePlan(context);
7850
8373
  diagnostics.push(...computed.diagnostics);
7851
- if (computed.blocked.length > 0) {
8374
+ if (computed.blocked.length > 0 && computed.report.actions.length === 0) {
7852
8375
  return finish("rejected", EXIT_CODES["unsupported-environment"], emptyReport("rejected"), diagnostics);
7853
8376
  }
7854
8377
  if (computed.report.conflicts.length > 0) {
@@ -8468,7 +8991,7 @@ async function runStatus(context) {
8468
8991
  providers.push({
8469
8992
  id: adapter.manifest.id,
8470
8993
  capabilities: adapter.manifest.capabilities,
8471
- assignable: detection.assignable
8994
+ assignableHarnesses: new Set(detection.assignableHarnesses)
8472
8995
  });
8473
8996
  }
8474
8997
  const resolution = resolveOwnership({
@@ -9681,7 +10204,7 @@ function usageText(topic) {
9681
10204
  }
9682
10205
 
9683
10206
  // apps/cli/dist/src/version.js
9684
- var TOOL_VERSION = "0.1.2";
10207
+ var TOOL_VERSION = "0.1.3";
9685
10208
 
9686
10209
  // apps/cli/dist/src/run.js
9687
10210
  var DEFAULT_COMMANDS = {