@akagilnc/pi-workflow-roles 0.1.1751
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/LICENSE +202 -0
- package/README.md +104 -0
- package/README.zh-CN.md +133 -0
- package/THIRD_PARTY_NOTICES.md +60 -0
- package/dist/activation-ledger-git.js +68 -0
- package/dist/activation-ledger-session.js +120 -0
- package/dist/activation-ledger-topology.js +239 -0
- package/dist/activation-reconciliation.js +61 -0
- package/dist/audit-escalation.js +108 -0
- package/dist/auditor-dossier-tool.js +35 -0
- package/dist/canonical-json.js +78 -0
- package/dist/compliance-transport.js +77 -0
- package/dist/doctor-contracts.js +172 -0
- package/dist/dossier-resolution.js +103 -0
- package/dist/evidence-child-executor.js +661 -0
- package/dist/exact-utf8.js +12 -0
- package/dist/git-object-id.js +7 -0
- package/dist/in-process-session.js +50 -0
- package/dist/merger-contracts.js +76 -0
- package/dist/navigator-attendance.js +995 -0
- package/dist/navigator-invocation-identity.js +220 -0
- package/dist/open-tool-schema.js +39 -0
- package/dist/package-contracts/collector-output.js +50 -0
- package/dist/package-contracts/fixer-output.js +72 -0
- package/dist/package-contracts/fixer-packet.js +77 -0
- package/dist/package-contracts/judge-output.js +17 -0
- package/dist/package-contracts/reviewer-output.js +82 -0
- package/dist/package-contracts/terminating-tools.js +173 -0
- package/dist/package-contracts/worker-output.js +13 -0
- package/dist/package-owned-tool-idle.js +104 -0
- package/dist/packaged-role-registry.js +34 -0
- package/dist/public-cli/main.js +23867 -0
- package/dist/public-command-renderer.js +20 -0
- package/dist/reviewer-agent.js +93 -0
- package/dist/reviewer-child-executor.js +23 -0
- package/dist/reviewer-construction.js +95 -0
- package/dist/reviewer-dispatch.js +77 -0
- package/dist/reviewer-execution-ledger.js +160 -0
- package/dist/reviewer-failure-diagnostic.js +17 -0
- package/dist/reviewer-git-snapshot.js +38 -0
- package/dist/reviewer-pinned-git.js +146 -0
- package/dist/reviewer-preflight-error.js +15 -0
- package/dist/reviewer-prompt-identity.js +10 -0
- package/dist/reviewer-scope-prompt.js +21 -0
- package/dist/reviewer-workspace.js +151 -0
- package/dist/sha256.js +5 -0
- package/dist/sitian-record-entry.js +33 -0
- package/dist/stderr-jsonl.js +26 -0
- package/dist/stream-idle-guard.js +75 -0
- package/dist/tool-execution-observation.js +141 -0
- package/dist/uuidv7.js +21 -0
- package/dist/work-subject-identity.js +53 -0
- package/extensions/role-runtime.ts +303 -0
- package/package.json +69 -0
- package/packets/fixer-prerequisites.json +6 -0
- package/packets/fixer-repair.md +5 -0
- package/packets/judge-apply.md +77 -0
- package/packets/judge-authority.md +64 -0
- package/packets/judge-plan.md +55 -0
- package/packets/judge-review.md +49 -0
- package/packets/judge-submission.md +34 -0
- package/resources/methods/code-review/SKILL.md +92 -0
- package/resources/methods/code-review/agents/openai.yaml +3 -0
- package/resources/methods/code-review/provenance.json +26 -0
- package/resources/methods/diagnosing-bugs/SKILL.md +134 -0
- package/resources/methods/diagnosing-bugs/agents/openai.yaml +3 -0
- package/resources/methods/diagnosing-bugs/provenance.json +31 -0
- package/resources/methods/diagnosing-bugs/scripts/hitl-loop.template.sh +41 -0
- package/resources/methods/resolving-merge-conflicts/SKILL.md +14 -0
- package/resources/methods/resolving-merge-conflicts/agents/openai.yaml +3 -0
- package/resources/methods/resolving-merge-conflicts/provenance.json +26 -0
- package/resources/methods/tdd/SKILL.md +38 -0
- package/resources/methods/tdd/agents/openai.yaml +3 -0
- package/resources/methods/tdd/mocking.md +59 -0
- package/resources/methods/tdd/provenance.json +36 -0
- package/resources/methods/tdd/tests.md +77 -0
- package/resources/navigator-route-playbook.md +32 -0
- package/schemas/tool-execution-observation.schema.json +107 -0
- package/scripts/build-package.mjs +65 -0
- package/scripts/generate-tool-execution-observation-schema.ts +7 -0
- package/souls/coder.md +10 -0
- package/souls/collector.md +11 -0
- package/souls/doctor-auditor.md +23 -0
- package/souls/doctor.md +8 -0
- package/souls/fixer-auditor.md +33 -0
- package/souls/fixer.md +13 -0
- package/souls/judge-auditor.md +33 -0
- package/souls/judge.md +74 -0
- package/souls/merger.md +5 -0
- package/souls/navigator.md +5 -0
- package/souls/reviewer-auditor.md +25 -0
- package/souls/reviewer.md +11 -0
- package/src/activation-ledger-git.ts +96 -0
- package/src/activation-ledger-session.ts +188 -0
- package/src/activation-ledger-topology.ts +301 -0
- package/src/activation-ledger.ts +240 -0
- package/src/activation-reconciliation.ts +163 -0
- package/src/activation-trace.ts +38 -0
- package/src/audit-escalation.ts +177 -0
- package/src/auditor-dossier-tool.ts +48 -0
- package/src/auditor-soul.ts +28 -0
- package/src/canonical-json.ts +74 -0
- package/src/canonical-skill-binding.ts +107 -0
- package/src/collector-config.ts +89 -0
- package/src/collector-evidence.ts +461 -0
- package/src/collector-github.ts +656 -0
- package/src/collector-identity.ts +161 -0
- package/src/collector-ledger.ts +827 -0
- package/src/collector-receipt.ts +87 -0
- package/src/collector-role.ts +592 -0
- package/src/collector-tool-schemas.ts +19 -0
- package/src/compliance-transport.ts +130 -0
- package/src/doctor-auditor.ts +53 -0
- package/src/doctor-contracts.ts +166 -0
- package/src/doctor-evidence.ts +47 -0
- package/src/doctor-role.ts +18 -0
- package/src/dossier-resolution.ts +137 -0
- package/src/evidence-child-executor.ts +775 -0
- package/src/exact-utf8.ts +9 -0
- package/src/factory-board.ts +1822 -0
- package/src/git-object-id.ts +11 -0
- package/src/human-format.ts +65 -0
- package/src/in-process-session.ts +78 -0
- package/src/judge-auditor.ts +55 -0
- package/src/judge-recording-anti-forge.ts +53 -0
- package/src/judge-role.ts +160 -0
- package/src/merger-contracts.ts +71 -0
- package/src/merger-git-state.ts +76 -0
- package/src/merger-role.ts +60 -0
- package/src/navigator-attendance.ts +1254 -0
- package/src/navigator-invocation-identity.ts +446 -0
- package/src/open-tool-schema.ts +46 -0
- package/src/package-contracts/collector-output.ts +109 -0
- package/src/package-contracts/fixer-output.ts +81 -0
- package/src/package-contracts/fixer-packet.ts +93 -0
- package/src/package-contracts/judge-output.ts +39 -0
- package/src/package-contracts/reviewer-output.ts +115 -0
- package/src/package-contracts/terminating-tools.ts +259 -0
- package/src/package-contracts/worker-output.ts +36 -0
- package/src/package-owned-tool-idle.ts +134 -0
- package/src/package-resources/method-skill-binding.ts +87 -0
- package/src/package-resources/method-skill.ts +358 -0
- package/src/packaged-role-registry.ts +36 -0
- package/src/public-cli/cli-errors.ts +11 -0
- package/src/public-cli/cli-io.ts +4 -0
- package/src/public-cli/cli.ts +912 -0
- package/src/public-cli/coder-run.ts +575 -0
- package/src/public-cli/collector-run.ts +375 -0
- package/src/public-cli/command-renderer.ts +8 -0
- package/src/public-cli/config.ts +346 -0
- package/src/public-cli/doctor-run.ts +355 -0
- package/src/public-cli/explicit-internal.ts +274 -0
- package/src/public-cli/fixer-run.ts +587 -0
- package/src/public-cli/host-pi-runtime.ts +112 -0
- package/src/public-cli/invocation.ts +1958 -0
- package/src/public-cli/judge-run.ts +507 -0
- package/src/public-cli/main.ts +15 -0
- package/src/public-cli/merger-run.ts +681 -0
- package/src/public-cli/public-run-credentials.ts +71 -0
- package/src/public-cli/registry.ts +153 -0
- package/src/public-cli/reviewer-run.ts +561 -0
- package/src/public-cli/run-lifecycle.ts +884 -0
- package/src/public-cli/settlement.ts +3765 -0
- package/src/public-cli/terminal.ts +325 -0
- package/src/public-command-renderer.ts +43 -0
- package/src/reviewer-agent.ts +94 -0
- package/src/reviewer-auditor.ts +53 -0
- package/src/reviewer-child-executor.ts +31 -0
- package/src/reviewer-construction.ts +137 -0
- package/src/reviewer-dispatch.ts +94 -0
- package/src/reviewer-execution-ledger.ts +206 -0
- package/src/reviewer-failure-diagnostic.ts +18 -0
- package/src/reviewer-git-snapshot.ts +53 -0
- package/src/reviewer-pinned-git.ts +144 -0
- package/src/reviewer-preflight-error.ts +14 -0
- package/src/reviewer-prompt-identity.ts +17 -0
- package/src/reviewer-role.ts +193 -0
- package/src/reviewer-scope-prompt.ts +24 -0
- package/src/reviewer-settlement.ts +63 -0
- package/src/reviewer-workspace.ts +111 -0
- package/src/role-runtime.ts +884 -0
- package/src/sha256.ts +6 -0
- package/src/sitian-record-entry.ts +57 -0
- package/src/stderr-jsonl.ts +28 -0
- package/src/stream-idle-guard.ts +98 -0
- package/src/ticket-snapshot.ts +662 -0
- package/src/ticket-trajectory.ts +1000 -0
- package/src/tool-execution-observation.ts +168 -0
- package/src/uuidv7.ts +1 -0
- package/src/work-subject-identity.ts +94 -0
- package/src/worker-role.ts +434 -0
- package/src/worker-submission-gates.ts +225 -0
|
@@ -0,0 +1,461 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
GitHubIssueComment,
|
|
5
|
+
GitHubMachineIdentity,
|
|
6
|
+
GitHubPageDiagnostics,
|
|
7
|
+
GitHubPullRequest,
|
|
8
|
+
GitHubPullRequestReaction,
|
|
9
|
+
GitHubReview,
|
|
10
|
+
GitHubReviewComment,
|
|
11
|
+
GitHubUser,
|
|
12
|
+
} from "./collector-github.ts";
|
|
13
|
+
|
|
14
|
+
export const COLLECTOR_ELIGIBILITY_MS = 15 * 60 * 1000;
|
|
15
|
+
|
|
16
|
+
export type WindowRelation = "before" | "within" | "after" | "uncertain";
|
|
17
|
+
export type HeadRelation = "current" | "prior";
|
|
18
|
+
|
|
19
|
+
export type CollectorEvidenceKind =
|
|
20
|
+
| "pull_request"
|
|
21
|
+
| "review"
|
|
22
|
+
| "reaction"
|
|
23
|
+
| "issue_comment"
|
|
24
|
+
| "review_comment"
|
|
25
|
+
| "authenticated_user"
|
|
26
|
+
| "request_attempt"
|
|
27
|
+
| "wait"
|
|
28
|
+
| "transport";
|
|
29
|
+
|
|
30
|
+
export type CollectorEvidenceRecord = {
|
|
31
|
+
evidenceId: string;
|
|
32
|
+
kind: CollectorEvidenceKind;
|
|
33
|
+
stableGitHubId?: string;
|
|
34
|
+
/** Typed GitHub facts retained from the transport's single normalization pass. */
|
|
35
|
+
githubId?: number;
|
|
36
|
+
machineIdentity?: GitHubMachineIdentity | null;
|
|
37
|
+
versionId: string;
|
|
38
|
+
contentDigest: string;
|
|
39
|
+
authorLogin?: string;
|
|
40
|
+
state?: string;
|
|
41
|
+
body?: string;
|
|
42
|
+
commitOid?: string | null;
|
|
43
|
+
htmlUrl?: string;
|
|
44
|
+
path?: string;
|
|
45
|
+
line?: number | null;
|
|
46
|
+
/** Outdated inline location when current line is null. */
|
|
47
|
+
originalLine?: number | null;
|
|
48
|
+
side?: string | null;
|
|
49
|
+
position?: number | null;
|
|
50
|
+
pullRequestReviewId?: number | null;
|
|
51
|
+
/** GitHub-supplied submission clock for reviews (metadata only). */
|
|
52
|
+
submittedAt?: string | null;
|
|
53
|
+
authoritativeTime?: string | null;
|
|
54
|
+
firstObservedAt: string;
|
|
55
|
+
windowRelation?: WindowRelation;
|
|
56
|
+
raw: unknown;
|
|
57
|
+
pagination?: {
|
|
58
|
+
surface: string;
|
|
59
|
+
complete: boolean;
|
|
60
|
+
pages: GitHubPageDiagnostics[];
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export type CollectorSnapshot = {
|
|
65
|
+
snapshotId: string;
|
|
66
|
+
observedAt: string;
|
|
67
|
+
/** Wall clock when observe finished successfully. */
|
|
68
|
+
completedAt: string;
|
|
69
|
+
/** Monotonic ms when observe finished successfully. */
|
|
70
|
+
completedMono: number;
|
|
71
|
+
host: "github.com";
|
|
72
|
+
repository: string;
|
|
73
|
+
prNumber: number;
|
|
74
|
+
prState: string;
|
|
75
|
+
headOid: string;
|
|
76
|
+
complete: boolean;
|
|
77
|
+
evidenceIds: string[];
|
|
78
|
+
pageDiagnostics: GitHubPageDiagnostics[];
|
|
79
|
+
normalizedByteLength: number;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export type CollectorClock = {
|
|
83
|
+
wallNow(): Date;
|
|
84
|
+
monoNow(): number;
|
|
85
|
+
sleep(ms: number, signal?: AbortSignal): Promise<void>;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export function createSystemCollectorClock(): CollectorClock {
|
|
89
|
+
const start = process.hrtime.bigint();
|
|
90
|
+
return {
|
|
91
|
+
wallNow: () => new Date(),
|
|
92
|
+
monoNow: () => Number(process.hrtime.bigint() - start) / 1e6,
|
|
93
|
+
sleep: (ms, signal) =>
|
|
94
|
+
new Promise((resolve, reject) => {
|
|
95
|
+
if (signal?.aborted) {
|
|
96
|
+
reject(signal.reason ?? new Error("aborted"));
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
const timer = setTimeout(resolve, ms);
|
|
100
|
+
const onAbort = () => {
|
|
101
|
+
clearTimeout(timer);
|
|
102
|
+
reject(signal?.reason ?? new Error("aborted"));
|
|
103
|
+
};
|
|
104
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
105
|
+
}),
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function sha256Text(text: string): string {
|
|
110
|
+
return createHash("sha256").update(text, "utf8").digest("hex");
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function computeWindowRelation(
|
|
114
|
+
authoritativeTime: string | null | undefined,
|
|
115
|
+
activationTime: Date,
|
|
116
|
+
deadlineTime: Date,
|
|
117
|
+
): WindowRelation {
|
|
118
|
+
if (authoritativeTime === undefined || authoritativeTime === null || authoritativeTime.length === 0) {
|
|
119
|
+
return "uncertain";
|
|
120
|
+
}
|
|
121
|
+
const ms = Date.parse(authoritativeTime);
|
|
122
|
+
if (!Number.isFinite(ms)) return "uncertain";
|
|
123
|
+
const activationMs = activationTime.getTime();
|
|
124
|
+
const deadlineMs = deadlineTime.getTime();
|
|
125
|
+
if (ms < activationMs) return "before";
|
|
126
|
+
if (ms <= deadlineMs) return "within";
|
|
127
|
+
return "after";
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function stableId(kind: string, githubId: string | number): string {
|
|
131
|
+
return `${kind}:${githubId}`;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function versionDigest(parts: Record<string, unknown>): string {
|
|
135
|
+
return sha256Text(JSON.stringify(parts));
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export function evidenceIdFor(kind: string, versionId: string): string {
|
|
139
|
+
return sha256Text(`${kind}:${versionId}`).slice(0, 16);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export function normalizeAuthenticatedUserEvidence(
|
|
143
|
+
user: GitHubUser,
|
|
144
|
+
observedAt: string,
|
|
145
|
+
): CollectorEvidenceRecord {
|
|
146
|
+
const login = user.login.toLowerCase();
|
|
147
|
+
const rawId = (user.raw as { id?: unknown } | undefined)?.id;
|
|
148
|
+
const stableNumericOrStringId =
|
|
149
|
+
typeof rawId === "number" || typeof rawId === "string" ? rawId : undefined;
|
|
150
|
+
// Retain only correlation identity — never the full /user profile.
|
|
151
|
+
const retainedRaw: { login: string; id?: number | string } = { login };
|
|
152
|
+
if (stableNumericOrStringId !== undefined) {
|
|
153
|
+
retainedRaw.id = stableNumericOrStringId;
|
|
154
|
+
}
|
|
155
|
+
const contentDigest = versionDigest({
|
|
156
|
+
login,
|
|
157
|
+
...(stableNumericOrStringId !== undefined ? { id: stableNumericOrStringId } : {}),
|
|
158
|
+
});
|
|
159
|
+
const versionId = `user:${login}:${contentDigest.slice(0, 12)}`;
|
|
160
|
+
return {
|
|
161
|
+
evidenceId: evidenceIdFor("authenticated_user", versionId),
|
|
162
|
+
kind: "authenticated_user",
|
|
163
|
+
stableGitHubId: stableId("user", login),
|
|
164
|
+
versionId,
|
|
165
|
+
contentDigest,
|
|
166
|
+
authorLogin: login,
|
|
167
|
+
firstObservedAt: observedAt,
|
|
168
|
+
raw: retainedRaw,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export function normalizePullRequestEvidence(
|
|
173
|
+
pr: GitHubPullRequest,
|
|
174
|
+
observedAt: string,
|
|
175
|
+
): CollectorEvidenceRecord {
|
|
176
|
+
const contentDigest = versionDigest({
|
|
177
|
+
number: pr.number,
|
|
178
|
+
state: pr.state,
|
|
179
|
+
headOid: pr.headOid,
|
|
180
|
+
updatedAt: pr.updatedAt ?? null,
|
|
181
|
+
htmlUrl: pr.url,
|
|
182
|
+
});
|
|
183
|
+
const versionId = `pr:${pr.number}:${contentDigest.slice(0, 12)}`;
|
|
184
|
+
return {
|
|
185
|
+
evidenceId: evidenceIdFor("pull_request", versionId),
|
|
186
|
+
kind: "pull_request",
|
|
187
|
+
stableGitHubId: stableId("pr", pr.number),
|
|
188
|
+
versionId,
|
|
189
|
+
contentDigest,
|
|
190
|
+
state: pr.state,
|
|
191
|
+
commitOid: pr.headOid,
|
|
192
|
+
htmlUrl: pr.url,
|
|
193
|
+
authoritativeTime: pr.updatedAt ?? null,
|
|
194
|
+
firstObservedAt: observedAt,
|
|
195
|
+
raw: pr.raw,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export function normalizeReviewEvidence(
|
|
200
|
+
review: GitHubReview,
|
|
201
|
+
observedAt: string,
|
|
202
|
+
): CollectorEvidenceRecord {
|
|
203
|
+
const authorLogin =
|
|
204
|
+
review.userLogin === null || review.userLogin === undefined
|
|
205
|
+
? undefined
|
|
206
|
+
: review.userLogin.toLowerCase();
|
|
207
|
+
const contentDigest = versionDigest({
|
|
208
|
+
id: review.id,
|
|
209
|
+
state: review.state,
|
|
210
|
+
body: review.body,
|
|
211
|
+
commitId: review.commitId,
|
|
212
|
+
submittedAt: review.submittedAt,
|
|
213
|
+
htmlUrl: review.htmlUrl,
|
|
214
|
+
userLogin: authorLogin ?? null,
|
|
215
|
+
machineIdentity: review.machineIdentity ?? null,
|
|
216
|
+
});
|
|
217
|
+
const versionId = `review:${review.id}:${contentDigest.slice(0, 12)}`;
|
|
218
|
+
return {
|
|
219
|
+
evidenceId: evidenceIdFor("review", versionId),
|
|
220
|
+
kind: "review",
|
|
221
|
+
stableGitHubId: stableId("review", review.id),
|
|
222
|
+
githubId: review.id,
|
|
223
|
+
machineIdentity: review.machineIdentity ?? null,
|
|
224
|
+
versionId,
|
|
225
|
+
contentDigest,
|
|
226
|
+
...(authorLogin === undefined ? {} : { authorLogin }),
|
|
227
|
+
state: review.state,
|
|
228
|
+
body: review.body,
|
|
229
|
+
commitOid: review.commitId,
|
|
230
|
+
htmlUrl: review.htmlUrl,
|
|
231
|
+
// Submission metadata only; ledger history assigns authoritativeTime.
|
|
232
|
+
submittedAt: review.submittedAt,
|
|
233
|
+
authoritativeTime: review.submittedAt,
|
|
234
|
+
firstObservedAt: observedAt,
|
|
235
|
+
raw: review.raw,
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export function normalizePullRequestReactionEvidence(
|
|
240
|
+
reaction: GitHubPullRequestReaction,
|
|
241
|
+
observedAt: string,
|
|
242
|
+
): CollectorEvidenceRecord {
|
|
243
|
+
const authorLogin = reaction.userLogin?.toLowerCase();
|
|
244
|
+
const contentDigest = versionDigest({
|
|
245
|
+
id: reaction.id,
|
|
246
|
+
content: reaction.content,
|
|
247
|
+
createdAt: reaction.createdAt,
|
|
248
|
+
machineIdentity: reaction.machineIdentity ?? null,
|
|
249
|
+
});
|
|
250
|
+
const versionId = `reaction:${reaction.id}:${contentDigest.slice(0, 12)}`;
|
|
251
|
+
return {
|
|
252
|
+
evidenceId: evidenceIdFor("reaction", versionId),
|
|
253
|
+
kind: "reaction",
|
|
254
|
+
stableGitHubId: stableId("reaction", reaction.id),
|
|
255
|
+
githubId: reaction.id,
|
|
256
|
+
machineIdentity: reaction.machineIdentity ?? null,
|
|
257
|
+
versionId,
|
|
258
|
+
contentDigest,
|
|
259
|
+
...(authorLogin === undefined ? {} : { authorLogin }),
|
|
260
|
+
body: reaction.content,
|
|
261
|
+
authoritativeTime: reaction.createdAt,
|
|
262
|
+
firstObservedAt: observedAt,
|
|
263
|
+
raw: reaction.raw,
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export function normalizeIssueCommentEvidence(
|
|
268
|
+
comment: GitHubIssueComment,
|
|
269
|
+
observedAt: string,
|
|
270
|
+
): CollectorEvidenceRecord {
|
|
271
|
+
const authorLogin =
|
|
272
|
+
comment.userLogin === null || comment.userLogin === undefined
|
|
273
|
+
? undefined
|
|
274
|
+
: comment.userLogin.toLowerCase();
|
|
275
|
+
const contentDigest = versionDigest({
|
|
276
|
+
id: comment.id,
|
|
277
|
+
body: comment.body,
|
|
278
|
+
updatedAt: comment.updatedAt,
|
|
279
|
+
userLogin: authorLogin ?? null,
|
|
280
|
+
machineIdentity: comment.machineIdentity ?? null,
|
|
281
|
+
htmlUrl: comment.htmlUrl,
|
|
282
|
+
});
|
|
283
|
+
const versionId = `issue_comment:${comment.id}:${contentDigest.slice(0, 12)}`;
|
|
284
|
+
return {
|
|
285
|
+
evidenceId: evidenceIdFor("issue_comment", versionId),
|
|
286
|
+
kind: "issue_comment",
|
|
287
|
+
stableGitHubId: stableId("issue_comment", comment.id),
|
|
288
|
+
githubId: comment.id,
|
|
289
|
+
machineIdentity: comment.machineIdentity ?? null,
|
|
290
|
+
versionId,
|
|
291
|
+
contentDigest,
|
|
292
|
+
...(authorLogin === undefined ? {} : { authorLogin }),
|
|
293
|
+
body: comment.body,
|
|
294
|
+
htmlUrl: comment.htmlUrl,
|
|
295
|
+
authoritativeTime: comment.updatedAt ?? null,
|
|
296
|
+
firstObservedAt: observedAt,
|
|
297
|
+
raw: comment.raw,
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
export function normalizeReviewCommentEvidence(
|
|
302
|
+
comment: GitHubReviewComment,
|
|
303
|
+
observedAt: string,
|
|
304
|
+
): CollectorEvidenceRecord {
|
|
305
|
+
const authorLogin =
|
|
306
|
+
comment.userLogin === null || comment.userLogin === undefined
|
|
307
|
+
? undefined
|
|
308
|
+
: comment.userLogin.toLowerCase();
|
|
309
|
+
const contentDigest = versionDigest({
|
|
310
|
+
id: comment.id,
|
|
311
|
+
body: comment.body,
|
|
312
|
+
path: comment.path,
|
|
313
|
+
line: comment.line,
|
|
314
|
+
originalLine: comment.originalLine,
|
|
315
|
+
side: comment.side,
|
|
316
|
+
position: comment.position,
|
|
317
|
+
updatedAt: comment.updatedAt,
|
|
318
|
+
commitId: comment.commitId,
|
|
319
|
+
pullRequestReviewId: comment.pullRequestReviewId,
|
|
320
|
+
userLogin: authorLogin ?? null,
|
|
321
|
+
machineIdentity: comment.machineIdentity ?? null,
|
|
322
|
+
htmlUrl: comment.htmlUrl,
|
|
323
|
+
});
|
|
324
|
+
const versionId = `review_comment:${comment.id}:${contentDigest.slice(0, 12)}`;
|
|
325
|
+
return {
|
|
326
|
+
evidenceId: evidenceIdFor("review_comment", versionId),
|
|
327
|
+
kind: "review_comment",
|
|
328
|
+
stableGitHubId: stableId("review_comment", comment.id),
|
|
329
|
+
githubId: comment.id,
|
|
330
|
+
machineIdentity: comment.machineIdentity ?? null,
|
|
331
|
+
versionId,
|
|
332
|
+
contentDigest,
|
|
333
|
+
...(authorLogin === undefined ? {} : { authorLogin }),
|
|
334
|
+
body: comment.body,
|
|
335
|
+
commitOid: comment.commitId,
|
|
336
|
+
htmlUrl: comment.htmlUrl,
|
|
337
|
+
path: comment.path,
|
|
338
|
+
line: comment.line,
|
|
339
|
+
originalLine: comment.originalLine,
|
|
340
|
+
side: comment.side,
|
|
341
|
+
position: comment.position,
|
|
342
|
+
pullRequestReviewId: comment.pullRequestReviewId,
|
|
343
|
+
authoritativeTime: comment.updatedAt ?? null,
|
|
344
|
+
firstObservedAt: observedAt,
|
|
345
|
+
raw: comment.raw,
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Ledger history step: first distinct review version may keep submitted_at
|
|
351
|
+
* only when surface-completion mono is at or before deadlineMono; after-deadline
|
|
352
|
+
* first sightings and non-finite mono fail closed to null (no submitted_at
|
|
353
|
+
* backdate). Wall firstObservedAt is receipt/metadata only. Every later distinct
|
|
354
|
+
* review version without a GitHub version timestamp is uncertain. Known
|
|
355
|
+
* versionIds reuse the stored authoritative clock including null. Comments keep
|
|
356
|
+
* their own updated_at per version (null if missing).
|
|
357
|
+
*/
|
|
358
|
+
export function applyEvidenceVersionHistory(
|
|
359
|
+
pending: CollectorEvidenceRecord[],
|
|
360
|
+
priorEvidence: readonly CollectorEvidenceRecord[],
|
|
361
|
+
cutoff: {
|
|
362
|
+
deadlineMono: number;
|
|
363
|
+
firstObservedMono: number;
|
|
364
|
+
},
|
|
365
|
+
): void {
|
|
366
|
+
const versionsByStable = new Map<string, Set<string>>();
|
|
367
|
+
const add = (stableIdValue: string, versionId: string) => {
|
|
368
|
+
let set = versionsByStable.get(stableIdValue);
|
|
369
|
+
if (set === undefined) {
|
|
370
|
+
set = new Set<string>();
|
|
371
|
+
versionsByStable.set(stableIdValue, set);
|
|
372
|
+
}
|
|
373
|
+
set.add(versionId);
|
|
374
|
+
};
|
|
375
|
+
for (const record of priorEvidence) {
|
|
376
|
+
if (record.stableGitHubId !== undefined) {
|
|
377
|
+
add(record.stableGitHubId, record.versionId);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
const priorByVersionId = new Map<string, CollectorEvidenceRecord>();
|
|
381
|
+
for (const record of priorEvidence) {
|
|
382
|
+
priorByVersionId.set(record.versionId, record);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
for (const record of pending) {
|
|
386
|
+
if (record.stableGitHubId === undefined) continue;
|
|
387
|
+
const priorVersions = versionsByStable.get(record.stableGitHubId) ?? new Set<string>();
|
|
388
|
+
const isNewVersion = !priorVersions.has(record.versionId);
|
|
389
|
+
const hadEarlierDistinctVersion = [...priorVersions].some(
|
|
390
|
+
(versionId) => versionId !== record.versionId,
|
|
391
|
+
);
|
|
392
|
+
|
|
393
|
+
if (record.kind === "review") {
|
|
394
|
+
if (!isNewVersion) {
|
|
395
|
+
// Known versionId must keep the previously stored authoritative clock.
|
|
396
|
+
const prior = priorByVersionId.get(record.versionId);
|
|
397
|
+
if (prior !== undefined) {
|
|
398
|
+
record.authoritativeTime = prior.authoritativeTime ?? null;
|
|
399
|
+
}
|
|
400
|
+
} else if (hadEarlierDistinctVersion) {
|
|
401
|
+
// Reviews have no GitHub-supplied per-edit timestamp.
|
|
402
|
+
record.authoritativeTime = null;
|
|
403
|
+
} else {
|
|
404
|
+
// First submission version may use submitted_at only when surface
|
|
405
|
+
// completion mono is at/before deadlineMono; after-cutoff first
|
|
406
|
+
// sighting must not backdate via submitted_at. Wall firstObservedAt
|
|
407
|
+
// is metadata only and must not gate trust.
|
|
408
|
+
const { deadlineMono, firstObservedMono } = cutoff;
|
|
409
|
+
if (
|
|
410
|
+
!Number.isFinite(deadlineMono) ||
|
|
411
|
+
!Number.isFinite(firstObservedMono) ||
|
|
412
|
+
firstObservedMono > deadlineMono
|
|
413
|
+
) {
|
|
414
|
+
record.authoritativeTime = null;
|
|
415
|
+
} else {
|
|
416
|
+
record.authoritativeTime = record.submittedAt ?? null;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
} else if (
|
|
420
|
+
record.kind === "issue_comment" ||
|
|
421
|
+
record.kind === "review_comment"
|
|
422
|
+
) {
|
|
423
|
+
if (
|
|
424
|
+
record.authoritativeTime === undefined ||
|
|
425
|
+
record.authoritativeTime === ""
|
|
426
|
+
) {
|
|
427
|
+
record.authoritativeTime = null;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
if (isNewVersion) {
|
|
432
|
+
add(record.stableGitHubId, record.versionId);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
export function measureNormalizedBytes(records: readonly CollectorEvidenceRecord[]): number {
|
|
438
|
+
return Buffer.byteLength(JSON.stringify(records), "utf8");
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
export function assignWindowRelations(
|
|
442
|
+
records: CollectorEvidenceRecord[],
|
|
443
|
+
activationTime: Date | undefined,
|
|
444
|
+
deadlineTime: Date | undefined,
|
|
445
|
+
): void {
|
|
446
|
+
if (activationTime === undefined || deadlineTime === undefined) return;
|
|
447
|
+
for (const record of records) {
|
|
448
|
+
if (
|
|
449
|
+
record.kind === "review" ||
|
|
450
|
+
record.kind === "issue_comment" ||
|
|
451
|
+
record.kind === "review_comment" ||
|
|
452
|
+
record.kind === "pull_request"
|
|
453
|
+
) {
|
|
454
|
+
record.windowRelation = computeWindowRelation(
|
|
455
|
+
record.authoritativeTime,
|
|
456
|
+
activationTime,
|
|
457
|
+
deadlineTime,
|
|
458
|
+
);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
}
|