token-harness 0.1.0 → 0.1.2

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 (4) hide show
  1. package/README.md +577 -236
  2. package/package.json +1 -1
  3. package/sbom.json +5 -5
  4. package/token-harness.mjs +1756 -275
package/token-harness.mjs CHANGED
@@ -111,6 +111,113 @@ function classifyVersion(observed, range) {
111
111
  return compareVersions(version2, maximum) > 0 ? "unknown-newer" : "in-range";
112
112
  }
113
113
 
114
+ // packages/core/dist/src/domain/compatibility-rows.js
115
+ var COMPATIBILITY_ROWS = [
116
+ {
117
+ harness: "claude",
118
+ harnessVersion: { minimum: "2.1.220", maximum: "2.1.220" },
119
+ provider: "rtk",
120
+ providerVersion: "0.44.0",
121
+ platform: { os: "windows", wsl: false, supported: true, limitation: null },
122
+ configSchema: "claude-settings-json-hooks",
123
+ fixture: "tests/fixtures/rows/rtk-claude-windows",
124
+ verificationTier: "canary"
125
+ },
126
+ {
127
+ harness: "claude",
128
+ harnessVersion: { minimum: "2.1.220", maximum: "2.1.220" },
129
+ provider: "harnesstrim",
130
+ providerVersion: "0.1.0",
131
+ platform: { os: "windows", wsl: false, supported: true, limitation: null },
132
+ // The install writes skill files, not a document this build parses, so the schema it depends on
133
+ // is the skills directory layout rather than `settings.json`.
134
+ configSchema: "claude-skills-directory",
135
+ fixture: "tests/fixtures/rows/harnesstrim-claude-windows",
136
+ verificationTier: "config-only"
137
+ }
138
+ ];
139
+ function classifyRowHarnessVersion(rows, observed) {
140
+ if (rows.length === 0)
141
+ return null;
142
+ const parsed = parseSemanticVersion(observed);
143
+ if (parsed === null)
144
+ return null;
145
+ let lowestMinimum = null;
146
+ let highestMaximum = null;
147
+ for (const row2 of rows) {
148
+ const minimum = parseSemanticVersion(row2.harnessVersion.minimum);
149
+ const maximum = parseSemanticVersion(row2.harnessVersion.maximum);
150
+ if (minimum === null || maximum === null)
151
+ continue;
152
+ if (compareVersions(parsed, minimum) >= 0 && compareVersions(parsed, maximum) <= 0)
153
+ return "in-row";
154
+ if (lowestMinimum === null || compareVersions(minimum, lowestMinimum) < 0)
155
+ lowestMinimum = minimum;
156
+ if (highestMaximum === null || compareVersions(maximum, highestMaximum) > 0)
157
+ highestMaximum = maximum;
158
+ }
159
+ if (lowestMinimum === null || highestMaximum === null)
160
+ return null;
161
+ if (compareVersions(parsed, lowestMinimum) < 0)
162
+ return "unknown-older";
163
+ if (compareVersions(parsed, highestMaximum) > 0)
164
+ return "unknown-newer";
165
+ return "below-range";
166
+ }
167
+ function rowsFor(rows, combination) {
168
+ return rows.filter((row2) => row2.provider === combination.provider && row2.harness === combination.harness && row2.platform.os === combination.os && row2.platform.wsl === combination.wsl && row2.platform.supported);
169
+ }
170
+ function admitManagedMutation(rows, combination) {
171
+ const keyed = rowsFor(rows, combination);
172
+ if (combination.providerVersion === null)
173
+ return {
174
+ state: "refused",
175
+ verdict: "no-row",
176
+ missing: "the exact provider version a row would record could not be established"
177
+ };
178
+ if (combination.harnessVersion === null)
179
+ return {
180
+ state: "refused",
181
+ verdict: "no-row",
182
+ missing: "the exact harness version a row would cover could not be established"
183
+ };
184
+ if (keyed.length === 0)
185
+ return {
186
+ state: "refused",
187
+ verdict: "no-row",
188
+ missing: `a reviewed config schema and fixture for ${combination.provider} on ${combination.harness} at ${combination.providerVersion} on ${combination.os}`
189
+ };
190
+ const versioned = keyed.filter((row3) => row3.providerVersion === combination.providerVersion);
191
+ if (versioned.length === 0) {
192
+ const fixture = keyed[0]?.fixture ?? "a provider fixture";
193
+ return {
194
+ state: "refused",
195
+ verdict: "no-row",
196
+ missing: `a provider fixture covering ${combination.provider} at ${combination.providerVersion} (the nearest row's fixture is ${JSON.stringify(fixture)})`
197
+ };
198
+ }
199
+ const parsedHarness = parseSemanticVersion(combination.harnessVersion);
200
+ const row2 = versioned.find((candidate) => {
201
+ const minimum = parseSemanticVersion(candidate.harnessVersion.minimum);
202
+ const maximum = parseSemanticVersion(candidate.harnessVersion.maximum);
203
+ return minimum !== null && maximum !== null && parsedHarness !== null && compareVersions(parsedHarness, minimum) >= 0 && compareVersions(parsedHarness, maximum) <= 0;
204
+ });
205
+ if (row2 !== void 0)
206
+ return { state: "admitted", row: row2 };
207
+ const verdict = parsedHarness === null ? null : classifyRowHarnessVersion(versioned, combination.harnessVersion);
208
+ if (verdict === null || verdict === "in-row")
209
+ return {
210
+ state: "refused",
211
+ verdict: "no-row",
212
+ missing: `a reviewed config schema and fixture for ${combination.provider} on ${combination.harness}`
213
+ };
214
+ return {
215
+ state: "refused",
216
+ verdict,
217
+ missing: verdict === "unknown-newer" ? `a harness schema for ${combination.harness} newer than every row's maximum (the newest row names schema ${JSON.stringify(versioned[0]?.configSchema ?? "none")})` : verdict === "unknown-older" ? `a harness schema for ${combination.harness} older than every row's minimum (the oldest row names schema ${JSON.stringify(versioned[0]?.configSchema ?? "none")})` : `a harness schema covering ${combination.harness} at ${combination.harnessVersion}, between the rows that name schemas ${JSON.stringify(keyed.map((entry) => entry.configSchema))}`
218
+ };
219
+ }
220
+
114
221
  // packages/core/dist/src/domain/compatibility.js
115
222
  function coversHarness(rule, harness) {
116
223
  return rule.harnesses === "*" || rule.harnesses.includes(harness);
@@ -155,6 +262,7 @@ function diagnostic(init) {
155
262
  severity: init.severity,
156
263
  code: init.code,
157
264
  message: init.message,
265
+ subject: init.subject ?? null,
158
266
  path: init.path ?? null,
159
267
  remediation: init.remediation ?? null
160
268
  };
@@ -206,6 +314,7 @@ function harnessId(value) {
206
314
  }
207
315
  return value;
208
316
  }
317
+ var UNATTRIBUTED_PROJECT_ID = "p_unattributed";
209
318
 
210
319
  // packages/core/dist/src/domain/manifest.js
211
320
  var MANIFEST_SCHEMA_VERSION = 1;
@@ -263,6 +372,9 @@ function isStartableExecutable(resolved) {
263
372
  // packages/core/dist/src/domain/process.js
264
373
  var DEFAULT_MAX_OUTPUT_BYTES = 1024 * 1024;
265
374
  var DEFAULT_PROCESS_TIMEOUT_MS = 3e4;
375
+ function processSucceeded(outcome2) {
376
+ return outcome2.failure === null && outcome2.exitCode === 0;
377
+ }
266
378
 
267
379
  // packages/core/dist/src/domain/redaction.js
268
380
  var REDACTED = "[redacted]";
@@ -584,12 +696,13 @@ function resolveOwnership(input) {
584
696
  continue;
585
697
  }
586
698
  if (!provider.assignable) {
699
+ const observed = input.observedVersions[provider.id] ?? null;
587
700
  exclusions.push({
588
701
  scope,
589
702
  excluded: provider.id,
590
703
  retained: null,
591
704
  reason: [
592
- `${provider.id} implements ${capability} but no installer state produces it in isolation`,
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`,
593
706
  "RFC 0003: a capability the provider has but cannot be asked for is not an assignable capability",
594
707
  "It is still detected, adopted, reconciled against the owner, and measured"
595
708
  ]
@@ -630,6 +743,7 @@ function resolveOwnership(input) {
630
743
  }
631
744
  }
632
745
  }
746
+ narrowChannelOverlaps({ ownership, exclusions, conflicts, input });
633
747
  const ownerByScope = new Map(ownership.map((entry) => [formatCapabilityScope(entry.scope), entry.owner]));
634
748
  for (const exclusion of exclusions) {
635
749
  if (exclusion.retained !== null)
@@ -640,6 +754,66 @@ function resolveOwnership(input) {
640
754
  }
641
755
  return { ownership, exclusions, conflicts, pipelineId: derivePipelineId(ownership) };
642
756
  }
757
+ function narrowChannelOverlaps(context) {
758
+ const { ownership, exclusions, conflicts, input } = context;
759
+ const channels = /* @__PURE__ */ new Map();
760
+ for (const entry of ownership) {
761
+ if (entry.mode !== "exclusive")
762
+ continue;
763
+ const key = `${entry.scope.harness}/${entry.scope.toolFamily}/${entry.scope.capability}`;
764
+ const group = channels.get(key) ?? [];
765
+ group.push(entry);
766
+ channels.set(key, group);
767
+ }
768
+ const dropped = /* @__PURE__ */ new Set();
769
+ for (const group of channels.values()) {
770
+ const owners = [...new Set(group.map((entry) => entry.owner))];
771
+ if (owners.length < 2)
772
+ continue;
773
+ const first = group[0];
774
+ if (first === void 0)
775
+ continue;
776
+ const { harness, toolFamily, capability } = first.scope;
777
+ const rule = findCompatibilityRule(input.rules, { providers: owners, harness, capability });
778
+ if (rule === null || rule.outcome !== "narrowed" || rule.retains === void 0) {
779
+ conflicts.push({
780
+ code: "exclusive-scope-contested",
781
+ scope: `${harness}/${toolFamily}/${capability}`,
782
+ claimants: [...owners].sort(),
783
+ detail: [
784
+ `${owners.join(" and ")} each own ${capability} on ${harness}/${toolFamily}, at different interception points`,
785
+ "The interception points differ but the payload does not, so the reduction would run twice",
786
+ rule === null ? "No rule names this pair on this channel, and an unreviewed overlap is not permission" : `Rule ${rule.id} does not say which provider keeps the channel`
787
+ ],
788
+ remediation: `Choose one owner for ${harness}/${toolFamily}/${capability} with \`profile: custom\``
789
+ });
790
+ for (const entry of group)
791
+ dropped.add(entry);
792
+ continue;
793
+ }
794
+ const retains = rule.retains;
795
+ for (const entry of group) {
796
+ if (entry.owner === retains)
797
+ continue;
798
+ dropped.add(entry);
799
+ exclusions.push({
800
+ scope: entry.scope,
801
+ excluded: entry.owner,
802
+ retained: retains,
803
+ reason: [
804
+ `rule ${rule.id} keeps ${capability} on ${harness}/${toolFamily} with ${retains}, at every interception point`,
805
+ `${entry.owner} is narrowed to the install its own flags produce rather than excluded whole`,
806
+ rule.rationale
807
+ ]
808
+ });
809
+ }
810
+ }
811
+ if (dropped.size > 0) {
812
+ const kept = ownership.filter((entry) => !dropped.has(entry));
813
+ ownership.length = 0;
814
+ ownership.push(...kept);
815
+ }
816
+ }
643
817
  function resolveContested(context) {
644
818
  const { scope, claims, capability, input, ownership, exclusions, conflicts } = context;
645
819
  const claimants = claims.map((claim) => claim.provider);
@@ -715,6 +889,41 @@ function resolveContested(context) {
715
889
  }
716
890
  return;
717
891
  }
892
+ if (rule.outcome === "narrowed") {
893
+ const retains = rule.retains;
894
+ if (retains === void 0) {
895
+ conflicts.push({
896
+ code: "compatibility-rule-malformed",
897
+ scope: formatCapabilityScope(scope),
898
+ claimants,
899
+ detail: [
900
+ `Rule ${rule.id} narrows a pair without naming which provider keeps the channel`,
901
+ "Choosing one here would make the resolver decide what the rule exists to record"
902
+ ],
903
+ remediation: `Fix rule ${rule.id} so it names the provider that retains the capability`
904
+ });
905
+ return;
906
+ }
907
+ for (const claimant of claimants) {
908
+ if (claimant === retains)
909
+ continue;
910
+ exclusions.push({
911
+ scope,
912
+ excluded: claimant,
913
+ retained: retains,
914
+ reason: [
915
+ `rule ${rule.id} keeps ${capability} on ${scope.harness} with ${retains}`,
916
+ `${claimant} is narrowed to the install its own flags produce rather than excluded whole`,
917
+ rule.rationale
918
+ ]
919
+ });
920
+ }
921
+ const retainedClaim = claims.find((claim) => claim.provider === retains);
922
+ if (retainedClaim !== void 0) {
923
+ ownership.push({ scope, owner: retains, mode: retainedClaim.mode, order: 0 });
924
+ }
925
+ return;
926
+ }
718
927
  const winner = rule.providers.find((provider) => claimants.includes(provider)) ?? claimants[0];
719
928
  for (const claimant of claimants) {
720
929
  if (claimant === winner)
@@ -745,9 +954,10 @@ var RTK_HARNESSTRIM_SHELL_OVERLAP = {
745
954
  // for different reasons. Listing them individually would imply a fourth harness is exempt.
746
955
  harnesses: "*",
747
956
  capabilities: ["shell.output.reduce"],
748
- outcome: "conflict",
749
- testedVersions: { rtk: "0.42.0", harnesstrim: "0.0.5" },
750
- rationale: "HarnessTrim 0.0.5 reduces the same command output RTK already reduced, and no configuration narrows it: its Claude and Codex adapters match Bash only, and its OpenCode plugin reduces every tool result. Reducing an already reduced payload loses signal and counts the saving twice.",
957
+ outcome: "narrowed",
958
+ retains: RTK,
959
+ testedVersions: { rtk: "0.44.0", harnesstrim: "0.1.0" },
960
+ rationale: "RTK keeps shell output reduction: it rewrites the command, so the output arriving at any later point is already reduced, and reducing it again loses signal and counts the saving twice. HarnessTrim 0.1.0 no longer has to be excluded whole for that \u2014 `--no-hook` and `--no-instructions` produce a Claude install that adds skills and reduces nothing, and `--tools` confines its OpenCode reduction to a chosen tool family. So it is narrowed to the form that leaves this channel alone, rather than refused. At 0.0.5 no flag produced any of those states, which is why this rule read `conflict` and said no configuration narrows it.",
751
961
  fixtures: ["docs/rfcs/0003-capabilities-and-conflicts.md#the-table-is-an-intent"]
752
962
  };
753
963
  var COMPATIBILITY_RULES = [RTK_HARNESSTRIM_SHELL_OVERLAP];
@@ -882,12 +1092,12 @@ function isUsableSalt(value) {
882
1092
  }
883
1093
 
884
1094
  // packages/core/dist/src/metrics/local-database.js
885
- function numberAt(row, column2) {
886
- const value = row[column2];
1095
+ function numberAt(row2, column2) {
1096
+ const value = row2[column2];
887
1097
  return typeof value === "number" ? value : null;
888
1098
  }
889
- function stringAt(row, column2) {
890
- const value = row[column2];
1099
+ function stringAt(row2, column2) {
1100
+ const value = row2[column2];
891
1101
  return typeof value === "string" ? value : null;
892
1102
  }
893
1103
 
@@ -988,7 +1198,7 @@ function aggregateEvents(input) {
988
1198
  const managed = new Set(input.managedProviders ?? []);
989
1199
  const modes = input.adapterModes ?? {};
990
1200
  const classes = MEASUREMENT_CLASSES.map((measurementClass) => {
991
- const rows = [...classTotals.values()].filter((row2) => row2.class === measurementClass);
1201
+ const rows = [...classTotals.values()].filter((row3) => row3.class === measurementClass);
992
1202
  if (rows.length > 1) {
993
1203
  return {
994
1204
  class: measurementClass,
@@ -996,11 +1206,11 @@ function aggregateEvents(input) {
996
1206
  before: null,
997
1207
  after: null,
998
1208
  saved: null,
999
- note: `recorded in ${rows.map((row2) => row2.unit).join(" and ")}, which are not addable`
1209
+ note: `recorded in ${rows.map((row3) => row3.unit).join(" and ")}, which are not addable`
1000
1210
  };
1001
1211
  }
1002
- const row = rows[0];
1003
- if (row === void 0) {
1212
+ const row2 = rows[0];
1213
+ if (row2 === void 0) {
1004
1214
  return {
1005
1215
  class: measurementClass,
1006
1216
  unit: null,
@@ -1012,22 +1222,22 @@ function aggregateEvents(input) {
1012
1222
  }
1013
1223
  return {
1014
1224
  class: measurementClass,
1015
- unit: row.unit,
1016
- before: row.before,
1017
- after: row.after,
1018
- saved: row.before - row.after,
1225
+ unit: row2.unit,
1226
+ before: row2.before,
1227
+ after: row2.after,
1228
+ saved: row2.before - row2.after,
1019
1229
  note: null
1020
1230
  };
1021
1231
  });
1022
- const providers = [...providerTotals.values()].sort((left, right) => right.saved - left.saved || left.providerId.localeCompare(right.providerId)).map((row) => ({
1023
- providerId: row.providerId,
1024
- saved: row.saved,
1025
- unit: row.unit,
1026
- class: row.class,
1027
- operations: row.operations,
1028
- harnesses: [...row.harnesses].sort(),
1029
- managedByTokenHarness: managed.has(row.providerId),
1030
- adapterMode: modes[row.providerId] ?? null
1232
+ const providers = [...providerTotals.values()].sort((left, right) => right.saved - left.saved || left.providerId.localeCompare(right.providerId)).map((row2) => ({
1233
+ providerId: row2.providerId,
1234
+ saved: row2.saved,
1235
+ unit: row2.unit,
1236
+ class: row2.class,
1237
+ operations: row2.operations,
1238
+ harnesses: [...row2.harnesses].sort(),
1239
+ managedByTokenHarness: managed.has(row2.providerId),
1240
+ adapterMode: modes[row2.providerId] ?? null
1031
1241
  }));
1032
1242
  const operations = realized + bypassed;
1033
1243
  return {
@@ -1266,6 +1476,201 @@ async function queryAvailableVersion(input) {
1266
1476
  }
1267
1477
  return { status: "found", version: version2, destination, diagnostics };
1268
1478
  }
1479
+ var INVENTORY_COMMANDS = {
1480
+ /**
1481
+ * `winget list --id <id> --exact` prints the same dashes-anchored table as `show --versions`,
1482
+ * with the installed version as the last token of the row. The header is localized, which is
1483
+ * why the parse is anchored on the separator and the row tail rather than on a column title.
1484
+ *
1485
+ * A missing package makes winget exit non-zero with a localized "no package found" message,
1486
+ * which this build cannot tell from any other failure — so that case is `failed` at the caller,
1487
+ * never `absent`.
1488
+ */
1489
+ winget: {
1490
+ executable: "winget",
1491
+ args: (packageName) => ["list", "--id", packageName, "--exact"],
1492
+ parse: (stdout, _packageName) => {
1493
+ const lines = stdout.split(/\r?\n/);
1494
+ const separator = lines.findIndex((line) => /^-{3,}\s*$/.test(line.trim()));
1495
+ if (separator < 0)
1496
+ return { status: "unknown", version: null };
1497
+ for (const line of lines.slice(separator + 1)) {
1498
+ const row2 = line.trim();
1499
+ if (row2 === "")
1500
+ continue;
1501
+ const candidate = row2.split(/\s+/).at(-1) ?? "";
1502
+ if (parseSemanticVersion(candidate) === null)
1503
+ return { status: "unknown", version: null };
1504
+ return { status: "captured", version: candidate };
1505
+ }
1506
+ return { status: "unknown", version: null };
1507
+ },
1508
+ verified: false
1509
+ },
1510
+ /**
1511
+ * `cargo install --list` prints one `crate v0.1.0:` line per installed crate, so absence is a
1512
+ * positive answer: the crate simply does not appear. This is the one inventory among the six
1513
+ * that can confirm absence.
1514
+ */
1515
+ cargo: {
1516
+ executable: "cargo",
1517
+ args: () => ["install", "--list"],
1518
+ parse: (stdout, packageName) => {
1519
+ const pattern = new RegExp(`^\\s*${packageName}\\s+v(\\S+)\\s*:`, "m");
1520
+ const match = pattern.exec(stdout);
1521
+ if (match === null)
1522
+ return { status: "absent", version: null };
1523
+ const candidate = match[1] ?? "";
1524
+ if (parseSemanticVersion(candidate) === null)
1525
+ return { status: "unknown", version: null };
1526
+ return { status: "captured", version: candidate };
1527
+ },
1528
+ verified: false
1529
+ },
1530
+ npm: {
1531
+ executable: "npm",
1532
+ args: (packageName) => ["ls", "-g", packageName, "--depth=0"],
1533
+ parse: (stdout, packageName) => {
1534
+ const pattern = new RegExp(`(?:^|[^@\\w.-])${packageName}@(\\d[^\\s]*)`);
1535
+ const match = pattern.exec(stdout);
1536
+ const candidate = match?.[1] ?? null;
1537
+ if (candidate === null || parseSemanticVersion(candidate) === null) {
1538
+ return { status: "unknown", version: null };
1539
+ }
1540
+ return { status: "captured", version: candidate };
1541
+ },
1542
+ verified: false
1543
+ },
1544
+ homebrew: {
1545
+ executable: "brew",
1546
+ args: (packageName) => ["list", "--versions", packageName],
1547
+ parse: (stdout, packageName) => {
1548
+ const pattern = new RegExp(`^${packageName}\\s+(.+)$`);
1549
+ const match = pattern.exec(stdout.trim());
1550
+ const candidate = match?.[1]?.trim().split(/\s+/).at(-1) ?? null;
1551
+ if (candidate === null || parseSemanticVersion(candidate) === null) {
1552
+ return { status: "unknown", version: null };
1553
+ }
1554
+ return { status: "captured", version: candidate };
1555
+ },
1556
+ verified: false
1557
+ },
1558
+ uv: {
1559
+ executable: "uv",
1560
+ args: () => ["tool", "list"],
1561
+ parse: (stdout, packageName) => {
1562
+ const pattern = new RegExp(`^${packageName}\\s+v?(\\S+)`, "m");
1563
+ const match = pattern.exec(stdout);
1564
+ if (match === null)
1565
+ return { status: "absent", version: null };
1566
+ const candidate = match[1] ?? "";
1567
+ if (parseSemanticVersion(candidate) === null)
1568
+ return { status: "unknown", version: null };
1569
+ return { status: "captured", version: candidate };
1570
+ },
1571
+ verified: false
1572
+ },
1573
+ pipx: {
1574
+ executable: "pipx",
1575
+ args: () => ["list"],
1576
+ parse: (stdout, packageName) => {
1577
+ const pattern = new RegExp(`^\\s*${packageName}\\s+(\\S+)`, "m");
1578
+ const match = pattern.exec(stdout);
1579
+ if (match === null)
1580
+ return { status: "absent", version: null };
1581
+ const candidate = match[1] ?? "";
1582
+ if (parseSemanticVersion(candidate) === null)
1583
+ return { status: "unknown", version: null };
1584
+ return { status: "captured", version: candidate };
1585
+ },
1586
+ verified: false
1587
+ }
1588
+ };
1589
+ function channelCanReportInventory(channelId) {
1590
+ return Object.hasOwn(INVENTORY_COMMANDS, channelId);
1591
+ }
1592
+ var DEFAULT_INVENTORY_TIMEOUT_MS = 6e4;
1593
+ async function queryPackageInventory(input) {
1594
+ const recipe = INVENTORY_COMMANDS[input.channel];
1595
+ if (recipe === void 0) {
1596
+ return {
1597
+ channel: input.channel,
1598
+ packageName: input.packageName,
1599
+ status: "unsupported",
1600
+ version: null,
1601
+ diagnostics: [
1602
+ diagnostic({
1603
+ severity: "warning",
1604
+ code: "inventory-query-unsupported",
1605
+ message: `This build cannot ask ${input.channel} what it has installed`,
1606
+ remediation: null
1607
+ })
1608
+ ]
1609
+ };
1610
+ }
1611
+ if (input.runner === null) {
1612
+ return {
1613
+ channel: input.channel,
1614
+ packageName: input.packageName,
1615
+ status: "failed",
1616
+ version: null,
1617
+ diagnostics: [
1618
+ diagnostic({
1619
+ severity: "error",
1620
+ code: "no-process-runner",
1621
+ message: "No process runner is available, so no inventory can be captured",
1622
+ remediation: null
1623
+ })
1624
+ ]
1625
+ };
1626
+ }
1627
+ const diagnostics = [];
1628
+ if (!recipe.verified) {
1629
+ diagnostics.push(diagnostic({
1630
+ severity: "warning",
1631
+ code: "inventory-query-unverified",
1632
+ message: `The ${input.channel} inventory query follows that tool's documented output but has not been observed working`,
1633
+ remediation: "Confirm the rollback receipt after any failure"
1634
+ }));
1635
+ }
1636
+ const outcome2 = await input.runner.run({
1637
+ executable: recipe.executable,
1638
+ args: recipe.args(input.packageName),
1639
+ cwd: input.cwd,
1640
+ timeoutMs: input.timeoutMs ?? DEFAULT_INVENTORY_TIMEOUT_MS
1641
+ });
1642
+ if (outcome2.failure !== null || outcome2.exitCode !== 0) {
1643
+ diagnostics.push(diagnostic({
1644
+ severity: "warning",
1645
+ code: "inventory-query-failed",
1646
+ message: outcome2.failure === null ? `${outcome2.displayCommand} exited with ${String(outcome2.exitCode)}` : `${recipe.executable} could not be run: ${outcome2.failure.reason}`,
1647
+ remediation: null
1648
+ }));
1649
+ return {
1650
+ channel: input.channel,
1651
+ packageName: input.packageName,
1652
+ status: "failed",
1653
+ version: null,
1654
+ diagnostics
1655
+ };
1656
+ }
1657
+ const reading = recipe.parse(outcome2.stdout, input.packageName);
1658
+ if (reading.status === "unknown") {
1659
+ diagnostics.push(diagnostic({
1660
+ severity: "warning",
1661
+ code: "inventory-query-unreadable",
1662
+ message: `${recipe.executable} answered, but its output did not name an installed version in the form this build reads`,
1663
+ remediation: null
1664
+ }));
1665
+ }
1666
+ return {
1667
+ channel: input.channel,
1668
+ packageName: input.packageName,
1669
+ status: reading.status,
1670
+ version: reading.version,
1671
+ diagnostics
1672
+ };
1673
+ }
1269
1674
  var DEFAULT_INSTALL_TIMEOUT_MS = 6e5;
1270
1675
  async function runPackageManagerInstall(input) {
1271
1676
  const { action } = input;
@@ -1345,14 +1750,119 @@ async function runPackageManagerInstall(input) {
1345
1750
  }));
1346
1751
  return { status: "failed", diagnostics };
1347
1752
  }
1348
- diagnostics.push(diagnostic({
1349
- severity: "info",
1350
- code: "install-not-reversible",
1351
- message: `${action.packageName} was installed through ${action.packageManager}; a rollback restores files and will not uninstall it`,
1352
- remediation: null
1353
- }));
1354
1753
  return { status: "installed", diagnostics };
1355
1754
  }
1755
+ async function restorePackageInventory(input) {
1756
+ const { capture } = input;
1757
+ if (capture.status === "absent") {
1758
+ return {
1759
+ restored: false,
1760
+ diagnostics: [
1761
+ diagnostic({
1762
+ severity: "info",
1763
+ code: "package-inventory-unrestored",
1764
+ message: `${capture.packageName} was not installed before the transaction; restoring that absence would require an uninstall command, so it stays installed`,
1765
+ remediation: null
1766
+ })
1767
+ ]
1768
+ };
1769
+ }
1770
+ if (capture.status !== "captured" || capture.version === null) {
1771
+ return {
1772
+ restored: false,
1773
+ diagnostics: [
1774
+ diagnostic({
1775
+ severity: "info",
1776
+ code: "package-inventory-unrestored",
1777
+ message: `${capture.packageName} was installed without a captured inventory, so it could not be restored`,
1778
+ remediation: null
1779
+ })
1780
+ ]
1781
+ };
1782
+ }
1783
+ if (input.runner === null) {
1784
+ return {
1785
+ restored: false,
1786
+ diagnostics: [
1787
+ diagnostic({
1788
+ severity: "info",
1789
+ code: "package-inventory-unrestored",
1790
+ message: `${capture.packageName} could not be restored: no process runner is available`,
1791
+ remediation: null
1792
+ })
1793
+ ]
1794
+ };
1795
+ }
1796
+ const restoreAction = {
1797
+ kind: "package-manager-install",
1798
+ id: `restore-${capture.packageName}`,
1799
+ riskClass: "delegated",
1800
+ requiresNetwork: true,
1801
+ requiresElevation: false,
1802
+ affectedPaths: [],
1803
+ affectedProcesses: [capture.channel],
1804
+ preconditions: [],
1805
+ postconditions: [],
1806
+ rollbackData: "none",
1807
+ explanation: `Restore ${capture.packageName} to ${capture.version} after a rollback`,
1808
+ packageManager: capture.channel,
1809
+ packageName: capture.packageName,
1810
+ version: capture.version
1811
+ };
1812
+ const result = await runPackageManagerInstall({
1813
+ action: restoreAction,
1814
+ runner: input.runner,
1815
+ cwd: input.cwd,
1816
+ ...input.timeoutMs === void 0 ? {} : { timeoutMs: input.timeoutMs }
1817
+ });
1818
+ if (result.status !== "installed") {
1819
+ return {
1820
+ restored: false,
1821
+ diagnostics: [
1822
+ ...result.diagnostics,
1823
+ diagnostic({
1824
+ severity: "error",
1825
+ code: "package-restore-failed",
1826
+ message: `${capture.packageName} could not be restored to ${capture.version} through ${capture.channel}`,
1827
+ path: null,
1828
+ remediation: `Install ${capture.packageName} at ${capture.version} yourself, then run the command again`
1829
+ })
1830
+ ]
1831
+ };
1832
+ }
1833
+ const verified = await queryPackageInventory({
1834
+ channel: capture.channel,
1835
+ packageName: capture.packageName,
1836
+ runner: input.runner,
1837
+ cwd: input.cwd,
1838
+ ...input.timeoutMs === void 0 ? {} : { timeoutMs: input.timeoutMs }
1839
+ });
1840
+ if (verified.status !== "captured" || verified.version !== capture.version) {
1841
+ return {
1842
+ restored: false,
1843
+ diagnostics: [
1844
+ ...verified.diagnostics,
1845
+ diagnostic({
1846
+ severity: "error",
1847
+ code: "package-restore-failed",
1848
+ message: `${capture.packageName} was reinstalled but the inventory does not report ${capture.version}`,
1849
+ remediation: `Confirm what ${capture.channel} has installed and correct it by hand`
1850
+ })
1851
+ ]
1852
+ };
1853
+ }
1854
+ return {
1855
+ restored: true,
1856
+ diagnostics: [
1857
+ diagnostic({
1858
+ severity: "info",
1859
+ code: "package-inventory-restored",
1860
+ message: `${capture.packageName} was restored to ${capture.version} through ${capture.channel} and the inventory was re-read`,
1861
+ remediation: null
1862
+ })
1863
+ ]
1864
+ };
1865
+ }
1356
1866
 
1357
1867
  // packages/core/dist/src/state/json-merge.js
1358
1868
  function isJsonObject(value) {
@@ -1772,7 +2282,8 @@ function outcome(action, status, parts = {}) {
1772
2282
  status,
1773
2283
  snapshots: parts.snapshots ?? [],
1774
2284
  ownership: parts.ownership ?? [],
1775
- diagnostics: parts.diagnostics ?? []
2285
+ diagnostics: parts.diagnostics ?? [],
2286
+ packageInventory: parts.packageInventory ?? null
1776
2287
  };
1777
2288
  }
1778
2289
  function drift(action, path, message) {
@@ -2037,6 +2548,141 @@ async function applyRemoveOwnedChange(action, context) {
2037
2548
  await fs.writeFile(action.target.path, UTF8.encode(removal.text), null);
2038
2549
  return outcome(action, "applied", { snapshots: [snapshot] });
2039
2550
  }
2551
+ async function scanTree(fs, path, entries = []) {
2552
+ const stat3 = await fs.stat(path);
2553
+ if (stat3 === null)
2554
+ return entries;
2555
+ const digest = stat3.kind === "file" ? digestBytes(await fs.readFile(path)) : null;
2556
+ entries.push({ path, kind: stat3.kind, digest, byteLength: stat3.byteLength });
2557
+ if (stat3.kind === "directory") {
2558
+ for (const child of await fs.readDirectory(path)) {
2559
+ await scanTree(fs, fs.join(path, child), entries);
2560
+ }
2561
+ }
2562
+ return entries;
2563
+ }
2564
+ function equalTreeEntry(left, right) {
2565
+ if (left === void 0 && right === void 0)
2566
+ return true;
2567
+ return left !== void 0 && right !== void 0 && left.kind === right.kind && left.digest === right.digest;
2568
+ }
2569
+ async function applyDelegatedProviderInstall(action, context) {
2570
+ if (context.runner === null || context.runner === void 0 || context.cwd === void 0) {
2571
+ return refusal(action, "process-runner-unavailable", action.executable, "A delegated provider install requires a process runner and working directory", "Run this command through the Token Harness CLI");
2572
+ }
2573
+ const before = /* @__PURE__ */ new Map();
2574
+ for (const boundary of action.containmentBoundary) {
2575
+ for (const entry of await scanTree(context.fs, boundary))
2576
+ before.set(entry.path, entry);
2577
+ }
2578
+ if (action.expectedArtifacts.every((artifact) => before.get(artifact.path)?.kind === "file" && before.get(artifact.path)?.digest === artifact.digest)) {
2579
+ return outcome(action, "already-satisfied");
2580
+ }
2581
+ const bytes = [...before.values()].reduce((total, entry) => total + entry.byteLength, 0);
2582
+ if (bytes > action.snapshotSizeCapBytes) {
2583
+ return refusal(action, "delegated-install-snapshot-too-large", action.containmentBoundary[0] ?? action.executable, `The delegated install boundary is ${String(bytes)} bytes, above its ${String(action.snapshotSizeCapBytes)} byte snapshot cap`, "Narrow the provider containment boundary before applying this plan");
2584
+ }
2585
+ const snapshots = [];
2586
+ const capturedPaths = /* @__PURE__ */ new Set();
2587
+ for (const boundary of action.containmentBoundary) {
2588
+ const entries = await scanTree(context.fs, boundary);
2589
+ if (entries.length === 0) {
2590
+ snapshots.push(await context.snapshots.capture(boundary));
2591
+ capturedPaths.add(boundary);
2592
+ continue;
2593
+ }
2594
+ for (const entry of entries) {
2595
+ snapshots.push(await context.snapshots.capture(entry.path));
2596
+ capturedPaths.add(entry.path);
2597
+ }
2598
+ }
2599
+ const result = await context.runner.run({
2600
+ executable: action.executable,
2601
+ args: action.args,
2602
+ cwd: context.cwd,
2603
+ timeoutMs: 3e4
2604
+ });
2605
+ if (!processSucceeded(result)) {
2606
+ return outcome(action, "failed", {
2607
+ snapshots,
2608
+ diagnostics: [
2609
+ diagnostic({
2610
+ severity: "error",
2611
+ code: "delegated-install-failed",
2612
+ message: `${action.executable} did not complete its delegated install successfully`,
2613
+ remediation: "Review the provider output and rerun the plan after correcting the problem"
2614
+ })
2615
+ ]
2616
+ });
2617
+ }
2618
+ const after = /* @__PURE__ */ new Map();
2619
+ for (const boundary of action.containmentBoundary) {
2620
+ for (const entry of await scanTree(context.fs, boundary))
2621
+ after.set(entry.path, entry);
2622
+ }
2623
+ for (const entry of after.values()) {
2624
+ if (!before.has(entry.path) && !capturedPaths.has(entry.path)) {
2625
+ snapshots.push(context.snapshots.captureAbsent(entry.path));
2626
+ capturedPaths.add(entry.path);
2627
+ }
2628
+ }
2629
+ const protectedPath = action.protectedPaths.find((path) => !equalTreeEntry(before.get(path), after.get(path)));
2630
+ if (protectedPath !== void 0) {
2631
+ return outcome(action, "failed", {
2632
+ snapshots,
2633
+ diagnostics: [
2634
+ diagnostic({
2635
+ severity: "error",
2636
+ code: "delegated-install-protected-path-changed",
2637
+ message: `The delegated installer changed the protected path ${protectedPath}`,
2638
+ path: protectedPath,
2639
+ remediation: "Do not apply this provider until its reviewed write set is updated"
2640
+ })
2641
+ ]
2642
+ });
2643
+ }
2644
+ const changed = /* @__PURE__ */ new Set([...before.keys(), ...after.keys()]);
2645
+ const undeclared = [...changed].map((path) => after.get(path) ?? before.get(path)).find((entry) => entry !== void 0 && !equalTreeEntry(before.get(entry.path), after.get(entry.path)) && !action.expectedArtifacts.some((artifact) => artifact.path === entry.path || entry.kind === "directory" && context.fs.isInside(artifact.path, entry.path)));
2646
+ if (undeclared !== void 0) {
2647
+ return outcome(action, "failed", {
2648
+ snapshots,
2649
+ diagnostics: [
2650
+ diagnostic({
2651
+ severity: "error",
2652
+ code: "delegated-install-undeclared-write",
2653
+ message: `The delegated installer changed an undeclared path inside its containment boundary: ${undeclared.path}`,
2654
+ path: undeclared.path,
2655
+ remediation: "Do not apply this provider until its reviewed write set is updated"
2656
+ })
2657
+ ]
2658
+ });
2659
+ }
2660
+ const missing = action.expectedArtifacts.find((artifact) => after.get(artifact.path)?.kind !== "file" || after.get(artifact.path)?.digest !== artifact.digest);
2661
+ if (missing !== void 0) {
2662
+ return outcome(action, "failed", {
2663
+ snapshots,
2664
+ diagnostics: [
2665
+ diagnostic({
2666
+ severity: "error",
2667
+ code: "delegated-install-artifact-mismatch",
2668
+ message: `The delegated installer did not produce the reviewed artifact ${missing.path}`,
2669
+ path: missing.path,
2670
+ remediation: "Do not apply this provider until its reviewed artifact digests are updated"
2671
+ })
2672
+ ]
2673
+ });
2674
+ }
2675
+ const ownership = [];
2676
+ for (const artifact of action.expectedArtifacts) {
2677
+ ownership.push({
2678
+ kind: "owned-file",
2679
+ path: artifact.path,
2680
+ digest: artifact.digest,
2681
+ mode: (await context.fs.stat(artifact.path))?.mode ?? null
2682
+ });
2683
+ }
2684
+ return outcome(action, "applied", { snapshots, ownership });
2685
+ }
2040
2686
  function notImplemented(action) {
2041
2687
  return outcome(action, "failed", {
2042
2688
  diagnostics: [
@@ -2063,9 +2709,10 @@ async function applyAction(action, context) {
2063
2709
  return applyMergeJson(action, context);
2064
2710
  case "package-manager-install":
2065
2711
  return applyPackageManagerInstall(action, context);
2712
+ case "delegated-provider-install":
2713
+ return applyDelegatedProviderInstall(action, context);
2066
2714
  case "download-artifact":
2067
2715
  case "run-installer-command":
2068
- case "delegated-provider-install":
2069
2716
  case "merge-toml":
2070
2717
  case "merge-yaml":
2071
2718
  case "register-mcp-server":
@@ -2074,13 +2721,38 @@ async function applyAction(action, context) {
2074
2721
  }
2075
2722
  }
2076
2723
  async function applyPackageManagerInstall(action, context) {
2724
+ const wantsInventory = action.rollbackData === "package-inventory" && !action.requiresElevation;
2725
+ const packageInventory = wantsInventory && context.runner !== void 0 ? await queryPackageInventory({
2726
+ channel: action.packageManager,
2727
+ packageName: action.packageName,
2728
+ runner: context.runner,
2729
+ cwd: context.cwd ?? ""
2730
+ }) : null;
2077
2731
  const result = await runPackageManagerInstall({
2078
2732
  action,
2079
2733
  runner: context.runner ?? null,
2080
2734
  cwd: context.cwd ?? ""
2081
2735
  });
2082
2736
  const status = result.status === "installed" ? "applied" : result.status === "refused" ? "refused" : "failed";
2083
- return outcome(action, status, { diagnostics: result.diagnostics });
2737
+ const diagnostics = [...result.diagnostics];
2738
+ if (status === "applied") {
2739
+ if (packageInventory?.status === "captured" && packageInventory.version !== null) {
2740
+ diagnostics.push(diagnostic({
2741
+ severity: "info",
2742
+ code: "install-inventory-captured",
2743
+ message: `${action.packageName} was installed through ${action.packageManager}; the prior state (${packageInventory.version}) was captured, so a rollback can restore it`,
2744
+ remediation: null
2745
+ }));
2746
+ } else {
2747
+ diagnostics.push(diagnostic({
2748
+ severity: "info",
2749
+ code: "install-not-reversible",
2750
+ message: `${action.packageName} was installed through ${action.packageManager}; a rollback restores files and will not uninstall it`,
2751
+ remediation: null
2752
+ }));
2753
+ }
2754
+ }
2755
+ return outcome(action, status, { diagnostics, packageInventory });
2084
2756
  }
2085
2757
 
2086
2758
  // packages/core/dist/src/state/journal.js
@@ -2559,23 +3231,25 @@ var TransactionSnapshotStore = class _TransactionSnapshotStore {
2559
3231
  this.taken.push(snapshot);
2560
3232
  return snapshot;
2561
3233
  }
3234
+ captureAbsent(path) {
3235
+ return this.remember({
3236
+ schemaVersion: 1,
3237
+ path,
3238
+ existed: false,
3239
+ wasDirectory: false,
3240
+ digest: null,
3241
+ mode: null,
3242
+ byteLength: null,
3243
+ contentRef: null,
3244
+ capturedAt: this.input.now()
3245
+ });
3246
+ }
2562
3247
  async capture(path) {
2563
3248
  const { fs } = this.input;
2564
3249
  const stat3 = await fs.stat(path);
2565
3250
  const capturedAt = this.input.now();
2566
- if (stat3 === null) {
2567
- return this.remember({
2568
- schemaVersion: 1,
2569
- path,
2570
- existed: false,
2571
- wasDirectory: false,
2572
- digest: null,
2573
- mode: null,
2574
- byteLength: null,
2575
- contentRef: null,
2576
- capturedAt
2577
- });
2578
- }
3251
+ if (stat3 === null)
3252
+ return this.captureAbsent(path);
2579
3253
  if (stat3.kind === "directory") {
2580
3254
  return this.remember({
2581
3255
  schemaVersion: 1,
@@ -2674,6 +3348,24 @@ async function verifyRestoration(fs, snapshots) {
2674
3348
  }
2675
3349
  return unrestored;
2676
3350
  }
3351
+ async function restorePackageInventories(records, runner, cwd) {
3352
+ const diagnostics = [];
3353
+ for (const record of records) {
3354
+ if (record.kind !== "package-manager-install")
3355
+ continue;
3356
+ if (record.status !== "applied")
3357
+ continue;
3358
+ if (record.packageInventory === null || record.packageInventory === void 0)
3359
+ continue;
3360
+ const outcome2 = await restorePackageInventory({
3361
+ capture: record.packageInventory,
3362
+ runner,
3363
+ cwd
3364
+ });
3365
+ diagnostics.push(...outcome2.diagnostics);
3366
+ }
3367
+ return diagnostics;
3368
+ }
2677
3369
  async function executeTransaction(request) {
2678
3370
  const startedAt = request.now();
2679
3371
  const entries = [];
@@ -2717,7 +3409,10 @@ async function executeTransaction(request) {
2717
3409
  restoreError = error instanceof Error ? error.message : String(error);
2718
3410
  }
2719
3411
  const unrestored = await verifyRestoration(request.fs, snapshots);
2720
- if (restoreError === null && unrestored.length === 0) {
3412
+ const packageDiagnostics = await restorePackageInventories(applied, request.runner ?? null, request.projectRoot);
3413
+ diagnostics.push(...packageDiagnostics);
3414
+ const packageRestoreFailed = packageDiagnostics.some((entry) => entry.severity === "error");
3415
+ if (restoreError === null && unrestored.length === 0 && !packageRestoreFailed) {
2721
3416
  diagnostics.push(diagnostic({
2722
3417
  severity: "info",
2723
3418
  code: "transaction-rolled-back",
@@ -2745,6 +3440,7 @@ async function executeTransaction(request) {
2745
3440
  status: "failed",
2746
3441
  snapshots: [],
2747
3442
  ownership: [],
3443
+ packageInventory: null,
2748
3444
  diagnostics: [
2749
3445
  diagnostic({
2750
3446
  severity: "error",
@@ -2762,7 +3458,8 @@ async function executeTransaction(request) {
2762
3458
  status: result.status,
2763
3459
  snapshots: result.snapshots,
2764
3460
  ownership: result.ownership,
2765
- diagnostics: result.diagnostics
3461
+ diagnostics: result.diagnostics,
3462
+ packageInventory: result.packageInventory
2766
3463
  };
2767
3464
  entries.push(entry);
2768
3465
  applied.push(result);
@@ -2847,7 +3544,10 @@ async function rollbackTransaction(request) {
2847
3544
  restoreError = error instanceof Error ? error.message : String(error);
2848
3545
  }
2849
3546
  const unrestored = await verifyRestoration(request.fs, snapshots);
2850
- if (restoreError === null && unrestored.length === 0) {
3547
+ const packageDiagnostics = await restorePackageInventories(journal.entries, request.runner ?? null, request.cwd ?? journal.projectRoot);
3548
+ diagnostics.push(...packageDiagnostics);
3549
+ const packageRestoreFailed = packageDiagnostics.some((entry) => entry.severity === "error");
3550
+ if (restoreError === null && unrestored.length === 0 && !packageRestoreFailed) {
2851
3551
  journal.outcome = "rolled-back";
2852
3552
  journal.ownership = [];
2853
3553
  journal.finishedAt = request.now();
@@ -3168,9 +3868,9 @@ async function readLocalDatabase(request) {
3168
3868
  database = new construct(request.path, { readOnly: true });
3169
3869
  const statement = database.prepare(request.sql);
3170
3870
  const raw = statement.all(...request.parameters);
3171
- const rows = raw.map((row) => {
3871
+ const rows = raw.map((row2) => {
3172
3872
  const mapped = {};
3173
- for (const [column2, value] of Object.entries(row))
3873
+ for (const [column2, value] of Object.entries(row2))
3174
3874
  mapped[column2] = toValue(value);
3175
3875
  return mapped;
3176
3876
  });
@@ -4447,9 +5147,20 @@ async function inspect(context) {
4447
5147
  diagnostics.push(diagnostic({
4448
5148
  severity: "warning",
4449
5149
  code: "tool-family-not-covered",
4450
- message: `No configured matcher covers the ${uncoveredToolFamilies.join(", ")} tool ${uncoveredToolFamilies.length === 1 ? "family" : "families"}, so commands routed through ${uncoveredToolFamilies.length === 1 ? "it" : "them"} bypass the pipeline`,
5150
+ subject: CLAUDE,
5151
+ /**
5152
+ * Says whose gap it is, and that the tool is working.
5153
+ *
5154
+ * The first wording — "No configured matcher covers the PowerShell tool family" — read like a
5155
+ * defect in Token Harness on a first run. It is not: the hook in the file below matches some
5156
+ * tool families and not others, Claude Code routes shell commands through more than one on
5157
+ * Windows, and the commands going through the uncovered ones never reach the provider at all.
5158
+ */
5159
+ // Short enough to survive the one-line human rendering without being cut.
5160
+ message: `${uncoveredToolFamilies.join(", ")} has no hook, so those commands are not optimized`,
4451
5161
  path: configs.find((config) => config.configuredPoints.length > 0)?.path ?? null,
4452
- remediation: "Widen the matcher, or accept the gap and read coverage figures with it in mind"
5162
+ // Short on purpose: a remediation nobody finishes reading is not one.
5163
+ remediation: "Optional. Widen the matcher, or accept the gap"
4453
5164
  }));
4454
5165
  }
4455
5166
  return {
@@ -4743,6 +5454,7 @@ async function inspect2(context) {
4743
5454
  diagnostic({
4744
5455
  severity: "info",
4745
5456
  code: "hook-enablement-unobservable",
5457
+ subject: CODEX,
4746
5458
  message: "Codex declares this hook, but its persisted enablement and trust state cannot be read outside the TUI",
4747
5459
  path: hook.path,
4748
5460
  remediation: "Open Codex and confirm the hook is enabled and trusted after any hook edit"
@@ -4852,20 +5564,47 @@ var MANIFEST3 = {
4852
5564
  * configuration must be able to exit 0. A declared limitation is not a problem, and reporting
4853
5565
  * it as one is the fastest way to teach users to ignore the exit code."
4854
5566
  *
4855
- * A single point rather than a range, matching the Codex adapter: one version was observed, so
4856
- * one version is claimed. An older 1.x reports `unknown-older`, which is a warning and not a
5567
+ * A range now, because spike 9.1 observed two more: the CLI at `1.18.11` and OpenCode Desktop at
5568
+ * `1.18.14`, both loading plugins from the same directories. The span is what was watched, not a
5569
+ * guess that every 1.18.x behaves alike — and the two ends differ in a way that matters, which is
5570
+ * exactly why both are named. An older 1.x reports `unknown-older`, which is a warning and not a
4857
5571
  * problem, and a newer one reports `unknown-newer` — which is then a true statement.
4858
5572
  */
4859
- testedVersions: { minimum: "1.18.9", maximum: "1.18.9" },
5573
+ testedVersions: { minimum: "1.18.9", maximum: "1.18.14" },
4860
5574
  // RFC 0007: a managed provider can emit a receipt, but an adopted generated wrapper
4861
5575
  // has no externally observable receipt. The harness adapter alone therefore declares
4862
5576
  // the achievable baseline, not a promise about a provider it does not own.
4863
5577
  verificationTier: "config-only",
4864
5578
  versionCommand: { executable: "opencode", args: ["--version"] },
4865
- interceptionPoints: [{ scopeId: "tool-execute-after", eventName: "tool.execute.after" }],
5579
+ interceptionPoints: [
5580
+ { scopeId: "tool-execute-before", eventName: "tool.execute.before" },
5581
+ { scopeId: "tool-execute-after", eventName: "tool.execute.after" }
5582
+ ],
5583
+ /**
5584
+ * Two kinds of entry, because OpenCode has two ways to register a plugin and only one of them is
5585
+ * a document.
5586
+ *
5587
+ * The `opencode.jsonc` files carry a `plugin` string array. The directories are auto-loaded: any
5588
+ * module dropped in one is registered without being named anywhere, which is how both real
5589
+ * integrations install — `rtk init -g --opencode` writes `.config/opencode/plugins/rtk.ts` and
5590
+ * touches no JSON at all, and HarnessTrim's generated wrapper says in its own header not to
5591
+ * reference it from `opencode.json`. An adapter that read only the array would report every such
5592
+ * installation as absent, which is the state this file was in before spike 9.1.
5593
+ *
5594
+ * Both spellings are listed because both load. Spike 9.1 put a probe in each and one run fired
5595
+ * both; the upstream documentation names the plural, the binary carries both strings, and the two
5596
+ * shipped integrations disagree with each other about which to use.
5597
+ *
5598
+ * `markers` is the parser because there is nothing to parse: the presence of a file is the
5599
+ * configuration, and its contents are a plugin module rather than a document this build reads.
5600
+ */
4866
5601
  configFiles: [
4867
5602
  { path: ".config/opencode/opencode.jsonc", scope: "user", parser: "jsonc", primary: true },
4868
- { path: "opencode.jsonc", scope: "project", parser: "jsonc", primary: false }
5603
+ { path: "opencode.jsonc", scope: "project", parser: "jsonc", primary: false },
5604
+ { path: ".config/opencode/plugins", scope: "user", parser: "markers", primary: false },
5605
+ { path: ".config/opencode/plugin", scope: "user", parser: "markers", primary: false },
5606
+ { path: ".opencode/plugins", scope: "project", parser: "markers", primary: false },
5607
+ { path: ".opencode/plugin", scope: "project", parser: "markers", primary: false }
4869
5608
  ],
4870
5609
  toolFamilies: [
4871
5610
  { id: "tool.execute", platforms: ["windows", "macos", "linux"], executesShellCommands: true }
@@ -4882,7 +5621,38 @@ function pluginEntries(document2) {
4882
5621
  return [];
4883
5622
  return document2["plugin"].filter((item) => typeof item === "string");
4884
5623
  }
5624
+ var PLUGIN_MODULE_PATTERN = /\.(ts|js|mjs|cjs|mts|cts)$/i;
5625
+ var PLUGIN_POINTS = ["tool-execute-before", "tool-execute-after"];
5626
+ async function resolvePluginDirectory(declaration, context) {
5627
+ const path = resolveConfigPath(declaration, context);
5628
+ const stat3 = await context.fs.stat(path);
5629
+ if (stat3 === null || stat3.kind !== "directory") {
5630
+ return {
5631
+ declaration,
5632
+ path,
5633
+ exists: false,
5634
+ parsed: false,
5635
+ configuredPoints: [],
5636
+ matchers: [],
5637
+ commands: []
5638
+ };
5639
+ }
5640
+ const modules = (await context.fs.readDirectory(path)).filter((entry) => PLUGIN_MODULE_PATTERN.test(entry));
5641
+ return {
5642
+ declaration,
5643
+ path,
5644
+ exists: true,
5645
+ // A directory listing cannot fail to parse. `markers` declares that there is nothing to read.
5646
+ parsed: true,
5647
+ configuredPoints: modules.length > 0 ? [...PLUGIN_POINTS] : [],
5648
+ // The plugin API sees every tool result; this is not a shell matcher.
5649
+ matchers: modules.length > 0 ? ["tool.execute"] : [],
5650
+ commands: modules.map((entry) => context.fs.join(path, entry))
5651
+ };
5652
+ }
4885
5653
  async function resolveConfig3(declaration, context) {
5654
+ if (declaration.parser === "markers")
5655
+ return resolvePluginDirectory(declaration, context);
4886
5656
  const path = resolveConfigPath(declaration, context);
4887
5657
  const stat3 = await context.fs.stat(path);
4888
5658
  if (stat3 === null || stat3.kind !== "file") {
@@ -4914,7 +5684,9 @@ async function resolveConfig3(declaration, context) {
4914
5684
  path,
4915
5685
  exists: true,
4916
5686
  parsed: true,
4917
- configuredPoints: plugins.length > 0 ? ["tool-execute-after"] : [],
5687
+ // A `plugin` array entry is the same kind of thing as a module in a plugin directory: a
5688
+ // registration whose hooks are only known once it runs. Same claim, same reason.
5689
+ configuredPoints: plugins.length > 0 ? [...PLUGIN_POINTS] : [],
4918
5690
  // The plugin API sees every tool result; this is not a shell matcher.
4919
5691
  matchers: plugins.length > 0 ? ["tool.execute"] : [],
4920
5692
  commands: plugins
@@ -5072,8 +5844,24 @@ function listHarnessAdapters() {
5072
5844
  var HARNESSTRIM2 = providerId("harnesstrim");
5073
5845
  var CLAUDE2 = harnessId("claude");
5074
5846
  var CODEX2 = harnessId("codex");
5847
+ var CLAUDE_ARTIFACT_DIGESTS = {
5848
+ "compact-handoff/SKILL.md": "sha256:0efbf35581c559359e755b204778b64a289dab4d20dadc1cba3d5b5c995b5f01",
5849
+ "debug-log-slim/SKILL.md": "sha256:17f5ccc34d29d7aba083444e0e4d87fd3d49916ebcb4e6544caf8c89f13f0045",
5850
+ "delegate-bulk/SKILL.md": "sha256:3753de2ad18271c24832e4cda63115d353620515d5d80f40e93bc07d2a7257d7",
5851
+ "delta-response/SKILL.md": "sha256:b6a71f4bdcfcadde3b5242994baa017d0371fe11652dd763f55a2f6d3840cfa8",
5852
+ "delta-response/references/examples.md": "sha256:c67a1f57e63550b396043c3072b7e1a3a0c1522376471f53d6829253339e64e7",
5853
+ "review-delta/SKILL.md": "sha256:4dfbf9d6ec08dff27b4759726536b43928370e288706fa01f5b498648e741388",
5854
+ "scaffold-fast/SKILL.md": "sha256:1a18d52c4d335fbd3f74e2559bc20d6346193a40eef7b85149f51f44f697d182"
5855
+ };
5075
5856
  var OPENCODE2 = harnessId("opencode");
5857
+ var MODE_CARRYING_HARNESSES = /* @__PURE__ */ new Set([
5858
+ OPENCODE2,
5859
+ harnessId("hermes"),
5860
+ harnessId("pi"),
5861
+ harnessId("omp")
5862
+ ]);
5076
5863
  var HOOK_COMMAND_PATTERN = /(^|[\\/\s"'])harnesstrim(\.cmd|\.exe)?([\s"']|$)/i;
5864
+ var PLUGIN_MODULE_PATTERN2 = /(^|[\\/])harnesstrim\.(ts|js|mjs|cjs|mts|cts)$/i;
5077
5865
  var MANIFEST4 = {
5078
5866
  schemaVersion: MANIFEST_SCHEMA_VERSION,
5079
5867
  id: HARNESSTRIM2,
@@ -5150,19 +5938,219 @@ var MANIFEST4 = {
5150
5938
  }
5151
5939
  ],
5152
5940
  metrics: {
5153
- // RFC 0005 §Importer degradation policy: `legacy` is the character-only `TrimEvent` JSONL, and
5154
- // it is "a supported steady state, not a warning". `native` would need upstream to expose token
5155
- // counts and native ids, which `0.0.5` does not.
5941
+ // RFC 0005 §Importer degradation policy. `native` since PLAN §15 item 43d: the `0.1.0`
5942
+ // telemetry lines carry a producer `eventId` and nullable token counts, so dedup uses the
5943
+ // native ID and a token count is a token count where one exists. Legacy schema 0 lines
5944
+ // still parse, stay `estimated-local`, and never merge with the native figures.
5156
5945
  source: "jsonl",
5157
- mode: "legacy",
5158
- locations: [".harnesstrim/metrics.jsonl", "~/.hermes/harnesstrim-metrics.jsonl"]
5946
+ mode: "native",
5947
+ // PLAN §15 item 25: Hermes has no adapter, tested range or tier, so the home-relative path that
5948
+ // names it is not read. Item 30 readmits it together with the adapter and a matrix row; the
5949
+ // registry assertion in `registries.test.ts` refuses a home-relative location that names a
5950
+ // harness the registry does not know.
5951
+ locations: [".harnesstrim/metrics.jsonl"]
5159
5952
  },
5160
- delegatedInstallReview: null
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
5960
+ }
5161
5961
  };
5162
- var TESTED_UPSTREAM = "0.0.5";
5163
- var TESTED_VERSIONS = { minimum: TESTED_UPSTREAM, maximum: TESTED_UPSTREAM };
5962
+ var TESTED_UPSTREAM = "0.0.7";
5963
+ var TESTED_VERSIONS = { minimum: "0.0.5", maximum: TESTED_UPSTREAM };
5164
5964
  function identifiesCommand(command) {
5165
- return HOOK_COMMAND_PATTERN.test(command);
5965
+ return HOOK_COMMAND_PATTERN.test(command) || PLUGIN_MODULE_PATTERN2.test(command);
5966
+ }
5967
+ function isStringArray(value) {
5968
+ return Array.isArray(value) && value.every((item) => typeof item === "string");
5969
+ }
5970
+ function parseHarnessCapabilities(value) {
5971
+ if (typeof value !== "object" || value === null)
5972
+ return null;
5973
+ const record = value;
5974
+ const surfaces = record["surfaces"];
5975
+ const writeSet = record["writeSet"];
5976
+ const narrowing = record["narrowing"];
5977
+ if (!isStringArray(surfaces) || !isStringArray(writeSet) || !Array.isArray(narrowing)) {
5978
+ return null;
5979
+ }
5980
+ const flags = [];
5981
+ for (const item of narrowing) {
5982
+ if (typeof item !== "object" || item === null)
5983
+ return null;
5984
+ const flag = item["flag"];
5985
+ const produces = item["produces"];
5986
+ if (typeof flag !== "string" || typeof produces !== "string")
5987
+ return null;
5988
+ flags.push({ flag, produces });
5989
+ }
5990
+ return {
5991
+ adapter: typeof record["adapter"] === "string" ? record["adapter"] : "",
5992
+ surfaces,
5993
+ writeSet,
5994
+ narrowing: flags
5995
+ };
5996
+ }
5997
+ function parseCapabilities(stdout) {
5998
+ let parsed;
5999
+ try {
6000
+ parsed = JSON.parse(stdout);
6001
+ } catch {
6002
+ return null;
6003
+ }
6004
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed))
6005
+ return null;
6006
+ const record = parsed;
6007
+ const version2 = record["version"];
6008
+ const harnesses = record["harnesses"];
6009
+ if (typeof version2 !== "string" || typeof harnesses !== "object" || harnesses === null) {
6010
+ return null;
6011
+ }
6012
+ const entries = {};
6013
+ for (const [harness, value] of Object.entries(harnesses)) {
6014
+ const entry = parseHarnessCapabilities(value);
6015
+ if (entry === null)
6016
+ return null;
6017
+ entries[harness] = entry;
6018
+ }
6019
+ return { version: version2, harnesses: entries };
6020
+ }
6021
+ async function probeCapabilities(context) {
6022
+ const outcome2 = await context.runner.run({
6023
+ executable: "harnesstrim",
6024
+ args: ["capabilities"],
6025
+ cwd: context.projectRoot,
6026
+ timeoutMs: 2e4
6027
+ });
6028
+ if (outcome2.failure !== null || outcome2.exitCode !== 0) {
6029
+ return {
6030
+ capabilities: null,
6031
+ evidence: [
6032
+ evidence({
6033
+ kind: "absence",
6034
+ source: "harnesstrim capabilities",
6035
+ detail: "did not answer with a machine-readable declaration"
6036
+ })
6037
+ ]
6038
+ };
6039
+ }
6040
+ const capabilities = parseCapabilities(outcome2.stdout);
6041
+ if (capabilities === null) {
6042
+ return {
6043
+ capabilities: null,
6044
+ evidence: [
6045
+ evidence({
6046
+ kind: "version-output",
6047
+ source: "harnesstrim capabilities",
6048
+ path: outcome2.executablePath,
6049
+ detail: "answered, but not with a declaration this build can read"
6050
+ })
6051
+ ]
6052
+ };
6053
+ }
6054
+ return {
6055
+ capabilities,
6056
+ evidence: [
6057
+ evidence({
6058
+ kind: "version-output",
6059
+ source: "harnesstrim capabilities",
6060
+ path: outcome2.executablePath,
6061
+ detail: `declared ${capabilities.version} for ${Object.keys(capabilities.harnesses).join(", ")}`
6062
+ })
6063
+ ]
6064
+ };
6065
+ }
6066
+ var SURFACE_ANCHORS = {
6067
+ "post-tool-use": "PostToolUse",
6068
+ "tool-execute-after": "tool.execute.after"
6069
+ };
6070
+ function surfaceMatchesDeclaration(surface, declaration) {
6071
+ const pointAnchor = SURFACE_ANCHORS[declaration.interceptionPoint];
6072
+ if (pointAnchor !== void 0 && !surface.includes(pointAnchor))
6073
+ return false;
6074
+ if (declaration.toolFamily !== EVERY_TOOL_FAMILY && !surface.includes(declaration.toolFamily)) {
6075
+ return false;
6076
+ }
6077
+ return true;
6078
+ }
6079
+ function writeSetPath(entry) {
6080
+ return entry.replace(/\s*\([^)]*\)\s*$/, "").replaceAll("\\", "/");
6081
+ }
6082
+ function coveredBy(path, declared) {
6083
+ const base = declared.endsWith("/") ? declared : `${declared}/`;
6084
+ return path === declared || path.startsWith(base);
6085
+ }
6086
+ function driftWarning(message, remediation) {
6087
+ return diagnostic({
6088
+ severity: "warning",
6089
+ code: "provider-capabilities-drift",
6090
+ subject: "harnesstrim",
6091
+ message,
6092
+ remediation
6093
+ });
6094
+ }
6095
+ function formatSurface(surface) {
6096
+ return `${surface.toolFamily}/${surface.interceptionPoint}`;
6097
+ }
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) => {
6103
+ const observed = capabilities.harnesses[harness];
6104
+ return observed !== void 0 && observed.narrowing.length > 0;
6105
+ });
6106
+ }
6107
+ function writeSetStillReviewed(capabilities, harness) {
6108
+ const review = MANIFEST4.delegatedInstallReview;
6109
+ if (review === null || capabilities === null)
6110
+ return false;
6111
+ const observed = capabilities.harnesses[harness];
6112
+ if (observed === void 0)
6113
+ return false;
6114
+ const declared = observed.writeSet.map(writeSetPath);
6115
+ const boundary = review.containmentBoundary.map(writeSetPath);
6116
+ const everyDeclaredPathContained = declared.every((entry) => boundary.some((prefix) => coveredBy(entry, prefix)));
6117
+ const everyReviewedPathCovered = review.reviewedWriteSet.every((reviewed) => declared.some((entry) => coveredBy(reviewed, entry)));
6118
+ return everyDeclaredPathContained && everyReviewedPathCovered;
6119
+ }
6120
+ function compareCapabilities(manifest, capabilities) {
6121
+ const warnings = [];
6122
+ for (const declaration of manifest.capabilities) {
6123
+ for (const harness of declaration.harnesses) {
6124
+ const observed = capabilities.harnesses[harness];
6125
+ if (observed === void 0) {
6126
+ warnings.push(driftWarning(`the manifest declares ${declaration.capability} on ${harness}, but \`harnesstrim capabilities\` (${capabilities.version}) lists no ${harness} entry`, `Re-test the ${harness} integration: either the manifest is stale or ${harness} lost its HarnessTrim surface`));
6127
+ continue;
6128
+ }
6129
+ const matched = declaration.surfaces.some((surface) => observed.surfaces.some((entry) => surfaceMatchesDeclaration(entry, surface)));
6130
+ if (!matched) {
6131
+ warnings.push(driftWarning(`the manifest records ${declaration.capability} on ${harness} over ${declaration.surfaces.map(formatSurface).join(", ")}, but \`harnesstrim capabilities\` (${capabilities.version}) reports ${observed.surfaces.length === 0 ? "no surfaces" : observed.surfaces.join(" | ")}`, `Re-test the ${harness} integration and update the manifest declaration`));
6132
+ }
6133
+ }
6134
+ }
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)))
6150
+ 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"));
6152
+ }
6153
+ return warnings;
5166
6154
  }
5167
6155
  function harnessesWiredToHarnessTrim(configs) {
5168
6156
  const wired = /* @__PURE__ */ new Set();
@@ -5245,16 +6233,17 @@ async function probeExecutable(context) {
5245
6233
  };
5246
6234
  }
5247
6235
  function metricsLocations(context) {
5248
- return [
5249
- context.fs.join(context.projectRoot, ".harnesstrim", "metrics.jsonl"),
5250
- context.fs.join(context.paths.home, ".hermes", "harnesstrim-metrics.jsonl")
5251
- ];
6236
+ return [context.fs.join(context.projectRoot, ".harnesstrim", "metrics.jsonl")];
5252
6237
  }
5253
6238
  async function detect4(context) {
5254
6239
  const probe = await probeExecutable(context);
6240
+ const observed = probe.installed ? await probeCapabilities(context) : null;
5255
6241
  const configured = harnessesWiredToHarnessTrim(context.harnessConfigs);
5256
6242
  const warnings = [];
5257
- const evidenceItems = [...probe.evidence];
6243
+ const evidenceItems = [...probe.evidence, ...observed?.evidence ?? []];
6244
+ if (observed !== null && observed.capabilities !== null) {
6245
+ warnings.push(...compareCapabilities(MANIFEST4, observed.capabilities));
6246
+ }
5258
6247
  for (const harness of configured) {
5259
6248
  evidenceItems.push(evidence({
5260
6249
  kind: "config-entry",
@@ -5292,6 +6281,10 @@ async function detect4(context) {
5292
6281
  // circumstantial: PLAN §11 says Token Harness never installs it, so every installation it ever
5293
6282
  // sees is the user's.
5294
6283
  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),
5295
6288
  evidence: evidenceItems,
5296
6289
  warnings
5297
6290
  };
@@ -5313,6 +6306,12 @@ function parseTrimEvent(line) {
5313
6306
  if (typeof ts !== "string" || typeof harness !== "string" || typeof before !== "number" || typeof after !== "number") {
5314
6307
  return null;
5315
6308
  }
6309
+ const schemaVersion = typeof record["schemaVersion"] === "number" ? record["schemaVersion"] : 0;
6310
+ if (schemaVersion > 1)
6311
+ return null;
6312
+ const eventId = schemaVersion === 1 && typeof record["eventId"] === "string" && record["eventId"] !== "" ? record["eventId"] : null;
6313
+ if (schemaVersion === 1 && eventId === null)
6314
+ return null;
5316
6315
  return {
5317
6316
  ts,
5318
6317
  harness,
@@ -5320,6 +6319,11 @@ function parseTrimEvent(line) {
5320
6319
  reducer: typeof record["reducer"] === "string" ? record["reducer"] : null,
5321
6320
  beforeChars: before,
5322
6321
  afterChars: after,
6322
+ schemaVersion,
6323
+ eventId,
6324
+ changed: typeof record["changed"] === "boolean" ? record["changed"] : null,
6325
+ beforeTokens: typeof record["beforeTokens"] === "number" ? record["beforeTokens"] : null,
6326
+ afterTokens: typeof record["afterTokens"] === "number" ? record["afterTokens"] : null,
5323
6327
  ...typeof record["mode"] === "string" ? { mode: record["mode"] } : {}
5324
6328
  };
5325
6329
  }
@@ -5331,16 +6335,18 @@ function toEvent(event, sourceId, ordinal, line, context) {
5331
6335
  const instant = new Date(event.ts);
5332
6336
  if (Number.isNaN(instant.getTime()))
5333
6337
  return null;
6338
+ const native = event.schemaVersion === 1;
5334
6339
  const dryrun = event.mode === "dryrun";
5335
- const eventId = synthesizeEventId(sourceId, ordinal, line);
6340
+ const eventId = native ? event.eventId : synthesizeEventId(sourceId, ordinal, line);
6341
+ const passThrough = native && event.changed === false;
6342
+ const hasTokens = native && event.beforeTokens !== null && event.afterTokens !== null;
6343
+ const hasUnprovenMode = native && MODE_CARRYING_HARNESSES.has(event.harness) && !hasTokens && !passThrough;
5336
6344
  return {
5337
6345
  schemaVersion: OPTIMIZATION_EVENT_SCHEMA_VERSION,
5338
6346
  eventId,
5339
6347
  timestamp: instant.toISOString(),
5340
6348
  provider: { id: HARNESSTRIM2, version: null },
5341
6349
  context: {
5342
- // The project the metrics file belongs to, which for the default location is the project it
5343
- // sits inside.
5344
6350
  projectId: context.projectIdFor(context.projectRoot),
5345
6351
  // Unlike RTK's database, a `TrimEvent` names its harness, so this is read rather than left
5346
6352
  // unknown.
@@ -5353,25 +6359,34 @@ function toEvent(event, sourceId, ordinal, line, context) {
5353
6359
  capability: event.harness === OPENCODE2 ? "tool.output.reduce" : "shell.output.reduce"
5354
6360
  },
5355
6361
  measurement: {
5356
- class: dryrun ? "counterfactual" : "estimated-local",
6362
+ class: dryrun || hasUnprovenMode ? "counterfactual" : hasTokens ? "exact-local" : "estimated-local",
5357
6363
  beforeChars: event.beforeChars,
5358
6364
  afterChars: event.afterChars,
5359
- // RFC 0005: "never derived silently". `0.0.5` counts characters and nothing else.
5360
- beforeTokens: null,
5361
- afterTokens: null,
6365
+ // RFC 0005: "never derived silently". `0.0.5` counts characters and nothing else; the
6366
+ // `0.1.0` producer's counts are used as they are because a token count is a token count
6367
+ // where one exists.
6368
+ beforeTokens: hasTokens ? event.beforeTokens : null,
6369
+ afterTokens: hasTokens ? event.afterTokens : null,
5362
6370
  tokenizer: null,
5363
6371
  confidenceLow: null,
5364
6372
  confidenceHigh: null
5365
6373
  },
5366
6374
  outcome: {
5367
- // A dryrun leaves `output.output` untouched, so nothing the model saw changed.
5368
- changed: !dryrun && event.afterChars !== event.beforeChars,
5369
- bypassReason: dryrun ? "dryrun" : event.afterChars === event.beforeChars ? "no-reduction-applied" : null,
6375
+ // A dryrun leaves `output.output` untouched; a native line states whether its attempt
6376
+ // actually changed the output. A native line whose mode the envelope cannot carry is
6377
+ // deliberately not asserted as a change: the figure may describe a dryrun, and reporting
6378
+ // `changed: true` would claim the model received fewer bytes than it did.
6379
+ changed: dryrun || hasUnprovenMode ? false : native ? event.changed ?? true : event.afterChars !== event.beforeChars,
6380
+ bypassReason: dryrun ? "dryrun" : (
6381
+ // Same reason value as the diagnostic code for the residual ambiguity: nothing refutes a
6382
+ // dryrun, and nothing proves one, so the bypass is the envelope's own silence.
6383
+ hasUnprovenMode ? "mode-unresolved" : native ? passThrough ? "pass-through" : null : event.afterChars === event.beforeChars ? "no-reduction-applied" : null
6384
+ ),
5370
6385
  originalReference: null,
5371
6386
  latencyMs: null,
5372
6387
  errorCode: null
5373
6388
  },
5374
- source: { nativeEventId: null, importedAt: context.now() }
6389
+ source: { nativeEventId: native ? eventId : null, importedAt: context.now() }
5375
6390
  };
5376
6391
  }
5377
6392
  async function verify4(context) {
@@ -5412,7 +6427,7 @@ async function verify4(context) {
5412
6427
  checks.push({
5413
6428
  id: "canary-intercepted",
5414
6429
  status: receipt === null ? "not-exercised" : "pass",
5415
- summary: receipt === null ? "no telemetry file exists, so no interception has been recorded" : `${String(receipt.operations)} reductions recorded, most recently ${receipt.observedAt}`,
6430
+ summary: receipt === null ? "no telemetry file yet, so nothing has been observed" : `${String(receipt.operations)} reductions recorded, most recently ${receipt.observedAt}`,
5416
6431
  // A recorded reduction is the provider witnessing its own interception, which is what `canary`
5417
6432
  // means in RFC 0007's tier table.
5418
6433
  achievedTier: receipt === null ? null : "canary",
@@ -5422,7 +6437,7 @@ async function verify4(context) {
5422
6437
  const agents = context.fs.join(context.projectRoot, "AGENTS.md");
5423
6438
  if (await context.fs.stat(agents) !== null) {
5424
6439
  const text = new TextDecoder().decode(await context.fs.readFile(agents));
5425
- if (identifiesCommand(text)) {
6440
+ if (HOOK_COMMAND_PATTERN.test(text)) {
5426
6441
  checks.push({
5427
6442
  id: "instruction-path-present",
5428
6443
  status: "info",
@@ -5458,6 +6473,8 @@ async function collectMetrics(context, store) {
5458
6473
  let skipped = 0;
5459
6474
  let lastCursor = null;
5460
6475
  let readAny = false;
6476
+ let sawNative = false;
6477
+ let unprovableModeEvents = 0;
5461
6478
  for (const path of metricsLocations(context)) {
5462
6479
  const stat3 = await context.fs.stat(path);
5463
6480
  if (stat3 === null)
@@ -5508,6 +6525,10 @@ async function collectMetrics(context, store) {
5508
6525
  ordinal += 1;
5509
6526
  continue;
5510
6527
  }
6528
+ if (parsed.schemaVersion === 1)
6529
+ sawNative = true;
6530
+ if (event.outcome.bypassReason === "mode-unresolved")
6531
+ unprovableModeEvents += 1;
5511
6532
  events.push(event);
5512
6533
  lastLine = trimmed;
5513
6534
  ordinal += 1;
@@ -5559,21 +6580,104 @@ async function collectMetrics(context, store) {
5559
6580
  diagnostics
5560
6581
  };
5561
6582
  }
6583
+ if (unprovableModeEvents > 0) {
6584
+ diagnostics.push(diagnostic({
6585
+ severity: "info",
6586
+ code: "provider-metrics-mode-unresolved",
6587
+ message: `${String(unprovableModeEvents)} native event${unprovableModeEvents === 1 ? "" : "s"} from a mode-carrying harness could not be classed as realized: schema 1 carries no reduction mode`,
6588
+ remediation: "Treat these figures as counterfactual; only OpenCode can later prove its mode from the configured plugin option"
6589
+ }));
6590
+ }
5562
6591
  return {
5563
6592
  providerId: HARNESSTRIM2,
5564
- // RFC 0005 §Importer degradation policy: character-only events are `legacy`, and "running in
5565
- // legacy mode is a supported steady state, not a warning".
5566
- mode: "legacy",
5567
- source: `harnesstrim TrimEvent JSONL (${TESTED_UPSTREAM} shape)`,
6593
+ // RFC 0005 §Importer degradation policy, decided by what this run read: `native` when the
6594
+ // file carried schema 1 events with producer IDs, `legacy` when it only had the character
6595
+ // stream. Legacy inside a native run is not a problem — those lines are older files.
6596
+ //
6597
+ // A native stream whose mode-carrying harness lines cannot prove a realized reduction (no
6598
+ // tokens, no pass-through) is `native-with-residue`: the import is native, but a part of it
6599
+ // could not be classed as realized, and the count above qualifies exactly how much. A bare
6600
+ // `native` must keep meaning "everything was classed".
6601
+ mode: sawNative && unprovableModeEvents > 0 ? "native-with-residue" : sawNative ? "native" : "legacy",
6602
+ source: `harnesstrim TrimEvent JSONL (schema 0 and schema 1)`,
5568
6603
  imported,
5569
6604
  skipped,
5570
6605
  cursor: lastCursor,
5571
6606
  diagnostics
5572
6607
  };
5573
6608
  }
5574
- async function plan(_context, request) {
5575
- await Promise.resolve();
5576
- return { providerId: HARNESSTRIM2, desiredState: request.desiredState, actions: [] };
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("/")),
6614
+ digest
6615
+ }));
6616
+ if (!request.harnesses.some((harness) => harness.id === CLAUDE2)) {
6617
+ return { providerId: HARNESSTRIM2, desiredState: request.desiredState, actions: [] };
6618
+ }
6619
+ if (request.desiredState === "absent") {
6620
+ const actions = expectedArtifacts.map((artifact) => ({
6621
+ kind: "remove-owned-change",
6622
+ id: `harnesstrim-claude-skill-remove-${digestText(artifact.path).slice(7, 15)}`,
6623
+ riskClass: "reversible",
6624
+ requiresNetwork: false,
6625
+ requiresElevation: false,
6626
+ affectedPaths: [artifact.path],
6627
+ affectedProcesses: [],
6628
+ preconditions: ["the HarnessTrim skill still matches the reviewed 0.0.7 artifact"],
6629
+ postconditions: ["the owned HarnessTrim skill is absent"],
6630
+ rollbackData: "file-snapshot",
6631
+ explanation: `Remove the owned HarnessTrim Claude skill ${context.fs.basename(context.fs.dirname(artifact.path))}`,
6632
+ 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 };
6637
+ }
6638
+ const installed = await probeExecutable(context);
6639
+ if (!installed.installed) {
6640
+ return { providerId: HARNESSTRIM2, desiredState: "configured", actions: [] };
6641
+ }
6642
+ 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: [] };
6646
+ }
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] };
5577
6681
  }
5578
6682
  var harnesstrimAdapter = {
5579
6683
  manifest: MANIFEST4,
@@ -5581,11 +6685,13 @@ var harnesstrimAdapter = {
5581
6685
  identifiesCommand,
5582
6686
  verify: verify4,
5583
6687
  collectMetrics,
5584
- plan
6688
+ plan,
6689
+ plansWithoutOwnership: true
5585
6690
  };
5586
6691
 
5587
6692
  // packages/adapters/dist/src/providers/rtk-plan.js
5588
6693
  var RTK_HOOK_COMMAND_PREFIX = "rtk hook";
6694
+ var HOOK_LIST_HARNESSES = /* @__PURE__ */ new Set(["claude"]);
5589
6695
  function hookListPointer(eventName) {
5590
6696
  return `hooks.${eventName.replace(/\./g, "\\.")}`;
5591
6697
  }
@@ -5601,6 +6707,8 @@ function planTargets(context, request) {
5601
6707
  const harness = request.harnesses.find((entry) => entry.id === owned.scope.harness);
5602
6708
  if (harness === void 0)
5603
6709
  continue;
6710
+ if (!HOOK_LIST_HARNESSES.has(harness.id))
6711
+ continue;
5604
6712
  const point = harness.interceptionPoints.find((entry) => entry.scopeId === owned.scope.interceptionPoint);
5605
6713
  if (point === void 0)
5606
6714
  continue;
@@ -5733,11 +6841,11 @@ function installAction(context, manifest) {
5733
6841
  channel.digestAvailable ? "the release artifact carries a digest that will be verified" : "this channel publishes no digest, so the artifact cannot be verified by content"
5734
6842
  ],
5735
6843
  postconditions: ["`rtk --version` reports a version inside the tested range"],
5736
- // Not `package-inventory`: that promises an inventory-based reversal nothing implements. RFC
5737
- // 0004 allows rollback only by restoring snapshots, "never by inventing an uninstall command",
5738
- // and a package is not a file — so an installed package survives a rollback and the executor
5739
- // says so rather than letting the report imply otherwise.
5740
- rollbackData: "none",
6844
+ // The declared value the executor now implements (RFC 0009 §Initial delivery order item 1):
6845
+ // the executor captures whatever the channel has installed before the install runs, and a
6846
+ // rollback restores that captured version through the same channel. `none` is left only for
6847
+ // channels this build cannot ask about — there the executor says the package stayed instead.
6848
+ rollbackData: channelCanReportInventory(channel.id) ? "package-inventory" : "none",
5741
6849
  explanation: `Install RTK through ${channel.id}`,
5742
6850
  packageManager: channel.id,
5743
6851
  // The channel's own name for the package, because they differ: `rtk-ai.rtk` on winget and
@@ -5776,6 +6884,7 @@ function buildRtkPlan(input) {
5776
6884
  // packages/adapters/dist/src/providers/rtk.js
5777
6885
  var RTK2 = providerId("rtk");
5778
6886
  var CLAUDE3 = harnessId("claude");
6887
+ var OPENCODE3 = harnessId("opencode");
5779
6888
  var MANIFEST5 = {
5780
6889
  schemaVersion: MANIFEST_SCHEMA_VERSION,
5781
6890
  id: RTK2,
@@ -5814,10 +6923,53 @@ var MANIFEST5 = {
5814
6923
  sourceReference: "docs/spikes/2.5-live-verification-log.md",
5815
6924
  upstreamVersion: "0.42.0"
5816
6925
  }
6926
+ },
6927
+ /**
6928
+ * OpenCode, from spike 9.1.
6929
+ *
6930
+ * Separate entries rather than adding `OPENCODE` to the two above, because the surface is not
6931
+ * the same one: RTK reaches Claude Code through a `PreToolUse` hook matching `Bash`, and
6932
+ * OpenCode through a plugin on `tool.execute.before`. A capability entry names one surface
6933
+ * list, and merging these would claim that RTK serves `Bash/pre-tool-use` on OpenCode — a
6934
+ * point OpenCode does not have.
6935
+ *
6936
+ * `shell.command.rewrite` and `shell.output.reduce` again arrive together, for the reason they
6937
+ * do on Claude Code: the plugin calls `rtk rewrite`, and the rewritten command is what filters
6938
+ * its own output. One interception point, two capabilities.
6939
+ */
6940
+ {
6941
+ capability: "shell.command.rewrite",
6942
+ mode: "exclusive",
6943
+ harnesses: [OPENCODE3],
6944
+ // `tool.execute` is the family the harness manifest declares as shell-executing. The plugin
6945
+ // narrows to `tool === "bash" || tool === "shell"` inside that family, which is every shell
6946
+ // route OpenCode exposes — unlike Claude Code on Windows, there is no second one to miss.
6947
+ surfaces: [{ toolFamily: "tool.execute", interceptionPoint: "tool-execute-before" }],
6948
+ evidence: {
6949
+ sourceReference: "docs/spikes/9.1-rtk-opencode-observation-log.md",
6950
+ upstreamVersion: "0.44.0"
6951
+ }
6952
+ },
6953
+ {
6954
+ capability: "shell.output.reduce",
6955
+ mode: "exclusive",
6956
+ harnesses: [OPENCODE3],
6957
+ surfaces: [{ toolFamily: "tool.execute", interceptionPoint: "tool-execute-before" }],
6958
+ evidence: {
6959
+ sourceReference: "docs/spikes/9.1-rtk-opencode-observation-log.md",
6960
+ upstreamVersion: "0.44.0"
6961
+ }
5817
6962
  }
5818
6963
  ],
5819
6964
  platforms: [
5820
- { os: "windows", wsl: false, supported: true, limitation: null },
6965
+ {
6966
+ os: "windows",
6967
+ wsl: false,
6968
+ supported: true,
6969
+ // Spike 9.1. Named on Windows alone because Windows is where Desktop was run; the same
6970
+ // defect on another platform's Desktop build would be a guess until someone watches it.
6971
+ limitation: "RTK's OpenCode plugin is inert under OpenCode Desktop, which supplies no `$` shell helper to plugins, so the plugin's own guard disables it silently"
6972
+ },
5821
6973
  { os: "linux", wsl: true, supported: true, limitation: null },
5822
6974
  { os: "macos", wsl: false, supported: true, limitation: null },
5823
6975
  { os: "linux", wsl: false, supported: true, limitation: null }
@@ -5827,6 +6979,31 @@ var MANIFEST5 = {
5827
6979
  harness: CLAUDE3,
5828
6980
  testedVersions: { minimum: "2.0.0", maximum: "2.1.212" },
5829
6981
  verificationTier: "canary"
6982
+ },
6983
+ {
6984
+ harness: OPENCODE3,
6985
+ // Spike 9.1 watched interception on the CLI at `1.18.11` and watched it fail on Desktop at
6986
+ // `1.18.14`. The range covers both because both were observed; the failure is a declared
6987
+ // platform limitation below rather than a narrower range, because it is a property of the
6988
+ // host application and not of the OpenCode version — the same 1.18.x plugin loads in both.
6989
+ testedVersions: { minimum: "1.18.11", maximum: "1.18.14" },
6990
+ /**
6991
+ * `config-only`, even though spike 9.1 watched a canary-grade interception here — the counter
6992
+ * moved by exactly the number of shell calls the OpenCode session made.
6993
+ *
6994
+ * A tier is what `verify` can prove on the user's machine, not what a spike proved once on a
6995
+ * clean one. RTK's receipt is its history database, and the `commands` table carries
6996
+ * `timestamp`, token counts, `exec_time_ms`, and `project_path` — no harness column, and no
6997
+ * column standing in for one. The spike could attribute those rows because it ran OpenCode
6998
+ * alone in a scratch directory and read the counter either side; `verify` runs where both
6999
+ * harnesses are wired to the same binary and writing into the same table, and cannot say
7000
+ * which of them a row came from.
7001
+ *
7002
+ * Declaring `canary` here would credit RTK's whole command history to whichever harness the
7003
+ * reader happened to be looking at. Claude Code keeps `canary` because its own hook receipt
7004
+ * is per-harness; this one is provider-wide, and RFC 0007 asks the question per harness.
7005
+ */
7006
+ verificationTier: "config-only"
5830
7007
  }
5831
7008
  ],
5832
7009
  installationChannels: [
@@ -5870,9 +7047,10 @@ var MANIFEST5 = {
5870
7047
  },
5871
7048
  delegatedInstallReview: null
5872
7049
  };
5873
- var TESTED_VERSIONS2 = { minimum: "0.40.0", maximum: "0.42.0" };
7050
+ var TESTED_VERSIONS2 = { minimum: "0.40.0", maximum: "0.44.0" };
5874
7051
  var VERSION_PATTERN5 = /(\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?)/;
5875
7052
  var HOOK_COMMAND_PATTERN2 = /(^|[\\/\s"'])rtk(\.exe)?([\s"']|$)/i;
7053
+ var PLUGIN_MODULE_PATTERN3 = /(^|[\\/])rtk\.(ts|js|mjs|cjs|mts|cts)$/i;
5876
7054
  function isRecord4(value) {
5877
7055
  return typeof value === "object" && value !== null && !Array.isArray(value);
5878
7056
  }
@@ -5915,7 +7093,7 @@ function parseRtkAnalytics(text) {
5915
7093
  function harnessesWiredToRtk(configs) {
5916
7094
  const wired = /* @__PURE__ */ new Set();
5917
7095
  for (const config of configs) {
5918
- if (config.commands.some((command) => HOOK_COMMAND_PATTERN2.test(command))) {
7096
+ if (config.commands.some((command) => identifiesCommand2(command))) {
5919
7097
  wired.add(config.harnessId);
5920
7098
  }
5921
7099
  }
@@ -6031,13 +7209,32 @@ async function detect5(context) {
6031
7209
  versionVerdict: version2.verdict,
6032
7210
  configuredHarnesses: configured,
6033
7211
  unmanagedHarnessesConfigured: [],
6034
- // RFC 0002 §Providers may exceed the managed surface. RTK's manifest covers Claude
6035
- // Code only, so the qualifier RFC 0006's doctor transcript uses for HarnessTrim does
6036
- // not apply here.
7212
+ /**
7213
+ * RFC 0002 §Providers may exceed the managed surface.
7214
+ *
7215
+ * The field asks whether this manifest names a harness with no adapter behind it. It names two,
7216
+ * Claude Code and OpenCode, and both are managed — so the qualifier RFC 0006's doctor transcript
7217
+ * uses for HarnessTrim does not apply, and the report says "not configured for any harness"
7218
+ * without hedging.
7219
+ *
7220
+ * `rtk init --agent` separately targets Cursor, Windsurf, Cline, Kilo Code, Antigravity, Kimi,
7221
+ * Pi, Hermes, and Droid. That is RTK exceeding *Token Harness*, not this manifest exceeding the
7222
+ * managed set, and it stays out of the flag: claiming those here would make the resolver
7223
+ * responsible for harnesses this build has no adapter to inspect.
7224
+ */
6037
7225
  supportsUnmanagedHarnesses: false,
6038
7226
  // RFC 0004 §Brownfield adoption: nothing has been applied by Token Harness yet, so
6039
7227
  // every installation it finds is the user's.
6040
7228
  managedByTokenHarness: false,
7229
+ /**
7230
+ * Always, and independent of the observed version.
7231
+ *
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.
7236
+ */
7237
+ assignable: true,
6041
7238
  evidence: evidenceItems,
6042
7239
  warnings
6043
7240
  };
@@ -6138,17 +7335,17 @@ function rtkDatabasePath(context) {
6138
7335
  function cursorGeneration(low) {
6139
7336
  return `rtk-history:low=${String(low ?? 0)}`;
6140
7337
  }
6141
- function toEvent2(row, context) {
6142
- const id = numberAt(row, "id");
6143
- const timestamp = stringAt(row, "timestamp");
6144
- const before = numberAt(row, "input_tokens");
6145
- const after = numberAt(row, "output_tokens");
7338
+ function toEvent2(row2, context) {
7339
+ const id = numberAt(row2, "id");
7340
+ const timestamp = stringAt(row2, "timestamp");
7341
+ const before = numberAt(row2, "input_tokens");
7342
+ const after = numberAt(row2, "output_tokens");
6146
7343
  if (id === null || timestamp === null || before === null || after === null)
6147
7344
  return null;
6148
7345
  const instant = new Date(timestamp);
6149
7346
  if (Number.isNaN(instant.getTime()))
6150
7347
  return null;
6151
- const projectPath = stringAt(row, "project_path");
7348
+ const projectPath = stringAt(row2, "project_path");
6152
7349
  return {
6153
7350
  schemaVersion: OPTIMIZATION_EVENT_SCHEMA_VERSION,
6154
7351
  // Native, not synthesized: RFC 0005 prefers the upstream identifier when there is one,
@@ -6159,7 +7356,7 @@ function toEvent2(row, context) {
6159
7356
  context: {
6160
7357
  // An empty `project_path` means RTK recorded no directory; attributing the event to
6161
7358
  // the directory `metrics` happens to run in would invent an attribution.
6162
- projectId: projectPath === null || projectPath === "" ? "p_unattributed" : context.projectIdFor(projectPath),
7359
+ projectId: projectPath === null || projectPath === "" ? UNATTRIBUTED_PROJECT_ID : context.projectIdFor(projectPath),
6163
7360
  // RTK proxies commands for whichever harness invoked it and does not record which.
6164
7361
  // The hook that wires it is per harness, but a *row* carries no harness, and reading
6165
7362
  // one off the current configuration would attribute months of history to today's
@@ -6292,11 +7489,11 @@ async function collectMetrics2(context, store) {
6292
7489
  const events = [];
6293
7490
  let skipped = 0;
6294
7491
  let highest = from;
6295
- for (const row of batch.rows) {
6296
- const id = numberAt(row, "id");
7492
+ for (const row2 of batch.rows) {
7493
+ const id = numberAt(row2, "id");
6297
7494
  if (id !== null && id > highest)
6298
7495
  highest = id;
6299
- const event = toEvent2(row, context);
7496
+ const event = toEvent2(row2, context);
6300
7497
  if (event === null) {
6301
7498
  skipped += 1;
6302
7499
  continue;
@@ -6349,7 +7546,7 @@ async function collectMetrics2(context, store) {
6349
7546
  }
6350
7547
  var SOURCE_LABEL = "rtk history.db (commands)";
6351
7548
  function identifiesCommand2(command) {
6352
- return HOOK_COMMAND_PATTERN2.test(command);
7549
+ return HOOK_COMMAND_PATTERN2.test(command) || PLUGIN_MODULE_PATTERN3.test(command);
6353
7550
  }
6354
7551
  async function plan2(context, request) {
6355
7552
  const version2 = await readVersion(context);
@@ -6378,7 +7575,6 @@ function listProviderAdapters() {
6378
7575
 
6379
7576
  // apps/cli/dist/src/commands/plan.js
6380
7577
  var DEFAULT_PROFILE = "safe";
6381
- var ASSIGNABLE_PROVIDERS = ["rtk"];
6382
7578
  async function computePlan(context) {
6383
7579
  const diagnostics = [];
6384
7580
  const providerAdapters = listProviderAdapters().filter((adapter) => context.provider === null || adapter.manifest.id === context.provider);
@@ -6405,11 +7601,6 @@ async function computePlan(context) {
6405
7601
  harnessConfigs.push(...inspection.summaries);
6406
7602
  }
6407
7603
  }
6408
- const providers = providerAdapters.map((adapter) => ({
6409
- id: adapter.manifest.id,
6410
- capabilities: adapter.manifest.capabilities,
6411
- assignable: ASSIGNABLE_PROVIDERS.includes(adapter.manifest.id)
6412
- }));
6413
7604
  const providerContext = detectionContext === null || context.adapters === null ? null : {
6414
7605
  ...detectionContext,
6415
7606
  harnessConfigs,
@@ -6417,12 +7608,19 @@ async function computePlan(context) {
6417
7608
  localDatabase: context.adapters.localDatabase,
6418
7609
  projectIdFor: context.adapters.projectIdFor
6419
7610
  };
7611
+ const assignable = /* @__PURE__ */ new Map();
6420
7612
  if (providerContext !== null) {
6421
7613
  for (const adapter of providerAdapters) {
6422
7614
  const detection = await adapter.detect(providerContext);
6423
7615
  versions.providers[adapter.manifest.id] = detection.version;
7616
+ assignable.set(adapter.manifest.id, detection.assignable);
6424
7617
  }
6425
7618
  }
7619
+ const providers = providerAdapters.map((adapter) => ({
7620
+ id: adapter.manifest.id,
7621
+ capabilities: adapter.manifest.capabilities,
7622
+ assignable: assignable.get(adapter.manifest.id) ?? false
7623
+ }));
6426
7624
  const resolution = resolveOwnership({
6427
7625
  profile,
6428
7626
  harnesses: present,
@@ -6432,17 +7630,47 @@ async function computePlan(context) {
6432
7630
  harness: context.harness
6433
7631
  });
6434
7632
  const actions = [];
7633
+ const blocked = [];
6435
7634
  if (providerContext !== null) {
7635
+ const rows = context.compatibilityRows ?? COMPATIBILITY_ROWS;
6436
7636
  for (const adapter of providerAdapters) {
6437
7637
  const owned = resolution.ownership.filter((entry) => entry.owner === adapter.manifest.id);
6438
- if (owned.length === 0)
7638
+ if (owned.length === 0 && adapter.plansWithoutOwnership !== true)
6439
7639
  continue;
6440
7640
  const providerPlan = await adapter.plan(providerContext, {
6441
7641
  ownership: owned,
6442
7642
  harnesses: present,
6443
7643
  desiredState: "configured"
6444
7644
  });
6445
- actions.push(...providerPlan.actions);
7645
+ if (providerPlan.actions.length === 0)
7646
+ continue;
7647
+ const touched = [...new Set(owned.map((entry) => entry.scope.harness))];
7648
+ if (touched.length === 0)
7649
+ touched.push(...present.map((harness) => harness.id));
7650
+ let admitted = true;
7651
+ for (const harness of touched) {
7652
+ const admission = admitManagedMutation(rows, {
7653
+ provider: adapter.manifest.id,
7654
+ providerVersion: versions.providers[adapter.manifest.id] ?? null,
7655
+ harness,
7656
+ harnessVersion: versions.harnesses[harness] ?? null,
7657
+ os: context.platform.os,
7658
+ wsl: context.platform.isWsl
7659
+ });
7660
+ if (admission.state !== "admitted") {
7661
+ admitted = false;
7662
+ blocked.push({
7663
+ provider: adapter.manifest.id,
7664
+ providerVersion: versions.providers[adapter.manifest.id] ?? null,
7665
+ harness,
7666
+ harnessVersion: versions.harnesses[harness] ?? null,
7667
+ verdict: admission.verdict,
7668
+ missing: admission.missing
7669
+ });
7670
+ }
7671
+ }
7672
+ if (admitted)
7673
+ actions.push(...providerPlan.actions);
6446
7674
  }
6447
7675
  }
6448
7676
  const report = {
@@ -6500,7 +7728,7 @@ async function computePlan(context) {
6500
7728
  remediation: null
6501
7729
  }));
6502
7730
  }
6503
- if (resolution.ownership.length > 0 && actions.length === 0) {
7731
+ if (resolution.ownership.length > 0 && actions.length === 0 && blocked.length === 0) {
6504
7732
  diagnostics.push(diagnostic({
6505
7733
  severity: "info",
6506
7734
  code: "already-in-desired-state",
@@ -6508,6 +7736,14 @@ async function computePlan(context) {
6508
7736
  remediation: null
6509
7737
  }));
6510
7738
  }
7739
+ for (const entry of blocked) {
7740
+ diagnostics.push(diagnostic({
7741
+ severity: "error",
7742
+ code: "managed-mutation-blocked",
7743
+ message: `plan refuses managed mutation of ${entry.harness} by ${entry.provider}${entry.providerVersion !== null ? ` at ${entry.providerVersion}` : ""}: ${entry.missing}`,
7744
+ remediation: "Add a compatibility row whose fixture proves this combination, or configure this integration by hand"
7745
+ }));
7746
+ }
6511
7747
  for (const conflict of resolution.conflicts) {
6512
7748
  diagnostics.push(diagnostic({
6513
7749
  severity: "error",
@@ -6516,7 +7752,7 @@ async function computePlan(context) {
6516
7752
  remediation: conflict.remediation
6517
7753
  }));
6518
7754
  }
6519
- return { report, versions, diagnostics, present, harnessConfigs };
7755
+ return { report, versions, diagnostics, present, harnessConfigs, blocked };
6520
7756
  }
6521
7757
  async function persist(context, computed) {
6522
7758
  if (context.adapters === null || context.stateRoot === null)
@@ -6541,7 +7777,7 @@ async function runPlan(context) {
6541
7777
  const computed = await computePlan(context);
6542
7778
  const persisted = await persist(context, computed);
6543
7779
  const report = { ...computed.report, persisted };
6544
- const exitCode = report.conflicts.length > 0 ? EXIT_CODES["blocked-by-conflict"] : EXIT_CODES.ok;
7780
+ const exitCode = computed.blocked.length > 0 ? EXIT_CODES["unsupported-environment"] : report.conflicts.length > 0 ? EXIT_CODES["blocked-by-conflict"] : EXIT_CODES.ok;
6545
7781
  return commandResult({
6546
7782
  command: "plan",
6547
7783
  exitCode,
@@ -6612,6 +7848,9 @@ async function runApply(context) {
6612
7848
  const diagnostics = [];
6613
7849
  const computed = await computePlan(context);
6614
7850
  diagnostics.push(...computed.diagnostics);
7851
+ if (computed.blocked.length > 0) {
7852
+ return finish("rejected", EXIT_CODES["unsupported-environment"], emptyReport("rejected"), diagnostics);
7853
+ }
6615
7854
  if (computed.report.conflicts.length > 0) {
6616
7855
  return finish("rejected", EXIT_CODES["blocked-by-conflict"], emptyReport("rejected"), diagnostics);
6617
7856
  }
@@ -6766,10 +8005,40 @@ async function runDoctor(context) {
6766
8005
  projectIdFor: context.adapters?.projectIdFor ?? (() => "p_unattributed")
6767
8006
  })));
6768
8007
  const problemCount = harnesses.filter(isBroken).length + providers.filter(isBroken).length + [...harnesses, ...providers].filter((detection) => detection.versionVerdict === "unknown-newer").length;
8008
+ const rows = context.compatibilityRows ?? COMPATIBILITY_ROWS;
8009
+ const providersWithCoverage = providers.map((detection) => {
8010
+ if (detection.state !== "configured" || detection.configuredHarnesses.length === 0) {
8011
+ return detection;
8012
+ }
8013
+ const uncovered = detection.configuredHarnesses.flatMap((harness) => {
8014
+ const harnessVersion = harnesses.find((candidate) => candidate.harnessId === harness)?.version ?? null;
8015
+ const admission = admitManagedMutation(rows, {
8016
+ provider: detection.providerId,
8017
+ providerVersion: detection.version,
8018
+ harness,
8019
+ harnessVersion,
8020
+ os: context.platform.os,
8021
+ wsl: context.platform.isWsl
8022
+ });
8023
+ if (admission.state === "admitted")
8024
+ return [];
8025
+ return [
8026
+ diagnostic({
8027
+ severity: "warning",
8028
+ code: "no-compatibility-row",
8029
+ message: `no compatibility row covers ${detection.providerId} on ${harness}${detection.version !== null ? ` at ${detection.version}` : ""} \u2014 ${admission.missing}`,
8030
+ remediation: "Add a compatibility row whose fixture proves this combination, or configure this integration by hand"
8031
+ })
8032
+ ];
8033
+ });
8034
+ if (uncovered.length === 0)
8035
+ return detection;
8036
+ return { ...detection, warnings: [...detection.warnings, ...uncovered] };
8037
+ });
6769
8038
  const report = {
6770
8039
  platform: context.platform,
6771
8040
  harnesses,
6772
- providers,
8041
+ providers: providersWithCoverage,
6773
8042
  problemCount
6774
8043
  };
6775
8044
  return commandResult({
@@ -6861,14 +8130,42 @@ async function runMetrics(context) {
6861
8130
  }
6862
8131
  }
6863
8132
  }
8133
+ const projectId = context.adapters === null ? null : context.adapters.projectIdFor(context.projectRoot);
6864
8134
  const events = [];
6865
8135
  for await (const event of store.query({
6866
8136
  since: window.sinceInstant,
6867
8137
  until: window.untilInstant,
8138
+ ...projectId === null ? {} : { projectId },
6868
8139
  ...context.provider === null ? {} : { providerIds: [context.provider] }
6869
8140
  })) {
6870
8141
  events.push(event);
6871
8142
  }
8143
+ if (projectId === null) {
8144
+ diagnostics.push(diagnostic({
8145
+ severity: "info",
8146
+ code: "metrics-not-project-scoped",
8147
+ message: "This report covers every project in the store: no project identity was available",
8148
+ remediation: null
8149
+ }));
8150
+ } else {
8151
+ let unattributed = 0;
8152
+ for await (const _event of store.query({
8153
+ since: window.sinceInstant,
8154
+ until: window.untilInstant,
8155
+ projectId: UNATTRIBUTED_PROJECT_ID,
8156
+ ...context.provider === null ? {} : { providerIds: [context.provider] }
8157
+ })) {
8158
+ unattributed += 1;
8159
+ }
8160
+ if (unattributed > 0) {
8161
+ diagnostics.push(diagnostic({
8162
+ severity: "info",
8163
+ code: "metrics-unattributed-excluded",
8164
+ message: `${String(unattributed)} operation${unattributed === 1 ? "" : "s"} named no project and are excluded from this report`,
8165
+ remediation: null
8166
+ }));
8167
+ }
8168
+ }
6872
8169
  const report = aggregateEvents({
6873
8170
  events,
6874
8171
  windowStart: window.windowStart,
@@ -6993,7 +8290,12 @@ async function runRollback(context) {
6993
8290
  fs,
6994
8291
  snapshots: built.store,
6995
8292
  journal: built.journal,
6996
- now: context.now
8293
+ now: context.now,
8294
+ // Package inventories, when the transaction captured any: restoring a package needs the same
8295
+ // process runner the install used. Absent, the rollback reports those packages as not
8296
+ // restored instead of attempting a silent reinstall.
8297
+ runner: context.adapters.runner,
8298
+ cwd: context.projectRoot
6997
8299
  });
6998
8300
  diagnostics.push(...result.diagnostics);
6999
8301
  const report = empty(result.exitCode === 0 ? "rolled-back" : "dirty");
@@ -7115,7 +8417,6 @@ async function runUninstall(context) {
7115
8417
  }
7116
8418
 
7117
8419
  // apps/cli/dist/src/commands/status.js
7118
- var ASSIGNABLE_PROVIDERS2 = ["rtk"];
7119
8420
  async function runStatus(context) {
7120
8421
  const report = {
7121
8422
  platform: context.platform,
@@ -7153,12 +8454,8 @@ async function runStatus(context) {
7153
8454
  configs.push(...inspection.summaries);
7154
8455
  }
7155
8456
  const providerAdapters = listProviderAdapters();
7156
- const providers = providerAdapters.map((adapter) => ({
7157
- id: adapter.manifest.id,
7158
- capabilities: adapter.manifest.capabilities,
7159
- assignable: ASSIGNABLE_PROVIDERS2.includes(adapter.manifest.id)
7160
- }));
7161
8457
  const observedVersions = {};
8458
+ const providers = [];
7162
8459
  for (const adapter of providerAdapters) {
7163
8460
  const detection = await adapter.detect({
7164
8461
  ...detectionContext,
@@ -7168,6 +8465,11 @@ async function runStatus(context) {
7168
8465
  projectIdFor: context.adapters.projectIdFor
7169
8466
  });
7170
8467
  observedVersions[adapter.manifest.id] = detection.version;
8468
+ providers.push({
8469
+ id: adapter.manifest.id,
8470
+ capabilities: adapter.manifest.capabilities,
8471
+ assignable: detection.assignable
8472
+ });
7171
8473
  }
7172
8474
  const resolution = resolveOwnership({
7173
8475
  profile: "safe",
@@ -7228,14 +8530,15 @@ function upgradeAction(input) {
7228
8530
  ],
7229
8531
  postconditions: [`${input.packageName} reports ${input.target}`],
7230
8532
  /**
7231
- * `none`, and the consequence is worth stating where a reviewer sees it.
8533
+ * `package-inventory` where the channel can report what is installed, `none` otherwise.
7232
8534
  *
7233
8535
  * An update replaces a binary. Rolling the transaction back restores files and leaves the new
7234
- * version in place, so the executor's `install-not-reversible` diagnostic is not boilerplate
7235
- * here — it is the difference between the machine being restored and the *configuration*
7236
- * being restored.
8536
+ * version in place, so the executor's receipt is not boilerplate here — it is the difference
8537
+ * between the machine being restored and the *configuration* being restored. With an
8538
+ * inventory-capturing channel the rollback also reinstalls the previous version, and the
8539
+ * receipt says per package whether that reinstall took.
7237
8540
  */
7238
- rollbackData: "none",
8541
+ rollbackData: channelCanReportInventory(input.channel) ? "package-inventory" : "none",
7239
8542
  explanation: `Update ${input.packageName} from ${input.installed} to ${input.target} through ${input.channel}`,
7240
8543
  packageManager: input.channel,
7241
8544
  packageName: input.packageName,
@@ -7288,7 +8591,7 @@ async function runUpdate(context) {
7288
8591
  for (const adapter of providerAdapters) {
7289
8592
  const detection = await adapter.detect(providerContext);
7290
8593
  const channel = preferredInstallationChannel(adapter.manifest.installationChannels, context.platform.os);
7291
- const row = {
8594
+ const row2 = {
7292
8595
  providerId: adapter.manifest.id,
7293
8596
  installed: detection.version,
7294
8597
  available: null,
@@ -7297,24 +8600,24 @@ async function runUpdate(context) {
7297
8600
  pin: pins.pins.get(adapter.manifest.id) ?? null
7298
8601
  };
7299
8602
  if (detection.state === "absent") {
7300
- row.verdict = "not-installed";
7301
- report.providers.push(row);
8603
+ row2.verdict = "not-installed";
8604
+ report.providers.push(row2);
7302
8605
  continue;
7303
8606
  }
7304
- if (row.pin !== null) {
7305
- row.verdict = "pinned";
7306
- report.providers.push(row);
8607
+ if (row2.pin !== null) {
8608
+ row2.verdict = "pinned";
8609
+ report.providers.push(row2);
7307
8610
  diagnostics.push(diagnostic({
7308
8611
  severity: "info",
7309
8612
  code: "provider-pinned",
7310
- message: `${adapter.manifest.id} is pinned at ${row.pin}, so no update was planned for it`,
8613
+ message: `${adapter.manifest.id} is pinned at ${row2.pin}, so no update was planned for it`,
7311
8614
  remediation: `Remove it from the pin file to allow updates`
7312
8615
  }));
7313
8616
  continue;
7314
8617
  }
7315
8618
  if (channel === null) {
7316
- row.verdict = "no-channel";
7317
- report.providers.push(row);
8619
+ row2.verdict = "no-channel";
8620
+ report.providers.push(row2);
7318
8621
  continue;
7319
8622
  }
7320
8623
  const packageName = channel.packageId ?? adapter.manifest.id;
@@ -7327,21 +8630,21 @@ async function runUpdate(context) {
7327
8630
  diagnostics.push(...query.diagnostics);
7328
8631
  if (query.destination !== null)
7329
8632
  destinations.add(query.destination);
7330
- row.available = query.version;
8633
+ row2.available = query.version;
7331
8634
  if (query.status !== "found" || query.version === null) {
7332
- row.verdict = query.status === "unknown" ? "unknown" : "unavailable";
7333
- report.providers.push(row);
8635
+ row2.verdict = query.status === "unknown" ? "unknown" : "unavailable";
8636
+ report.providers.push(row2);
7334
8637
  continue;
7335
8638
  }
7336
8639
  const installed = detection.version === null ? null : parseSemanticVersion(detection.version);
7337
8640
  const offered = parseSemanticVersion(query.version);
7338
8641
  if (installed === null || offered === null || compareVersions(offered, installed) <= 0) {
7339
- row.verdict = "current";
7340
- report.providers.push(row);
8642
+ row2.verdict = "current";
8643
+ report.providers.push(row2);
7341
8644
  continue;
7342
8645
  }
7343
- row.verdict = "upgradable";
7344
- report.providers.push(row);
8646
+ row2.verdict = "upgradable";
8647
+ report.providers.push(row2);
7345
8648
  actions.push(upgradeAction({
7346
8649
  providerId: adapter.manifest.id,
7347
8650
  channel: channel.id,
@@ -7508,11 +8811,12 @@ async function runVerify(context) {
7508
8811
  presentHarnesses
7509
8812
  );
7510
8813
  for (const harnessId2 of harnesses) {
8814
+ const declaredTier = adapter.manifest.harnesses.find((entry) => entry.harness === harnessId2)?.verificationTier ?? verification.declaredTier;
7511
8815
  results.push({
7512
8816
  providerId: adapter.manifest.id,
7513
8817
  harnessId: harnessId2,
7514
- status: statusFor(verification.achievedTier, verification.declaredTier),
7515
- declaredTier: verification.declaredTier,
8818
+ status: statusFor(verification.achievedTier, declaredTier),
8819
+ declaredTier,
7516
8820
  managedByTokenHarness: MANAGED_PROVIDERS.includes(adapter.manifest.id),
7517
8821
  checks: verification.checks
7518
8822
  });
@@ -7556,11 +8860,52 @@ function statusFor(achieved, declared) {
7556
8860
  }
7557
8861
 
7558
8862
  // apps/cli/dist/src/render/layout.js
8863
+ var MAX_WIDTH = 78;
8864
+ function wrap(text, indent, width = MAX_WIDTH) {
8865
+ const limit = Math.max(20, width - indent);
8866
+ const words = text.split(/\s+/).filter((word) => word !== "");
8867
+ if (words.length === 0)
8868
+ return [];
8869
+ const lines = [];
8870
+ let current = "";
8871
+ for (const word of words) {
8872
+ if (current === "") {
8873
+ current = word;
8874
+ continue;
8875
+ }
8876
+ if (`${current} ${word}`.length <= limit) {
8877
+ current = `${current} ${word}`;
8878
+ continue;
8879
+ }
8880
+ lines.push(current);
8881
+ current = word;
8882
+ }
8883
+ lines.push(current);
8884
+ const pad = " ".repeat(indent);
8885
+ return lines.map((line, index) => index === 0 ? pad + line : pad + line);
8886
+ }
7559
8887
  function column(value, width, minimumGutter = 2) {
7560
8888
  if (value.length >= width)
7561
8889
  return value + " ".repeat(minimumGutter);
7562
8890
  return value.padEnd(width, " ");
7563
8891
  }
8892
+ function truncate(value, width) {
8893
+ const characters = [...value];
8894
+ if (characters.length <= width)
8895
+ return value;
8896
+ return `${characters.slice(0, Math.max(1, width - 1)).join("").trimEnd()}\u2026`;
8897
+ }
8898
+ var COLUMN_SEPARATOR = " - ";
8899
+ function row(cells) {
8900
+ const rendered = cells.map(([value, width], index) => index === cells.length - 1 ? value : column(value, width, 0));
8901
+ return rendered.join(COLUMN_SEPARATOR);
8902
+ }
8903
+ function truncatePath(value, width) {
8904
+ const characters = [...value];
8905
+ if (characters.length <= width)
8906
+ return value;
8907
+ return `\u2026${characters.slice(characters.length - (width - 1)).join("")}`;
8908
+ }
7564
8909
  function rightAlign(value, width) {
7565
8910
  return value.padStart(width, " ");
7566
8911
  }
@@ -7639,7 +8984,7 @@ var HEADERS = {
7639
8984
  rollback: "Rollback",
7640
8985
  uninstall: "Uninstall"
7641
8986
  };
7642
- function renderApplyReport(report, _context, command = "apply") {
8987
+ function renderApplyReport(report, context, command = "apply") {
7643
8988
  const lines = [];
7644
8989
  const header = [HEADERS[command] ?? "Apply"];
7645
8990
  header.push(report.planId === null ? "no plan" : `plan ${report.planId}`);
@@ -7652,72 +8997,155 @@ function renderApplyReport(report, _context, command = "apply") {
7652
8997
  if (report.results.length > 0) {
7653
8998
  lines.push("Actions");
7654
8999
  for (const [index, result] of report.results.entries()) {
7655
- lines.push(` ${String(index + 1)}. ${column(result.kind, KIND_WIDTH)}${column(result.status, STATUS_WIDTH)}${result.path ?? ""}`);
9000
+ lines.push(` ${String(index + 1)}. ${row([
9001
+ [result.kind, KIND_WIDTH],
9002
+ [result.status, STATUS_WIDTH],
9003
+ // Abbreviated: an absolute Windows path under a long account name ran past 140
9004
+ // characters, and `~/` is both shorter and what every other command shows.
9005
+ [result.path === null ? "" : displayPath(result.path, context.home), 0]
9006
+ ])}`);
7656
9007
  }
7657
9008
  lines.push("");
7658
9009
  }
7659
9010
  if (report.unrestored.length > 0) {
7660
9011
  lines.push("Not restored");
7661
9012
  for (const path of report.unrestored)
7662
- lines.push(` ${path}`);
9013
+ lines.push(` ${displayPath(path, context.home)}`);
7663
9014
  lines.push("");
7664
9015
  }
7665
- lines.push((OUTCOME_LINES[command] ?? OUTCOME_LINES["apply"])?.[report.outcome] ?? "");
9016
+ const removedNothing = command === "uninstall" && report.outcome === "committed" && report.results.length > 0 && report.results.every((result) => result.status !== "applied");
9017
+ lines.push(removedNothing ? "Removed nothing: no owned entry was recognised. Everything is untouched." : (OUTCOME_LINES[command] ?? OUTCOME_LINES["apply"])?.[report.outcome] ?? "");
7666
9018
  return document(lines);
7667
9019
  }
7668
9020
 
7669
9021
  // apps/cli/dist/src/render/doctor.js
7670
- var HARNESS_ID_WIDTH = 12;
7671
- var HARNESS_STATE_WIDTH = 12;
7672
- var PROVIDER_ID_WIDTH = 14;
7673
- var PROVIDER_STATE_WIDTH = 14;
7674
- var PROVIDER_VERSION_WIDTH = 8;
7675
- function harnessDetail(detection, context) {
7676
- if (detection.configPath === null)
7677
- return "";
7678
- return displayPath(detection.configPath, context.home);
7679
- }
7680
- function providerNote(detection) {
7681
- if (detection.state === "broken") {
7682
- return "installed but not usable";
7683
- }
7684
- if (detection.state === "absent" || detection.state === "available") {
7685
- return "";
7686
- }
7687
- if (detection.configuredHarnesses.length === 0) {
7688
- const qualifier = detection.supportsUnmanagedHarnesses ? "managed harness" : "harness";
7689
- return `not configured for any ${qualifier}`;
9022
+ var NAME_WIDTH = 15;
9023
+ var STATE_WIDTH = 12;
9024
+ var VERSION_WIDTH = 10;
9025
+ var WIRED_WIDTH = 14;
9026
+ function harnessState(detection) {
9027
+ switch (detection.state) {
9028
+ case "absent":
9029
+ return "not found";
9030
+ case "detected":
9031
+ return "no hooks";
9032
+ case "configured":
9033
+ return "hooks";
9034
+ case "broken":
9035
+ return "unreadable";
9036
+ }
9037
+ }
9038
+ function wiredTo(detection) {
9039
+ if (detection.state === "broken")
9040
+ return "cannot run";
9041
+ if (detection.state === "absent")
9042
+ return "not installed";
9043
+ if (detection.configuredHarnesses.length === 0)
9044
+ return "nothing yet";
9045
+ return detection.configuredHarnesses.join(", ");
9046
+ }
9047
+ function setUpBy(detection) {
9048
+ if (detection.state === "absent" || detection.configuredHarnesses.length === 0)
9049
+ return "\u2014";
9050
+ return detection.managedByTokenHarness ? "this tool" : "you";
9051
+ }
9052
+ function notes(report) {
9053
+ const lines = [];
9054
+ for (const harness of report.harnesses) {
9055
+ if (harness.state === "broken") {
9056
+ lines.push(` ${row([
9057
+ [harness.harnessId, NAME_WIDTH - 2],
9058
+ ["configuration could not be read", 0]
9059
+ ])}`);
9060
+ }
9061
+ if (harness.versionVerdict === "unknown-newer") {
9062
+ lines.push(` ${row([
9063
+ [harness.harnessId, NAME_WIDTH - 2],
9064
+ [`${harness.version ?? ""} is newer than any tested version`, 0]
9065
+ ])}`);
9066
+ }
9067
+ }
9068
+ for (const provider of report.providers) {
9069
+ if (provider.state === "broken") {
9070
+ lines.push(` ${row([
9071
+ [provider.providerId, NAME_WIDTH - 2],
9072
+ ["installed but could not be run", 0]
9073
+ ])}`);
9074
+ }
9075
+ if (provider.versionVerdict === "unknown-newer") {
9076
+ lines.push(` ${row([
9077
+ [provider.providerId, NAME_WIDTH - 2],
9078
+ [`${provider.version ?? ""} is newer than any tested version`, 0]
9079
+ ])}`);
9080
+ }
7690
9081
  }
7691
- const list = detection.configuredHarnesses.join(", ");
7692
- const adopted = detection.managedByTokenHarness ? "" : " (adopted, not managed)";
7693
- return `configured for ${list}${adopted}`;
9082
+ return lines;
7694
9083
  }
7695
9084
  function renderDoctorReport(report, context) {
7696
9085
  const lines = [];
7697
9086
  lines.push(`Token Harness ${context.toolVersion} \u2014 ${renderPlatformSummary(report.platform)}, Node ${report.platform.nodeVersion}`);
7698
9087
  lines.push("");
7699
- lines.push("Harnesses");
9088
+ lines.push(` ${row([
9089
+ ["HARNESSES", NAME_WIDTH - 2],
9090
+ ["STATE", STATE_WIDTH],
9091
+ ["CONFIG FILE", 0]
9092
+ ])}`);
7700
9093
  if (report.harnesses.length === 0) {
7701
- lines.push(" no harness adapters registered");
9094
+ lines.push(" none registered");
7702
9095
  } else {
7703
9096
  for (const harness of report.harnesses) {
7704
- lines.push(` ${column(harness.harnessId, HARNESS_ID_WIDTH)}${column(harness.state, HARNESS_STATE_WIDTH)}${harnessDetail(harness, context)}`);
9097
+ lines.push(` ${row([
9098
+ [harness.harnessId, NAME_WIDTH - 2],
9099
+ [harnessState(harness), STATE_WIDTH],
9100
+ [
9101
+ harness.configPath === null ? "" : (
9102
+ // The columns before this one, plus their separators, consume a fixed prefix; the
9103
+ // path gets what is left and is cut from the left so the file name survives.
9104
+ truncatePath(displayPath(harness.configPath, context.home), MAX_WIDTH - (2 + NAME_WIDTH - 2 + 3 + STATE_WIDTH + 3))
9105
+ ),
9106
+ 0
9107
+ ]
9108
+ ])}`);
7705
9109
  }
7706
9110
  }
7707
9111
  lines.push("");
7708
- lines.push("Providers");
9112
+ lines.push(` ${row([
9113
+ ["PROVIDERS", NAME_WIDTH - 2],
9114
+ ["VERSION", VERSION_WIDTH],
9115
+ ["WIRED TO", WIRED_WIDTH],
9116
+ ["SET UP BY", 0]
9117
+ ])}`);
7709
9118
  if (report.providers.length === 0) {
7710
- lines.push(" no provider adapters registered");
9119
+ lines.push(" none registered");
7711
9120
  } else {
7712
9121
  for (const provider of report.providers) {
7713
- lines.push(` ${column(provider.providerId, PROVIDER_ID_WIDTH)}${column(provider.state, PROVIDER_STATE_WIDTH)}${column(provider.version ?? "", PROVIDER_VERSION_WIDTH)}${providerNote(provider)}`);
9122
+ lines.push(` ${row([
9123
+ [provider.providerId, NAME_WIDTH - 2],
9124
+ [provider.version ?? "-", VERSION_WIDTH],
9125
+ [wiredTo(provider), WIRED_WIDTH],
9126
+ [setUpBy(provider), 0]
9127
+ ])}`);
7714
9128
  }
7715
9129
  }
9130
+ const notable = notes(report);
9131
+ if (notable.length > 0) {
9132
+ lines.push("");
9133
+ lines.push("NOTES");
9134
+ lines.push(...notable);
9135
+ }
9136
+ const unwired = report.providers.filter((provider) => provider.state !== "absent" && provider.configuredHarnesses.length === 0);
7716
9137
  lines.push("");
7717
- if (report.problemCount === 0) {
7718
- lines.push("Nothing is broken. Run `token-harness plan` to see what would change.");
7719
- } else {
7720
- lines.push(`${report.problemCount} ${pluralize(report.problemCount, "problem")} found. Fix ${pluralize(report.problemCount, "it", "them")} before running \`token-harness plan\`.`);
9138
+ lines.push("NEXT");
9139
+ const suggestions = report.providers.every((provider) => provider.state === "absent") || unwired.length > 0 ? [["token-harness plan", "see what would change \u2014 writes nothing"]] : [
9140
+ ["token-harness verify", "check the pipeline actually intercepts"],
9141
+ ["token-harness metrics --since 7d", "see what it saved"]
9142
+ ];
9143
+ const commandWidth = Math.max(...suggestions.map(([command]) => command.length));
9144
+ for (const [command, description] of suggestions) {
9145
+ lines.push(` ${row([
9146
+ [command, commandWidth],
9147
+ [description, 0]
9148
+ ])}`);
7721
9149
  }
7722
9150
  return document(lines);
7723
9151
  }
@@ -7725,11 +9153,10 @@ function renderDoctorReport(report, context) {
7725
9153
  // apps/cli/dist/src/render/metrics.js
7726
9154
  var CLASS_LABEL_WIDTH = 23;
7727
9155
  var FIGURE_BLOCK_GUTTER = 4;
7728
- var PROVIDER_ID_WIDTH2 = 15;
7729
- var PROVIDER_SAVED_WIDTH = 23;
7730
- var PROVIDER_CLASS_WIDTH = 17;
7731
- var PROVIDER_OPERATIONS_WIDTH = 19;
7732
- var PROVIDER_CONTINUATION_INDENT = 2 + PROVIDER_ID_WIDTH2;
9156
+ var PROVIDER_ID_WIDTH = 13;
9157
+ var PROVIDER_SAVED_WIDTH = 21;
9158
+ var PROVIDER_CLASS_WIDTH = 15;
9159
+ var PROVIDER_CONTINUATION_INDENT = 2 + PROVIDER_ID_WIDTH;
7733
9160
  var CLASS_LABELS = {
7734
9161
  "exact-local": "Exact local",
7735
9162
  "estimated-local": "Estimated local",
@@ -7737,21 +9164,21 @@ var CLASS_LABELS = {
7737
9164
  "end-to-end-billed": "End-to-end billed"
7738
9165
  };
7739
9166
  function renderClassRows(report) {
7740
- const byClass = new Map(report.classes.map((row) => [row.class, row]));
7741
- const ordered = MEASUREMENT_CLASSES.map((id) => byClass.get(id)).filter((row) => row !== void 0);
7742
- const withFigures = ordered.filter((row) => row.before !== null && row.after !== null);
7743
- const beforeWidth = Math.max(0, ...withFigures.map((row) => formatCount(row.before).length));
7744
- const afterWidth = Math.max(0, ...withFigures.map((row) => formatCount(row.after).length));
7745
- const blocks = ordered.map((row) => {
7746
- if (row.before === null || row.after === null)
9167
+ const byClass = new Map(report.classes.map((row2) => [row2.class, row2]));
9168
+ const ordered = MEASUREMENT_CLASSES.map((id) => byClass.get(id)).filter((row2) => row2 !== void 0);
9169
+ const withFigures = ordered.filter((row2) => row2.before !== null && row2.after !== null);
9170
+ const beforeWidth = Math.max(0, ...withFigures.map((row2) => formatCount(row2.before).length));
9171
+ const afterWidth = Math.max(0, ...withFigures.map((row2) => formatCount(row2.after).length));
9172
+ const blocks = ordered.map((row2) => {
9173
+ if (row2.before === null || row2.after === null)
7747
9174
  return "";
7748
- return `${rightAlign(formatCount(row.before), beforeWidth)} -> ${rightAlign(formatCount(row.after), afterWidth)} ${row.unit ?? ""}`;
9175
+ return `${rightAlign(formatCount(row2.before), beforeWidth)} -> ${rightAlign(formatCount(row2.after), afterWidth)} ${row2.unit ?? ""}`;
7749
9176
  });
7750
9177
  const blockWidth = Math.max(0, ...blocks.map((block) => block.length)) + FIGURE_BLOCK_GUTTER;
7751
- return ordered.map((row, index) => {
7752
- const label = column(CLASS_LABELS[row.class], CLASS_LABEL_WIDTH);
9178
+ return ordered.map((row2, index) => {
9179
+ const label = column(CLASS_LABELS[row2.class], CLASS_LABEL_WIDTH);
7753
9180
  const block = (blocks[index] ?? "").padEnd(blockWidth, " ");
7754
- const tail = row.saved === null ? row.note ?? "" : `saved ${formatCount(row.saved)}`;
9181
+ const tail = row2.saved === null ? row2.note ?? "" : `saved ${formatCount(row2.saved)}`;
7755
9182
  return `${label}${block}${tail}`;
7756
9183
  });
7757
9184
  }
@@ -7769,14 +9196,21 @@ function renderMetricsReport(report, _context) {
7769
9196
  lines.push(" no provider reported a measurable saving in this window");
7770
9197
  } else {
7771
9198
  for (const provider of report.providers) {
7772
- lines.push(` ${column(provider.providerId, PROVIDER_ID_WIDTH2)}${column(`saved ${formatCount(provider.saved)} ${provider.unit}`, PROVIDER_SAVED_WIDTH)}${column(provider.class, PROVIDER_CLASS_WIDTH)}${column(`${formatCount(provider.operations)} operations`, PROVIDER_OPERATIONS_WIDTH)}${provider.harnesses.join(", ")}`);
7773
- const notes = [];
9199
+ lines.push(` ${row([
9200
+ [provider.providerId, PROVIDER_ID_WIDTH],
9201
+ [`saved ${formatCount(provider.saved)} ${provider.unit}`, PROVIDER_SAVED_WIDTH],
9202
+ [provider.class, PROVIDER_CLASS_WIDTH],
9203
+ [`${formatCount(provider.operations)} operations`, 0]
9204
+ ])}`);
9205
+ const notes2 = [];
9206
+ if (provider.harnesses.length > 0)
9207
+ notes2.push(`on ${provider.harnesses.join(", ")}`);
7774
9208
  if (!provider.managedByTokenHarness)
7775
- notes.push("adopted, not managed");
9209
+ notes2.push("set up by you");
7776
9210
  if (provider.adapterMode !== null)
7777
- notes.push(`adapter mode ${provider.adapterMode}`);
7778
- if (notes.length > 0) {
7779
- lines.push(`${" ".repeat(PROVIDER_CONTINUATION_INDENT)}${notes.join(" \u2014 ")}`);
9211
+ notes2.push(`mode ${provider.adapterMode}`);
9212
+ if (notes2.length > 0) {
9213
+ lines.push(`${" ".repeat(PROVIDER_CONTINUATION_INDENT)}${notes2.join(" \u2014 ")}`);
7780
9214
  }
7781
9215
  }
7782
9216
  }
@@ -7808,8 +9242,8 @@ function renderConflicts(report) {
7808
9242
  lines.push("");
7809
9243
  lines.push(` ${column("conflict", CONFLICT_LABEL_WIDTH)}${conflict.code}`);
7810
9244
  for (const detail2 of conflict.detail)
7811
- lines.push(` ${detail2}`);
7812
- lines.push(` Fix: ${conflict.remediation}`);
9245
+ lines.push(...wrap(detail2, 4));
9246
+ lines.push(...wrap(`Fix: ${conflict.remediation}`, 4));
7813
9247
  }
7814
9248
  lines.push("");
7815
9249
  lines.push("No plan was produced.");
@@ -7852,13 +9286,24 @@ function renderPlanReport(report, context) {
7852
9286
  if (report.exclusions.length > 0) {
7853
9287
  lines.push("");
7854
9288
  lines.push("Excluded");
9289
+ const grouped = /* @__PURE__ */ new Map();
7855
9290
  for (const exclusion of report.exclusions) {
7856
- const [first = "", ...rest] = exclusion.reason;
7857
- lines.push(` ${column(exclusion.excluded, EXCLUSION_PROVIDER_WIDTH)}${column(exclusion.scope.capability, EXCLUSION_CAPABILITY_WIDTH)}${first}`);
7858
- for (const line of rest) {
7859
- lines.push(`${" ".repeat(EXCLUSION_CONTINUATION_INDENT)}${line}`);
9291
+ const key = `${exclusion.excluded}\0${exclusion.scope.capability}`;
9292
+ const existing = grouped.get(key);
9293
+ if (existing === void 0) {
9294
+ grouped.set(key, {
9295
+ capability: exclusion.scope.capability,
9296
+ harnesses: [exclusion.scope.harness]
9297
+ });
9298
+ } else if (!existing.harnesses.includes(exclusion.scope.harness)) {
9299
+ existing.harnesses.push(exclusion.scope.harness);
7860
9300
  }
7861
9301
  }
9302
+ for (const [key, entry] of grouped) {
9303
+ const provider = key.slice(0, key.indexOf("\0"));
9304
+ lines.push(` ${column(provider, EXCLUSION_PROVIDER_WIDTH)}${column(entry.capability, EXCLUSION_CAPABILITY_WIDTH)}${entry.harnesses.join(", ")}`);
9305
+ lines.push(...wrap("its installer cannot produce this in isolation, so it is measured but not given the scope", EXCLUSION_CONTINUATION_INDENT));
9306
+ }
7862
9307
  }
7863
9308
  lines.push("");
7864
9309
  lines.push("Actions");
@@ -7934,22 +9379,22 @@ var VERDICT_WIDTH = 15;
7934
9379
  function widthOf(values, floor) {
7935
9380
  return Math.max(floor, ...values.map((value) => value.length + 2));
7936
9381
  }
7937
- function detail(row) {
7938
- switch (row.verdict) {
9382
+ function detail(row2) {
9383
+ switch (row2.verdict) {
7939
9384
  case "upgradable":
7940
- return `${String(row.installed)} \u2192 ${String(row.available)} via ${String(row.channel)}`;
9385
+ return `${String(row2.installed)} \u2192 ${String(row2.available)} via ${String(row2.channel)}`;
7941
9386
  case "current":
7942
- return `${String(row.installed)} is the newest ${String(row.channel)} offers`;
9387
+ return `${String(row2.installed)} is the newest ${String(row2.channel)} offers`;
7943
9388
  case "pinned":
7944
- return `held at ${String(row.pin)} by a pin`;
9389
+ return `held at ${String(row2.pin)} by a pin`;
7945
9390
  case "not-installed":
7946
9391
  return "not installed, so there is nothing to update";
7947
9392
  case "no-channel":
7948
9393
  return "no installation channel on this platform";
7949
9394
  case "unknown":
7950
- return `${String(row.channel)} answered without a version this build can read`;
9395
+ return `${String(row2.channel)} answered without a version this build can read`;
7951
9396
  case "unavailable":
7952
- return `${String(row.channel)} could not be asked`;
9397
+ return `${String(row2.channel)} could not be asked`;
7953
9398
  }
7954
9399
  }
7955
9400
  function renderUpdateReport(report, _context) {
@@ -7958,16 +9403,20 @@ function renderUpdateReport(report, _context) {
7958
9403
  lines.push("No provider was inspected.");
7959
9404
  return document(lines);
7960
9405
  }
7961
- const providerWidth = widthOf(report.providers.map((row) => row.providerId), PROVIDER_WIDTH);
7962
- const verdictWidth = widthOf(report.providers.map((row) => row.verdict), VERDICT_WIDTH);
7963
- for (const row of report.providers) {
7964
- lines.push(`${column(row.providerId, providerWidth)}${column(row.verdict, verdictWidth)}${detail(row)}`);
9406
+ const providerWidth = Math.min(20, widthOf(report.providers.map((entry) => entry.providerId), PROVIDER_WIDTH));
9407
+ const detailStart = providerWidth + 3 + VERDICT_WIDTH + 3;
9408
+ for (const entry of report.providers) {
9409
+ lines.push(truncate(row([
9410
+ [entry.providerId, providerWidth],
9411
+ [entry.verdict, VERDICT_WIDTH],
9412
+ [truncate(detail(entry), MAX_WIDTH - detailStart), 0]
9413
+ ]), MAX_WIDTH));
7965
9414
  }
7966
9415
  if (report.network.length > 0) {
7967
9416
  lines.push("");
7968
9417
  lines.push(`Network: ${report.network.join(", ")}`);
7969
9418
  }
7970
- const upgradable = report.providers.filter((row) => row.verdict === "upgradable").length;
9419
+ const upgradable = report.providers.filter((row2) => row2.verdict === "upgradable").length;
7971
9420
  const execution = report.execution;
7972
9421
  lines.push("");
7973
9422
  if (execution === null || execution.outcome === "nothing-to-do") {
@@ -7983,33 +9432,34 @@ function renderUpdateReport(report, _context) {
7983
9432
  }
7984
9433
 
7985
9434
  // apps/cli/dist/src/render/verify.js
7986
- var CHECK_STATUS_WIDTH = 6;
7987
- var CHECK_ID_WIDTH = 27;
7988
- function statusWidth(report) {
7989
- const longest = Math.max(0, ...report.results.flatMap((result) => result.checks.map((check) => check.status.length)));
7990
- return Math.max(CHECK_STATUS_WIDTH, longest + 2);
9435
+ var STATUS_WIDTH2 = 16;
9436
+ var CHECK_WIDTH = 12;
9437
+ function shortCheck(id) {
9438
+ return truncate(id.replace(/-resolves$|-registered$|-intercepted$/, ""), CHECK_WIDTH);
7991
9439
  }
7992
9440
  function renderVerifyReport(report, _context) {
7993
9441
  const lines = [];
7994
- const width = statusWidth(report);
7995
- lines.push(report.receiptId === null || report.appliedAt === null ? "No receipt \u2014 verifying the live configuration" : `Receipt ${report.receiptId} \u2014 applied ${report.appliedAt}`);
7996
- lines.push("");
9442
+ lines.push(report.receiptId === null || report.appliedAt === null ? "No receipt: verifying what is on the machine now" : `Receipt ${report.receiptId} \u2014 applied ${report.appliedAt}`);
7997
9443
  for (const result of report.results) {
7998
- const header = [result.providerId, result.harnessId];
7999
- if (!result.managedByTokenHarness)
8000
- header.push("adopted, not managed");
8001
- header.push(`declared tier: ${result.declaredTier}`);
8002
- lines.push(header.join(" \u2014 "));
9444
+ lines.push("");
9445
+ lines.push(`${result.providerId} on ${result.harnessId} \u2014 set up by ${result.managedByTokenHarness ? "this tool" : "you"}, tier ${result.declaredTier}`);
8003
9446
  for (const check of result.checks) {
8004
- lines.push(` ${column(check.status, width)}${column(check.id, CHECK_ID_WIDTH)}${check.summary}`);
9447
+ const indent = 2 + CHECK_WIDTH + 3 + STATUS_WIDTH2 + 3;
9448
+ const wrapped = wrap(check.summary, indent);
9449
+ lines.push(` ${row([
9450
+ [shortCheck(check.id), CHECK_WIDTH],
9451
+ [check.status, STATUS_WIDTH2],
9452
+ [(wrapped[0] ?? "").trimStart(), 0]
9453
+ ])}`);
9454
+ lines.push(...wrapped.slice(1));
8005
9455
  }
8006
9456
  }
8007
9457
  lines.push("");
8008
9458
  if (report.healthyAtDeclaredTier) {
8009
- lines.push("Pipeline healthy at the declared tier for every provider.");
9459
+ lines.push("Healthy at the declared tier for every provider.");
8010
9460
  } else {
8011
9461
  const failures = report.results.flatMap((result) => result.checks).filter((check) => check.status === "fail").length;
8012
- lines.push(`${failures} ${pluralize(failures, "check")} below the declared tier. The pipeline is not proven.`);
9462
+ lines.push(`${String(failures)} ${pluralize(failures, "check")} below tier. Not proven.`);
8013
9463
  }
8014
9464
  return document(lines);
8015
9465
  }
@@ -8053,27 +9503,37 @@ function renderHuman(result, context) {
8053
9503
  }
8054
9504
 
8055
9505
  // apps/cli/dist/src/usage.js
8056
- var ROOT_USAGE = `token-harness \u2014 one control plane for token-efficient coding agents
9506
+ var ROOT_USAGE = `token-harness \u2014 set up and measure token-saving tools for coding agents
9507
+
9508
+ First time here
9509
+ 1 token-harness doctor what is on this machine writes nothing
9510
+ 2 token-harness plan what would change writes nothing
9511
+ 3 token-harness apply --yes make those changes WRITES
9512
+ 4 token-harness verify is it actually intercepting writes nothing
9513
+ 5 token-harness metrics what it saved writes nothing
9514
+
9515
+ Steps 1 and 2 are safe to run right now. Nothing changes until step 3.
8057
9516
 
8058
9517
  Usage
8059
9518
  token-harness <command> [flags]
9519
+ token-harness help <command>
8060
9520
 
8061
- Commands
8062
- apply Execute a plan inside a reversible transaction
8063
- doctor Report detected harnesses and providers
8064
- metrics Import provider records and report savings by measurement class
8065
- plan Compute a dry-run plan; nothing is changed
9521
+ Commands, in the order you would use them
9522
+ doctor What is installed, what is wired up, what is worth knowing
9523
+ plan Compute what would change, file by file. Changes nothing
9524
+ apply Run that plan inside a transaction that can be rolled back
9525
+ verify Check the pipeline actually intercepts, at its declared tier
9526
+ metrics Report what was saved, labelled with how it was measured
8066
9527
  status Report applied pipelines, drift, and importer modes
8067
- verify Check that an integration actually intercepts, at its declared tier
8068
- rollback Restore the files a transaction changed, as they were before it
8069
- uninstall Remove what Token Harness owns, leaving everything else
8070
- update Update the installed providers to what their channel offers
9528
+ update Update installed providers to what their channel offers
9529
+ rollback Restore the files a transaction changed, as they were before
9530
+ uninstall Remove what Token Harness set up, leaving everything else
8071
9531
 
8072
9532
  Flags
8073
9533
  --json Emit one machine-readable envelope on stdout
8074
9534
  --harness <id> Restrict the operation to one harness
8075
9535
  --provider <id> Restrict the operation to one provider
8076
- --project <dir> Operate on that project instead of the current directory
9536
+ --project <dir> Use that project instead of the current directory
8077
9537
  --since <window> Report from this point: a duration like 7d, or a date
8078
9538
  --until <window> Report up to this point; defaults to now
8079
9539
  --plan <id> Apply a previously computed plan by id
@@ -8081,8 +9541,8 @@ Flags
8081
9541
  --version Print the version and exit 0
8082
9542
  --help Print usage and exit 0
8083
9543
 
8084
- Mutating commands are dry-run by default and there is no flag that skips
8085
- planning. Exit codes and the JSON envelope are specified in RFC 0006.`;
9544
+ Only apply, update, rollback and uninstall change anything, and none of
9545
+ them do without --yes. Exit codes and JSON are specified in RFC 0006.`;
8086
9546
  var COMMAND_USAGE = {
8087
9547
  apply: `token-harness apply \u2014 execute a plan inside a reversible transaction
8088
9548
 
@@ -8221,7 +9681,7 @@ function usageText(topic) {
8221
9681
  }
8222
9682
 
8223
9683
  // apps/cli/dist/src/version.js
8224
- var TOOL_VERSION = "0.1.0";
9684
+ var TOOL_VERSION = "0.1.2";
8225
9685
 
8226
9686
  // apps/cli/dist/src/run.js
8227
9687
  var DEFAULT_COMMANDS = {
@@ -8235,12 +9695,31 @@ var DEFAULT_COMMANDS = {
8235
9695
  update: runUpdate,
8236
9696
  verify: runVerify
8237
9697
  };
8238
- function formatDiagnostic(entry) {
8239
- const lines = [`${entry.severity} ${entry.code}: ${entry.message}`];
8240
- if (entry.path !== null)
8241
- lines.push(` ${entry.path}`);
8242
- if (entry.remediation !== null)
8243
- lines.push(` Fix: ${entry.remediation}`);
9698
+ var SUBJECT_WIDTH = 12;
9699
+ function diagnosticLine(entry) {
9700
+ const label = entry.subject ?? entry.code;
9701
+ return truncate(` ${row([
9702
+ [label, SUBJECT_WIDTH],
9703
+ [entry.message, 0]
9704
+ ])}`, MAX_WIDTH);
9705
+ }
9706
+ function formatDiagnostics(diagnostics) {
9707
+ const errors = diagnostics.filter((entry) => entry.severity === "error");
9708
+ const warnings = diagnostics.filter((entry) => entry.severity === "warning");
9709
+ if (errors.length === 0 && warnings.length === 0)
9710
+ return "";
9711
+ const lines = [];
9712
+ for (const [title, group] of [
9713
+ ["ERRORS", errors],
9714
+ ["WARNINGS", warnings]
9715
+ ]) {
9716
+ if (group.length === 0)
9717
+ continue;
9718
+ lines.push("");
9719
+ lines.push(title);
9720
+ for (const entry of group)
9721
+ lines.push(diagnosticLine(entry));
9722
+ }
8244
9723
  return `${lines.join("\n")}
8245
9724
  `;
8246
9725
  }
@@ -8282,8 +9761,9 @@ function emit(result, options, renderContext, json) {
8282
9761
  const rendering = renderHuman(result, renderContext);
8283
9762
  if (rendering.report !== "")
8284
9763
  options.streams.out(rendering.report);
8285
- for (const entry of rendering.stderrDiagnostics)
8286
- options.streams.err(formatDiagnostic(entry));
9764
+ const diagnostics = formatDiagnostics(rendering.stderrDiagnostics);
9765
+ if (diagnostics !== "")
9766
+ options.streams.err(diagnostics);
8287
9767
  return result.exitCode;
8288
9768
  }
8289
9769
  function emitHelpOrVersion(invocation, options) {
@@ -8378,7 +9858,8 @@ async function run(options) {
8378
9858
  until: invocation.options.until,
8379
9859
  planId: invocation.options.plan,
8380
9860
  confirmed: invocation.options.yes,
8381
- metrics: options.metrics ?? null
9861
+ metrics: options.metrics ?? null,
9862
+ compatibilityRows: options.compatibilityRows ?? null
8382
9863
  };
8383
9864
  const table = options.commands ?? DEFAULT_COMMANDS;
8384
9865
  let result;