@mmnto/totem 1.102.0 → 1.103.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 (39) hide show
  1. package/dist/artifacts/verdict.d.ts +70 -14
  2. package/dist/artifacts/verdict.d.ts.map +1 -1
  3. package/dist/artifacts/verdict.js +43 -3
  4. package/dist/artifacts/verdict.js.map +1 -1
  5. package/dist/artifacts/verdict.test.js +169 -1
  6. package/dist/artifacts/verdict.test.js.map +1 -1
  7. package/dist/autoclose/index.d.ts +12 -0
  8. package/dist/autoclose/index.d.ts.map +1 -0
  9. package/dist/autoclose/index.js +9 -0
  10. package/dist/autoclose/index.js.map +1 -0
  11. package/dist/autoclose/matcher.d.ts +115 -0
  12. package/dist/autoclose/matcher.d.ts.map +1 -0
  13. package/dist/autoclose/matcher.js +164 -0
  14. package/dist/autoclose/matcher.js.map +1 -0
  15. package/dist/autoclose/matcher.test.d.ts +2 -0
  16. package/dist/autoclose/matcher.test.d.ts.map +1 -0
  17. package/dist/autoclose/matcher.test.js +133 -0
  18. package/dist/autoclose/matcher.test.js.map +1 -0
  19. package/dist/autoclose/merge-config.d.ts +73 -0
  20. package/dist/autoclose/merge-config.d.ts.map +1 -0
  21. package/dist/autoclose/merge-config.js +81 -0
  22. package/dist/autoclose/merge-config.js.map +1 -0
  23. package/dist/autoclose/merge-config.test.d.ts +2 -0
  24. package/dist/autoclose/merge-config.test.d.ts.map +1 -0
  25. package/dist/autoclose/merge-config.test.js +108 -0
  26. package/dist/autoclose/merge-config.test.js.map +1 -0
  27. package/dist/autoclose/receipt.d.ts +160 -0
  28. package/dist/autoclose/receipt.d.ts.map +1 -0
  29. package/dist/autoclose/receipt.js +296 -0
  30. package/dist/autoclose/receipt.js.map +1 -0
  31. package/dist/autoclose/receipt.test.d.ts +2 -0
  32. package/dist/autoclose/receipt.test.d.ts.map +1 -0
  33. package/dist/autoclose/receipt.test.js +248 -0
  34. package/dist/autoclose/receipt.test.js.map +1 -0
  35. package/dist/index.d.ts +2 -0
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +1 -0
  38. package/dist/index.js.map +1 -1
  39. package/package.json +1 -1
@@ -0,0 +1,296 @@
1
+ /**
2
+ * Declared-intent receipt + reconciliation for the auto-close enforcement seam
3
+ * (mmnto-ai/totem#1762).
4
+ *
5
+ * D1 (PR-time required check) scans the PR corpus — title, description, and ALL
6
+ * branch commit messages (config-verified: the governed repos compose squash
7
+ * bodies from `COMMIT_MESSAGES`, so every branch commit is a squash-seed input)
8
+ * — for close-keyword-adjacent refs via {@link findAutoCloseRefs}, and fails on
9
+ * any ref that is not AUTHORIZED.
10
+ *
11
+ * AUTHORIZATION IS PROVENANCE-DISTINCT (codex #3 — the circularity fix). GitHub
12
+ * DERIVES `closingIssuesReferences` FROM the PR body's own close keywords, so a
13
+ * body keyword would self-whitelist against it. The ONLY authorizing channel is
14
+ * therefore the provenance-distinct `totem-close` marker (an HTML comment or a
15
+ * `Totem-Close:` trailer the author writes — see {@link parseDeclaredCloseIntent}).
16
+ * `closingIssuesReferences` is recorded on the receipt as OBSERVED GitHub state
17
+ * (informational), never as an authorization. Author workflow: declare every
18
+ * intended close with the marker.
19
+ *
20
+ * D2 (post-merge reconciliation, OBSERVATION MODE) compares the merged HEAD
21
+ * commit message against the receipt via {@link reconcile}. It alerts loud —
22
+ * never auto-reopens — until positive+negative controls arm enforcement (the
23
+ * Tenet 9 sense→enforce gate).
24
+ *
25
+ * Never scan issue/PR COMMENT bodies anywhere — comments never auto-close.
26
+ */
27
+ import { autoCloseKeyForms, findAutoCloseRefs } from './matcher.js';
28
+ // Bumped to 2 for the declaredByMarker / closingIssuesReferences split (the
29
+ // codex #3 circularity fix). No live v1 receipts exist (D1 is not yet deployed),
30
+ // so there is nothing to migrate; a stale-shape receipt fails isValidReceipt and
31
+ // D2 reports `ambiguous-receipt` (alert, never guess).
32
+ export const AUTO_CLOSE_RECEIPT_SCHEMA_VERSION = 2;
33
+ // The provenance-distinct authorization channel: an author who intends a
34
+ // close-keyword ref declares it either as an HTML comment
35
+ // `<!-- totem-close: #N, owner/repo#M -->` or a git trailer `Totem-Close: #N`.
36
+ // This is the ONLY authorizing channel (closingIssuesReferences is GitHub-derived
37
+ // and thus self-whitelisting — codex #3).
38
+ const INTENT_MARKER_RE = /<!--\s*totem-close:\s*([^>]*?)\s*-->|^[ \t]*totem-close:[ \t]*(.+)$/gim;
39
+ const INTENT_REF_RE = /([A-Za-z0-9._-]+\/[A-Za-z0-9._-]+)?#(\d+)/g;
40
+ /**
41
+ * Parse the structured-intent declarations out of `text`. Returns the refs the
42
+ * author explicitly whitelisted for closure. Does NOT interpret close keywords —
43
+ * a marker carries bare/qualified refs only.
44
+ */
45
+ export function parseDeclaredCloseIntent(text) {
46
+ if (typeof text !== 'string' || text.length === 0)
47
+ return [];
48
+ const out = [];
49
+ for (const marker of text.matchAll(INTENT_MARKER_RE)) {
50
+ const inner = marker[1] ?? marker[2] ?? '';
51
+ for (const ref of inner.matchAll(INTENT_REF_RE)) {
52
+ const issue = Number(ref[2]);
53
+ if (!Number.isFinite(issue))
54
+ continue;
55
+ out.push({ ...(ref[1] ? { qualifier: ref[1] } : {}), issue });
56
+ }
57
+ }
58
+ return out;
59
+ }
60
+ /**
61
+ * Strip structured-intent markers so a marker's own refs never read as a finding.
62
+ * The marker text `totem-close: #N` itself contains `close: #N` — a
63
+ * keyword-adjacent ref — so this MUST run before {@link findAutoCloseRefs}, or a
64
+ * marker would self-flag (verified by test).
65
+ */
66
+ function stripIntentMarkers(text) {
67
+ return text
68
+ .replace(/<!--\s*totem-close:[^>]*-->/gi, ' ')
69
+ .replace(/^[ \t]*totem-close:.*$/gim, ' ');
70
+ }
71
+ /** Normalize a set of refs to their comparison-key set (union of equivalence forms). */
72
+ function keysFromRefs(refs, repo) {
73
+ const keys = new Set();
74
+ for (const r of refs)
75
+ for (const k of autoCloseKeyForms(r, repo))
76
+ keys.add(k);
77
+ return [...keys];
78
+ }
79
+ /** Normalize GitHub's closingIssuesReferences to comparison keys (informational). */
80
+ function keysFromClosingRefs(closing, repo) {
81
+ return keysFromRefs(closing.map((c) => ({
82
+ ...(c.repoWithOwner ? { qualifier: c.repoWithOwner } : {}),
83
+ issue: c.number,
84
+ })), repo);
85
+ }
86
+ /** Is any equivalence-form of `match` present in the authorizing set? */
87
+ function isDeclared(match, declared, repo) {
88
+ return autoCloseKeyForms(match, repo).some((k) => declared.has(k));
89
+ }
90
+ /**
91
+ * D1: scan the PR corpus (title + body + every branch commit message) for
92
+ * close-keyword-adjacent refs and split them into authorized vs undeclared. A
93
+ * finding is authorized ONLY by the provenance-distinct `totem-close` marker —
94
+ * NOT by GitHub's `closingIssuesReferences` (which GitHub derives from the same
95
+ * body keywords, so it would self-whitelist; codex #3). Comment bodies are NEVER
96
+ * part of the corpus.
97
+ */
98
+ export function scanPrCorpus(corpus) {
99
+ const surfaces = [corpus.title, corpus.body, ...corpus.commitMessages].map((s) => typeof s === 'string' ? s : '');
100
+ const intent = parseDeclaredCloseIntent(surfaces.join('\n'));
101
+ const declaredByMarker = keysFromRefs(intent, corpus.repo);
102
+ const closingIssuesReferences = keysFromClosingRefs(corpus.closingIssuesReferences, corpus.repo);
103
+ const authorizingSet = new Set(declaredByMarker.map((k) => k.toLowerCase()));
104
+ const matches = surfaces.flatMap((s) => findAutoCloseRefs(stripIntentMarkers(s)));
105
+ const findings = dedupe(matches.map((m) => m.ref));
106
+ const undeclared = dedupe(matches.filter((m) => !isDeclared(m, authorizingSet, corpus.repo)).map((m) => m.ref));
107
+ return {
108
+ ok: undeclared.length === 0,
109
+ findings,
110
+ declaredByMarker,
111
+ closingIssuesReferences,
112
+ undeclared,
113
+ };
114
+ }
115
+ /** Assemble the durable D1 receipt from a corpus scan. */
116
+ export function buildReceipt(corpus, prNumber, headSha, scan, now = new Date()) {
117
+ return {
118
+ schemaVersion: AUTO_CLOSE_RECEIPT_SCHEMA_VERSION,
119
+ repo: corpus.repo,
120
+ prNumber,
121
+ headSha,
122
+ declaredByMarker: scan.declaredByMarker,
123
+ closingIssuesReferences: scan.closingIssuesReferences,
124
+ corpusFindings: scan.findings,
125
+ generatedAt: now.toISOString(),
126
+ note: 'Declared-intended-close receipt for the auto-close enforcement seam ' +
127
+ '(mmnto-ai/totem#1762). D2 authorizes the merged HEAD message against ' +
128
+ 'declaredByMarker ONLY (closingIssuesReferences is GitHub-derived / ' +
129
+ 'informational). Absent receipt + a closure-capable body => alert, never guess.',
130
+ };
131
+ }
132
+ function isValidReceipt(r) {
133
+ if (r === null || typeof r !== 'object')
134
+ return false;
135
+ const o = r;
136
+ return (typeof o['schemaVersion'] === 'number' &&
137
+ Array.isArray(o['declaredByMarker']) &&
138
+ o['declaredByMarker'].every((k) => typeof k === 'string'));
139
+ }
140
+ /**
141
+ * D2: reconcile the merged HEAD commit message against the D1 receipt.
142
+ * OBSERVATION MODE — every non-clean outcome ALERTS (the caller decides exit
143
+ * code); NONE reopens.
144
+ *
145
+ * - `clean` — no close-keyword-adjacent ref, or every ref is
146
+ * authorized. Quiet path (empty / trailer-only body).
147
+ * - `anomaly` — a closure-capable message with ≥1 UNAUTHORIZED ref.
148
+ * The zero-allowed-set (`declaredByMarker: []`) + a
149
+ * closure-capable message is the #2471 specimen.
150
+ * - `missing-receipt` — a closure-capable message but NO receipt (PR merged
151
+ * before D1 existed, or the artifact expired /
152
+ * could not be downloaded). Alert, never guess.
153
+ * - `ambiguous-receipt`— a closure-capable message but the receipt is malformed
154
+ * or is for a different PR. Alert, never guess.
155
+ * - `unexpected-body` — a non-empty non-trailer body under BLANK with NO
156
+ * unauthorized ref: posture-drift / `--body`-override
157
+ * evidence (no closure harm). The caller surfaces it as
158
+ * a non-failing signal (interpretation call).
159
+ */
160
+ export function reconcile(receipt, mergedBody, opts = {}) {
161
+ const message = mergedBody ?? '';
162
+ // Body-presence FIRST (E-lever addendum, mmnto-ai/totem#1762): under the BLANK
163
+ // squash posture the server composes no prose body — but RFC-822 attribution
164
+ // trailers (Co-authored-by / Signed-off-by) DO survive (0330Z, first live
165
+ // merge), so presence is evaluated on the body AFTER trailer-strip. The content
166
+ // scan runs over the whole message — a close-keyword ref in the SUBJECT (now
167
+ // deterministically the PR_TITLE) still auto-closes.
168
+ const bodyPresent = messageBody(message).length > 0;
169
+ // Content scan / harm axis: an UNAUTHORIZED close-keyword ref is the
170
+ // top-severity alert (the accidental-closure harm) and always wins.
171
+ const harm = evaluateContent(receipt, message, bodyPresent, opts);
172
+ if (harm.status !== 'clean')
173
+ return harm;
174
+ // No unauthorized close-keyword harm. A non-empty non-trailer body under BLANK
175
+ // is the posture signal: only a local `gh pr merge --body` override (the
176
+ // confirmed-vector class), a config-drift regression, or a non-squash merge
177
+ // produces one. INTERPRETATION CALL (E-lever addendum): surfaced as
178
+ // posture-drift EVIDENCE (`unexpected-body`), distinct from a hard close-anomaly
179
+ // (no issue-closure harm), with NO reopen candidates.
180
+ if (bodyPresent) {
181
+ return {
182
+ status: 'unexpected-body',
183
+ findings: harm.findings,
184
+ undeclared: [],
185
+ reopenCandidates: [],
186
+ bodyPresent: true,
187
+ message: `Merged commit carries a NON-EMPTY body (after trailer-strip) under the BLANK squash ` +
188
+ `posture (findings: ${harm.findings.length > 0 ? harm.findings.join(', ') : 'none'}). No ` +
189
+ 'unauthorized close-keyword ref, so no accidental-closure harm — but under BLANK the ' +
190
+ 'server composes no body, so this is posture-drift / local `--body`-override evidence ' +
191
+ '(the confirmed-vector fingerprint — triage it, do not ignore). Surfaced (observation ' +
192
+ 'mode; no auto-reopen). Verify the merge-config posture (D1 asserts it) and that no local ' +
193
+ '`--body` override was used. mmnto-ai/totem#1762.',
194
+ };
195
+ }
196
+ return { ...harm };
197
+ }
198
+ /**
199
+ * The content/harm axis of {@link reconcile}: scan the whole merged message
200
+ * (subject + body) for close-keyword refs and authorize them against the
201
+ * receipt's marker set. Returns `clean` | `anomaly` | `missing-receipt` |
202
+ * `ambiguous-receipt` only — the `unexpected-body` posture leaf is decided by
203
+ * {@link reconcile}.
204
+ */
205
+ function evaluateContent(receipt, message, bodyPresent, opts) {
206
+ const repo = opts.repo;
207
+ const matches = findAutoCloseRefs(stripIntentMarkers(message));
208
+ const findings = dedupe(matches.map((m) => m.ref));
209
+ if (matches.length === 0) {
210
+ return {
211
+ status: 'clean',
212
+ findings,
213
+ undeclared: [],
214
+ reopenCandidates: [],
215
+ bodyPresent,
216
+ message: 'No close-keyword-adjacent issue reference in the merged commit message.',
217
+ };
218
+ }
219
+ // A closure-capable message with no usable receipt: alert, never guess.
220
+ if (receipt === null || !isValidReceipt(receipt)) {
221
+ const why = receipt === null ? 'missing-receipt' : 'ambiguous-receipt';
222
+ return {
223
+ status: why,
224
+ findings,
225
+ undeclared: findings,
226
+ reopenCandidates: findings,
227
+ bodyPresent,
228
+ message: `Merged message closes ${findings.join(', ')} but the D1 receipt is ` +
229
+ `${receipt === null ? 'ABSENT' : 'MALFORMED'} — cannot verify intent. ` +
230
+ 'Alerting (observation mode; no auto-reopen). Verify the closure was intended; ' +
231
+ 'if not, `gh issue reopen <n>`.',
232
+ };
233
+ }
234
+ if (opts.expectedPrNumber !== undefined && receipt.prNumber !== opts.expectedPrNumber) {
235
+ return {
236
+ status: 'ambiguous-receipt',
237
+ findings,
238
+ undeclared: findings,
239
+ reopenCandidates: findings,
240
+ bodyPresent,
241
+ message: `Merged message closes ${findings.join(', ')} but the fetched receipt is for ` +
242
+ `PR #${receipt.prNumber}, not the merged PR #${opts.expectedPrNumber} — ` +
243
+ 'cannot verify intent. Alerting (observation mode; no auto-reopen).',
244
+ };
245
+ }
246
+ // Authorize against the MARKER set only (closingIssuesReferences is
247
+ // informational — codex #3 circularity fix).
248
+ const authorizingSet = new Set(receipt.declaredByMarker.map((k) => k.toLowerCase()));
249
+ const undeclared = dedupe(matches.filter((m) => !isDeclared(m, authorizingSet, repo ?? receipt.repo)).map((m) => m.ref));
250
+ if (undeclared.length > 0) {
251
+ return {
252
+ status: 'anomaly',
253
+ findings,
254
+ undeclared,
255
+ reopenCandidates: undeclared,
256
+ bodyPresent,
257
+ message: `Merged message closes ${undeclared.join(', ')} but the D1 receipt did NOT ` +
258
+ `authorize ${undeclared.length > 1 ? 'them' : 'it'} via a totem-close marker ` +
259
+ `(marker-authorized: ${receipt.declaredByMarker.length === 0 ? '[] (zero-allowed-set)' : receipt.declaredByMarker.join(', ')}). ` +
260
+ 'This is an accidental-closure anomaly. Alerting (observation mode; no auto-reopen). ' +
261
+ 'If the closure was unintended, `gh issue reopen <n>`.',
262
+ };
263
+ }
264
+ return {
265
+ status: 'clean',
266
+ findings,
267
+ undeclared: [],
268
+ reopenCandidates: [],
269
+ bodyPresent,
270
+ message: `All closes in the merged message (${findings.join(', ')}) were marker-authorized at PR time.`,
271
+ };
272
+ }
273
+ /** RFC-822-style trailer line (`Co-authored-by:`, `Signed-off-by:`, and kin). */
274
+ const TRAILER_LINE_RE = /^[A-Za-z][A-Za-z-]*:\s/;
275
+ /** Strip RFC-822 trailer lines so an attribution-only body reads as empty (0330Z). */
276
+ function stripTrailerLines(text) {
277
+ return text
278
+ .split(/\r?\n/)
279
+ .filter((line) => !TRAILER_LINE_RE.test(line))
280
+ .join('\n');
281
+ }
282
+ /**
283
+ * The commit BODY presence surface — content after the first line (the subject),
284
+ * with RFC-822 attribution trailers stripped, then trimmed. Under BLANK a
285
+ * co-authored / dependabot squash body is trailers-only → reads as empty → clean
286
+ * (0330Z: those trailers survive the BLANK message setting).
287
+ */
288
+ function messageBody(message) {
289
+ const nl = message.indexOf('\n');
290
+ const rawBody = nl === -1 ? '' : message.slice(nl + 1);
291
+ return stripTrailerLines(rawBody).trim();
292
+ }
293
+ function dedupe(xs) {
294
+ return [...new Set(xs)];
295
+ }
296
+ //# sourceMappingURL=receipt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"receipt.js","sourceRoot":"","sources":["../../src/autoclose/receipt.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=receipt.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"receipt.test.d.ts","sourceRoot":"","sources":["../../src/autoclose/receipt.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,248 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { AUTO_CLOSE_RECEIPT_SCHEMA_VERSION, buildReceipt, parseDeclaredCloseIntent, reconcile, scanPrCorpus, } from './receipt.js';
3
+ const REPO = 'mmnto-ai/totem';
4
+ /** Verbatim body of commit b8aa74a2 on main — the real #2471→#2466 specimen. */
5
+ const B8AA74A2_BODY = 'fix(review): deterministic skip paths no longer stamp the push gate (#2466) (#2471)\n' +
6
+ '\n' +
7
+ 'Three deterministic skip paths (all-generated, all-non-code, filtered-empty) no longer ' +
8
+ 'mint the reviewed-content stamp; they log a shared NON-REVIEW notice instead. ' +
9
+ 'Does not close #2466 (live exit-0 half deferred to #2473).';
10
+ /**
11
+ * Verbatim squash body of totem-strategy#948 (dependabot, first merge under
12
+ * BLANK — strategy-claude 0330Z). BLANK suppresses the prose body but RFC-822
13
+ * attribution trailers survive; after trailer-strip the body is empty → clean.
14
+ */
15
+ const STRATEGY_948_BODY = 'build(deps): bump actions/setup-node from 6 to 7 (#948)\n' +
16
+ '\n' +
17
+ 'Signed-off-by: dependabot[bot] <support@github.com>\n' +
18
+ 'Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>';
19
+ describe('parseDeclaredCloseIntent', () => {
20
+ it('parses an HTML-comment marker with multiple refs', () => {
21
+ expect(parseDeclaredCloseIntent('<!-- totem-close: #12, mmnto-ai/other#34 -->')).toEqual([
22
+ { issue: 12 },
23
+ { qualifier: 'mmnto-ai/other', issue: 34 },
24
+ ]);
25
+ });
26
+ it('parses a Totem-Close: trailer line', () => {
27
+ expect(parseDeclaredCloseIntent('body text\nTotem-Close: #99')).toEqual([{ issue: 99 }]);
28
+ });
29
+ it('returns [] when no marker present', () => {
30
+ expect(parseDeclaredCloseIntent('just prose #5')).toEqual([]);
31
+ });
32
+ });
33
+ describe('scanPrCorpus (D1) — marker-only authorization (codex #3 circularity fix)', () => {
34
+ it('FAILS on an undeclared close-keyword ref in the corpus', () => {
35
+ const r = scanPrCorpus({
36
+ title: 'chore: cleanup',
37
+ body: 'Does not close #2466',
38
+ commitMessages: ['chore: cleanup'],
39
+ closingIssuesReferences: [],
40
+ repo: REPO,
41
+ });
42
+ expect(r.ok).toBe(false);
43
+ expect(r.undeclared).toEqual(['#2466']);
44
+ expect(r.declaredByMarker).toEqual([]);
45
+ });
46
+ it('FAILS even when closingIssuesReferences lists the ref (GitHub-derived => cannot authorize)', () => {
47
+ // The circular self-whitelist: GitHub DERIVES closingIssuesReferences from the
48
+ // body keyword, so it must NOT authorize the same finding (codex #3). This
49
+ // replaces the prior test that locked the circular pass.
50
+ const r = scanPrCorpus({
51
+ title: 'feat: thing',
52
+ body: 'Closes #2466',
53
+ commitMessages: ['feat: thing'],
54
+ closingIssuesReferences: [{ number: 2466 }],
55
+ repo: REPO,
56
+ });
57
+ expect(r.ok).toBe(false);
58
+ expect(r.undeclared).toEqual(['#2466']);
59
+ // Recorded as observed GitHub state, but NOT authorizing.
60
+ expect(r.closingIssuesReferences).toContain('#2466');
61
+ expect(r.declaredByMarker).toEqual([]);
62
+ });
63
+ it('PASSES when the ref is authorized by a provenance-distinct totem-close marker', () => {
64
+ const r = scanPrCorpus({
65
+ title: 'feat: thing',
66
+ body: 'Fixes #700\n<!-- totem-close: #700 -->',
67
+ commitMessages: [],
68
+ closingIssuesReferences: [],
69
+ repo: REPO,
70
+ });
71
+ expect(r.ok).toBe(true);
72
+ expect(r.declaredByMarker).toContain('#700');
73
+ });
74
+ it('the totem-close marker does NOT self-flag (stripIntentMarkers runs first)', () => {
75
+ // `totem-close: #700` contains `close: #700` — a keyword-adjacent ref — so a
76
+ // marker-only body must produce ZERO findings, else the marker whitelists a
77
+ // finding it itself created.
78
+ const r = scanPrCorpus({
79
+ title: 't',
80
+ body: '<!-- totem-close: #700 -->',
81
+ commitMessages: [],
82
+ closingIssuesReferences: [],
83
+ repo: REPO,
84
+ });
85
+ expect(r.findings).toEqual([]);
86
+ expect(r.ok).toBe(true);
87
+ expect(r.declaredByMarker).toContain('#700');
88
+ });
89
+ it('scans branch COMMIT MESSAGES, not just the PR description', () => {
90
+ const r = scanPrCorpus({
91
+ title: 'feat: thing',
92
+ body: 'clean description',
93
+ commitMessages: ['wip', 'fixes #321 in passing'],
94
+ closingIssuesReferences: [],
95
+ repo: REPO,
96
+ });
97
+ expect(r.ok).toBe(false);
98
+ expect(r.undeclared).toEqual(['#321']);
99
+ });
100
+ it('catches the issue-URL close form (kimi BLOCKING-1)', () => {
101
+ const r = scanPrCorpus({
102
+ title: 't',
103
+ body: 'Fixes https://github.com/mmnto-ai/totem/issues/2466',
104
+ commitMessages: [],
105
+ closingIssuesReferences: [],
106
+ repo: REPO,
107
+ });
108
+ expect(r.ok).toBe(false);
109
+ expect(r.undeclared).toEqual(['mmnto-ai/totem#2466']);
110
+ });
111
+ it('scans a >100-commit branch to exhaustion (codex #4 — no 100-cap in the scan)', () => {
112
+ const commitMessages = Array.from({ length: 150 }, (_, i) => i === 120 ? 'fixes #4242 in passing' : `chore: commit ${i}`);
113
+ const r = scanPrCorpus({
114
+ title: 'feat: big',
115
+ body: 'clean',
116
+ commitMessages,
117
+ closingIssuesReferences: [],
118
+ repo: REPO,
119
+ });
120
+ expect(r.ok).toBe(false);
121
+ expect(r.undeclared).toEqual(['#4242']);
122
+ });
123
+ });
124
+ describe('buildReceipt', () => {
125
+ it('records the marker set + informational closing refs and stamps the schema version', () => {
126
+ const scan = scanPrCorpus({
127
+ title: 't',
128
+ body: 'Fixes #5\n<!-- totem-close: #5 -->',
129
+ commitMessages: [],
130
+ closingIssuesReferences: [{ number: 5 }],
131
+ repo: REPO,
132
+ });
133
+ const receipt = buildReceipt({ repo: REPO }, 42, 'deadbeef', scan, new Date('2026-07-21T00:00:00Z'));
134
+ expect(receipt.schemaVersion).toBe(AUTO_CLOSE_RECEIPT_SCHEMA_VERSION);
135
+ expect(receipt.prNumber).toBe(42);
136
+ expect(receipt.headSha).toBe('deadbeef');
137
+ expect(receipt.declaredByMarker).toContain('#5');
138
+ expect(receipt.closingIssuesReferences).toContain('#5');
139
+ expect(receipt.generatedAt).toBe('2026-07-21T00:00:00.000Z');
140
+ });
141
+ });
142
+ describe('reconcile (D2, observation mode)', () => {
143
+ const receiptWith = (markerKeys) => ({
144
+ schemaVersion: AUTO_CLOSE_RECEIPT_SCHEMA_VERSION,
145
+ repo: REPO,
146
+ prNumber: 2471,
147
+ headSha: 'abc',
148
+ declaredByMarker: markerKeys,
149
+ closingIssuesReferences: [],
150
+ corpusFindings: [],
151
+ generatedAt: '2026-07-21T00:00:00.000Z',
152
+ note: '',
153
+ });
154
+ // ── positive controls: the b8aa74a2 specimen ──────────────────────────────
155
+ it('POSITIVE CONTROL: zero-allowed-set receipt + b8aa74a2 body => anomaly', () => {
156
+ const r = reconcile(receiptWith([]), B8AA74A2_BODY, { repo: REPO });
157
+ expect(r.status).toBe('anomaly');
158
+ expect(r.undeclared).toEqual(['#2466']);
159
+ expect(r.reopenCandidates).toEqual(['#2466']);
160
+ expect(r.message).toMatch(/zero-allowed-set/);
161
+ });
162
+ it('POSITIVE CONTROL: missing receipt + b8aa74a2 body => missing-receipt', () => {
163
+ const r = reconcile(null, B8AA74A2_BODY, { repo: REPO });
164
+ expect(r.status).toBe('missing-receipt');
165
+ expect(r.findings).toEqual(['#2466']);
166
+ expect(r.reopenCandidates).toEqual(['#2466']);
167
+ });
168
+ // ── negative controls ─────────────────────────────────────────────────────
169
+ it('NEGATIVE CONTROL: a marker-authorized close => clean', () => {
170
+ const r = reconcile(receiptWith(['#2466', 'mmnto-ai/totem#2466']), 'Closes #2466', {
171
+ repo: REPO,
172
+ });
173
+ expect(r.status).toBe('clean');
174
+ expect(r.undeclared).toEqual([]);
175
+ });
176
+ it('NEGATIVE CONTROL: an empty-body subject with NO close keyword => clean even with null receipt', () => {
177
+ const r = reconcile(null, 'refactor: tidy the widget (#2471)', { repo: REPO });
178
+ expect(r.status).toBe('clean');
179
+ expect(r.findings).toEqual([]);
180
+ expect(r.bodyPresent).toBe(false);
181
+ });
182
+ it('TRAILER-STRIP: the totem-strategy#948 dependabot squash body => clean (0330Z)', () => {
183
+ // Attribution trailers survive BLANK; after trailer-strip the body is empty.
184
+ const r = reconcile(null, STRATEGY_948_BODY, { repo: 'mmnto-ai/totem-strategy' });
185
+ expect(r.status).toBe('clean');
186
+ expect(r.bodyPresent).toBe(false);
187
+ expect(r.findings).toEqual([]);
188
+ });
189
+ it('reconciles a self-qualified marker declaration against a bare body ref', () => {
190
+ const r = reconcile(receiptWith(['mmnto-ai/totem#2466']), 'Closes #2466', { repo: REPO });
191
+ expect(r.status).toBe('clean');
192
+ });
193
+ // ── ambiguous: alert, never guess ─────────────────────────────────────────
194
+ it('malformed receipt + closure-capable body => ambiguous-receipt', () => {
195
+ const bad = { schemaVersion: 2 };
196
+ const r = reconcile(bad, 'Closes #2466', { repo: REPO });
197
+ expect(r.status).toBe('ambiguous-receipt');
198
+ });
199
+ it('a v1-shaped receipt (declaredCloseKeys, no declaredByMarker) => ambiguous-receipt', () => {
200
+ const stale = {
201
+ schemaVersion: 1,
202
+ declaredCloseKeys: ['#2466'],
203
+ };
204
+ const r = reconcile(stale, 'Closes #2466', { repo: REPO });
205
+ expect(r.status).toBe('ambiguous-receipt');
206
+ });
207
+ it('receipt for the wrong PR => ambiguous-receipt', () => {
208
+ const r = reconcile(receiptWith(['#2466']), 'Closes #2466', {
209
+ repo: REPO,
210
+ expectedPrNumber: 9999,
211
+ });
212
+ expect(r.status).toBe('ambiguous-receipt');
213
+ });
214
+ it('never populates a side-effecting field — reopenCandidates is advisory only', () => {
215
+ const r = reconcile(receiptWith([]), B8AA74A2_BODY, { repo: REPO });
216
+ expect(Array.isArray(r.reopenCandidates)).toBe(true);
217
+ });
218
+ // ── E-lever addendum: body-presence-first + unexpected-body (#1762 0235Z) ──
219
+ it('EMPTY body + no close keyword => clean, bodyPresent=false (the BLANK normal state)', () => {
220
+ const r = reconcile(null, 'chore: bump deps (#2500)', { repo: REPO });
221
+ expect(r.status).toBe('clean');
222
+ expect(r.bodyPresent).toBe(false);
223
+ });
224
+ it('NON-EMPTY body with NO close-keyword ref => unexpected-body (surfaced, not silent)', () => {
225
+ const r = reconcile(null, 'feat: thing (#2500)\n\nSome authored body text, no issue closed.', {
226
+ repo: REPO,
227
+ });
228
+ expect(r.status).toBe('unexpected-body');
229
+ expect(r.bodyPresent).toBe(true);
230
+ expect(r.reopenCandidates).toEqual([]);
231
+ expect(r.message).toMatch(/posture-drift|--body/);
232
+ });
233
+ it('an UNAUTHORIZED close-keyword ref beats the posture signal (body-present anomaly wins)', () => {
234
+ const r = reconcile(receiptWith([]), 'feat: thing (#2500)\n\nAlso closes #2466 in passing.', {
235
+ repo: REPO,
236
+ });
237
+ expect(r.status).toBe('anomaly');
238
+ expect(r.undeclared).toEqual(['#2466']);
239
+ expect(r.bodyPresent).toBe(true);
240
+ });
241
+ it('a close keyword in the SUBJECT (PR_TITLE) with empty body still reconciles', () => {
242
+ const r = reconcile(receiptWith([]), 'Fix #2466: the widget', { repo: REPO });
243
+ expect(r.status).toBe('anomaly');
244
+ expect(r.undeclared).toEqual(['#2466']);
245
+ expect(r.bodyPresent).toBe(false);
246
+ });
247
+ });
248
+ //# sourceMappingURL=receipt.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"receipt.test.js","sourceRoot":"","sources":["../../src/autoclose/receipt.test.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -219,4 +219,6 @@ export type { MinedReviewFinding, ReviewCatchMineResult } from './capability/rev
219
219
  export { mineReviewCatch, resolveActorId } from './capability/review-catch.js';
220
220
  export type { CapabilityClaim, CapabilityLedger, CapabilityLedgerRow, CapabilityProvenance, CapabilityResolution, Outcome, ResolutionSource, TaskType, } from './capability/schema.js';
221
221
  export { CapabilityClaimSchema, CapabilityLedgerSchema, CapabilityResolutionSchema, deriveClaimId, OutcomeSchema, ResolutionSourceSchema, TaskTypeSchema, } from './capability/schema.js';
222
+ export type { AutoCloseMatch, AutoCloseReceipt, ClosingIssueRef, DeclaredIntentRef, MergeConfigPosture, MergeConfigStatus, MergeConfigVerdict, PrCorpus, PrScanResult, ReconcileOptions, ReconcileResult, ReconcileStatus, } from './autoclose/index.js';
223
+ export { AUTO_CLOSE_KEYWORDS, AUTO_CLOSE_RECEIPT_SCHEMA_VERSION, AUTO_CLOSE_REGEX_SOURCE, autoCloseKeyForms, buildReceipt, evaluateMergeConfigPosture, findAutoCloseRefs, parseDeclaredCloseIntent, reconcile, REQUIRED_SQUASH_MERGE_MESSAGE, REQUIRED_SQUASH_MERGE_TITLE, scanPrCorpus, } from './autoclose/index.js';
222
224
  //# sourceMappingURL=index.d.ts.map