@codyswann/lisa 4.84.7 → 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 (150) 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 +101 -12
  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/parity-safety-net.sh +3 -1
  47. package/plugins/lisa/rules/eager/00-rule-index.md +1 -0
  48. package/plugins/lisa/rules/reference/completion-authorization.md +10 -0
  49. package/plugins/lisa/skills/lisa-drive-pr-to-merge/SKILL.md +15 -1
  50. package/plugins/lisa/skills/lisa-tracker-evidence/SKILL.md +7 -1
  51. package/plugins/lisa/skills/lisa-tracker-evidence/references/completion-authorization.md +193 -0
  52. package/plugins/lisa-agy/hooks/parity-safety-net.sh +3 -1
  53. package/plugins/lisa-agy/plugin.json +1 -1
  54. package/plugins/lisa-agy/skills/lisa-drive-pr-to-merge/SKILL.md +15 -1
  55. package/plugins/lisa-agy/skills/lisa-tracker-evidence/SKILL.md +7 -1
  56. package/plugins/lisa-agy/skills/lisa-tracker-evidence/references/completion-authorization.md +193 -0
  57. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  58. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  59. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  60. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  61. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  62. package/plugins/lisa-cdk-devin/.claude-plugin/plugin.json +1 -1
  63. package/plugins/lisa-cdk-devin/.devin-plugin/plugin.json +1 -1
  64. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  65. package/plugins/lisa-copilot/hooks/parity-safety-net.sh +3 -1
  66. package/plugins/lisa-copilot/rules/eager/00-rule-index.md +1 -0
  67. package/plugins/lisa-copilot/rules/reference/completion-authorization.md +10 -0
  68. package/plugins/lisa-copilot/skills/lisa-drive-pr-to-merge/SKILL.md +15 -1
  69. package/plugins/lisa-copilot/skills/lisa-tracker-evidence/SKILL.md +7 -1
  70. package/plugins/lisa-copilot/skills/lisa-tracker-evidence/references/completion-authorization.md +193 -0
  71. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  72. package/plugins/lisa-cursor/hooks/parity-safety-net.sh +3 -1
  73. package/plugins/lisa-cursor/rules/00-rule-index.mdc +1 -0
  74. package/plugins/lisa-cursor/rules/completion-authorization-reference.mdc +15 -0
  75. package/plugins/lisa-cursor/skills/lisa-drive-pr-to-merge/SKILL.md +15 -1
  76. package/plugins/lisa-cursor/skills/lisa-tracker-evidence/SKILL.md +7 -1
  77. package/plugins/lisa-cursor/skills/lisa-tracker-evidence/references/completion-authorization.md +193 -0
  78. package/plugins/lisa-devin/.claude-plugin/plugin.json +1 -1
  79. package/plugins/lisa-devin/.devin-plugin/plugin.json +1 -1
  80. package/plugins/lisa-devin/hooks/parity-safety-net.sh +3 -1
  81. package/plugins/lisa-devin/rules/eager/00-rule-index.md +1 -0
  82. package/plugins/lisa-devin/rules/reference/completion-authorization.md +10 -0
  83. package/plugins/lisa-devin/skills/lisa-drive-pr-to-merge/SKILL.md +15 -1
  84. package/plugins/lisa-devin/skills/lisa-tracker-evidence/SKILL.md +7 -1
  85. package/plugins/lisa-devin/skills/lisa-tracker-evidence/references/completion-authorization.md +193 -0
  86. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  87. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  88. package/plugins/lisa-expo-agy/plugin.json +1 -1
  89. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  90. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  91. package/plugins/lisa-expo-devin/.claude-plugin/plugin.json +1 -1
  92. package/plugins/lisa-expo-devin/.devin-plugin/plugin.json +1 -1
  93. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  94. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  95. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  96. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  97. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  98. package/plugins/lisa-harper-fabric-devin/.claude-plugin/plugin.json +1 -1
  99. package/plugins/lisa-harper-fabric-devin/.devin-plugin/plugin.json +1 -1
  100. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  101. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  102. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  103. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  104. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  105. package/plugins/lisa-nestjs-devin/.claude-plugin/plugin.json +1 -1
  106. package/plugins/lisa-nestjs-devin/.devin-plugin/plugin.json +1 -1
  107. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  108. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  109. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  110. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  111. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  112. package/plugins/lisa-openclaw-devin/.claude-plugin/plugin.json +1 -1
  113. package/plugins/lisa-openclaw-devin/.devin-plugin/plugin.json +1 -1
  114. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  115. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  116. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  117. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  118. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  119. package/plugins/lisa-phaser-devin/.claude-plugin/plugin.json +1 -1
  120. package/plugins/lisa-phaser-devin/.devin-plugin/plugin.json +1 -1
  121. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  122. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  123. package/plugins/lisa-rails-agy/plugin.json +1 -1
  124. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  125. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  126. package/plugins/lisa-rails-devin/.claude-plugin/plugin.json +1 -1
  127. package/plugins/lisa-rails-devin/.devin-plugin/plugin.json +1 -1
  128. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  129. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  130. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  131. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  132. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  133. package/plugins/lisa-typescript-devin/.claude-plugin/plugin.json +1 -1
  134. package/plugins/lisa-typescript-devin/.devin-plugin/plugin.json +1 -1
  135. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  136. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  137. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  138. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  139. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  140. package/plugins/lisa-wiki-devin/.claude-plugin/plugin.json +1 -1
  141. package/plugins/lisa-wiki-devin/.devin-plugin/plugin.json +1 -1
  142. package/plugins/src/base/hooks/parity-safety-net.sh +3 -1
  143. package/plugins/src/base/rules/eager/00-rule-index.md +1 -0
  144. package/plugins/src/base/rules/reference/completion-authorization.md +10 -0
  145. package/plugins/src/base/skills/lisa-drive-pr-to-merge/SKILL.md +15 -1
  146. package/plugins/src/base/skills/lisa-tracker-evidence/SKILL.md +7 -1
  147. package/plugins/src/base/skills/lisa-tracker-evidence/references/completion-authorization.md +193 -0
  148. package/scripts/two-channel-couplings.json +2 -2
  149. package/typescript/copy-overwrite/.prettierignore +15 -0
  150. package/typescript/copy-overwrite/scripts/check-skipped-required-checks.mjs +32 -0
@@ -0,0 +1,282 @@
1
+ // This file is managed by Lisa and IS replaced on each `lisa` run.
2
+ // Do not edit directly — durable changes belong upstream in Lisa.
3
+
4
+ /** Shared terminal precondition for GitHub direct/sweep and Linear direct. */
5
+ import {
6
+ CompletionProofError,
7
+ MAX_ATTACHMENT_BYTES,
8
+ MAX_TOTAL_BYTES,
9
+ declaredScope,
10
+ sha256,
11
+ requireProof,
12
+ primarySpecProjection,
13
+ isDigest,
14
+ isId,
15
+ isTime,
16
+ isOid,
17
+ } from "./completion-proof-shape.mjs";
18
+ import { githubProofTransport } from "./completion-proof-github.mjs";
19
+ import {
20
+ readTrustedRecord,
21
+ recordFingerprint,
22
+ locatorHistoryFingerprint,
23
+ } from "./completion-proof-reader.mjs";
24
+ import {
25
+ validateTypedBytes,
26
+ validateVerdict,
27
+ validateManifest,
28
+ validateObligations,
29
+ validateVerifyUsage,
30
+ } from "./completion-proof-content.mjs";
31
+ import {
32
+ validateSource,
33
+ validateFrontier,
34
+ validateDelivery,
35
+ } from "./completion-proof-source.mjs";
36
+ import { validateConformance } from "./completion-proof-conformance.mjs";
37
+
38
+ const PENDING_PROOF = "pending-proof";
39
+
40
+ /** Stable user-facing next action, without declaring merely pending delivery failed. */
41
+ const NEXT =
42
+ "Run canonical Verify/evidence, publish the accepted completion proof, then retry completion.";
43
+
44
+ /** Fetch each native attachment positively; missing or wrong-type bytes refuse. */
45
+ function validateAttachments(record, transport) {
46
+ let total = 0;
47
+ const evidence = new Map();
48
+ for (const item of record.evidence) {
49
+ requireProof(
50
+ typeof item.evidence_id === "string" &&
51
+ !evidence.has(item.evidence_id) &&
52
+ isId(item.asset_id) &&
53
+ isDigest(item.sha256) &&
54
+ isTime(item.captured_at) &&
55
+ isOid(item.artifact_head_sha) &&
56
+ Number.isSafeInteger(item.bytes) &&
57
+ item.bytes > 0,
58
+ "A typed proof attachment has an ambiguous identity, digest, capture time or byte count."
59
+ );
60
+ const asset = transport.asset(
61
+ item.asset_id,
62
+ Math.min(MAX_ATTACHMENT_BYTES, MAX_TOTAL_BYTES - total)
63
+ );
64
+ total += asset.bytes.length;
65
+ requireProof(
66
+ asset.bytes.length === item.bytes && sha256(asset.bytes) === item.sha256,
67
+ `Evidence ${item.evidence_id} has changed bytes or an incomplete attachment.`
68
+ );
69
+ validateTypedBytes(item.kind, asset.bytes, item.evidence_id);
70
+ evidence.set(item.evidence_id, item);
71
+ }
72
+ const verdicts = [];
73
+ for (const item of record.verdicts) {
74
+ requireProof(
75
+ isId(item.asset_id) && isDigest(item.sha256),
76
+ "A required verdict has no native immutable attachment."
77
+ );
78
+ const asset = transport.asset(
79
+ item.asset_id,
80
+ Math.min(MAX_ATTACHMENT_BYTES, MAX_TOTAL_BYTES - total)
81
+ );
82
+ total += asset.bytes.length;
83
+ requireProof(
84
+ sha256(asset.bytes) === item.sha256 && asset.bytes.length === item.bytes,
85
+ "The required verification verdict's bytes do not match its accepted digest."
86
+ );
87
+ try {
88
+ verdicts.push({
89
+ value: JSON.parse(asset.bytes.toString("utf8")),
90
+ role: item.role,
91
+ });
92
+ } catch {
93
+ throw new CompletionProofError(
94
+ "invalid-proof",
95
+ "The required verification verdict is malformed JSON."
96
+ );
97
+ }
98
+ }
99
+ return { evidence, verdicts };
100
+ }
101
+
102
+ /** Complete evaluation of exact native requirements, not an advisory local stop. */
103
+ export function evaluateAcceptedRecord(chosen, context, subject, transport) {
104
+ const { record } = chosen;
105
+ requireProof(
106
+ record.state !== "revoked",
107
+ "The latest trusted completion proof was revoked."
108
+ );
109
+ requireProof(
110
+ record.state === "accepted-for-completion",
111
+ "Delivery/runtime verification proof is still pending.",
112
+ PENDING_PROOF
113
+ );
114
+ requireProof(
115
+ record.environment === context.environment &&
116
+ record.branch === context.branch &&
117
+ record.spec_sha256 === sha256(primarySpecProjection(subject.body)),
118
+ "The accepted proof does not match the current full primary specification or target environment."
119
+ );
120
+ const scope = declaredScope(subject.body, context.environment);
121
+ const { evidence, verdicts } = validateAttachments(record, transport);
122
+ const claims = new Map();
123
+ for (const { value: verdict, role } of verdicts) {
124
+ requireProof(
125
+ ["remote", "quality"].includes(role) &&
126
+ verdict.artifact?.repository === context.repository,
127
+ "The verdict has an unknown role or was captured for another repository."
128
+ );
129
+ if (role === "remote")
130
+ requireProof(
131
+ verdict.artifact.environment === context.environment &&
132
+ verdict.artifact.head_sha === record.source.delivered_sha,
133
+ "Remote verification was not captured against the accepted delivered source/environment."
134
+ );
135
+ else
136
+ requireProof(
137
+ [record.source.tested_head, record.source.pr_head].includes(
138
+ verdict.artifact.head_sha
139
+ ),
140
+ "The quality verdict does not identify its actual reconciled tested execution head."
141
+ );
142
+ for (const [id, claim] of validateVerdict(
143
+ verdict,
144
+ evidence,
145
+ scope.runtime
146
+ )) {
147
+ requireProof(
148
+ !claims.has(id),
149
+ `Required claim ${id} is ambiguous across verdicts.`
150
+ );
151
+ claims.set(id, { ...claim, verification_role: role });
152
+ }
153
+ }
154
+ validateManifest(record, subject.body, evidence);
155
+ validateObligations(record, scope, claims);
156
+ validateConformance(record, context, subject, transport);
157
+ for (const obligation of Object.values(record.obligations)) {
158
+ if (obligation.evidence_refs)
159
+ requireProof(
160
+ obligation.evidence_refs.every(id => evidence.has(id)),
161
+ "A required completion obligation cites an unattached evidence record."
162
+ );
163
+ }
164
+ validateVerifyUsage(
165
+ [subject.body, ...(subject.comments ?? []).map(row => row.body)],
166
+ context.ref,
167
+ record.obligations.usage.entry_ids,
168
+ context
169
+ );
170
+ const primary = validateSource(record, context, transport);
171
+ requireProof(
172
+ context.hasBacklink(subject, primary.html_url),
173
+ "The exact primary PR has no verified native or managed work-item backlink."
174
+ );
175
+ validateDelivery(record, context, transport);
176
+ validateFrontier(record, context, transport);
177
+ return { primary, scope };
178
+ }
179
+
180
+ /** Fresh checks run after byte validation and before any terminal mutation. */
181
+ export function authorizeCompletion(context) {
182
+ try {
183
+ let subject = context.readSubject();
184
+ if ((subject.comments ?? []).length > 0)
185
+ subject = { ...subject, comments: context.readComments() };
186
+ const hints = subject.comments ?? [];
187
+ if (
188
+ !hints.some(row =>
189
+ row.body?.startsWith("[lisa-completion-authorization]\n")
190
+ )
191
+ ) {
192
+ return {
193
+ category: PENDING_PROOF,
194
+ applied: false,
195
+ native: subject,
196
+ reason: `Required delivery/runtime verification and accepted evidence proof are pending. ${NEXT}`,
197
+ };
198
+ }
199
+ const transport = githubProofTransport(
200
+ context.run,
201
+ context.repository,
202
+ context.policy
203
+ );
204
+ const chosen = readTrustedRecord(context, subject, transport);
205
+ requireProof(
206
+ chosen,
207
+ `Accepted delivery/runtime evidence proof is absent or has no eligible publisher. ${NEXT}`,
208
+ PENDING_PROOF
209
+ );
210
+ // Full native comments include canonical usage and the complete locator history.
211
+ subject = { ...subject, comments: context.readComments() };
212
+ const evaluated = evaluateAcceptedRecord(
213
+ chosen,
214
+ context,
215
+ subject,
216
+ transport
217
+ );
218
+ validateSource(chosen.record, context, transport);
219
+ validateDelivery(chosen.record, context, transport);
220
+ validateFrontier(chosen.record, context, transport);
221
+ const current = readTrustedRecord(context, subject, transport);
222
+ requireProof(
223
+ current && recordFingerprint(current) === recordFingerprint(chosen),
224
+ "Completion proof changed while it was being verified; run canonical Verify/evidence again.",
225
+ PENDING_PROOF
226
+ );
227
+ // All slow source, delivery, asset and issuer reads precede these final
228
+ // native reads. No provider read intervenes between local final checks and
229
+ // the caller's lifecycle-policy check/first terminal write.
230
+ const comments = context.readComments();
231
+ const fresh = { ...context.readSubject(), comments };
232
+ requireProof(
233
+ locatorHistoryFingerprint(comments) === current.history,
234
+ "The native completion locator history changed before the terminal write; retry canonical verification.",
235
+ PENDING_PROOF
236
+ );
237
+ requireProof(
238
+ sha256(primarySpecProjection(fresh.body)) === chosen.record.spec_sha256,
239
+ "Work-item requirements changed before the terminal write; fresh verification proof is pending.",
240
+ PENDING_PROOF
241
+ );
242
+ validateVerifyUsage(
243
+ [fresh.body, ...comments.map(row => row.body)],
244
+ context.ref,
245
+ chosen.record.obligations.usage.entry_ids,
246
+ context
247
+ );
248
+ requireProof(
249
+ context.hasBacklink(fresh, evaluated.primary.html_url),
250
+ "The primary pull-request backlink changed before the terminal write; restore canonical linkage.",
251
+ PENDING_PROOF
252
+ );
253
+ return {
254
+ category: "authorized",
255
+ applied: false,
256
+ native: fresh,
257
+ record: chosen.record,
258
+ primary: chosen.record.source.pull_request,
259
+ };
260
+ } catch (error) {
261
+ if (!(error instanceof CompletionProofError)) throw error;
262
+ return {
263
+ category: error.category,
264
+ applied: false,
265
+ reason: `${error.message} ${NEXT}`,
266
+ };
267
+ }
268
+ }
269
+
270
+ /** The same outcome vocabulary is consumed by direct CLI and workflow summaries. */
271
+ export function completionDenial(ref, result) {
272
+ const prefix =
273
+ result.category === PENDING_PROOF
274
+ ? "work-item pending-proof"
275
+ : "work-item NOT completed";
276
+ return {
277
+ ...result,
278
+ terminal: null,
279
+ merged: [],
280
+ report: `${prefix}: ${ref} [${result.category}]\n${result.reason}`,
281
+ };
282
+ }
@@ -0,0 +1,213 @@
1
+ // This file is managed by Lisa and IS replaced on each `lisa` run.
2
+ // Do not edit directly — durable changes belong upstream in Lisa.
3
+
4
+ /** Provider-native proof discovery/publication adapters; credentials stay in CLI. */
5
+ import {
6
+ CompletionProofError,
7
+ MAX_NATIVE_JSON_BYTES,
8
+ requireProof,
9
+ } from "./completion-proof-shape.mjs";
10
+
11
+ const READ_ERROR = "read-error";
12
+ const WRITE_ERROR = "write-error";
13
+
14
+ /** Construct the same strict authorizer context for supported lifecycle writers. */
15
+ export function completionContext(options) {
16
+ const { ref, contract, run, declares, hasBacklink } = options;
17
+ const github = contract.provider === "github";
18
+ const repository = github ? ref.split("#")[0] : options.repository;
19
+ const number = ref.split("#")[1];
20
+ const environment = contract.lifecycle.productionEnvironment;
21
+ const branch = [...contract.deployBranches].find(
22
+ ([, env]) => env === environment
23
+ )?.[0];
24
+ requireProof(branch, "The configured production branch cannot be resolved.");
25
+ function linear(query, variables, purpose) {
26
+ try {
27
+ return options.linear(query, variables, purpose);
28
+ } catch (error) {
29
+ if (error instanceof CompletionProofError) throw error;
30
+ throw new CompletionProofError(
31
+ query.startsWith("mutation") ? WRITE_ERROR : READ_ERROR,
32
+ `The required native ${purpose} did not complete; restore Linear access and retry canonical verification.`
33
+ );
34
+ }
35
+ }
36
+ function gh(args, { write = false, input } = {}) {
37
+ const result = run("gh", args, {
38
+ allowFailure: true,
39
+ input,
40
+ maxBuffer: MAX_NATIVE_JSON_BYTES,
41
+ });
42
+ if (
43
+ result.status !== 0 ||
44
+ result.error ||
45
+ Buffer.byteLength(result.stdout ?? "") > MAX_NATIVE_JSON_BYTES
46
+ )
47
+ throw new CompletionProofError(
48
+ write ? WRITE_ERROR : READ_ERROR,
49
+ `The required native ${write ? "proof publication" : "work-item proof read"} did not complete.`
50
+ );
51
+ try {
52
+ return JSON.parse(result.stdout);
53
+ } catch {
54
+ throw new CompletionProofError(
55
+ READ_ERROR,
56
+ "A native work-item proof response is not complete JSON."
57
+ );
58
+ }
59
+ }
60
+ function readSubject() {
61
+ if (github) {
62
+ const issue = gh([
63
+ "issue",
64
+ "view",
65
+ number,
66
+ "--repo",
67
+ repository,
68
+ "--json",
69
+ "id,number,url,body,comments,labels,state,stateReason,assignees,updatedAt,closedByPullRequestsReferences",
70
+ ]);
71
+ requireProof(
72
+ String(issue.number) === number && Array.isArray(issue.comments),
73
+ "The native work-item proof read returned another item or unreadable comments."
74
+ );
75
+ return {
76
+ native_id: issue.id,
77
+ ref,
78
+ body: issue.body ?? "",
79
+ comments: issue.comments,
80
+ labels: (issue.labels ?? []).map(row =>
81
+ typeof row === "string" ? row : row.name
82
+ ),
83
+ state: issue.state,
84
+ reason: issue.stateReason ?? "",
85
+ assignees: issue.assignees ?? [],
86
+ raw: issue,
87
+ };
88
+ }
89
+ const issue = linear(
90
+ "query($id:String!){issue(id:$id){id identifier description updatedAt team{key states{nodes{id name type}}} state{id name type} attachments{nodes{url}} comments(first:100){nodes{id body createdAt updatedAt user{id}} pageInfo{hasNextPage endCursor}}}}",
91
+ { id: ref },
92
+ "Linear completion proof read"
93
+ ).issue;
94
+ requireProof(
95
+ issue?.identifier === ref && issue.team?.key === contract.teamKey,
96
+ "The native Linear completion read returned another item or team."
97
+ );
98
+ return {
99
+ native_id: issue.id,
100
+ ref,
101
+ body: issue.description ?? "",
102
+ comments: issue.comments?.nodes ?? [],
103
+ state: issue.state,
104
+ raw: issue,
105
+ };
106
+ }
107
+ function readComments() {
108
+ if (github) {
109
+ const pages = gh([
110
+ "api",
111
+ "--paginate",
112
+ "--slurp",
113
+ `repos/${repository}/issues/${number}/comments?per_page=100`,
114
+ ]);
115
+ requireProof(
116
+ Array.isArray(pages) &&
117
+ pages.length <= 100 &&
118
+ pages.every(Array.isArray) &&
119
+ pages.flat().length <= 10000 &&
120
+ pages.flat().every(row => typeof row.body === "string"),
121
+ "The native proof-locator comment pagination is incomplete or over its bound."
122
+ );
123
+ return pages.flat();
124
+ }
125
+ const comments = [];
126
+ const seen = new Set();
127
+ let after = null;
128
+ for (let page = 0; page < 100; page += 1) {
129
+ const result = linear(
130
+ "query($id:String!,$after:String){issue(id:$id){comments(first:100,after:$after){nodes{id body createdAt updatedAt user{id}} pageInfo{hasNextPage endCursor}}}}",
131
+ { id: ref, after },
132
+ "Linear completion locator pagination"
133
+ ).issue?.comments;
134
+ requireProof(
135
+ Array.isArray(result?.nodes) &&
136
+ typeof result.pageInfo?.hasNextPage === "boolean",
137
+ "The native Linear proof-locator pagination is incomplete."
138
+ );
139
+ comments.push(...result.nodes);
140
+ requireProof(
141
+ comments.length <= 10000,
142
+ "The Linear proof-locator pagination exceeds its bound."
143
+ );
144
+ if (!result.pageInfo.hasNextPage) return comments;
145
+ const cursor = result.pageInfo.endCursor;
146
+ requireProof(
147
+ typeof cursor === "string" && !seen.has(cursor),
148
+ "Linear proof-locator pagination repeated or omitted a cursor."
149
+ );
150
+ seen.add(cursor);
151
+ after = cursor;
152
+ }
153
+ throw new CompletionProofError(
154
+ READ_ERROR,
155
+ "The Linear proof-locator pagination exceeds its page bound."
156
+ );
157
+ }
158
+ return {
159
+ ref,
160
+ provider: contract.provider,
161
+ scope: github ? repository : `${contract.workspace}/${contract.teamKey}`,
162
+ repository,
163
+ environment,
164
+ branch,
165
+ suppliedPrUrl: options.prUrl,
166
+ policy: options.config.verification?.completion ?? {},
167
+ run,
168
+ declares,
169
+ readSubject,
170
+ readComments,
171
+ hasBacklink: (subject, url) =>
172
+ hasBacklink(
173
+ {
174
+ ...subject.raw,
175
+ comments: github ? subject.comments : { nodes: subject.comments },
176
+ },
177
+ url
178
+ ),
179
+ trackerActor(githubActor) {
180
+ return github
181
+ ? String(githubActor)
182
+ : String(
183
+ linear("query{viewer{id}}", {}, "Linear proof publisher lookup")
184
+ .viewer?.id
185
+ );
186
+ },
187
+ postLocator(body, nativeId) {
188
+ if (github)
189
+ return gh(
190
+ [
191
+ "api",
192
+ "--method",
193
+ "POST",
194
+ `repos/${repository}/issues/${number}/comments`,
195
+ "--input",
196
+ "-",
197
+ ],
198
+ { write: true, input: JSON.stringify({ body }) }
199
+ );
200
+ const result = linear(
201
+ "mutation($input:CommentCreateInput!){commentCreate(input:$input){success comment{id body createdAt updatedAt user{id}}}}",
202
+ { input: { issueId: nativeId, body } },
203
+ "Linear completion proof publication"
204
+ ).commentCreate;
205
+ requireProof(
206
+ result?.success === true && result.comment?.id,
207
+ "The native Linear completion-proof publication failed.",
208
+ WRITE_ERROR
209
+ );
210
+ return result.comment;
211
+ },
212
+ };
213
+ }
@@ -0,0 +1,115 @@
1
+ // This file is managed by Lisa and IS replaced on each `lisa` run.
2
+ // Do not edit directly — durable changes belong upstream in Lisa.
3
+
4
+ /** The accepted conformance map is itself a hash-pinned native attachment. */
5
+ import {
6
+ requireProof,
7
+ isId,
8
+ isDigest,
9
+ isObject,
10
+ isText,
11
+ MAX_RECORD_BYTES,
12
+ sha256,
13
+ section,
14
+ } from "./completion-proof-shape.mjs";
15
+
16
+ /** Compare exact required conjunctions without depending on display order. */
17
+ function groups(values) {
18
+ requireProof(
19
+ Array.isArray(values),
20
+ "The attached conformance requirement map is unreadable."
21
+ );
22
+ requireProof(
23
+ values.every(
24
+ row =>
25
+ isObject(row) &&
26
+ isText(row.requirement_id) &&
27
+ Array.isArray(row.claim_ids)
28
+ ),
29
+ "The attached conformance map contains malformed requirement rows."
30
+ );
31
+ return values
32
+ .map(row => ({
33
+ requirement_id: row.requirement_id,
34
+ operator: row.operator,
35
+ claim_ids: [...(row.claim_ids ?? [])].sort(),
36
+ }))
37
+ .sort((left, right) =>
38
+ left.requirement_id.localeCompare(right.requirement_id)
39
+ );
40
+ }
41
+
42
+ /** Match the original multiline Scenario grammar with one forward scan. */
43
+ function scenarioNames(text) {
44
+ const names = [];
45
+ const lineBreak = character => /[\r\n\u2028\u2029]/.test(character);
46
+ let index = 0;
47
+ while (index < text.length) {
48
+ while (/\s/.test(text[index] ?? "")) index += 1;
49
+ const prefix = text.startsWith("Scenario Outline:", index)
50
+ ? "Scenario Outline:"
51
+ : "Scenario:";
52
+ if (text.startsWith(prefix, index)) {
53
+ const first = index + prefix.length;
54
+ let title = first;
55
+ while (/\s/.test(text[title] ?? "")) title += 1;
56
+ if (title === text.length) {
57
+ title -= 1;
58
+ while (title >= first && lineBreak(text[title])) title -= 1;
59
+ }
60
+ if (title >= first && title < text.length) {
61
+ let end = title;
62
+ while (end < text.length && !lineBreak(text[end])) end += 1;
63
+ names.push(text.slice(title, end).trim());
64
+ index = end;
65
+ continue;
66
+ }
67
+ }
68
+ while (index < text.length && !lineBreak(text[index])) index += 1;
69
+ index += 1;
70
+ }
71
+ return names;
72
+ }
73
+
74
+ /** CONFORMS text alone cannot replace the current complete coverage report. */
75
+ export function validateConformance(record, context, subject, transport) {
76
+ const pointer = record.obligations.conformance.report;
77
+ requireProof(
78
+ isId(pointer?.asset_id) &&
79
+ isDigest(pointer.sha256) &&
80
+ Number.isSafeInteger(pointer.bytes),
81
+ "The required CONFORMS report has no immutable native attachment."
82
+ );
83
+ const asset = transport.asset(pointer.asset_id, MAX_RECORD_BYTES);
84
+ requireProof(
85
+ asset.bytes.length === pointer.bytes &&
86
+ sha256(asset.bytes) === pointer.sha256,
87
+ "The CONFORMS report does not match its accepted bytes."
88
+ );
89
+ let report;
90
+ try {
91
+ report = JSON.parse(asset.bytes.toString("utf8"));
92
+ } catch {
93
+ requireProof(false, "The CONFORMS report is not readable structured JSON.");
94
+ }
95
+ requireProof(
96
+ isObject(report) &&
97
+ report.verdict === "CONFORMS" &&
98
+ report.spec_sha256 === record.spec_sha256 &&
99
+ report.work_item === context.ref &&
100
+ Array.isArray(report.requirements) &&
101
+ report.requirements.every(
102
+ row => isObject(row) && row.status === "pass"
103
+ ) &&
104
+ JSON.stringify(groups(report.requirements)) ===
105
+ JSON.stringify(groups(record.requirements)),
106
+ "The attached CONFORMS report does not establish the exact current requirement conjunctions."
107
+ );
108
+ const scenarios = scenarioNames(section(subject.body, "Acceptance Criteria"));
109
+ requireProof(
110
+ Array.isArray(report.scenario_names) &&
111
+ new Set(report.scenario_names).size === report.scenario_names.length &&
112
+ scenarios.every(name => report.scenario_names.includes(name)),
113
+ "The attached conformance report omits a primary acceptance scenario."
114
+ );
115
+ }