@codyswann/lisa 4.84.6 → 4.84.8

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 (165) hide show
  1. package/all/copy-overwrite/scripts/lib/completion-authorization.mjs +282 -0
  2. package/all/copy-overwrite/scripts/lib/completion-proof-adapters.mjs +213 -0
  3. package/all/copy-overwrite/scripts/lib/completion-proof-conformance.mjs +115 -0
  4. package/all/copy-overwrite/scripts/lib/completion-proof-content.mjs +408 -0
  5. package/all/copy-overwrite/scripts/lib/completion-proof-errors.mjs +16 -0
  6. package/all/copy-overwrite/scripts/lib/completion-proof-github.mjs +233 -0
  7. package/all/copy-overwrite/scripts/lib/completion-proof-markdown.mjs +70 -0
  8. package/all/copy-overwrite/scripts/lib/completion-proof-projection.mjs +169 -0
  9. package/all/copy-overwrite/scripts/lib/completion-proof-publisher.mjs +238 -0
  10. package/all/copy-overwrite/scripts/lib/completion-proof-reader.mjs +175 -0
  11. package/all/copy-overwrite/scripts/lib/completion-proof-shape.mjs +374 -0
  12. package/all/copy-overwrite/scripts/lib/completion-proof-source.mjs +174 -0
  13. package/all/copy-overwrite/scripts/lib/history-secret-scanner.mjs +20 -5
  14. package/all/copy-overwrite/scripts/lib/npm-update-helper-inventory.mjs +15 -0
  15. package/all/copy-overwrite/scripts/lib/usage-effectiveness.d.mts +27 -0
  16. package/all/copy-overwrite/scripts/lib/usage-effectiveness.mjs +156 -0
  17. package/all/copy-overwrite/scripts/lib/usage-ledger-integrity.d.mts +8 -0
  18. package/all/copy-overwrite/scripts/lib/usage-ledger-integrity.mjs +141 -0
  19. package/all/copy-overwrite/scripts/lib/usage-ledger.d.mts +89 -0
  20. package/all/copy-overwrite/scripts/lib/usage-ledger.mjs +206 -0
  21. package/all/copy-overwrite/scripts/lisa-hooks/parity-safety-net.sh +3 -1
  22. package/all/copy-overwrite/scripts/lisa-work-item.mjs +255 -39
  23. package/all/copy-overwrite/scripts/npm-updater-helper-graph.json +141 -3
  24. package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
  25. package/dist/core/lisa-owned-hash-ledger.js +186 -0
  26. package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
  27. package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
  28. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  29. package/dist/core/upstream-evidence-manifest.js +108 -15
  30. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  31. package/dist/opencode/plugin-templates/parity-safety-net.sh +3 -1
  32. package/dist/utils/effectiveness.d.ts +4 -41
  33. package/dist/utils/effectiveness.d.ts.map +1 -1
  34. package/dist/utils/effectiveness.js +2 -126
  35. package/dist/utils/effectiveness.js.map +1 -1
  36. package/dist/utils/usage-accounting.d.ts +5 -100
  37. package/dist/utils/usage-accounting.d.ts.map +1 -1
  38. package/dist/utils/usage-accounting.js +4 -287
  39. package/dist/utils/usage-accounting.js.map +1 -1
  40. package/package.json +4 -4
  41. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  42. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  43. package/plugins/lisa/.codex-plugin/skills/lisa-drive-pr-to-merge/SKILL.md +15 -1
  44. package/plugins/lisa/.codex-plugin/skills/lisa-tracker-evidence/SKILL.md +7 -1
  45. package/plugins/lisa/.codex-plugin/skills/lisa-tracker-evidence/references/completion-authorization.md +193 -0
  46. package/plugins/lisa/hooks/block-host-name-leak.mjs +25 -13
  47. package/plugins/lisa/hooks/host-name-command-context.mjs +16 -4
  48. package/plugins/lisa/hooks/parity-safety-net.sh +3 -1
  49. package/plugins/lisa/rules/eager/00-rule-index.md +1 -0
  50. package/plugins/lisa/rules/reference/completion-authorization.md +10 -0
  51. package/plugins/lisa/skills/lisa-drive-pr-to-merge/SKILL.md +15 -1
  52. package/plugins/lisa/skills/lisa-tracker-evidence/SKILL.md +7 -1
  53. package/plugins/lisa/skills/lisa-tracker-evidence/references/completion-authorization.md +193 -0
  54. package/plugins/lisa-agy/hooks/block-host-name-leak.mjs +25 -13
  55. package/plugins/lisa-agy/hooks/host-name-command-context.mjs +16 -4
  56. package/plugins/lisa-agy/hooks/parity-safety-net.sh +3 -1
  57. package/plugins/lisa-agy/plugin.json +1 -1
  58. package/plugins/lisa-agy/skills/lisa-drive-pr-to-merge/SKILL.md +15 -1
  59. package/plugins/lisa-agy/skills/lisa-tracker-evidence/SKILL.md +7 -1
  60. package/plugins/lisa-agy/skills/lisa-tracker-evidence/references/completion-authorization.md +193 -0
  61. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  62. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  63. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  64. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  65. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  66. package/plugins/lisa-cdk-devin/.claude-plugin/plugin.json +1 -1
  67. package/plugins/lisa-cdk-devin/.devin-plugin/plugin.json +1 -1
  68. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  69. package/plugins/lisa-copilot/hooks/block-host-name-leak.mjs +25 -13
  70. package/plugins/lisa-copilot/hooks/host-name-command-context.mjs +16 -4
  71. package/plugins/lisa-copilot/hooks/parity-safety-net.sh +3 -1
  72. package/plugins/lisa-copilot/rules/eager/00-rule-index.md +1 -0
  73. package/plugins/lisa-copilot/rules/reference/completion-authorization.md +10 -0
  74. package/plugins/lisa-copilot/skills/lisa-drive-pr-to-merge/SKILL.md +15 -1
  75. package/plugins/lisa-copilot/skills/lisa-tracker-evidence/SKILL.md +7 -1
  76. package/plugins/lisa-copilot/skills/lisa-tracker-evidence/references/completion-authorization.md +193 -0
  77. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  78. package/plugins/lisa-cursor/hooks/block-host-name-leak.mjs +25 -13
  79. package/plugins/lisa-cursor/hooks/host-name-command-context.mjs +16 -4
  80. package/plugins/lisa-cursor/hooks/parity-safety-net.sh +3 -1
  81. package/plugins/lisa-cursor/rules/00-rule-index.mdc +1 -0
  82. package/plugins/lisa-cursor/rules/completion-authorization-reference.mdc +15 -0
  83. package/plugins/lisa-cursor/skills/lisa-drive-pr-to-merge/SKILL.md +15 -1
  84. package/plugins/lisa-cursor/skills/lisa-tracker-evidence/SKILL.md +7 -1
  85. package/plugins/lisa-cursor/skills/lisa-tracker-evidence/references/completion-authorization.md +193 -0
  86. package/plugins/lisa-devin/.claude-plugin/plugin.json +1 -1
  87. package/plugins/lisa-devin/.devin-plugin/plugin.json +1 -1
  88. package/plugins/lisa-devin/hooks/block-host-name-leak.mjs +25 -13
  89. package/plugins/lisa-devin/hooks/host-name-command-context.mjs +16 -4
  90. package/plugins/lisa-devin/hooks/parity-safety-net.sh +3 -1
  91. package/plugins/lisa-devin/rules/eager/00-rule-index.md +1 -0
  92. package/plugins/lisa-devin/rules/reference/completion-authorization.md +10 -0
  93. package/plugins/lisa-devin/skills/lisa-drive-pr-to-merge/SKILL.md +15 -1
  94. package/plugins/lisa-devin/skills/lisa-tracker-evidence/SKILL.md +7 -1
  95. package/plugins/lisa-devin/skills/lisa-tracker-evidence/references/completion-authorization.md +193 -0
  96. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  97. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  98. package/plugins/lisa-expo-agy/plugin.json +1 -1
  99. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  100. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  101. package/plugins/lisa-expo-devin/.claude-plugin/plugin.json +1 -1
  102. package/plugins/lisa-expo-devin/.devin-plugin/plugin.json +1 -1
  103. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  104. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  105. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  106. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  107. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  108. package/plugins/lisa-harper-fabric-devin/.claude-plugin/plugin.json +1 -1
  109. package/plugins/lisa-harper-fabric-devin/.devin-plugin/plugin.json +1 -1
  110. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  111. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  112. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  113. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  114. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  115. package/plugins/lisa-nestjs-devin/.claude-plugin/plugin.json +1 -1
  116. package/plugins/lisa-nestjs-devin/.devin-plugin/plugin.json +1 -1
  117. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  118. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  119. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  120. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  121. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  122. package/plugins/lisa-openclaw-devin/.claude-plugin/plugin.json +1 -1
  123. package/plugins/lisa-openclaw-devin/.devin-plugin/plugin.json +1 -1
  124. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  125. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  126. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  127. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  128. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  129. package/plugins/lisa-phaser-devin/.claude-plugin/plugin.json +1 -1
  130. package/plugins/lisa-phaser-devin/.devin-plugin/plugin.json +1 -1
  131. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  132. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  133. package/plugins/lisa-rails-agy/plugin.json +1 -1
  134. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  135. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  136. package/plugins/lisa-rails-devin/.claude-plugin/plugin.json +1 -1
  137. package/plugins/lisa-rails-devin/.devin-plugin/plugin.json +1 -1
  138. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  139. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  140. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  141. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  142. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  143. package/plugins/lisa-typescript-devin/.claude-plugin/plugin.json +1 -1
  144. package/plugins/lisa-typescript-devin/.devin-plugin/plugin.json +1 -1
  145. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  146. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  147. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  148. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  149. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  150. package/plugins/lisa-wiki-devin/.claude-plugin/plugin.json +1 -1
  151. package/plugins/lisa-wiki-devin/.devin-plugin/plugin.json +1 -1
  152. package/plugins/src/base/hooks/block-host-name-leak.mjs +25 -13
  153. package/plugins/src/base/hooks/host-name-command-context.mjs +16 -4
  154. package/plugins/src/base/hooks/parity-safety-net.sh +3 -1
  155. package/plugins/src/base/rules/eager/00-rule-index.md +1 -0
  156. package/plugins/src/base/rules/reference/completion-authorization.md +10 -0
  157. package/plugins/src/base/skills/lisa-drive-pr-to-merge/SKILL.md +15 -1
  158. package/plugins/src/base/skills/lisa-tracker-evidence/SKILL.md +7 -1
  159. package/plugins/src/base/skills/lisa-tracker-evidence/references/completion-authorization.md +193 -0
  160. package/scripts/lib/host-name-expanded-command-cases.mjs +286 -0
  161. package/scripts/lib/host-name-field-occurrence-cases.mjs +60 -0
  162. package/scripts/two-channel-couplings.json +2 -2
  163. package/scripts/verify-host-name-target-scope.mjs +27 -1
  164. package/typescript/copy-overwrite/.prettierignore +15 -0
  165. package/typescript/copy-overwrite/scripts/check-skipped-required-checks.mjs +32 -0
@@ -24,6 +24,20 @@ import { dirname, join, resolve } from "node:path";
24
24
  import { isIP } from "node:net";
25
25
 
26
26
  import { invokedAsScript } from "./lib/invoked-as-script.mjs";
27
+ import {
28
+ authorizeCompletion,
29
+ completionDenial,
30
+ } from "./lib/completion-authorization.mjs";
31
+ import { completionContext } from "./lib/completion-proof-adapters.mjs";
32
+ import { publishCompletionProof } from "./lib/completion-proof-publisher.mjs";
33
+ import {
34
+ MAX_RECORD_BYTES,
35
+ CompletionProofError,
36
+ } from "./lib/completion-proof-shape.mjs";
37
+
38
+ const COMPLETION_WRITE_ERROR = "write-error";
39
+ const COMPLETION_READ_ERROR = "read-error";
40
+ const ALREADY_COMPLETED = "already-completed";
27
41
 
28
42
  /**
29
43
  * Contract version of the traceability gate's script half, reported by the
@@ -51,7 +65,7 @@ import { invokedAsScript } from "./lib/invoked-as-script.mjs";
51
65
  * Bump the MINOR whenever a change to this file would alter a verdict the gate
52
66
  * reports, so a consumer running old logic can be told how far behind it is.
53
67
  */
54
- export const WORK_ITEM_CONTRACT_VERSION = "1.1.0";
68
+ export const WORK_ITEM_CONTRACT_VERSION = "1.2.0";
55
69
 
56
70
  /**
57
71
  * Subject of a release-bot commit, which is exempt from the work-item trailer.
@@ -543,7 +557,7 @@ const CHILD_TIMEOUT_MS = Number(
543
557
  export function run(command, args, options = {}) {
544
558
  const result = spawnSync(command, args, {
545
559
  cwd: options.cwd ?? process.cwd(),
546
- encoding: "utf8",
560
+ encoding: options.binary === true ? null : "utf8",
547
561
  env: options.env ?? process.env,
548
562
  input: options.input,
549
563
  // Node's default is 1 MiB, and it is silent in exactly the wrong way: an
@@ -580,8 +594,13 @@ export function run(command, args, options = {}) {
580
594
  // added later inherits the guarantee instead of having to remember it.
581
595
  return {
582
596
  ...result,
583
- stdout: result.stdout ?? "",
584
- stderr: result.stderr ?? "",
597
+ stdout:
598
+ options.binary === true
599
+ ? Buffer.from(result.stdout ?? "")
600
+ : (result.stdout ?? ""),
601
+ stderr: Buffer.isBuffer(result.stderr)
602
+ ? result.stderr.toString("utf8")
603
+ : (result.stderr ?? ""),
585
604
  };
586
605
  }
587
606
 
@@ -5160,7 +5179,7 @@ function recordDeployRole(ref, before, role) {
5160
5179
  * @returns {{applied: boolean, bases: string, merged: number[], report: string, terminal: string|null}}
5161
5180
  * What was applied, and why.
5162
5181
  */
5163
- function completeGithubWorkItem(ref, contract) {
5182
+ function completeGithubWorkItem(ref, contract, prUrl) {
5164
5183
  const [repository] = ref.split("#");
5165
5184
  const merged = mergedPullRequestsIn(githubTimeline(ref), repository);
5166
5185
  if (merged.length === 0) {
@@ -5208,6 +5227,7 @@ function completeGithubWorkItem(ref, contract) {
5208
5227
  recordDeployRole(ref, before, decision.role);
5209
5228
  return {
5210
5229
  applied: true,
5230
+ category: "intermediate",
5211
5231
  bases,
5212
5232
  merged,
5213
5233
  report:
@@ -5216,20 +5236,43 @@ function completeGithubWorkItem(ref, contract) {
5216
5236
  terminal: decision.role,
5217
5237
  };
5218
5238
  }
5219
- reconcileGithubLifecycle(ref, before, contract);
5239
+ const authorization = authorizeCompletion(proofContext(ref, contract, prUrl));
5240
+ if (authorization.category !== "authorized")
5241
+ return completionDenial(ref, authorization);
5242
+ const current = authorization.native;
5243
+ assertNotAbandoned(ref, current);
5244
+ const already =
5245
+ current.state === "CLOSED" &&
5246
+ carriesLabel(current.labels, contract.lifecycle.terminalName) &&
5247
+ competingLifecycleRoles(
5248
+ current.labels,
5249
+ contract.lifecycle.roles,
5250
+ contract.lifecycle.terminalName
5251
+ ).length === 0;
5252
+ completionNativeOperation(
5253
+ COMPLETION_WRITE_ERROR,
5254
+ "The terminal mutation failed; reread native state before retrying",
5255
+ () => reconcileGithubLifecycle(ref, current, contract)
5256
+ );
5220
5257
  // A SECOND read, deliberately. The edit and the close each reported their own
5221
5258
  // success, and a writer that trusts those is asserting about tracker state it
5222
5259
  // never asked the tracker for — the same defect class as the one above it.
5223
- const after = githubLifecycleState(
5224
- ref,
5225
- `GitHub issue ${ref} completion readback`
5260
+ const after = completionNativeOperation(
5261
+ COMPLETION_READ_ERROR,
5262
+ "The post-write native state could not be read",
5263
+ () => githubLifecycleState(ref, `GitHub issue ${ref} completion readback`)
5264
+ );
5265
+ completionNativeOperation(
5266
+ COMPLETION_WRITE_ERROR,
5267
+ "The actual native readback did not establish completion",
5268
+ () => assertCompletionReadback(ref, after, contract)
5226
5269
  );
5227
- assertCompletionReadback(ref, after, contract);
5228
5270
  return {
5229
- applied: true,
5271
+ applied: !already,
5272
+ category: already ? ALREADY_COMPLETED : "completed",
5230
5273
  bases,
5231
5274
  merged,
5232
- report: `work-item completed: ${ref} -> ${contract.lifecycle.terminalName} (merged: ${evidence}) bases: ${bases}`,
5275
+ report: `work-item ${already ? ALREADY_COMPLETED : "completed"}: ${ref} -> ${contract.lifecycle.terminalName} (merged: ${evidence}) bases: ${bases}`,
5233
5276
  terminal: contract.lifecycle.terminalName,
5234
5277
  };
5235
5278
  }
@@ -5582,6 +5625,12 @@ function completeLinearWorkItem(ref, contract, prUrl) {
5582
5625
  );
5583
5626
  }
5584
5627
  assertBacklink(ref, evidence.url, contract, issue);
5628
+ const authorization = authorizeCompletion(
5629
+ proofContext(ref, contract, prUrl, token)
5630
+ );
5631
+ if (authorization.category !== "authorized")
5632
+ return completionDenial(ref, authorization);
5633
+ const freshIssue = authorization.native.raw;
5585
5634
  // Folded for MATCHING only. Linear workflow states are display strings a
5586
5635
  // human named on the board, so `Done` and `done` are the same state and the
5587
5636
  // comparison has to say so — but nothing a person reads should be the folded
@@ -5589,7 +5638,7 @@ function completeLinearWorkItem(ref, contract, prUrl) {
5589
5638
  // either the configured spelling or the one Linear itself returned.
5590
5639
  const terminal = contract.lifecycle.terminal;
5591
5640
  const configuredName = contract.lifecycle.terminalName;
5592
- const states = issue.team?.states?.nodes ?? [];
5641
+ const states = freshIssue.team?.states?.nodes ?? [];
5593
5642
  const target = states.find(
5594
5643
  state =>
5595
5644
  String(state?.name ?? "").toLowerCase() === terminal &&
@@ -5608,40 +5657,122 @@ function completeLinearWorkItem(ref, contract, prUrl) {
5608
5657
  // itself worth surfacing rather than hiding behind the config.
5609
5658
  const displayName = String(target.name ?? configuredName);
5610
5659
  if (
5611
- String(issue.state?.name ?? "").toLowerCase() === terminal &&
5612
- issue.state?.type === "completed"
5660
+ String(freshIssue.state?.name ?? "").toLowerCase() === terminal &&
5661
+ freshIssue.state?.type === "completed"
5613
5662
  ) {
5614
- return { merged: [evidence.number], terminal: displayName };
5663
+ return {
5664
+ merged: [evidence.number],
5665
+ terminal: displayName,
5666
+ category: ALREADY_COMPLETED,
5667
+ applied: false,
5668
+ };
5615
5669
  }
5616
- const update = linearGraphql(
5617
- token,
5618
- "mutation($id:String!,$stateId:String!){issueUpdate(id:$id,input:{stateId:$stateId}){success issue{id identifier state{id name type}}}}",
5619
- { id: issue.id, stateId: target.id },
5620
- `Linear issue ${ref} completion update`
5621
- ).issueUpdate;
5670
+ const update = completionNativeOperation(
5671
+ COMPLETION_WRITE_ERROR,
5672
+ "The Linear terminal mutation failed; reread native state before retrying",
5673
+ () =>
5674
+ linearGraphql(
5675
+ token,
5676
+ "mutation($id:String!,$stateId:String!){issueUpdate(id:$id,input:{stateId:$stateId}){success issue{id identifier state{id name type}}}}",
5677
+ { id: issue.id, stateId: target.id },
5678
+ `Linear issue ${ref} completion update`
5679
+ ).issueUpdate
5680
+ );
5622
5681
  if (update?.success !== true) {
5623
- throw new TrackingError(`Linear issue ${ref} completion update failed`);
5682
+ throw new CompletionProofError(
5683
+ COMPLETION_WRITE_ERROR,
5684
+ `Linear issue ${ref} completion update failed`
5685
+ );
5624
5686
  }
5625
- const readback = linearCompletionIssue(
5626
- ref,
5627
- token,
5628
- `Linear issue ${ref} completion readback`
5687
+ const readback = completionNativeOperation(
5688
+ COMPLETION_READ_ERROR,
5689
+ "The Linear post-write state could not be read",
5690
+ () =>
5691
+ linearCompletionIssue(
5692
+ ref,
5693
+ token,
5694
+ `Linear issue ${ref} completion readback`
5695
+ )
5629
5696
  );
5630
5697
  if (
5631
5698
  readback?.identifier !== ref ||
5632
5699
  String(readback?.state?.name ?? "").toLowerCase() !== terminal ||
5633
5700
  readback?.state?.type !== "completed"
5634
5701
  ) {
5635
- throw new TrackingError(
5702
+ throw new CompletionProofError(
5703
+ COMPLETION_WRITE_ERROR,
5636
5704
  `Linear issue ${ref} did not read back in workflow state ${displayName}`
5637
5705
  );
5638
5706
  }
5639
- return { merged: [evidence.number], terminal: displayName };
5707
+ return {
5708
+ merged: [evidence.number],
5709
+ terminal: displayName,
5710
+ category: "completed",
5711
+ applied: true,
5712
+ };
5713
+ }
5714
+
5715
+ /** Preserve operational outcome categories even after a partial native write. */
5716
+ function completionNativeOperation(category, description, operation) {
5717
+ try {
5718
+ return operation();
5719
+ } catch (error) {
5720
+ if (error instanceof CompletionProofError) throw error;
5721
+ throw new CompletionProofError(
5722
+ category,
5723
+ `${description}: ${error instanceof Error ? error.message : String(error)}`
5724
+ );
5725
+ }
5726
+ }
5727
+
5728
+ /** The same portable proof boundary is reached by every supported terminal writer. */
5729
+ function proofContext(ref, contract, prUrl, suppliedToken) {
5730
+ const token =
5731
+ contract.provider === "linear"
5732
+ ? (suppliedToken ?? readLinearKey(contract.workspace))
5733
+ : null;
5734
+ if (contract.provider === "linear" && !token)
5735
+ throw new TrackingError(
5736
+ "Linear completion proof requires the configured Linear API key."
5737
+ );
5738
+ return completionContext({
5739
+ ref,
5740
+ contract,
5741
+ prUrl,
5742
+ run,
5743
+ config: readConfig(),
5744
+ repository: currentRepository(),
5745
+ linear: (query, variables, purpose) =>
5746
+ linearGraphql(token, query, variables, purpose),
5747
+ declares(message, currentRef) {
5748
+ if (contract.provider === "github")
5749
+ return declaredWorkItemNumbers(message, ref.split("#")[0]).includes(
5750
+ Number(currentRef.split("#")[1])
5751
+ );
5752
+ return workItemLines(message).some(value => {
5753
+ try {
5754
+ return canonicalizeRef(value, contract) === currentRef;
5755
+ } catch (error) {
5756
+ if (error instanceof TrackingError) return false;
5757
+ throw error;
5758
+ }
5759
+ });
5760
+ },
5761
+ hasBacklink(issue, url) {
5762
+ try {
5763
+ assertBacklink(ref, url, contract, issue);
5764
+ return true;
5765
+ } catch (error) {
5766
+ if (error instanceof TrackingError) return false;
5767
+ throw error;
5768
+ }
5769
+ },
5770
+ });
5640
5771
  }
5641
5772
 
5642
5773
  function completeWorkItem(ref, contract, prUrl) {
5643
5774
  if (contract.provider === "github") {
5644
- return completeGithubWorkItem(ref, contract);
5775
+ return completeGithubWorkItem(ref, contract, prUrl);
5645
5776
  }
5646
5777
  if (contract.provider === "linear") {
5647
5778
  return completeLinearWorkItem(ref, contract, prUrl);
@@ -5669,10 +5800,53 @@ function complete(args) {
5669
5800
  // path has no such decision to report, so it keeps the original line.
5670
5801
  console.log(
5671
5802
  outcome.report ??
5672
- `work-item completed: ${ref} -> ${outcome.terminal} (merged: ${outcome.merged
5803
+ `work-item ${outcome.category === ALREADY_COMPLETED ? ALREADY_COMPLETED : "completed"}: ${ref} -> ${outcome.terminal} (merged: ${outcome.merged
5673
5804
  .map(number => `#${number}`)
5674
5805
  .join(", ")})`
5675
5806
  );
5807
+ if (
5808
+ ["invalid-proof", COMPLETION_READ_ERROR, COMPLETION_WRITE_ERROR].includes(
5809
+ outcome.category
5810
+ )
5811
+ )
5812
+ process.exitCode = 1;
5813
+ }
5814
+
5815
+ /** Publish a durable accepted-for-completion record without closing the item. */
5816
+ function publishProof(args) {
5817
+ const contract = trackerContract();
5818
+ if (!["github", "linear"].includes(contract.provider))
5819
+ throw new TrackingError(
5820
+ "Completion-proof publication supports github and linear; Jira completion remains unsupported."
5821
+ );
5822
+ const ref = canonicalizeRef(
5823
+ option(args, "--ref", "LISA_WORK_ITEM_REF") ?? boundRef(),
5824
+ contract
5825
+ );
5826
+ const file = option(args, "--record");
5827
+ if (!file)
5828
+ throw new TrackingError(
5829
+ "publish-completion-proof requires --record <verified-record.json>"
5830
+ );
5831
+ const path = resolve(process.cwd(), file);
5832
+ const metadata = lstatSync(path);
5833
+ if (
5834
+ !metadata.isFile() ||
5835
+ metadata.size <= 0 ||
5836
+ metadata.size > MAX_RECORD_BYTES
5837
+ )
5838
+ throw new TrackingError(
5839
+ "The completion record must be a bounded, nonempty regular JSON file."
5840
+ );
5841
+ const input = safeJson(
5842
+ readFileSync(path, "utf8"),
5843
+ "Completion publication record"
5844
+ );
5845
+ const result = publishCompletionProof(
5846
+ proofContext(ref, contract, pullRequestUrlOption(args)),
5847
+ input
5848
+ );
5849
+ console.log(JSON.stringify(result));
5676
5850
  }
5677
5851
 
5678
5852
  /**
@@ -5755,7 +5929,7 @@ function openIssuesWithRole(repository, role) {
5755
5929
  /**
5756
5930
  * The first of `origin/<branch>` and `<branch>` that resolves to a commit.
5757
5931
  * @param {string} branch Configured deploy branch name.
5758
- * @returns {string|undefined} A resolvable rev, or undefined.
5932
+ * @returns {{ref: string, sha: string}|undefined} Selected ref and pinned commit, or undefined.
5759
5933
  */
5760
5934
  function resolvedBranchRev(branch) {
5761
5935
  for (const candidate of [`origin/${branch}`, branch]) {
@@ -5764,7 +5938,8 @@ function resolvedBranchRev(branch) {
5764
5938
  ["rev-parse", "--verify", "--quiet", `${candidate}^{commit}`],
5765
5939
  { allowFailure: true }
5766
5940
  );
5767
- if (result.status === 0 && result.stdout.trim() !== "") return candidate;
5941
+ const sha = result.stdout.trim();
5942
+ if (result.status === 0 && sha !== "") return { ref: candidate, sha };
5768
5943
  }
5769
5944
  return undefined;
5770
5945
  }
@@ -5842,6 +6017,32 @@ function resolvedSinceRev(since) {
5842
6017
  return sha;
5843
6018
  }
5844
6019
 
6020
+ /**
6021
+ * Prove that a bounded push starts in the exact selected branch history.
6022
+ * A resolvable but disjoint commit excludes nothing and can widen the sweep.
6023
+ * @param {string} since Resolved pre-push commit.
6024
+ * @param {{ref: string, sha: string}} revision Selected deploy revision.
6025
+ */
6026
+ function assertSweepAncestor(since, revision) {
6027
+ const result =
6028
+ /** @type {import("node:child_process").SpawnSyncReturns<string>} */ (
6029
+ run("git", ["merge-base", "--is-ancestor", since, revision.sha], {
6030
+ allowFailure: true,
6031
+ })
6032
+ );
6033
+ if (!result.error && result.status === 0) return;
6034
+ const reason =
6035
+ !result.error && result.status === 1
6036
+ ? "is not an ancestor"
6037
+ : "could not be verified as an ancestor";
6038
+ throw new TrackingError(
6039
+ `NOT DETERMINED: ${SINCE_FLAG} ${since} ${reason} of ${revision.ref} (${revision.sha}), ` +
6040
+ `so the bounded sweep cannot reconcile any item.\n` +
6041
+ `Fetch the selected branch's complete history (\`git fetch --unshallow origin\` for a shallow checkout), ` +
6042
+ `verify its pre-push revision, and re-run. No completion mutations were attempted.`
6043
+ );
6044
+ }
6045
+
5845
6046
  /**
5846
6047
  * The sentence a report owes its reader when the scan was bounded.
5847
6048
  * @param {string | undefined} since The bound as supplied.
@@ -5901,11 +6102,17 @@ function deployedDeclarations(repository, contract, since, branches) {
5901
6102
  unresolved.push(branch);
5902
6103
  continue;
5903
6104
  }
6105
+ if (sinceRev) assertSweepAncestor(sinceRev, rev);
5904
6106
  const bound = sinceRev ? ["--not", sinceRev] : [];
5905
- const result = run("git", ["log", rev, ...bound, "-z", "--format=%H%n%B"], {
5906
- allowFailure: true,
5907
- maxBuffer: DECLARATION_LOG_MAX_BYTES,
5908
- });
6107
+ const logRev = sinceRev ? rev.sha : rev.ref;
6108
+ const result = run(
6109
+ "git",
6110
+ ["log", logRev, ...bound, "-z", "--format=%H%n%B"],
6111
+ {
6112
+ allowFailure: true,
6113
+ maxBuffer: DECLARATION_LOG_MAX_BYTES,
6114
+ }
6115
+ );
5909
6116
  // Loudly, not `continue`. This sweep's whole output is an absence claim,
5910
6117
  // and a read that failed is not an absence — under the default 1 MiB
5911
6118
  // buffer the real `origin/main` log (4.6 MiB) was truncated into an
@@ -5915,7 +6122,7 @@ function deployedDeclarations(repository, contract, since, branches) {
5915
6122
  // least trustworthy one.
5916
6123
  if (result.error || result.status !== 0) {
5917
6124
  throw new TrackingError(
5918
- `could not read the commit log of ${rev}, so no absence of drift can be reported: ` +
6125
+ `could not read the commit log of ${rev.ref}, so no absence of drift can be reported: ` +
5919
6126
  `${result.error ? result.error.message : result.stderr.trim() || "git log failed"}.\n` +
5920
6127
  `The sweep reports what a deploy branch declares; a log it could not read is not an empty one.`
5921
6128
  );
@@ -6905,10 +7112,11 @@ function main() {
6905
7112
  if (command === "validate-push") return validatePush(args);
6906
7113
  if (command === "validate-push-destination")
6907
7114
  return validatePushDestination(args);
7115
+ if (command === "publish-completion-proof") return publishProof(args);
6908
7116
  if (command === "validate-pr") return validatePr(args);
6909
7117
  if (command === DISCHARGE_COMMAND) return dischargePrGates(args);
6910
7118
  throw new TrackingError(
6911
- "Usage: lisa-work-item.mjs link|current|attach-branch|clear|verify-level|contract-version|backlink|complete|sweep|lane|prepare-commit-msg|validate-commit|validate-push|validate-push-destination|validate-pr|discharge-pr-gates" +
7119
+ "Usage: lisa-work-item.mjs link|current|attach-branch|clear|verify-level|contract-version|backlink|publish-completion-proof|complete|sweep|lane|prepare-commit-msg|validate-commit|validate-push|validate-push-destination|validate-pr|discharge-pr-gates" +
6912
7120
  "\n(`bind` is accepted as an alias for `link`, but some agent harnesses refuse the token `bind` in a command line.)"
6913
7121
  );
6914
7122
  }
@@ -6928,6 +7136,14 @@ export function runCli() {
6928
7136
  main();
6929
7137
  } catch (error) {
6930
7138
  const detail = error instanceof Error ? error.message : String(error);
7139
+ if (error instanceof CompletionProofError) {
7140
+ console.error(`work-item proof ${error.category}: ${detail}`);
7141
+ const pendingLifecycle =
7142
+ error.category === "pending-proof" &&
7143
+ ["complete", "sweep"].includes(process.argv[2]);
7144
+ process.exitCode = pendingLifecycle ? 0 : 1;
7145
+ return;
7146
+ }
6931
7147
  console.error(
6932
7148
  error?.selfExplanatory === true
6933
7149
  ? `\n❌ ${detail}\n`
@@ -73,6 +73,118 @@
73
73
  "dynamicImports": [],
74
74
  "children": []
75
75
  },
76
+ "lib/completion-authorization.mjs": {
77
+ "sha256": "7b5f81ae58eae89367ebda480a508c3c37a6924b4bd6a8b1dd64fff24c465e1e",
78
+ "staticImports": [
79
+ "lib/completion-proof-conformance.mjs",
80
+ "lib/completion-proof-content.mjs",
81
+ "lib/completion-proof-github.mjs",
82
+ "lib/completion-proof-reader.mjs",
83
+ "lib/completion-proof-shape.mjs",
84
+ "lib/completion-proof-source.mjs"
85
+ ],
86
+ "dynamicImports": [],
87
+ "children": []
88
+ },
89
+ "lib/completion-proof-adapters.mjs": {
90
+ "sha256": "0743d3ccb12a2dc4822170e7c299caa24576fc5a1862402adc4f6feb1bedd163",
91
+ "staticImports": [
92
+ "lib/completion-proof-shape.mjs"
93
+ ],
94
+ "dynamicImports": [],
95
+ "children": []
96
+ },
97
+ "lib/completion-proof-conformance.mjs": {
98
+ "sha256": "51b2bfccc8ecca684dd80a4e2ae35896e76fa519389fe650f0890ffc0fef421d",
99
+ "staticImports": [
100
+ "lib/completion-proof-shape.mjs"
101
+ ],
102
+ "dynamicImports": [],
103
+ "children": []
104
+ },
105
+ "lib/completion-proof-content.mjs": {
106
+ "sha256": "78b34223d054913c2e900830070cbb1fb2dc267423ec458ae76553ceed0d38c6",
107
+ "staticImports": [
108
+ "lib/completion-proof-shape.mjs",
109
+ "lib/usage-ledger-integrity.mjs",
110
+ "lib/usage-ledger.mjs"
111
+ ],
112
+ "dynamicImports": [],
113
+ "children": []
114
+ },
115
+ "lib/completion-proof-errors.mjs": {
116
+ "sha256": "3422df7049227eff77f3ef7bf954eeb74a63e9879b06d8126c7c3a2485b6d1f3",
117
+ "staticImports": [],
118
+ "dynamicImports": [],
119
+ "children": []
120
+ },
121
+ "lib/completion-proof-github.mjs": {
122
+ "sha256": "e8a5b585625c2d663c0f92046d8ff020b2e769860235634cd0e4bea4b3a234ae",
123
+ "staticImports": [
124
+ "lib/completion-proof-shape.mjs"
125
+ ],
126
+ "dynamicImports": [],
127
+ "children": []
128
+ },
129
+ "lib/completion-proof-markdown.mjs": {
130
+ "sha256": "00d00e2c251244d1eb0dc46464c1bb1e170f2576cd025b02518e132c0d467472",
131
+ "staticImports": [
132
+ "lib/completion-proof-errors.mjs"
133
+ ],
134
+ "dynamicImports": [],
135
+ "children": []
136
+ },
137
+ "lib/completion-proof-projection.mjs": {
138
+ "sha256": "a8acdd491693126fe6f1660f4bd29c7b218855ae59c76ec2b7c0d5703d8c0d43",
139
+ "staticImports": [
140
+ "lib/completion-proof-errors.mjs",
141
+ "lib/usage-ledger-integrity.mjs",
142
+ "lib/usage-ledger.mjs"
143
+ ],
144
+ "dynamicImports": [],
145
+ "children": []
146
+ },
147
+ "lib/completion-proof-publisher.mjs": {
148
+ "sha256": "801df8b694eced082695542dad3e189ce660aa65dda26f3bf11d962120487609",
149
+ "staticImports": [
150
+ "lib/completion-authorization.mjs",
151
+ "lib/completion-proof-github.mjs",
152
+ "lib/completion-proof-reader.mjs",
153
+ "lib/completion-proof-shape.mjs",
154
+ "node:fs",
155
+ "node:os",
156
+ "node:path"
157
+ ],
158
+ "dynamicImports": [],
159
+ "children": []
160
+ },
161
+ "lib/completion-proof-reader.mjs": {
162
+ "sha256": "f134b65158605510bf188429d6aa5e713ed75a4d78877efc6211b440b3a805f7",
163
+ "staticImports": [
164
+ "lib/completion-proof-shape.mjs"
165
+ ],
166
+ "dynamicImports": [],
167
+ "children": []
168
+ },
169
+ "lib/completion-proof-shape.mjs": {
170
+ "sha256": "2edf9120a019aa21a64b8ca01d69fb027b8ef6267a87db9747b3c05cf8a97840",
171
+ "staticImports": [
172
+ "lib/completion-proof-errors.mjs",
173
+ "lib/completion-proof-markdown.mjs",
174
+ "lib/completion-proof-projection.mjs",
175
+ "node:crypto"
176
+ ],
177
+ "dynamicImports": [],
178
+ "children": []
179
+ },
180
+ "lib/completion-proof-source.mjs": {
181
+ "sha256": "1028cbeb9c8bb9cf685dd3f532ea41de2a25456b989fbda23d3315d8532877c0",
182
+ "staticImports": [
183
+ "lib/completion-proof-shape.mjs"
184
+ ],
185
+ "dynamicImports": [],
186
+ "children": []
187
+ },
76
188
  "lib/gate-failure-diagnosis.mjs": {
77
189
  "sha256": "31f2acced121662d6395b624aed2ae0ad642c1edb52cdb0eded1497886daad71",
78
190
  "staticImports": [
@@ -154,7 +266,7 @@
154
266
  "children": []
155
267
  },
156
268
  "lib/history-secret-scanner.mjs": {
157
- "sha256": "56bcbea184d8938ae9722cd48b020d474d8d75dde9bb577b3b1d410787f2224f",
269
+ "sha256": "470a8e534252b3c3c8c62c2b5d83a11bde2287bd2187696107752c538bc20bda",
158
270
  "staticImports": [
159
271
  "lib/history-secret-evidence.mjs",
160
272
  "lib/history-secret-git.mjs",
@@ -522,7 +634,7 @@
522
634
  "children": []
523
635
  },
524
636
  "lib/npm-update-helper-inventory.mjs": {
525
- "sha256": "1a471b58bc9cb0b0e2786ccf57d1b11843d20ca0814d24446c09bbfcf2971894",
637
+ "sha256": "41ff29eb3c82b7cbff1f1b0ba88517df7c09fee1bbd951117371a2a826ce5823",
526
638
  "staticImports": [],
527
639
  "dynamicImports": [],
528
640
  "children": []
@@ -1100,6 +1212,28 @@
1100
1212
  "dynamicImports": [],
1101
1213
  "children": []
1102
1214
  },
1215
+ "lib/usage-effectiveness.mjs": {
1216
+ "sha256": "b4fb652293bf2c15f9a07b3f9f15cee75ddbfc17a5aa7fee30290629b77ba705",
1217
+ "staticImports": [],
1218
+ "dynamicImports": [],
1219
+ "children": []
1220
+ },
1221
+ "lib/usage-ledger-integrity.mjs": {
1222
+ "sha256": "67e115051fac99e846dea54297c8811f22aecce93d2743ce53bbf0b8e3572c5e",
1223
+ "staticImports": [
1224
+ "lib/usage-ledger.mjs"
1225
+ ],
1226
+ "dynamicImports": [],
1227
+ "children": []
1228
+ },
1229
+ "lib/usage-ledger.mjs": {
1230
+ "sha256": "cdeab41198a0284b6415975b1dd8cfd84114631b5673f1a6ab340996cd74f946",
1231
+ "staticImports": [
1232
+ "lib/usage-effectiveness.mjs"
1233
+ ],
1234
+ "dynamicImports": [],
1235
+ "children": []
1236
+ },
1103
1237
  "lib/windows-process-job.cs": {
1104
1238
  "sha256": "7bbe3b13bfc35ca2a0b85fbee9a8f0a6549bf8b05997f6ce4b361bd09d924218",
1105
1239
  "staticImports": [],
@@ -1273,8 +1407,12 @@
1273
1407
  "children": []
1274
1408
  },
1275
1409
  "lisa-work-item.mjs": {
1276
- "sha256": "cd1b16e056fefac17018d5c96ad063dc4002f304e4400765e473403186b7175a",
1410
+ "sha256": "e7127de03c08a66d39612b6027b25b6d9bdf0359bb3a8eccba2feb440d0c2f14",
1277
1411
  "staticImports": [
1412
+ "lib/completion-authorization.mjs",
1413
+ "lib/completion-proof-adapters.mjs",
1414
+ "lib/completion-proof-publisher.mjs",
1415
+ "lib/completion-proof-shape.mjs",
1278
1416
  "lib/invoked-as-script.mjs",
1279
1417
  "node:child_process",
1280
1418
  "node:crypto",
@@ -1 +1 @@
1
- {"version":3,"file":"lisa-owned-hash-ledger.d.ts","sourceRoot":"","sources":["../../src/core/lisa-owned-hash-ledger.ts"],"names":[],"mappings":"AACA,6EAA6E;AAE7E;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,EAAE,QAAQ,CAC3C,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAqkEjC,CAAC;AAEH;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,+BAA+B,EAAE,QAAQ,CACpD,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAw+DjC,CAAC"}
1
+ {"version":3,"file":"lisa-owned-hash-ledger.d.ts","sourceRoot":"","sources":["../../src/core/lisa-owned-hash-ledger.ts"],"names":[],"mappings":"AACA,6EAA6E;AAE7E;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,EAAE,QAAQ,CAC3C,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAkqEjC,CAAC;AAEH;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,+BAA+B,EAAE,QAAQ,CACpD,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAqkEjC,CAAC"}