@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.
- package/all/copy-overwrite/scripts/lib/completion-authorization.mjs +282 -0
- package/all/copy-overwrite/scripts/lib/completion-proof-adapters.mjs +213 -0
- package/all/copy-overwrite/scripts/lib/completion-proof-conformance.mjs +115 -0
- package/all/copy-overwrite/scripts/lib/completion-proof-content.mjs +408 -0
- package/all/copy-overwrite/scripts/lib/completion-proof-errors.mjs +16 -0
- package/all/copy-overwrite/scripts/lib/completion-proof-github.mjs +233 -0
- package/all/copy-overwrite/scripts/lib/completion-proof-markdown.mjs +70 -0
- package/all/copy-overwrite/scripts/lib/completion-proof-projection.mjs +169 -0
- package/all/copy-overwrite/scripts/lib/completion-proof-publisher.mjs +238 -0
- package/all/copy-overwrite/scripts/lib/completion-proof-reader.mjs +175 -0
- package/all/copy-overwrite/scripts/lib/completion-proof-shape.mjs +374 -0
- package/all/copy-overwrite/scripts/lib/completion-proof-source.mjs +174 -0
- package/all/copy-overwrite/scripts/lib/history-secret-scanner.mjs +20 -5
- package/all/copy-overwrite/scripts/lib/npm-update-helper-inventory.mjs +15 -0
- package/all/copy-overwrite/scripts/lib/usage-effectiveness.d.mts +27 -0
- package/all/copy-overwrite/scripts/lib/usage-effectiveness.mjs +156 -0
- package/all/copy-overwrite/scripts/lib/usage-ledger-integrity.d.mts +8 -0
- package/all/copy-overwrite/scripts/lib/usage-ledger-integrity.mjs +141 -0
- package/all/copy-overwrite/scripts/lib/usage-ledger.d.mts +89 -0
- package/all/copy-overwrite/scripts/lib/usage-ledger.mjs +206 -0
- package/all/copy-overwrite/scripts/lisa-hooks/parity-safety-net.sh +3 -1
- package/all/copy-overwrite/scripts/lisa-work-item.mjs +255 -39
- package/all/copy-overwrite/scripts/npm-updater-helper-graph.json +141 -3
- package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
- package/dist/core/lisa-owned-hash-ledger.js +186 -0
- package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
- package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
- package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
- package/dist/core/upstream-evidence-manifest.js +108 -15
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- package/dist/opencode/plugin-templates/parity-safety-net.sh +3 -1
- package/dist/utils/effectiveness.d.ts +4 -41
- package/dist/utils/effectiveness.d.ts.map +1 -1
- package/dist/utils/effectiveness.js +2 -126
- package/dist/utils/effectiveness.js.map +1 -1
- package/dist/utils/usage-accounting.d.ts +5 -100
- package/dist/utils/usage-accounting.d.ts.map +1 -1
- package/dist/utils/usage-accounting.js +4 -287
- package/dist/utils/usage-accounting.js.map +1 -1
- package/package.json +4 -4
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-drive-pr-to-merge/SKILL.md +15 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-tracker-evidence/SKILL.md +7 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-tracker-evidence/references/completion-authorization.md +193 -0
- package/plugins/lisa/hooks/block-host-name-leak.mjs +25 -13
- package/plugins/lisa/hooks/host-name-command-context.mjs +16 -4
- package/plugins/lisa/hooks/parity-safety-net.sh +3 -1
- package/plugins/lisa/rules/eager/00-rule-index.md +1 -0
- package/plugins/lisa/rules/reference/completion-authorization.md +10 -0
- package/plugins/lisa/skills/lisa-drive-pr-to-merge/SKILL.md +15 -1
- package/plugins/lisa/skills/lisa-tracker-evidence/SKILL.md +7 -1
- package/plugins/lisa/skills/lisa-tracker-evidence/references/completion-authorization.md +193 -0
- package/plugins/lisa-agy/hooks/block-host-name-leak.mjs +25 -13
- package/plugins/lisa-agy/hooks/host-name-command-context.mjs +16 -4
- package/plugins/lisa-agy/hooks/parity-safety-net.sh +3 -1
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/skills/lisa-drive-pr-to-merge/SKILL.md +15 -1
- package/plugins/lisa-agy/skills/lisa-tracker-evidence/SKILL.md +7 -1
- package/plugins/lisa-agy/skills/lisa-tracker-evidence/references/completion-authorization.md +193 -0
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-agy/plugin.json +1 -1
- package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-devin/.devin-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/hooks/block-host-name-leak.mjs +25 -13
- package/plugins/lisa-copilot/hooks/host-name-command-context.mjs +16 -4
- package/plugins/lisa-copilot/hooks/parity-safety-net.sh +3 -1
- package/plugins/lisa-copilot/rules/eager/00-rule-index.md +1 -0
- package/plugins/lisa-copilot/rules/reference/completion-authorization.md +10 -0
- package/plugins/lisa-copilot/skills/lisa-drive-pr-to-merge/SKILL.md +15 -1
- package/plugins/lisa-copilot/skills/lisa-tracker-evidence/SKILL.md +7 -1
- package/plugins/lisa-copilot/skills/lisa-tracker-evidence/references/completion-authorization.md +193 -0
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/hooks/block-host-name-leak.mjs +25 -13
- package/plugins/lisa-cursor/hooks/host-name-command-context.mjs +16 -4
- package/plugins/lisa-cursor/hooks/parity-safety-net.sh +3 -1
- package/plugins/lisa-cursor/rules/00-rule-index.mdc +1 -0
- package/plugins/lisa-cursor/rules/completion-authorization-reference.mdc +15 -0
- package/plugins/lisa-cursor/skills/lisa-drive-pr-to-merge/SKILL.md +15 -1
- package/plugins/lisa-cursor/skills/lisa-tracker-evidence/SKILL.md +7 -1
- package/plugins/lisa-cursor/skills/lisa-tracker-evidence/references/completion-authorization.md +193 -0
- package/plugins/lisa-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-devin/.devin-plugin/plugin.json +1 -1
- package/plugins/lisa-devin/hooks/block-host-name-leak.mjs +25 -13
- package/plugins/lisa-devin/hooks/host-name-command-context.mjs +16 -4
- package/plugins/lisa-devin/hooks/parity-safety-net.sh +3 -1
- package/plugins/lisa-devin/rules/eager/00-rule-index.md +1 -0
- package/plugins/lisa-devin/rules/reference/completion-authorization.md +10 -0
- package/plugins/lisa-devin/skills/lisa-drive-pr-to-merge/SKILL.md +15 -1
- package/plugins/lisa-devin/skills/lisa-tracker-evidence/SKILL.md +7 -1
- package/plugins/lisa-devin/skills/lisa-tracker-evidence/references/completion-authorization.md +193 -0
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-agy/plugin.json +1 -1
- package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-devin/.devin-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-devin/.devin-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-agy/plugin.json +1 -1
- package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-devin/.devin-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-agy/plugin.json +1 -1
- package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-devin/.devin-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-agy/plugin.json +1 -1
- package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-devin/.devin-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-agy/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-devin/.devin-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-agy/plugin.json +1 -1
- package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-devin/.devin-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-agy/plugin.json +1 -1
- package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-devin/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-devin/.devin-plugin/plugin.json +1 -1
- package/plugins/src/base/hooks/block-host-name-leak.mjs +25 -13
- package/plugins/src/base/hooks/host-name-command-context.mjs +16 -4
- package/plugins/src/base/hooks/parity-safety-net.sh +3 -1
- package/plugins/src/base/rules/eager/00-rule-index.md +1 -0
- package/plugins/src/base/rules/reference/completion-authorization.md +10 -0
- package/plugins/src/base/skills/lisa-drive-pr-to-merge/SKILL.md +15 -1
- package/plugins/src/base/skills/lisa-tracker-evidence/SKILL.md +7 -1
- package/plugins/src/base/skills/lisa-tracker-evidence/references/completion-authorization.md +193 -0
- package/scripts/lib/host-name-expanded-command-cases.mjs +286 -0
- package/scripts/lib/host-name-field-occurrence-cases.mjs +60 -0
- package/scripts/two-channel-couplings.json +2 -2
- package/scripts/verify-host-name-target-scope.mjs +27 -1
- package/typescript/copy-overwrite/.prettierignore +15 -0
- 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
|
+
}
|