@itsthelore/proofkeeper 2026.6.0

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 (156) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +10 -0
  3. package/README.md +207 -0
  4. package/dist/agent/adapters/claude.d.ts +93 -0
  5. package/dist/agent/adapters/claude.d.ts.map +1 -0
  6. package/dist/agent/adapters/claude.js +96 -0
  7. package/dist/agent/adapters/claude.js.map +1 -0
  8. package/dist/agent/drive.d.ts +53 -0
  9. package/dist/agent/drive.d.ts.map +1 -0
  10. package/dist/agent/drive.js +194 -0
  11. package/dist/agent/drive.js.map +1 -0
  12. package/dist/agent/loop.d.ts +40 -0
  13. package/dist/agent/loop.d.ts.map +1 -0
  14. package/dist/agent/loop.js +29 -0
  15. package/dist/agent/loop.js.map +1 -0
  16. package/dist/agent/model.d.ts +43 -0
  17. package/dist/agent/model.d.ts.map +1 -0
  18. package/dist/agent/model.js +10 -0
  19. package/dist/agent/model.js.map +1 -0
  20. package/dist/agent/observe.d.ts +48 -0
  21. package/dist/agent/observe.d.ts.map +1 -0
  22. package/dist/agent/observe.js +65 -0
  23. package/dist/agent/observe.js.map +1 -0
  24. package/dist/agent/tools.d.ts +74 -0
  25. package/dist/agent/tools.d.ts.map +1 -0
  26. package/dist/agent/tools.js +257 -0
  27. package/dist/agent/tools.js.map +1 -0
  28. package/dist/cli.d.ts +61 -0
  29. package/dist/cli.d.ts.map +1 -0
  30. package/dist/cli.js +648 -0
  31. package/dist/cli.js.map +1 -0
  32. package/dist/compiler/actions.d.ts +101 -0
  33. package/dist/compiler/actions.d.ts.map +1 -0
  34. package/dist/compiler/actions.js +13 -0
  35. package/dist/compiler/actions.js.map +1 -0
  36. package/dist/compiler/compiler.d.ts +25 -0
  37. package/dist/compiler/compiler.d.ts.map +1 -0
  38. package/dist/compiler/compiler.js +42 -0
  39. package/dist/compiler/compiler.js.map +1 -0
  40. package/dist/compiler/emit.d.ts +21 -0
  41. package/dist/compiler/emit.d.ts.map +1 -0
  42. package/dist/compiler/emit.js +164 -0
  43. package/dist/compiler/emit.js.map +1 -0
  44. package/dist/compiler/http.d.ts +30 -0
  45. package/dist/compiler/http.d.ts.map +1 -0
  46. package/dist/compiler/http.js +30 -0
  47. package/dist/compiler/http.js.map +1 -0
  48. package/dist/compiler/recorder.d.ts +62 -0
  49. package/dist/compiler/recorder.d.ts.map +1 -0
  50. package/dist/compiler/recorder.js +148 -0
  51. package/dist/compiler/recorder.js.map +1 -0
  52. package/dist/compiler/summary.d.ts +11 -0
  53. package/dist/compiler/summary.d.ts.map +1 -0
  54. package/dist/compiler/summary.js +56 -0
  55. package/dist/compiler/summary.js.map +1 -0
  56. package/dist/compiler/terminal.d.ts +42 -0
  57. package/dist/compiler/terminal.d.ts.map +1 -0
  58. package/dist/compiler/terminal.js +47 -0
  59. package/dist/compiler/terminal.js.map +1 -0
  60. package/dist/compiler/types.d.ts +25 -0
  61. package/dist/compiler/types.d.ts.map +1 -0
  62. package/dist/compiler/types.js +10 -0
  63. package/dist/compiler/types.js.map +1 -0
  64. package/dist/coverage/graph.d.ts +55 -0
  65. package/dist/coverage/graph.d.ts.map +1 -0
  66. package/dist/coverage/graph.js +87 -0
  67. package/dist/coverage/graph.js.map +1 -0
  68. package/dist/coverage/model.d.ts +36 -0
  69. package/dist/coverage/model.d.ts.map +1 -0
  70. package/dist/coverage/model.js +57 -0
  71. package/dist/coverage/model.js.map +1 -0
  72. package/dist/coverage/report.d.ts +27 -0
  73. package/dist/coverage/report.d.ts.map +1 -0
  74. package/dist/coverage/report.js +45 -0
  75. package/dist/coverage/report.js.map +1 -0
  76. package/dist/coverage/source.d.ts +23 -0
  77. package/dist/coverage/source.d.ts.map +1 -0
  78. package/dist/coverage/source.js +48 -0
  79. package/dist/coverage/source.js.map +1 -0
  80. package/dist/fidelity/gate.d.ts +34 -0
  81. package/dist/fidelity/gate.d.ts.map +1 -0
  82. package/dist/fidelity/gate.js +38 -0
  83. package/dist/fidelity/gate.js.map +1 -0
  84. package/dist/index.d.ts +69 -0
  85. package/dist/index.d.ts.map +1 -0
  86. package/dist/index.js +49 -0
  87. package/dist/index.js.map +1 -0
  88. package/dist/learning/store.d.ts +44 -0
  89. package/dist/learning/store.d.ts.map +1 -0
  90. package/dist/learning/store.js +64 -0
  91. package/dist/learning/store.js.map +1 -0
  92. package/dist/qa/concurrency.d.ts +7 -0
  93. package/dist/qa/concurrency.d.ts.map +1 -0
  94. package/dist/qa/concurrency.js +21 -0
  95. package/dist/qa/concurrency.js.map +1 -0
  96. package/dist/qa/run-qa.d.ts +87 -0
  97. package/dist/qa/run-qa.d.ts.map +1 -0
  98. package/dist/qa/run-qa.js +106 -0
  99. package/dist/qa/run-qa.js.map +1 -0
  100. package/dist/qa/run-scoped.d.ts +82 -0
  101. package/dist/qa/run-scoped.d.ts.map +1 -0
  102. package/dist/qa/run-scoped.js +96 -0
  103. package/dist/qa/run-scoped.js.map +1 -0
  104. package/dist/runner/playwright-report.d.ts +52 -0
  105. package/dist/runner/playwright-report.d.ts.map +1 -0
  106. package/dist/runner/playwright-report.js +90 -0
  107. package/dist/runner/playwright-report.js.map +1 -0
  108. package/dist/runner/playwright-runner.d.ts +38 -0
  109. package/dist/runner/playwright-runner.d.ts.map +1 -0
  110. package/dist/runner/playwright-runner.js +73 -0
  111. package/dist/runner/playwright-runner.js.map +1 -0
  112. package/dist/runner/types.d.ts +45 -0
  113. package/dist/runner/types.d.ts.map +1 -0
  114. package/dist/runner/types.js +10 -0
  115. package/dist/runner/types.js.map +1 -0
  116. package/dist/scaffold/scaffold.d.ts +22 -0
  117. package/dist/scaffold/scaffold.d.ts.map +1 -0
  118. package/dist/scaffold/scaffold.js +34 -0
  119. package/dist/scaffold/scaffold.js.map +1 -0
  120. package/dist/scope/config.d.ts +89 -0
  121. package/dist/scope/config.d.ts.map +1 -0
  122. package/dist/scope/config.js +172 -0
  123. package/dist/scope/config.js.map +1 -0
  124. package/dist/scope/diff-scope.d.ts +31 -0
  125. package/dist/scope/diff-scope.d.ts.map +1 -0
  126. package/dist/scope/diff-scope.js +42 -0
  127. package/dist/scope/diff-scope.js.map +1 -0
  128. package/dist/scope/glob.d.ts +17 -0
  129. package/dist/scope/glob.d.ts.map +1 -0
  130. package/dist/scope/glob.js +50 -0
  131. package/dist/scope/glob.js.map +1 -0
  132. package/dist/writeback/comment.d.ts +103 -0
  133. package/dist/writeback/comment.d.ts.map +1 -0
  134. package/dist/writeback/comment.js +150 -0
  135. package/dist/writeback/comment.js.map +1 -0
  136. package/dist/writeback/gateways/github-rest.d.ts +66 -0
  137. package/dist/writeback/gateways/github-rest.d.ts.map +1 -0
  138. package/dist/writeback/gateways/github-rest.js +107 -0
  139. package/dist/writeback/gateways/github-rest.js.map +1 -0
  140. package/dist/writeback/merge.d.ts +27 -0
  141. package/dist/writeback/merge.d.ts.map +1 -0
  142. package/dist/writeback/merge.js +89 -0
  143. package/dist/writeback/merge.js.map +1 -0
  144. package/dist/writeback/proposal.d.ts +52 -0
  145. package/dist/writeback/proposal.d.ts.map +1 -0
  146. package/dist/writeback/proposal.js +79 -0
  147. package/dist/writeback/proposal.js.map +1 -0
  148. package/dist/writeback/proposer.d.ts +94 -0
  149. package/dist/writeback/proposer.d.ts.map +1 -0
  150. package/dist/writeback/proposer.js +79 -0
  151. package/dist/writeback/proposer.js.map +1 -0
  152. package/dist/writeback/verified-by.d.ts +56 -0
  153. package/dist/writeback/verified-by.d.ts.map +1 -0
  154. package/dist/writeback/verified-by.js +60 -0
  155. package/dist/writeback/verified-by.js.map +1 -0
  156. package/package.json +62 -0
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Pull-request comment renderers — Proofkeeper Initiative 5.
3
+ *
4
+ * Two informational comments, both behind the human-review boundary (ADR-065):
5
+ * Proofkeeper never merges or approves; it only reports.
6
+ *
7
+ * - {@link renderWriteBackComment}: a confirmation on the write-back PR it
8
+ * raises — the linked evidence plus the fidelity result.
9
+ * - {@link renderCoverageComment}: a verification-coverage status for a
10
+ * developer's feature PR, driven by the coverage read-model.
11
+ *
12
+ * Pure string builders (no I/O) so they are unit-testable; the gateway posts.
13
+ */
14
+ const REVIEW_NOTE = "_Informational — a human reviewer accepts the link (ADR-065). Proofkeeper produces and runs the evidence; it does not merge or approve._";
15
+ /**
16
+ * Hidden, namespaced markers identifying each Proofkeeper comment kind. Embedded
17
+ * as invisible HTML comments at the top of a body so a pull request carries one
18
+ * canonical comment per kind that {@link upsertComment} finds and updates in
19
+ * place, regardless of how the prose is reformatted.
20
+ */
21
+ export const SCOPED_QA_MARKER = "<!-- proofkeeper:scoped-qa -->";
22
+ export const WRITE_BACK_MARKER = "<!-- proofkeeper:write-back -->";
23
+ export const COVERAGE_MARKER = "<!-- proofkeeper:coverage -->";
24
+ function linkLine(link) {
25
+ return `- \`${link.test}\`${link.trace ? ` (trace: \`${link.trace}\`)` : ""}`;
26
+ }
27
+ /** Confirmation comment for the write-back PR Proofkeeper raises. */
28
+ export function renderWriteBackComment(input) {
29
+ const lines = [
30
+ WRITE_BACK_MARKER,
31
+ `Proofkeeper recorded verification for **${input.capabilityId}**.`,
32
+ "",
33
+ "Linked evidence:",
34
+ ...input.links.map(linkLine),
35
+ ];
36
+ if (input.plan && input.plan.trim().length > 0) {
37
+ lines.push("", "Test plan:", "", input.plan.trim());
38
+ }
39
+ if (input.steps && input.steps.length > 0) {
40
+ lines.push("", "Steps exercised:");
41
+ input.steps.forEach((s, i) => lines.push(`${i + 1}. ${s}`));
42
+ }
43
+ const trace = input.links.find((l) => l.trace)?.trace;
44
+ if (trace) {
45
+ lines.push("", `Replay the trace locally: \`npx playwright show-trace ${trace}\``);
46
+ }
47
+ if (input.fidelity) {
48
+ const f = input.fidelity;
49
+ lines.push("", `Fidelity gate: ${f.passed}/${f.attempts} re-runs green — ${f.stable ? "stable, safe to commit" : "unstable, quarantined"}.`);
50
+ }
51
+ lines.push("", REVIEW_NOTE);
52
+ return lines.join("\n");
53
+ }
54
+ /** Verification-coverage status comment for a developer's feature PR. */
55
+ export function renderCoverageComment(report, options = {}) {
56
+ const heading = options.title ?? "Proofkeeper verification coverage";
57
+ const src = report.source ? ` for \`${report.source}\`` : "";
58
+ const lines = [
59
+ COVERAGE_MARKER,
60
+ `## ${heading}${src}`,
61
+ "",
62
+ `${report.verified.length}/${report.total} capabilities have a verifying test; ${report.unverified.length} unverified.`,
63
+ ];
64
+ if (report.verified.length > 0) {
65
+ lines.push("", "Verified by committed tests:");
66
+ for (const c of report.verified) {
67
+ // Bare paths backtick cleanly; only wrap a target that has no backtick of
68
+ // its own, so a legacy verbose target is shown verbatim rather than nested.
69
+ const targets = c.verifiedBy.map((t) => (t.includes("`") ? t : `\`${t}\``)).join(", ");
70
+ lines.push(`- **${c.id}** — ${c.title}: ${targets}`);
71
+ }
72
+ }
73
+ if (report.unverified.length > 0) {
74
+ lines.push("", "Still unverified:");
75
+ for (const c of report.unverified) {
76
+ lines.push(`- ${c.id} — ${c.title}`);
77
+ }
78
+ }
79
+ lines.push("", REVIEW_NOTE);
80
+ return lines.join("\n");
81
+ }
82
+ /** Scoped-QA evidence comment for the feature PR that triggered the run. */
83
+ export function renderScopedQaComment(input) {
84
+ const lines = [
85
+ SCOPED_QA_MARKER,
86
+ `## Proofkeeper QA — ${input.changedCount} changed file(s)`,
87
+ "",
88
+ input.driven.length > 0
89
+ ? `Drove ${input.driven.length} capability(ies) this change touched:`
90
+ : "No unverified capabilities were touched by this change.",
91
+ ];
92
+ for (const r of input.driven) {
93
+ if (r.error) {
94
+ lines.push(`- ⚠️ **${r.id}** — ${r.title}: ${r.error}`);
95
+ }
96
+ else if (r.stable) {
97
+ lines.push(`- ✅ **${r.id}** — ${r.title}: stable${r.writeBackUrl ? ` — proposed ${r.writeBackUrl}` : ""}`);
98
+ }
99
+ else {
100
+ lines.push(`- ❌ **${r.id}** — ${r.title}: unstable (quarantined)`);
101
+ }
102
+ }
103
+ if (input.alreadyVerified.length > 0) {
104
+ lines.push("", "Already verified, not re-driven:");
105
+ for (const c of input.alreadyVerified)
106
+ lines.push(`- ${c.id} — ${c.title}`);
107
+ }
108
+ if (input.unknown.length > 0) {
109
+ lines.push("", `Config ids not found as capabilities in the graph: ${input.unknown.join(", ")}`);
110
+ }
111
+ if (input.failureSuggestions && input.failureSuggestions.length > 0) {
112
+ lines.push("", "Known failure modes:");
113
+ for (const s of input.failureSuggestions) {
114
+ lines.push(`- **${s.id}** — ${s.title}:`);
115
+ for (const reason of s.reasons)
116
+ lines.push(` - ${reason}`);
117
+ }
118
+ }
119
+ lines.push("", REVIEW_NOTE);
120
+ return lines.join("\n");
121
+ }
122
+ /**
123
+ * Maintain exactly one comment per kind on a pull request: find the existing
124
+ * comment whose body carries `marker` and update it in place; otherwise create
125
+ * one (the body itself carries the marker, so it is self-identifying next time).
126
+ * Keys only on the marker, never on prose.
127
+ */
128
+ export async function upsertComment(gateway, input) {
129
+ const existing = await gateway.listComments(input.number);
130
+ const match = existing.find((c) => c.body.includes(input.marker));
131
+ if (match) {
132
+ const updated = await gateway.updateComment(match.id, input.body);
133
+ return { url: updated.url, updated: true };
134
+ }
135
+ const created = await gateway.commentOnPullRequest({ number: input.number, body: input.body });
136
+ return { url: created.url, updated: false };
137
+ }
138
+ /**
139
+ * Post (or update in place) a verification-coverage status comment on a feature
140
+ * pull request. The report comes from the coverage read-model (`computeCoverage`).
141
+ * Informational only — it never approves or merges.
142
+ */
143
+ export function commentCoverageStatus(gateway, input) {
144
+ return upsertComment(gateway, {
145
+ number: input.prNumber,
146
+ marker: COVERAGE_MARKER,
147
+ body: renderCoverageComment(input.report, input.options ?? {}),
148
+ });
149
+ }
150
+ //# sourceMappingURL=comment.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"comment.js","sourceRoot":"","sources":["../../src/writeback/comment.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * A concrete {@link RepoGateway} over the GitHub REST API — Proofkeeper
3
+ * Initiative 5.
4
+ *
5
+ * Dependency-free: it uses the global `fetch` (Node ≥ 20), so wiring the
6
+ * write-back to GitHub needs no SDK. `fetch` is injectable for testing. A token
7
+ * with `contents:write` + `pull_requests:write` on the target repo is required.
8
+ *
9
+ * It only ever branches, commits to that branch, and opens a pull request — it
10
+ * never writes the base branch (the proposer relies on that; this gateway
11
+ * exposes no base-write path either). The trust boundary stays human PR review
12
+ * (ADR-065).
13
+ */
14
+ import type { RepoGateway } from "../proposer.js";
15
+ type FetchLike = (url: string, init?: RequestInit) => Promise<Response>;
16
+ export interface GitHubRestGatewayOptions {
17
+ owner: string;
18
+ repo: string;
19
+ /** Token with contents:write and pull_requests:write on the repo. */
20
+ token: string;
21
+ /** Override for testing or GitHub Enterprise. Defaults to api.github.com. */
22
+ fetch?: FetchLike;
23
+ baseUrl?: string;
24
+ }
25
+ export declare class GitHubRestGateway implements RepoGateway {
26
+ private readonly owner;
27
+ private readonly repo;
28
+ private readonly token;
29
+ private readonly fetchImpl;
30
+ private readonly baseUrl;
31
+ constructor(options: GitHubRestGatewayOptions);
32
+ private request;
33
+ private repoPath;
34
+ getFileContent(path: string, ref: string): Promise<string>;
35
+ createBranch(name: string, fromRef: string): Promise<void>;
36
+ commitFile(input: {
37
+ branch: string;
38
+ path: string;
39
+ content: string;
40
+ message: string;
41
+ }): Promise<void>;
42
+ openPullRequest(input: {
43
+ base: string;
44
+ head: string;
45
+ title: string;
46
+ body: string;
47
+ }): Promise<{
48
+ url: string;
49
+ number: number;
50
+ }>;
51
+ commentOnPullRequest(input: {
52
+ number: number;
53
+ body: string;
54
+ }): Promise<{
55
+ url: string;
56
+ }>;
57
+ listComments(prNumber: number): Promise<{
58
+ id: number;
59
+ body: string;
60
+ }[]>;
61
+ updateComment(commentId: number, body: string): Promise<{
62
+ url: string;
63
+ }>;
64
+ }
65
+ export {};
66
+ //# sourceMappingURL=github-rest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github-rest.d.ts","sourceRoot":"","sources":["../../../src/writeback/gateways/github-rest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAElD,KAAK,SAAS,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AAExE,MAAM,WAAW,wBAAwB;IACvC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,qEAAqE;IACrE,KAAK,EAAE,MAAM,CAAC;IACd,6EAA6E;IAC7E,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,iBAAkB,YAAW,WAAW;IACnD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;IACtC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAErB,OAAO,EAAE,wBAAwB;YAW/B,OAAO;IAiBrB,OAAO,CAAC,QAAQ;IAIV,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAW1D,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAU1D,UAAU,CAAC,KAAK,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAoBpG,eAAe,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QACjG,GAAG,EAAE,MAAM,CAAC;QACZ,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;IAUI,oBAAoB,CAAC,KAAK,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IAQvF,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAQvE,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;CAM/E"}
@@ -0,0 +1,107 @@
1
+ /**
2
+ * A concrete {@link RepoGateway} over the GitHub REST API — Proofkeeper
3
+ * Initiative 5.
4
+ *
5
+ * Dependency-free: it uses the global `fetch` (Node ≥ 20), so wiring the
6
+ * write-back to GitHub needs no SDK. `fetch` is injectable for testing. A token
7
+ * with `contents:write` + `pull_requests:write` on the target repo is required.
8
+ *
9
+ * It only ever branches, commits to that branch, and opens a pull request — it
10
+ * never writes the base branch (the proposer relies on that; this gateway
11
+ * exposes no base-write path either). The trust boundary stays human PR review
12
+ * (ADR-065).
13
+ */
14
+ export class GitHubRestGateway {
15
+ owner;
16
+ repo;
17
+ token;
18
+ fetchImpl;
19
+ baseUrl;
20
+ constructor(options) {
21
+ this.owner = options.owner;
22
+ this.repo = options.repo;
23
+ this.token = options.token;
24
+ this.fetchImpl = options.fetch ?? globalThis.fetch;
25
+ this.baseUrl = options.baseUrl ?? "https://api.github.com";
26
+ if (!this.fetchImpl) {
27
+ throw new Error("GitHubRestGateway needs a fetch implementation (Node >= 20 or inject one).");
28
+ }
29
+ }
30
+ async request(method, path, body) {
31
+ const res = await this.fetchImpl(`${this.baseUrl}${path}`, {
32
+ method,
33
+ headers: {
34
+ authorization: `Bearer ${this.token}`,
35
+ accept: "application/vnd.github+json",
36
+ "x-github-api-version": "2022-11-28",
37
+ ...(body ? { "content-type": "application/json" } : {}),
38
+ },
39
+ ...(body ? { body: JSON.stringify(body) } : {}),
40
+ });
41
+ if (!res.ok) {
42
+ throw new Error(`GitHub ${method} ${path} failed: ${res.status} ${await res.text()}`);
43
+ }
44
+ return res.status === 204 ? undefined : (await res.json());
45
+ }
46
+ repoPath(suffix) {
47
+ return `/repos/${this.owner}/${this.repo}${suffix}`;
48
+ }
49
+ async getFileContent(path, ref) {
50
+ const data = (await this.request("GET", this.repoPath(`/contents/${encodeURIComponent(path).replace(/%2F/g, "/")}?ref=${encodeURIComponent(ref)}`)));
51
+ if (typeof data.content !== "string") {
52
+ throw new Error(`no file content at ${path}@${ref}`);
53
+ }
54
+ return Buffer.from(data.content, data.encoding ?? "base64").toString("utf8");
55
+ }
56
+ async createBranch(name, fromRef) {
57
+ const ref = (await this.request("GET", this.repoPath(`/git/ref/heads/${fromRef}`)));
58
+ await this.request("POST", this.repoPath("/git/refs"), {
59
+ ref: `refs/heads/${name}`,
60
+ sha: ref.object.sha,
61
+ });
62
+ }
63
+ async commitFile(input) {
64
+ // Look up the file's blob sha on the branch (required to update an existing file).
65
+ let sha;
66
+ try {
67
+ const existing = (await this.request("GET", this.repoPath(`/contents/${encodeURIComponent(input.path).replace(/%2F/g, "/")}?ref=${encodeURIComponent(input.branch)}`)));
68
+ sha = existing.sha;
69
+ }
70
+ catch {
71
+ sha = undefined; // new file
72
+ }
73
+ await this.request("PUT", this.repoPath(`/contents/${encodeURIComponent(input.path).replace(/%2F/g, "/")}`), {
74
+ message: input.message,
75
+ content: Buffer.from(input.content, "utf8").toString("base64"),
76
+ branch: input.branch,
77
+ ...(sha ? { sha } : {}),
78
+ });
79
+ }
80
+ async openPullRequest(input) {
81
+ const pr = (await this.request("POST", this.repoPath("/pulls"), {
82
+ title: input.title,
83
+ head: input.head,
84
+ base: input.base,
85
+ body: input.body,
86
+ }));
87
+ return { url: pr.html_url, number: pr.number };
88
+ }
89
+ async commentOnPullRequest(input) {
90
+ // PR comments are issue comments in the GitHub REST API.
91
+ const comment = (await this.request("POST", this.repoPath(`/issues/${input.number}/comments`), {
92
+ body: input.body,
93
+ }));
94
+ return { url: comment.html_url };
95
+ }
96
+ async listComments(prNumber) {
97
+ const data = (await this.request("GET", this.repoPath(`/issues/${prNumber}/comments?per_page=100`)));
98
+ return data.map((c) => ({ id: c.id, body: c.body ?? "" }));
99
+ }
100
+ async updateComment(commentId, body) {
101
+ const comment = (await this.request("PATCH", this.repoPath(`/issues/comments/${commentId}`), {
102
+ body,
103
+ }));
104
+ return { url: comment.html_url };
105
+ }
106
+ }
107
+ //# sourceMappingURL=github-rest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github-rest.js","sourceRoot":"","sources":["../../../src/writeback/gateways/github-rest.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Merge a `## Verified By` section into a requirement artifact's Markdown —
3
+ * Proofkeeper Initiative 5 (the write-back transform).
4
+ *
5
+ * Pure and idempotent: given the artifact's current content and a set of
6
+ * {@link VerificationLink}s, return the updated content with the links recorded
7
+ * under `## Verified By`. Re-running with the same links is a no-op (the diff is
8
+ * empty), which is what lets the proposer skip opening an empty pull request.
9
+ *
10
+ * Placement follows the engine's relationship-section ordering (ADR-084):
11
+ * `## Verified By` is the last relationship section, so it is inserted directly
12
+ * after the trailing `## Related *` block (or `## Supersedes`), and otherwise
13
+ * appended. An existing section is merged in place, preserving its lines.
14
+ *
15
+ * This is a pure transform. It never writes a file or touches a corpus — the
16
+ * proposer carries the result into a human-reviewed pull request (ADR-065).
17
+ */
18
+ import { type VerificationLink } from "./verified-by.js";
19
+ /**
20
+ * Merge `links` into the `## Verified By` section of `content`. Each link
21
+ * contributes its bare test reference and, when present, its bare trace
22
+ * reference (both are external-target references, ADR-084).
23
+ *
24
+ * @throws {Error} when `links` is empty — an empty section is never written.
25
+ */
26
+ export declare function mergeVerifiedBy(content: string, links: VerificationLink[]): string;
27
+ //# sourceMappingURL=merge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge.d.ts","sourceRoot":"","sources":["../../src/writeback/merge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAIL,KAAK,gBAAgB,EACtB,MAAM,kBAAkB,CAAC;AA4B1B;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,gBAAgB,EAAE,GAAG,MAAM,CAkDlF"}
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Merge a `## Verified By` section into a requirement artifact's Markdown —
3
+ * Proofkeeper Initiative 5 (the write-back transform).
4
+ *
5
+ * Pure and idempotent: given the artifact's current content and a set of
6
+ * {@link VerificationLink}s, return the updated content with the links recorded
7
+ * under `## Verified By`. Re-running with the same links is a no-op (the diff is
8
+ * empty), which is what lets the proposer skip opening an empty pull request.
9
+ *
10
+ * Placement follows the engine's relationship-section ordering (ADR-084):
11
+ * `## Verified By` is the last relationship section, so it is inserted directly
12
+ * after the trailing `## Related *` block (or `## Supersedes`), and otherwise
13
+ * appended. An existing section is merged in place, preserving its lines.
14
+ *
15
+ * This is a pure transform. It never writes a file or touches a corpus — the
16
+ * proposer carries the result into a human-reviewed pull request (ADR-065).
17
+ */
18
+ import { renderVerifiedByItem, verificationRefs, VERIFIED_BY_HEADING, } from "./verified-by.js";
19
+ const SECTION_TITLE = "verified by";
20
+ /** Level-2 (`## `) headings only — `### ` subsections belong to their parent. */
21
+ function level2Headings(lines) {
22
+ const headings = [];
23
+ lines.forEach((line, index) => {
24
+ const match = /^##\s+(.+?)\s*$/.exec(line);
25
+ if (match && !line.startsWith("###")) {
26
+ headings.push({ line: index, title: match[1].toLowerCase() });
27
+ }
28
+ });
29
+ return headings;
30
+ }
31
+ /** The first backtick-quoted token on a list item — its reference path. */
32
+ function itemRef(item) {
33
+ return /`([^`]+)`/.exec(item)?.[1];
34
+ }
35
+ /**
36
+ * Merge `links` into the `## Verified By` section of `content`. Each link
37
+ * contributes its bare test reference and, when present, its bare trace
38
+ * reference (both are external-target references, ADR-084).
39
+ *
40
+ * @throws {Error} when `links` is empty — an empty section is never written.
41
+ */
42
+ export function mergeVerifiedBy(content, links) {
43
+ if (links.length === 0) {
44
+ throw new Error("refusing to merge an empty `## Verified By` section");
45
+ }
46
+ const wanted = verificationRefs(links);
47
+ const lines = content.split("\n");
48
+ const headings = level2Headings(lines);
49
+ const existing = headings.find((h) => h.title === SECTION_TITLE);
50
+ if (existing) {
51
+ // Merge into the existing section: keep its lines, append only new refs.
52
+ const next = headings.find((h) => h.line > existing.line);
53
+ const end = next ? next.line : lines.length;
54
+ const sectionLines = lines.slice(existing.line, end);
55
+ const presentRefs = new Set(sectionLines.filter((l) => l.startsWith("- ")).map(itemRef).filter(Boolean));
56
+ const additions = wanted.filter((ref) => !presentRefs.has(ref)).map(renderVerifiedByItem);
57
+ if (additions.length === 0)
58
+ return content; // idempotent: nothing new
59
+ // Insert new items after the last existing list item (or after the heading).
60
+ let lastItem = existing.line;
61
+ for (let i = existing.line; i < end; i++) {
62
+ if (lines[i].startsWith("- "))
63
+ lastItem = i;
64
+ }
65
+ const merged = [...lines.slice(0, lastItem + 1), ...additions, ...lines.slice(lastItem + 1)];
66
+ return merged.join("\n");
67
+ }
68
+ // No existing section — build one and place it after the relationship block.
69
+ const section = [VERIFIED_BY_HEADING, "", ...wanted.map(renderVerifiedByItem)];
70
+ const anchor = [...headings].reverse().find((h) => h.title.startsWith("related ")) ??
71
+ headings.find((h) => h.title === "supersedes");
72
+ if (anchor) {
73
+ const next = headings.find((h) => h.line > anchor.line);
74
+ const insertAt = next ? next.line : lines.length;
75
+ const before = lines.slice(0, insertAt);
76
+ const after = lines.slice(insertAt);
77
+ // Ensure a blank line separates the new section from its neighbours.
78
+ while (before.length > 0 && before[before.length - 1] === "")
79
+ before.pop();
80
+ const block = ["", ...section, ...(after.length > 0 ? [""] : [])];
81
+ return [...before, ...block, ...after].join("\n");
82
+ }
83
+ // Append at end of document.
84
+ const trimmed = [...lines];
85
+ while (trimmed.length > 0 && trimmed[trimmed.length - 1] === "")
86
+ trimmed.pop();
87
+ return [...trimmed, "", ...section, ""].join("\n");
88
+ }
89
+ //# sourceMappingURL=merge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge.js","sourceRoot":"","sources":["../../src/writeback/merge.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Assemble a `## Verified By` write-back proposal — Proofkeeper Initiative 5.
3
+ *
4
+ * Pure and deterministic (no timestamps): given a target artifact's current
5
+ * content and the verification links, produce the updated content plus the
6
+ * branch name, PR title, and PR body for a human-reviewed pull request. The
7
+ * proposal carries `changed: false` when every link is already present, so the
8
+ * proposer can decline to open an empty PR.
9
+ */
10
+ import type { CandidateTest } from "../compiler/types.js";
11
+ import type { RunResult } from "../runner/types.js";
12
+ import type { VerificationLink } from "./verified-by.js";
13
+ export interface BuildProposalInput {
14
+ /** The capability (requirement id) being verified. */
15
+ capabilityId: string;
16
+ /** Path to the requirement artifact within the target corpus. */
17
+ targetPath: string;
18
+ /** Current content of the target artifact. */
19
+ originalContent: string;
20
+ /** The verification links to record. */
21
+ links: VerificationLink[];
22
+ /** Base branch the PR targets. Defaults to `main`. */
23
+ baseBranch?: string;
24
+ /** Prefix for the generated head branch. Defaults to `proofkeeper/verified-by`. */
25
+ branchPrefix?: string;
26
+ /** Readable step summary of the driven flow, shown in the PR body. */
27
+ steps?: string[];
28
+ /** The Markdown test plan the model wrote before driving, shown in the PR body. */
29
+ plan?: string;
30
+ }
31
+ export interface WriteBackProposal {
32
+ capabilityId: string;
33
+ targetPath: string;
34
+ baseBranch: string;
35
+ headBranch: string;
36
+ title: string;
37
+ body: string;
38
+ originalContent: string;
39
+ updatedContent: string;
40
+ links: VerificationLink[];
41
+ /** False when the merge produced no change (all links already present). */
42
+ changed: boolean;
43
+ }
44
+ /** Build a write-back proposal (does not touch any repo). */
45
+ export declare function buildProposal(input: BuildProposalInput): WriteBackProposal;
46
+ /**
47
+ * Derive verification links from a compiled test and its run results: the
48
+ * committed spec (the corpus verifier) plus the first replayable trace produced
49
+ * for it (surfaced in the PR).
50
+ */
51
+ export declare function linksFromResults(candidate: CandidateTest, results: RunResult[]): VerificationLink[];
52
+ //# sourceMappingURL=proposal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proposal.d.ts","sourceRoot":"","sources":["../../src/writeback/proposal.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAEpD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAKzD,MAAM,WAAW,kBAAkB;IACjC,sDAAsD;IACtD,YAAY,EAAE,MAAM,CAAC;IACrB,iEAAiE;IACjE,UAAU,EAAE,MAAM,CAAC;IACnB,8CAA8C;IAC9C,eAAe,EAAE,MAAM,CAAC;IACxB,wCAAwC;IACxC,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAC1B,sDAAsD;IACtD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mFAAmF;IACnF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,sEAAsE;IACtE,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,mFAAmF;IACnF,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,iBAAiB;IAChC,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAC1B,2EAA2E;IAC3E,OAAO,EAAE,OAAO,CAAC;CAClB;AAmDD,6DAA6D;AAC7D,wBAAgB,aAAa,CAAC,KAAK,EAAE,kBAAkB,GAAG,iBAAiB,CAgB1E;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,aAAa,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,gBAAgB,EAAE,CAKnG"}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Assemble a `## Verified By` write-back proposal — Proofkeeper Initiative 5.
3
+ *
4
+ * Pure and deterministic (no timestamps): given a target artifact's current
5
+ * content and the verification links, produce the updated content plus the
6
+ * branch name, PR title, and PR body for a human-reviewed pull request. The
7
+ * proposal carries `changed: false` when every link is already present, so the
8
+ * proposer can decline to open an empty PR.
9
+ */
10
+ import { mergeVerifiedBy } from "./merge.js";
11
+ const DEFAULT_BASE_BRANCH = "main";
12
+ const DEFAULT_BRANCH_PREFIX = "proofkeeper/verified-by";
13
+ /** Deterministic, filesystem/branch-safe slug. */
14
+ function slug(value) {
15
+ return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "capability";
16
+ }
17
+ function proposalBody(input) {
18
+ const items = input.links
19
+ .map((l) => `- \`${l.test}\`${l.trace ? ` (trace: \`${l.trace}\`)` : ""}`)
20
+ .join("\n");
21
+ const lines = [
22
+ `Proofkeeper proposes recording verification for **${input.capabilityId}**.`,
23
+ "",
24
+ `It adds a \`## Verified By\` section to \`${input.targetPath}\` linking the`,
25
+ "committed test(s) and replayable trace(s) that exercise this capability, as",
26
+ "bare reference paths.",
27
+ "",
28
+ "These are external-target links (ADR-084): the engine emits them with",
29
+ "`resolved: false` and the literal reference as target — that is expected, not",
30
+ "an error.",
31
+ "",
32
+ "Proposed links:",
33
+ items,
34
+ ];
35
+ if (input.plan && input.plan.trim().length > 0) {
36
+ lines.push("", "Test plan:", "", input.plan.trim());
37
+ }
38
+ if (input.steps && input.steps.length > 0) {
39
+ lines.push("", "Steps exercised:");
40
+ input.steps.forEach((s, i) => lines.push(`${i + 1}. ${s}`));
41
+ }
42
+ const trace = input.links.find((l) => l.trace)?.trace;
43
+ if (trace) {
44
+ lines.push("", `Replay the trace locally: \`npx playwright show-trace ${trace}\``);
45
+ }
46
+ lines.push("", "This is a proposal for human review (ADR-065). Proofkeeper produces and runs", "the evidence; a reviewer accepts the link. Merge only after confirming the", "referenced test and trace.");
47
+ return lines.join("\n");
48
+ }
49
+ /** Build a write-back proposal (does not touch any repo). */
50
+ export function buildProposal(input) {
51
+ const baseBranch = input.baseBranch ?? DEFAULT_BASE_BRANCH;
52
+ const branchPrefix = input.branchPrefix ?? DEFAULT_BRANCH_PREFIX;
53
+ const updatedContent = mergeVerifiedBy(input.originalContent, input.links);
54
+ return {
55
+ capabilityId: input.capabilityId,
56
+ targetPath: input.targetPath,
57
+ baseBranch,
58
+ headBranch: `${branchPrefix}/${slug(input.capabilityId)}`,
59
+ title: `docs(verify): record Verified By for ${input.capabilityId}`,
60
+ body: proposalBody(input),
61
+ originalContent: input.originalContent,
62
+ updatedContent,
63
+ links: input.links,
64
+ changed: updatedContent !== input.originalContent,
65
+ };
66
+ }
67
+ /**
68
+ * Derive verification links from a compiled test and its run results: the
69
+ * committed spec (the corpus verifier) plus the first replayable trace produced
70
+ * for it (surfaced in the PR).
71
+ */
72
+ export function linksFromResults(candidate, results) {
73
+ const trace = results.find((r) => r.testId === candidate.id && r.tracePath)?.tracePath;
74
+ const link = { test: candidate.specPath };
75
+ if (trace)
76
+ link.trace = trace;
77
+ return [link];
78
+ }
79
+ //# sourceMappingURL=proposal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proposal.js","sourceRoot":"","sources":["../../src/writeback/proposal.ts"],"names":[],"mappings":"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"}