@aperant/framework 0.6.4 → 0.6.5

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 (205) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/README.md +64 -10
  3. package/agents/apt-improver.md +99 -0
  4. package/agents/apt-planner.md +115 -10
  5. package/dist/__test-helpers/run-cmd.d.mts +4 -2
  6. package/dist/__test-helpers/run-cmd.d.mts.map +1 -1
  7. package/dist/__test-helpers/run-cmd.mjs +56 -15
  8. package/dist/__test-helpers/run-cmd.mjs.map +1 -1
  9. package/dist/cli/ci-watch/lock.d.mts +53 -0
  10. package/dist/cli/ci-watch/lock.d.mts.map +1 -0
  11. package/dist/cli/ci-watch/lock.mjs +172 -0
  12. package/dist/cli/ci-watch/lock.mjs.map +1 -0
  13. package/dist/cli/ci-watch/state.d.mts +36 -0
  14. package/dist/cli/ci-watch/state.d.mts.map +1 -0
  15. package/dist/cli/ci-watch/state.mjs +103 -0
  16. package/dist/cli/ci-watch/state.mjs.map +1 -0
  17. package/dist/cli/ci-watch/stop-matrix.d.mts +58 -0
  18. package/dist/cli/ci-watch/stop-matrix.d.mts.map +1 -0
  19. package/dist/cli/ci-watch/stop-matrix.mjs +164 -0
  20. package/dist/cli/ci-watch/stop-matrix.mjs.map +1 -0
  21. package/dist/cli/cli-wrappers/ci-watch.d.mts +2 -0
  22. package/dist/cli/cli-wrappers/ci-watch.d.mts.map +1 -0
  23. package/dist/cli/cli-wrappers/ci-watch.mjs +9 -0
  24. package/dist/cli/cli-wrappers/ci-watch.mjs.map +1 -0
  25. package/dist/cli/commands/adr.d.mts +5 -0
  26. package/dist/cli/commands/adr.d.mts.map +1 -0
  27. package/dist/cli/commands/adr.mjs +228 -0
  28. package/dist/cli/commands/adr.mjs.map +1 -0
  29. package/dist/cli/commands/ci-watch.d.mts +7 -0
  30. package/dist/cli/commands/ci-watch.d.mts.map +1 -0
  31. package/dist/cli/commands/ci-watch.mjs +465 -0
  32. package/dist/cli/commands/ci-watch.mjs.map +1 -0
  33. package/dist/cli/commands/context.d.mts +7 -0
  34. package/dist/cli/commands/context.d.mts.map +1 -0
  35. package/dist/cli/commands/context.mjs +224 -0
  36. package/dist/cli/commands/context.mjs.map +1 -0
  37. package/dist/cli/commands/event.d.mts.map +1 -1
  38. package/dist/cli/commands/event.mjs +59 -24
  39. package/dist/cli/commands/event.mjs.map +1 -1
  40. package/dist/cli/commands/host-detect.d.mts +1 -1
  41. package/dist/cli/commands/host-detect.d.mts.map +1 -1
  42. package/dist/cli/commands/host-detect.mjs +30 -3
  43. package/dist/cli/commands/host-detect.mjs.map +1 -1
  44. package/dist/cli/commands/modes.d.mts +13 -0
  45. package/dist/cli/commands/modes.d.mts.map +1 -0
  46. package/dist/cli/commands/modes.mjs +220 -0
  47. package/dist/cli/commands/modes.mjs.map +1 -0
  48. package/dist/cli/commands/route.mjs +1 -1
  49. package/dist/cli/commands/route.mjs.map +1 -1
  50. package/dist/cli/commands/task.d.mts.map +1 -1
  51. package/dist/cli/commands/task.mjs +108 -6
  52. package/dist/cli/commands/task.mjs.map +1 -1
  53. package/dist/cli/commands/tokens.d.mts.map +1 -1
  54. package/dist/cli/commands/tokens.mjs +150 -6
  55. package/dist/cli/commands/tokens.mjs.map +1 -1
  56. package/dist/cli/commands/triage.d.mts +8 -0
  57. package/dist/cli/commands/triage.d.mts.map +1 -0
  58. package/dist/cli/commands/triage.mjs +259 -0
  59. package/dist/cli/commands/triage.mjs.map +1 -0
  60. package/dist/cli/config/post-merge-sweep.d.mts +18 -0
  61. package/dist/cli/config/post-merge-sweep.d.mts.map +1 -1
  62. package/dist/cli/config/post-merge-sweep.mjs +48 -3
  63. package/dist/cli/config/post-merge-sweep.mjs.map +1 -1
  64. package/dist/cli/consistency/registry.d.mts +11 -0
  65. package/dist/cli/consistency/registry.d.mts.map +1 -1
  66. package/dist/cli/consistency/registry.mjs +13 -0
  67. package/dist/cli/consistency/registry.mjs.map +1 -1
  68. package/dist/cli/coordination/agent-identity.d.mts +9 -0
  69. package/dist/cli/coordination/agent-identity.d.mts.map +1 -1
  70. package/dist/cli/coordination/agent-identity.mjs +11 -0
  71. package/dist/cli/coordination/agent-identity.mjs.map +1 -1
  72. package/dist/cli/coordination/event-log.d.mts +27 -0
  73. package/dist/cli/coordination/event-log.d.mts.map +1 -1
  74. package/dist/cli/coordination/event-log.mjs +72 -2
  75. package/dist/cli/coordination/event-log.mjs.map +1 -1
  76. package/dist/cli/dispatch.d.mts.map +1 -1
  77. package/dist/cli/dispatch.mjs +14 -1
  78. package/dist/cli/dispatch.mjs.map +1 -1
  79. package/dist/cli/gate/registry.d.mts +11 -0
  80. package/dist/cli/gate/registry.d.mts.map +1 -1
  81. package/dist/cli/gate/registry.mjs +13 -0
  82. package/dist/cli/gate/registry.mjs.map +1 -1
  83. package/dist/cli/help.d.mts.map +1 -1
  84. package/dist/cli/help.mjs +1 -0
  85. package/dist/cli/help.mjs.map +1 -1
  86. package/dist/cli/host/detect.d.mts +1 -0
  87. package/dist/cli/host/detect.d.mts.map +1 -1
  88. package/dist/cli/host/detect.mjs +5 -0
  89. package/dist/cli/host/detect.mjs.map +1 -1
  90. package/dist/cli/route/envelope.d.mts +68 -4
  91. package/dist/cli/route/envelope.d.mts.map +1 -1
  92. package/dist/cli/route/envelope.mjs +140 -103
  93. package/dist/cli/route/envelope.mjs.map +1 -1
  94. package/dist/cli/route/skill-discover.d.mts +9 -0
  95. package/dist/cli/route/skill-discover.d.mts.map +1 -1
  96. package/dist/cli/route/skill-discover.mjs +11 -0
  97. package/dist/cli/route/skill-discover.mjs.map +1 -1
  98. package/dist/cli/skill-author/contract.d.mts +1 -0
  99. package/dist/cli/skill-author/contract.d.mts.map +1 -1
  100. package/dist/cli/skill-author/contract.mjs +7 -0
  101. package/dist/cli/skill-author/contract.mjs.map +1 -1
  102. package/dist/cli/task/worktree-cleanup.d.mts +9 -1
  103. package/dist/cli/task/worktree-cleanup.d.mts.map +1 -1
  104. package/dist/cli/task/worktree-cleanup.mjs +190 -9
  105. package/dist/cli/task/worktree-cleanup.mjs.map +1 -1
  106. package/dist/plugin/.claude-plugin/plugin.json +1 -1
  107. package/dist/plugin/agents/apt-planner.md +1 -1
  108. package/dist/plugin/skills/apt/SKILL.md +111 -5
  109. package/dist/plugin/skills/apt-author-skill/SKILL.md +11 -0
  110. package/dist/plugin/skills/apt-bootstrap/SKILL.md +1 -0
  111. package/dist/plugin/skills/apt-classify/SKILL.md +1 -0
  112. package/dist/plugin/skills/apt-close-task/SKILL.md +1 -0
  113. package/dist/plugin/skills/apt-create-docs/SKILL.md +1 -0
  114. package/dist/plugin/skills/apt-debug/SKILL.md +2 -0
  115. package/dist/plugin/skills/apt-design/SKILL.md +2 -0
  116. package/dist/plugin/skills/apt-discuss/SKILL.md +2 -0
  117. package/dist/plugin/skills/apt-docs/SKILL.md +2 -0
  118. package/dist/plugin/skills/apt-execute/SKILL.md +1 -0
  119. package/dist/plugin/skills/apt-mockup/SKILL.md +2 -0
  120. package/dist/plugin/skills/apt-pause/SKILL.md +1 -0
  121. package/dist/plugin/skills/apt-personas/SKILL.md +1 -0
  122. package/dist/plugin/skills/apt-plan/SKILL.md +2 -0
  123. package/dist/plugin/skills/apt-pr-review/SKILL.md +1 -0
  124. package/dist/plugin/skills/apt-quick/SKILL.md +2 -0
  125. package/dist/plugin/skills/apt-resume/SKILL.md +1 -0
  126. package/dist/plugin/skills/apt-review/SKILL.md +1 -0
  127. package/dist/plugin/skills/apt-roadmap/SKILL.md +1 -0
  128. package/dist/plugin/skills/apt-roundtable/SKILL.md +2 -0
  129. package/dist/plugin/skills/apt-run/SKILL.md +1 -0
  130. package/dist/plugin/skills/apt-scan/SKILL.md +1 -0
  131. package/dist/plugin/skills/apt-setup/SKILL.md +1 -0
  132. package/dist/plugin/skills/apt-ship/SKILL.md +6 -5
  133. package/dist/plugin/skills/apt-stress-test/SKILL.md +1 -0
  134. package/dist/plugin/skills/apt-terminal/SKILL.md +1 -0
  135. package/dist/plugin/skills/apt-update/SKILL.md +5 -0
  136. package/dist/plugin/skills/apt-verify/SKILL.md +1 -0
  137. package/dist/plugin/skills/apt-verify-proof/SKILL.md +1 -0
  138. package/dist/types/config.d.ts +85 -0
  139. package/dist/types/config.d.ts.map +1 -1
  140. package/package.json +125 -125
  141. package/prompts/coder.md +2 -0
  142. package/prompts/planner.md +12 -0
  143. package/prompts/spec_writer.md +9 -0
  144. package/skills/apt/SKILL.md +1 -0
  145. package/skills/apt-close-task/SKILL.md +32 -1
  146. package/skills/apt-debug/SKILL.md +39 -6
  147. package/skills/apt-debug/appendices/diagnose-discipline.md +119 -0
  148. package/skills/apt-diagram/SKILL.md +342 -0
  149. package/skills/apt-diagram/appendices/design-discipline.md +97 -0
  150. package/skills/apt-discuss/SKILL.md +25 -0
  151. package/skills/apt-discuss/appendices/grill-discipline.md +104 -0
  152. package/skills/apt-discuss/appendices/zoom-out-helper.md +79 -0
  153. package/skills/apt-execute/SKILL.md +48 -5
  154. package/skills/apt-execute/appendices/tdd-mode.md +107 -0
  155. package/skills/apt-improve/DEEPENING.md +84 -0
  156. package/skills/apt-improve/INTERFACE-DESIGN.md +97 -0
  157. package/skills/apt-improve/LANGUAGE.md +104 -0
  158. package/skills/apt-improve/SKILL.md +141 -0
  159. package/skills/apt-plan/SKILL.md +147 -4
  160. package/skills/apt-planner.md +42 -1
  161. package/skills/apt-pr-review/SKILL.md +46 -16
  162. package/skills/apt-prototype/LOGIC.md +109 -0
  163. package/skills/apt-prototype/SKILL.md +143 -0
  164. package/skills/apt-prototype/UI.md +90 -0
  165. package/skills/apt-quick/SKILL.md +30 -0
  166. package/skills/apt-review/SKILL.md +2 -0
  167. package/skills/apt-run/SKILL.md +32 -4
  168. package/skills/apt-setup/SKILL.md +128 -2
  169. package/skills/apt-ship/SKILL.md +47 -0
  170. package/skills/apt-triage/AGENT-BRIEF.md +84 -0
  171. package/skills/apt-triage/OUT-OF-SCOPE.md +75 -0
  172. package/skills/apt-triage/SKILL.md +169 -0
  173. package/skills/apt-verify/SKILL.md +3 -0
  174. package/skills/apt-verify-proof/SKILL.md +3 -0
  175. package/skills/apt-watch-ci/SKILL.md +163 -0
  176. package/skills/apt-zoom-out/SKILL.md +130 -0
  177. package/src/cli/ci-watch/lock.mjs +178 -0
  178. package/src/cli/ci-watch/state.mjs +103 -0
  179. package/src/cli/ci-watch/stop-matrix.mjs +181 -0
  180. package/src/cli/cli-wrappers/ci-watch.mjs +9 -0
  181. package/src/cli/commands/adr.mjs +243 -0
  182. package/src/cli/commands/ci-watch.mjs +503 -0
  183. package/src/cli/commands/context.mjs +244 -0
  184. package/src/cli/commands/event.mjs +63 -24
  185. package/src/cli/commands/host-detect.mjs +33 -7
  186. package/src/cli/commands/modes.mjs +215 -0
  187. package/src/cli/commands/route.mjs +1 -1
  188. package/src/cli/commands/task.mjs +125 -18
  189. package/src/cli/commands/tokens.mjs +157 -6
  190. package/src/cli/commands/triage.mjs +277 -0
  191. package/src/cli/config/post-merge-sweep.mjs +49 -3
  192. package/src/cli/consistency/registry.mjs +14 -0
  193. package/src/cli/coordination/agent-identity.mjs +12 -0
  194. package/src/cli/coordination/event-log.mjs +73 -2
  195. package/src/cli/dispatch.mjs +15 -1
  196. package/src/cli/gate/registry.mjs +14 -0
  197. package/src/cli/help.mjs +1 -0
  198. package/src/cli/host/detect.mjs +5 -0
  199. package/src/cli/route/envelope.mjs +140 -106
  200. package/src/cli/route/skill-discover.mjs +12 -0
  201. package/src/cli/skill-author/contract.mjs +7 -0
  202. package/src/cli/task/worktree-cleanup.mjs +191 -9
  203. package/templates/adr-format.md +56 -0
  204. package/templates/config.json +4 -0
  205. package/templates/context-format.md +34 -0
@@ -0,0 +1,172 @@
1
+ /**
2
+ * ci-watch/lock.mjs — per-PR O_EXCL atomic lock (D-05).
3
+ *
4
+ * Mirrors Claude Code's `cronTasksLock.ts`: a per-PR `.aperant/ci-watches/{pr}.lock`
5
+ * file created with `openSync(..., 'wx')` (O_EXCL). Two concurrent callers
6
+ * cannot both create the file; the loser reads the existing lock and decides
7
+ * whether to declare BUSY or recover a stale lock.
8
+ *
9
+ * Stale-recovery (spec.md §8): a lock is recoverable iff
10
+ * (a) holder PID is dead (`!isPidAlive`), AND
11
+ * (b) `Date.now() - acquiredAt > ttl_seconds*1000`.
12
+ * Live PID alone is NEVER recoverable, even past TTL — TTL alone is not
13
+ * enough. Per D-05: PID-based stale recovery.
14
+ *
15
+ * Lock file shape (spec.md §8):
16
+ * { sessionId, pid, acquiredAt, ttl_seconds }
17
+ *
18
+ * Return shapes:
19
+ * acquireLock → { status: 'ok', lock: {...} }
20
+ * OR { status: 'busy', holder: {...} }
21
+ * releaseLock → boolean (true on release, false if owner mismatch / missing)
22
+ * readLock → lock object or null
23
+ */
24
+ import { closeSync, existsSync, mkdirSync, openSync, readFileSync, unlinkSync, writeSync, } from 'node:fs';
25
+ import { join, resolve } from 'node:path';
26
+ import { isPidAlive } from '../lock/pid.mjs';
27
+ const TTL_SECONDS = 1800; // 30 min — D-05
28
+ function watchesDir(root) {
29
+ return join(resolve(root), '.aperant', 'ci-watches');
30
+ }
31
+ function lockPath(root, pr) {
32
+ return join(watchesDir(root), `${pr}.lock`);
33
+ }
34
+ /**
35
+ * @param {string} root
36
+ * @param {number|string} pr
37
+ */
38
+ export function readLock(root, pr) {
39
+ const path = lockPath(root, pr);
40
+ if (!existsSync(path))
41
+ return null;
42
+ try {
43
+ return JSON.parse(readFileSync(path, 'utf-8'));
44
+ }
45
+ catch {
46
+ // A corrupted lock is treated as no lock — re-acquire will overwrite.
47
+ return null;
48
+ }
49
+ }
50
+ function writeLockFile(path, payload) {
51
+ mkdirSync(resolve(path, '..'), { recursive: true });
52
+ const fd = openSync(path, 'wx');
53
+ try {
54
+ writeSync(fd, `${JSON.stringify(payload, null, 2)}\n`, 0, 'utf-8');
55
+ }
56
+ finally {
57
+ closeSync(fd);
58
+ }
59
+ }
60
+ /**
61
+ * Try to acquire the per-PR lock. On EEXIST, parse the existing lock — if the
62
+ * holder PID is dead AND TTL has elapsed, unlink + retry once. Otherwise
63
+ * return `{status:'busy', holder}`.
64
+ *
65
+ * @param {string} root
66
+ * @param {number|string} pr
67
+ * @param {string} sessionId
68
+ * @param {number} [pid=process.pid]
69
+ */
70
+ export function acquireLock(root, pr, sessionId, pid = process.pid) {
71
+ const path = lockPath(root, pr);
72
+ const payload = {
73
+ sessionId,
74
+ pid,
75
+ acquiredAt: new Date().toISOString(),
76
+ ttl_seconds: TTL_SECONDS,
77
+ };
78
+ try {
79
+ writeLockFile(path, payload);
80
+ return { status: 'ok', lock: payload };
81
+ }
82
+ catch (err) {
83
+ if (err.code !== 'EEXIST')
84
+ throw err;
85
+ }
86
+ // Lock file exists — inspect.
87
+ const existing = readLock(root, pr);
88
+ if (!existing) {
89
+ // Corrupted lock — treat as recoverable, unlink + retry once.
90
+ try {
91
+ unlinkSync(path);
92
+ }
93
+ catch {
94
+ /* race — another caller cleaned up */
95
+ }
96
+ try {
97
+ writeLockFile(path, payload);
98
+ return { status: 'ok', lock: payload };
99
+ }
100
+ catch (err) {
101
+ if (err.code === 'EEXIST') {
102
+ return { status: 'busy', holder: readLock(root, pr) };
103
+ }
104
+ throw err;
105
+ }
106
+ }
107
+ if (isStale(existing)) {
108
+ try {
109
+ unlinkSync(path);
110
+ }
111
+ catch {
112
+ /* race — another caller cleaned up */
113
+ }
114
+ try {
115
+ writeLockFile(path, payload);
116
+ return { status: 'ok', lock: payload };
117
+ }
118
+ catch (err) {
119
+ if (err.code === 'EEXIST') {
120
+ return { status: 'busy', holder: readLock(root, pr) };
121
+ }
122
+ throw err;
123
+ }
124
+ }
125
+ return { status: 'busy', holder: existing };
126
+ }
127
+ /**
128
+ * Release the per-PR lock. Owner must match (`sessionId` equality). Returns
129
+ * `true` if released, `false` if the lock is missing or owned by someone
130
+ * else.
131
+ *
132
+ * @param {string} root
133
+ * @param {number|string} pr
134
+ * @param {string} sessionId
135
+ */
136
+ export function releaseLock(root, pr, sessionId) {
137
+ const path = lockPath(root, pr);
138
+ if (!existsSync(path))
139
+ return false;
140
+ const existing = readLock(root, pr);
141
+ if (!existing) {
142
+ // Corrupted — fail open (let next caller clean up).
143
+ return false;
144
+ }
145
+ if (existing.sessionId !== sessionId)
146
+ return false;
147
+ try {
148
+ unlinkSync(path);
149
+ return true;
150
+ }
151
+ catch {
152
+ return false;
153
+ }
154
+ }
155
+ /**
156
+ * Is the given lock stale (i.e., recoverable by a new caller)? Per D-05,
157
+ * BOTH conditions are required: dead PID AND past TTL.
158
+ *
159
+ * Exposed for unit tests; the orchestrator does not call this directly.
160
+ *
161
+ * @param {{ pid: number, acquiredAt: string, ttl_seconds: number }} lockData
162
+ */
163
+ export function isStale(lockData) {
164
+ if (!lockData || typeof lockData !== 'object')
165
+ return false;
166
+ const ttlMs = (lockData.ttl_seconds || TTL_SECONDS) * 1000;
167
+ const age = Date.now() - new Date(lockData.acquiredAt).getTime();
168
+ if (age <= ttlMs)
169
+ return false;
170
+ return !isPidAlive(lockData.pid);
171
+ }
172
+ //# sourceMappingURL=lock.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lock.mjs","sourceRoot":"","sources":["../../../src/cli/ci-watch/lock.mjs"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Read the per-PR state file. Returns `null` when the file does not exist.
3
+ * Returns `{ error: 'corrupted', message }` when the file exists but is not
4
+ * parseable JSON — callers route that to the infra-backoff branch.
5
+ *
6
+ * @param {string} root
7
+ * @param {number|string} pr
8
+ */
9
+ export function readState(root: string, pr: number | string): any;
10
+ /**
11
+ * Atomically write the per-PR state file. Caller passes the full state object
12
+ * — this function does not merge, it overwrites. Use `readState` first if a
13
+ * merge is needed.
14
+ *
15
+ * @param {string} root
16
+ * @param {number|string} pr
17
+ * @param {object} state
18
+ */
19
+ export function writeState(root: string, pr: number | string, state: object): void;
20
+ /**
21
+ * Delete the per-PR state file. No-op when the file does not exist.
22
+ *
23
+ * @param {string} root
24
+ * @param {number|string} pr
25
+ */
26
+ export function deleteState(root: string, pr: number | string): void;
27
+ /**
28
+ * Enumerate every per-PR state file under `.aperant/ci-watches/`. Returns an
29
+ * array of parsed state objects in lexicographic filename order. Skips files
30
+ * whose name does not match `<pr>.json` and skips corrupted files (their
31
+ * presence does not block the rest of the listing).
32
+ *
33
+ * @param {string} root
34
+ */
35
+ export function listWatches(root: string): any[];
36
+ //# sourceMappingURL=state.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.d.mts","sourceRoot":"","sources":["../../../src/cli/ci-watch/state.mjs"],"names":[],"mappings":"AAsCA;;;;;;;GAOG;AACH,gCAHW,MAAM,MACN,MAAM,GAAC,MAAM,OAUvB;AAED;;;;;;;;GAQG;AACH,iCAJW,MAAM,MACN,MAAM,GAAC,MAAM,SACb,MAAM,QAIhB;AAED;;;;;GAKG;AACH,kCAHW,MAAM,MACN,MAAM,GAAC,MAAM,QAKvB;AAED;;;;;;;GAOG;AACH,kCAFW,MAAM,SAgBhB"}
@@ -0,0 +1,103 @@
1
+ /**
2
+ * ci-watch/state.mjs — per-PR state-file primitives for the /apt:watch-ci
3
+ * post-ship watcher (D-04).
4
+ *
5
+ * Storage shape (`.aperant/ci-watches/{pr}.json`), spec.md §8:
6
+ *
7
+ * {
8
+ * pr, task_id, host, scheduler_handle | null,
9
+ * attempts, started_at, deadline, last_poll,
10
+ * last_status | null, next_tick_interval_ms,
11
+ * paused, auto_merge_on_green
12
+ * }
13
+ *
14
+ * This module is pure read/write — classification logic lives in
15
+ * stop-matrix.mjs, the orchestrator lives in commands/ci-watch.mjs.
16
+ *
17
+ * Crash-safety: `writeState` goes through `atomicWriteJson` (tmp + fsync +
18
+ * rename). `readState` returns `null` for a missing file and never throws on
19
+ * a missing path — callers can distinguish "no watch yet" from "watch is
20
+ * corrupted".
21
+ *
22
+ * The corrupted-JSON branch returns a recoverable error envelope rather than
23
+ * throwing so the orchestrator can surface it without crashing the cron
24
+ * payload mid-tick.
25
+ */
26
+ import { existsSync, readdirSync, readFileSync, unlinkSync } from 'node:fs';
27
+ import { join, resolve } from 'node:path';
28
+ import { atomicWriteJson } from '../util/io.mjs';
29
+ function watchesDir(root) {
30
+ return join(resolve(root), '.aperant', 'ci-watches');
31
+ }
32
+ function stateFile(root, pr) {
33
+ return join(watchesDir(root), `${pr}.json`);
34
+ }
35
+ /**
36
+ * Read the per-PR state file. Returns `null` when the file does not exist.
37
+ * Returns `{ error: 'corrupted', message }` when the file exists but is not
38
+ * parseable JSON — callers route that to the infra-backoff branch.
39
+ *
40
+ * @param {string} root
41
+ * @param {number|string} pr
42
+ */
43
+ export function readState(root, pr) {
44
+ const path = stateFile(root, pr);
45
+ if (!existsSync(path))
46
+ return null;
47
+ try {
48
+ return JSON.parse(readFileSync(path, 'utf-8'));
49
+ }
50
+ catch (err) {
51
+ return { error: 'corrupted', message: err.message };
52
+ }
53
+ }
54
+ /**
55
+ * Atomically write the per-PR state file. Caller passes the full state object
56
+ * — this function does not merge, it overwrites. Use `readState` first if a
57
+ * merge is needed.
58
+ *
59
+ * @param {string} root
60
+ * @param {number|string} pr
61
+ * @param {object} state
62
+ */
63
+ export function writeState(root, pr, state) {
64
+ atomicWriteJson(stateFile(root, pr), state);
65
+ }
66
+ /**
67
+ * Delete the per-PR state file. No-op when the file does not exist.
68
+ *
69
+ * @param {string} root
70
+ * @param {number|string} pr
71
+ */
72
+ export function deleteState(root, pr) {
73
+ const path = stateFile(root, pr);
74
+ if (existsSync(path))
75
+ unlinkSync(path);
76
+ }
77
+ /**
78
+ * Enumerate every per-PR state file under `.aperant/ci-watches/`. Returns an
79
+ * array of parsed state objects in lexicographic filename order. Skips files
80
+ * whose name does not match `<pr>.json` and skips corrupted files (their
81
+ * presence does not block the rest of the listing).
82
+ *
83
+ * @param {string} root
84
+ */
85
+ export function listWatches(root) {
86
+ const dir = watchesDir(root);
87
+ if (!existsSync(dir))
88
+ return [];
89
+ const watches = [];
90
+ for (const f of readdirSync(dir).sort()) {
91
+ if (!/^\d+\.json$/.test(f))
92
+ continue;
93
+ try {
94
+ watches.push(JSON.parse(readFileSync(join(dir, f), 'utf-8')));
95
+ }
96
+ catch {
97
+ // Skip corrupted state files — listWatches is a read-only enumerator
98
+ // and must not throw on partial corruption.
99
+ }
100
+ }
101
+ return watches;
102
+ }
103
+ //# sourceMappingURL=state.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.mjs","sourceRoot":"","sources":["../../../src/cli/ci-watch/state.mjs"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAC3E,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAEhD,SAAS,UAAU,CAAC,IAAI;IACvB,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,UAAU,EAAE,YAAY,CAAC,CAAA;AACrD,CAAC;AAED,SAAS,SAAS,CAAC,IAAI,EAAE,EAAE;IAC1B,OAAO,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,CAAA;AAC5C,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,SAAS,CAAC,IAAI,EAAE,EAAE;IACjC,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,EAAE,EAAE,CAAC,CAAA;IAChC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAA;IAClC,IAAI,CAAC;QACJ,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAA;IAC/C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,CAAA;IACpD,CAAC;AACF,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,UAAU,CAAC,IAAI,EAAE,EAAE,EAAE,KAAK;IACzC,eAAe,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,KAAK,CAAC,CAAA;AAC5C,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,IAAI,EAAE,EAAE;IACnC,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,EAAE,EAAE,CAAC,CAAA;IAChC,IAAI,UAAU,CAAC,IAAI,CAAC;QAAE,UAAU,CAAC,IAAI,CAAC,CAAA;AACvC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CAAC,IAAI;IAC/B,MAAM,GAAG,GAAG,UAAU,CAAC,IAAI,CAAC,CAAA;IAC5B,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,EAAE,CAAA;IAC/B,MAAM,OAAO,GAAG,EAAE,CAAA;IAClB,KAAK,MAAM,CAAC,IAAI,WAAW,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;QACzC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC;YAAE,SAAQ;QACpC,IAAI,CAAC;YACJ,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAA;QAC9D,CAAC;QAAC,MAAM,CAAC;YACR,qEAAqE;YACrE,4CAA4C;QAC7C,CAAC;IACF,CAAC;IACD,OAAO,OAAO,CAAA;AACf,CAAC"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * @typedef {Object} CheckRow
3
+ * @property {string} [name]
4
+ * @property {string} [conclusion] e.g. 'success' | 'failure' | 'cancelled' | 'timed_out' | 'neutral'
5
+ * @property {string} [state]
6
+ * @property {string} [bucket]
7
+ * @property {string} [failure_class] — optional pre-classified hint (test-only seam)
8
+ * @property {string} [log_fingerprint] — optional digest for identical-log detection
9
+ *
10
+ * @typedef {Object} ClassifyInput
11
+ * @property {CheckRow[]} checks
12
+ * @property {Object} state — per-PR state (see state.mjs §8)
13
+ * @property {Object} prMetadata — { state: 'OPEN'|'CLOSED'|'MERGED', reviewDecision?: string }
14
+ *
15
+ * @typedef {Object} ClassifyOutput
16
+ * @property {string} action
17
+ * @property {boolean} counts_toward_budget
18
+ * @property {string} reason
19
+ *
20
+ * @param {ClassifyInput} input
21
+ * @returns {ClassifyOutput}
22
+ */
23
+ export function classify({ checks, state, prMetadata }: ClassifyInput): ClassifyOutput;
24
+ export function isFailing(check: any): boolean;
25
+ export type CheckRow = {
26
+ name?: string | undefined;
27
+ /**
28
+ * e.g. 'success' | 'failure' | 'cancelled' | 'timed_out' | 'neutral'
29
+ */
30
+ conclusion?: string | undefined;
31
+ state?: string | undefined;
32
+ bucket?: string | undefined;
33
+ /**
34
+ * — optional pre-classified hint (test-only seam)
35
+ */
36
+ failure_class?: string | undefined;
37
+ /**
38
+ * — optional digest for identical-log detection
39
+ */
40
+ log_fingerprint?: string | undefined;
41
+ };
42
+ export type ClassifyInput = {
43
+ checks: CheckRow[];
44
+ /**
45
+ * — per-PR state (see state.mjs §8)
46
+ */
47
+ state: Object;
48
+ /**
49
+ * — { state: 'OPEN'|'CLOSED'|'MERGED', reviewDecision?: string }
50
+ */
51
+ prMetadata: Object;
52
+ };
53
+ export type ClassifyOutput = {
54
+ action: string;
55
+ counts_toward_budget: boolean;
56
+ reason: string;
57
+ };
58
+ //# sourceMappingURL=stop-matrix.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stop-matrix.d.mts","sourceRoot":"","sources":["../../../src/cli/ci-watch/stop-matrix.mjs"],"names":[],"mappings":"AA8BA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wDAHW,aAAa,GACX,cAAc,CA6H1B;AAED,+CAGC;;;;;;;;;;;;;;;;;;;YA5Ia,QAAQ,EAAE;;;;WACV,MAAM;;;;gBACN,MAAM;;;YAGN,MAAM;0BACN,OAAO;YACP,MAAM"}
@@ -0,0 +1,164 @@
1
+ /**
2
+ * ci-watch/stop-matrix.mjs — pure stop-matrix classifier (D-06).
3
+ *
4
+ * `classify({checks, state, prMetadata})` returns
5
+ * { action, counts_toward_budget, reason }
6
+ *
7
+ * No I/O, no fs, no gh CLI shell-outs — caller pre-fetches `checks` (the
8
+ * payload from `gh pr checks --json name,bucket,state,conclusion,detailsUrl`)
9
+ * and `prMetadata` (`gh pr view --json state,reviewDecision`) and passes them
10
+ * in. The orchestrator in commands/ci-watch.mjs handles the side effects
11
+ * implied by the action.
12
+ *
13
+ * Predicate order matters — short-circuits go first (merged/closed/green/cap
14
+ * fire before any per-check inspection). See spec.md §9 table for the full
15
+ * ordering.
16
+ *
17
+ * The 11 actions:
18
+ * merged, closed, green, attempts-cap, wall-cap, reviewer-pause,
19
+ * identical-log, flake-skip, infra-backoff, diverged-rebased,
20
+ * diverged-bailed, fix
21
+ *
22
+ * `counts_toward_budget` is `true` for fix attempts (including rebase
23
+ * attempts and rebase-bail); `false` for flake-skip / infra-backoff; `false`
24
+ * (with a sentinel meaning "n/a") for terminal actions.
25
+ */
26
+ const ATTEMPTS_CAP = 5;
27
+ const INFRA_FAILURE_CLASSES = new Set(['runner-oom', 'runner-cancelled', 'gh-5xx', 'timeout']);
28
+ /**
29
+ * @typedef {Object} CheckRow
30
+ * @property {string} [name]
31
+ * @property {string} [conclusion] e.g. 'success' | 'failure' | 'cancelled' | 'timed_out' | 'neutral'
32
+ * @property {string} [state]
33
+ * @property {string} [bucket]
34
+ * @property {string} [failure_class] — optional pre-classified hint (test-only seam)
35
+ * @property {string} [log_fingerprint] — optional digest for identical-log detection
36
+ *
37
+ * @typedef {Object} ClassifyInput
38
+ * @property {CheckRow[]} checks
39
+ * @property {Object} state — per-PR state (see state.mjs §8)
40
+ * @property {Object} prMetadata — { state: 'OPEN'|'CLOSED'|'MERGED', reviewDecision?: string }
41
+ *
42
+ * @typedef {Object} ClassifyOutput
43
+ * @property {string} action
44
+ * @property {boolean} counts_toward_budget
45
+ * @property {string} reason
46
+ *
47
+ * @param {ClassifyInput} input
48
+ * @returns {ClassifyOutput}
49
+ */
50
+ export function classify({ checks, state, prMetadata }) {
51
+ const pr = prMetadata || {};
52
+ const st = state || {};
53
+ const rows = Array.isArray(checks) ? checks : [];
54
+ // 1. MERGED.
55
+ if (pr.state === 'MERGED') {
56
+ return { action: 'merged', counts_toward_budget: false, reason: 'pr is merged' };
57
+ }
58
+ // 2. CLOSED (not merged).
59
+ if (pr.state === 'CLOSED') {
60
+ return { action: 'closed', counts_toward_budget: false, reason: 'pr is closed unmerged' };
61
+ }
62
+ const failed = rows.filter((c) => isFailing(c));
63
+ const allGreen = rows.length > 0 && rows.every((c) => c.conclusion === 'success');
64
+ // 3. All checks green.
65
+ if (allGreen) {
66
+ return { action: 'green', counts_toward_budget: false, reason: 'all checks succeeded' };
67
+ }
68
+ // 3b. No checks registered yet — CI hasn't enqueued jobs. Wait without
69
+ // burning a fix attempt.
70
+ if (rows.length === 0) {
71
+ return { action: 'wait', counts_toward_budget: false, reason: 'no checks registered yet' };
72
+ }
73
+ // 4. Attempts cap (only when red present — otherwise green/merged/closed would have fired).
74
+ const attempts = st.attempts || 0;
75
+ if (attempts >= ATTEMPTS_CAP && failed.length > 0) {
76
+ return {
77
+ action: 'attempts-cap',
78
+ counts_toward_budget: false,
79
+ reason: `${attempts} fix attempts used; cap is ${ATTEMPTS_CAP}`,
80
+ };
81
+ }
82
+ // 5. Wall-clock cap.
83
+ if (st.deadline && Date.now() > new Date(st.deadline).getTime() && failed.length > 0) {
84
+ return {
85
+ action: 'wall-cap',
86
+ counts_toward_budget: false,
87
+ reason: 'past 4h deadline',
88
+ };
89
+ }
90
+ // 6. Reviewer asked for changes.
91
+ if (pr.reviewDecision === 'CHANGES_REQUESTED') {
92
+ return {
93
+ action: 'reviewer-pause',
94
+ counts_toward_budget: false,
95
+ reason: 'reviewer requested changes — pausing',
96
+ };
97
+ }
98
+ // 7. Identical-log → non-converging fixer.
99
+ // Compare aggregate fingerprints (same format as the write side in ci-watch.mjs).
100
+ if (attempts > 0 && st.last_status && failed.length > 0) {
101
+ const currentFingerprint = failed
102
+ .map((f) => `${f.name}:${f.log_fingerprint ?? ''}`)
103
+ .sort()
104
+ .join('|');
105
+ if (currentFingerprint === st.last_status) {
106
+ return {
107
+ action: 'identical-log',
108
+ counts_toward_budget: false,
109
+ reason: 'failed-check fingerprint identical to last attempt',
110
+ };
111
+ }
112
+ }
113
+ // 8. Flake-skip — same check failed then passed on rerun, no commit between.
114
+ // The classifier sees this as an explicit hint from the caller: failed rows
115
+ // classified as `bucket === 'flake'` or `failure_class === 'flake'` are the
116
+ // flaky-test variety and must not burn a fix attempt. Fires when EVERY
117
+ // remaining failure is a flake — if any non-flake failure is present, we
118
+ // fall through and the real failure governs.
119
+ if (failed.length > 0 &&
120
+ failed.every((c) => c.bucket === 'flake' || c.failure_class === 'flake')) {
121
+ return {
122
+ action: 'flake-skip',
123
+ counts_toward_budget: false,
124
+ reason: 'check transient — passed on rerun',
125
+ };
126
+ }
127
+ // 9. Infra failure class.
128
+ if (failed.some((c) => INFRA_FAILURE_CLASSES.has(c.failure_class))) {
129
+ return {
130
+ action: 'infra-backoff',
131
+ counts_toward_budget: false,
132
+ reason: 'infra-class failure — backoff and retry',
133
+ };
134
+ }
135
+ // 10. Diverged branch — caller has already done the fetch + dry-rebase and
136
+ // passes the outcome in `prMetadata.diverged`:
137
+ // { rebased: true } → action 'diverged-rebased' (counts)
138
+ // { rebased: false } → action 'diverged-bailed' (counts)
139
+ if (pr.diverged && typeof pr.diverged === 'object') {
140
+ if (pr.diverged.rebased === true) {
141
+ return {
142
+ action: 'diverged-rebased',
143
+ counts_toward_budget: true,
144
+ reason: 'origin ahead; rebased successfully',
145
+ };
146
+ }
147
+ return {
148
+ action: 'diverged-bailed',
149
+ counts_toward_budget: true,
150
+ reason: 'origin ahead; rebase conflict',
151
+ };
152
+ }
153
+ // 11. Default red — go fix.
154
+ return {
155
+ action: 'fix',
156
+ counts_toward_budget: true,
157
+ reason: 'red checks present — attempting fix',
158
+ };
159
+ }
160
+ export function isFailing(check) {
161
+ const c = check.conclusion;
162
+ return c === 'failure' || c === 'cancelled' || c === 'timed_out' || c === 'action_required';
163
+ }
164
+ //# sourceMappingURL=stop-matrix.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stop-matrix.mjs","sourceRoot":"","sources":["../../../src/cli/ci-watch/stop-matrix.mjs"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export { cmdCiWatch as cliCiWatch } from "../commands/ci-watch.mjs";
2
+ //# sourceMappingURL=ci-watch.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ci-watch.d.mts","sourceRoot":"","sources":["../../../src/cli/cli-wrappers/ci-watch.mjs"],"names":[],"mappings":""}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * cli-wrappers/ci-watch.mjs — Layer 2 wrapper for the ci-watch cmd.
3
+ *
4
+ * Today this is a passthrough — DISPATCH calls wrap(cmdCiWatch) directly.
5
+ * Re-exporting under cliCiWatch keeps the file-per-cmd structural seam
6
+ * (mirrors task.mjs / pr-review.mjs).
7
+ */
8
+ export { cmdCiWatch as cliCiWatch } from '../commands/ci-watch.mjs';
9
+ //# sourceMappingURL=ci-watch.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ci-watch.mjs","sourceRoot":"","sources":["../../../src/cli/cli-wrappers/ci-watch.mjs"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,UAAU,IAAI,UAAU,EAAE,MAAM,0BAA0B,CAAA"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * CLI entry: `apt-tools adr <subcommand> <project-dir> [flags]`.
3
+ */
4
+ export function cmdAdr(subcommand: any, projectDir: any, extraArgs: any): import("../util/result.mjs").CommandResult;
5
+ //# sourceMappingURL=adr.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adr.d.mts","sourceRoot":"","sources":["../../../src/cli/commands/adr.mjs"],"names":[],"mappings":"AAmIA;;GAEG;AACH,qHA4GC"}