faberun 0.3.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 (144) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +131 -0
  3. package/bin/faberun.mjs +25 -0
  4. package/integrations/claude-code/statusline-bench.sh +42 -0
  5. package/integrations/claude-code/statusline.sh +80 -0
  6. package/package.json +33 -0
  7. package/skills/faberun/SKILL.md +24 -0
  8. package/skills/faberun/references/contract.md +380 -0
  9. package/skills/faberun/references/engineering.md +29 -0
  10. package/skills/faberun/references/handoffs.md +26 -0
  11. package/skills/faberun/references/operations.md +184 -0
  12. package/skills/faberun/references/rules.md +35 -0
  13. package/skills/faberun/references/workflow.md +23 -0
  14. package/skills/init-agentkit/SKILL.md +108 -0
  15. package/skills/init-agentkit/scripts/install-agentkit.sh +127 -0
  16. package/skills/init-agentkit/templates/.claude/commands/create-adr.md +44 -0
  17. package/skills/init-agentkit/templates/.github/workflows/quality.yml +43 -0
  18. package/skills/init-agentkit/templates/.sentrux/baseline.json +9 -0
  19. package/skills/init-agentkit/templates/.sentrux/rules.toml +21 -0
  20. package/skills/init-agentkit/templates/AGENTS.md +110 -0
  21. package/skills/init-agentkit/templates/docs/ABSTRACTIONS.md +30 -0
  22. package/skills/init-agentkit/templates/docs/ARCHITECTURE.md +31 -0
  23. package/skills/init-agentkit/templates/docs/GETTING-STARTED.md +44 -0
  24. package/skills/init-agentkit/templates/docs/VISION.md +33 -0
  25. package/skills/init-agentkit/templates/docs/adr/0001-record-architecture-decisions.md +36 -0
  26. package/skills/init-agentkit/templates/docs/adr/0002-root-managed-ai-guidance.md +37 -0
  27. package/skills/init-agentkit/templates/docs/adr/0003-sentrux-structural-quality-gates.md +49 -0
  28. package/skills/init-agentkit/templates/docs/adr/README.md +52 -0
  29. package/skills/init-agentkit/templates/docs/sentrux.md +66 -0
  30. package/skills/init-agentkit/templates/githooks/commit-msg +22 -0
  31. package/skills/init-agentkit/templates/githooks/pre-commit +32 -0
  32. package/src/campaign/brief.mjs +394 -0
  33. package/src/campaign/chain.mjs +555 -0
  34. package/src/campaign/handoff.mjs +516 -0
  35. package/src/campaign/index.mjs +300 -0
  36. package/src/campaign/journal.mjs +347 -0
  37. package/src/campaign/layout.mjs +51 -0
  38. package/src/campaign/metrics-evals.mjs +25 -0
  39. package/src/campaign/metrics.mjs +517 -0
  40. package/src/campaign/projection.mjs +250 -0
  41. package/src/campaign/record.mjs +102 -0
  42. package/src/campaign/unpark.mjs +56 -0
  43. package/src/cli/brand.mjs +205 -0
  44. package/src/cli/campaign.mjs +730 -0
  45. package/src/cli/contract.mjs +67 -0
  46. package/src/cli/init.mjs +170 -0
  47. package/src/cli/launch.mjs +239 -0
  48. package/src/cli/seat.mjs +139 -0
  49. package/src/cli/setup.mjs +294 -0
  50. package/src/cli/skills.mjs +105 -0
  51. package/src/cli/update.mjs +216 -0
  52. package/src/cli.mjs +525 -0
  53. package/src/contract/articles.mjs +12 -0
  54. package/src/contract/assert.mjs +162 -0
  55. package/src/contract/definition-of-done.mjs +97 -0
  56. package/src/contract/final-verification.mjs +96 -0
  57. package/src/contract/index.mjs +641 -0
  58. package/src/contract/judge-envelope.mjs +25 -0
  59. package/src/contract/review-modes.mjs +151 -0
  60. package/src/contract/runtime.mjs +204 -0
  61. package/src/contract/schema-version.mjs +25 -0
  62. package/src/contract/scope-findings.mjs +77 -0
  63. package/src/contract/snapshot.mjs +639 -0
  64. package/src/contract/task-packet.mjs +495 -0
  65. package/src/contract/untrusted.mjs +75 -0
  66. package/src/contract/verification.mjs +185 -0
  67. package/src/contract/worker-result.mjs +138 -0
  68. package/src/engine/assignment.mjs +63 -0
  69. package/src/engine/backoff.mjs +492 -0
  70. package/src/engine/bulk-read.mjs +361 -0
  71. package/src/engine/cancel.mjs +177 -0
  72. package/src/engine/detach.mjs +101 -0
  73. package/src/engine/dispatch.mjs +752 -0
  74. package/src/engine/failover.mjs +192 -0
  75. package/src/engine/gate.mjs +183 -0
  76. package/src/engine/judge-gate.mjs +517 -0
  77. package/src/engine/lifecycle.mjs +772 -0
  78. package/src/engine/live-preflight.mjs +299 -0
  79. package/src/engine/mutation.mjs +146 -0
  80. package/src/engine/notify-queue.mjs +327 -0
  81. package/src/engine/process-identity.mjs +72 -0
  82. package/src/engine/process.mjs +774 -0
  83. package/src/engine/prompts.mjs +289 -0
  84. package/src/engine/recover.mjs +300 -0
  85. package/src/engine/result-file.mjs +222 -0
  86. package/src/engine/resume.mjs +635 -0
  87. package/src/engine/retry.mjs +334 -0
  88. package/src/engine/review.mjs +228 -0
  89. package/src/engine/run-command.mjs +287 -0
  90. package/src/engine/run-identity.mjs +411 -0
  91. package/src/engine/runtime-discovery.mjs +235 -0
  92. package/src/engine/scheduler.mjs +526 -0
  93. package/src/engine/scope.mjs +378 -0
  94. package/src/engine/settle.mjs +207 -0
  95. package/src/engine/state.mjs +148 -0
  96. package/src/engine/supervise.mjs +713 -0
  97. package/src/engine/verify.mjs +167 -0
  98. package/src/harnesses/agy/index.mjs +62 -0
  99. package/src/harnesses/catalogue.mjs +509 -0
  100. package/src/harnesses/claude/index.mjs +90 -0
  101. package/src/harnesses/codex/index.mjs +87 -0
  102. package/src/harnesses/dsh/closed-packet.patch.yml +42 -0
  103. package/src/harnesses/dsh/index.mjs +210 -0
  104. package/src/harnesses/dsh/runner.mjs +259 -0
  105. package/src/harnesses/exec-jsonl/index.mjs +788 -0
  106. package/src/harnesses/index.mjs +508 -0
  107. package/src/harnesses/protocol.mjs +531 -0
  108. package/src/harnesses/replay/bin.mjs +386 -0
  109. package/src/harnesses/replay/index.mjs +238 -0
  110. package/src/harnesses/zcode/index.mjs +276 -0
  111. package/src/host/config.mjs +87 -0
  112. package/src/host/home.mjs +149 -0
  113. package/src/host/package.mjs +23 -0
  114. package/src/host/preflight.mjs +520 -0
  115. package/src/host/tool-policy-decisions.mjs +341 -0
  116. package/src/host/tool-policy-hook.mjs +270 -0
  117. package/src/notify/index.mjs +359 -0
  118. package/src/notify/os-macos.mjs +81 -0
  119. package/src/repo/declared-paths.mjs +220 -0
  120. package/src/repo/integrate.mjs +546 -0
  121. package/src/repo/scope-closure.mjs +665 -0
  122. package/src/repo/signal-block.mjs +16 -0
  123. package/src/repo/signal.mjs +222 -0
  124. package/src/repo/source-identity.mjs +295 -0
  125. package/src/repo/workspace.mjs +557 -0
  126. package/src/repo/worktree.mjs +352 -0
  127. package/src/report/final.mjs +200 -0
  128. package/src/report/metrics-report.mjs +99 -0
  129. package/src/report/next.mjs +383 -0
  130. package/src/report/render.mjs +716 -0
  131. package/src/run/disk-gc.mjs +251 -0
  132. package/src/run/lock.mjs +329 -0
  133. package/src/run/node-store.mjs +62 -0
  134. package/src/run/operations.mjs +286 -0
  135. package/src/run/store.mjs +187 -0
  136. package/src/run/usage.mjs +337 -0
  137. package/src/seat/harnesses.mjs +83 -0
  138. package/src/seat/index.mjs +239 -0
  139. package/src/seat/tmux.mjs +208 -0
  140. package/src/util.mjs +0 -0
  141. package/src/web/api.mjs +371 -0
  142. package/src/web/boundary.mjs +88 -0
  143. package/src/web/index.html +299 -0
  144. package/src/web/server.mjs +552 -0
@@ -0,0 +1,148 @@
1
+ /**
2
+ * A node's persisted state and the event log beside it: every status change goes
3
+ * through `transition`, which writes the snapshot and appends one event.
4
+ *
5
+ * `ensureTerminalEvent` exists because a crash can leave a node terminal on disk
6
+ * with no event recorded for it, and the campaign readers project from events.
7
+ */
8
+ import { CONTRACT_VERSION, PROTOCOL_SCHEMA_VERSION } from "../harnesses/index.mjs";
9
+ import { SETTLED } from "./prompts.mjs";
10
+ import { appendJsonl } from "../run/store.mjs";
11
+ import { excerpt } from "../util.mjs";
12
+ import { hasOperationSettlement, operationNextState, settleInvocation } from "../run/operations.mjs";
13
+ import { join } from "node:path";
14
+ import { readFileSync } from "node:fs";
15
+
16
+ import { writeNodeSnapshot } from "../run/node-store.mjs";
17
+ import { validateEvent } from "../contract/snapshot.mjs";
18
+
19
+ /** @typedef {ReturnType<typeof import("../run/lock.mjs").acquire>} LockHandle */
20
+ /** @typedef {import("../contract/index.mjs").NodeSnapshot} NodeSnapshot */
21
+
22
+ /**
23
+ * @param {string} runDir
24
+ * @param {NodeSnapshot} state
25
+ * @param {import("../contract/index.mjs").NodeStatus} status
26
+ * @param {Record<string, unknown>} [patch]
27
+ * @param {LockHandle|null} [lock]
28
+ */
29
+ export function transition(runDir, state, status, patch = {}, lock = null) {
30
+ lock?.assert();
31
+ const from = state.status;
32
+ const updatedAt = new Date().toISOString();
33
+ Object.assign(state, patch, { status, updatedAt });
34
+ writeNode(runDir, state, lock);
35
+ if (status === "done" && process.env.FABERUN_INTEGRATION_INTERRUPT === "after-state") {
36
+ throw new Error("integration interrupted after node state write");
37
+ }
38
+ const invocation = state.invocations?.at(-1);
39
+ if (invocation && hasOperationSettlement(runDir, invocation.id)) {
40
+ settleInvocation(runDir, invocation, { nextState: operationNextState(state) });
41
+ }
42
+ appendTransitionEvent(runDir, state, from, status, {}, lock);
43
+ if (SETTLED.has(status)) {
44
+ const note = state.gate?.summary ?? resultSummary(state.result) ?? state.error?.message;
45
+ process.stdout.write(`[node] ${state.id} ${status}${note ? ` · ${note}` : ""}\n`);
46
+ }
47
+ }
48
+ /**
49
+ * @param {string} runDir
50
+ * @param {NodeSnapshot} state
51
+ * @param {LockHandle|null} [lock]
52
+ */
53
+ export function ensureTerminalEvent(runDir, state, lock = null) {
54
+ if (!hasDoneEvent(runDir, state.id, state.attempt)) {
55
+ appendTransitionEvent(runDir, state, "done", "done", { recovery: "terminal side effects replayed" }, lock);
56
+ }
57
+ }
58
+ /**
59
+ * Whether a `done` transition for this node and attempt was already durably
60
+ * recorded, at any point in the past. This is the idempotent signal for "this
61
+ * attempt's integration effects were already fully applied at least once" —
62
+ * a later, unrelated change to the node's current status is not evidence
63
+ * that they need reapplying.
64
+ *
65
+ * @param {string} runDir
66
+ * @param {string} nodeId
67
+ * @param {number} attempt
68
+ * @returns {boolean}
69
+ */
70
+ export function hasDoneEvent(runDir, nodeId, attempt) {
71
+ let text = "";
72
+ try { text = readFileSync(join(runDir, "events.jsonl"), "utf8"); } catch {
73
+ // ENOENT: no events file yet means no terminal event was recorded.
74
+ }
75
+ return text.split("\n").filter(Boolean).some((line) => {
76
+ try {
77
+ const event = JSON.parse(line);
78
+ return event.node === nodeId && event.to === "done" && event.attempt === attempt;
79
+ } catch {
80
+ return false;
81
+ }
82
+ });
83
+ }
84
+ /**
85
+ * @param {unknown} result
86
+ * @returns {string|null}
87
+ */
88
+ function resultSummary(result) {
89
+ if (typeof result === "object" && result !== null && "summary" in result) {
90
+ const summary = /** @type {{summary?: unknown}} */ (result).summary;
91
+ if (typeof summary === "string") return summary;
92
+ }
93
+ return typeof result === "string" && result ? excerpt(result) : null;
94
+ }
95
+ /**
96
+ * @param {string} runDir
97
+ * @param {NodeSnapshot} state
98
+ * @param {string} from
99
+ * @param {string} to
100
+ * @param {Record<string, unknown>} [details]
101
+ * @param {LockHandle|null} [lock]
102
+ */
103
+ export function appendTransitionEvent(runDir, state, from, to, details = {}, lock = null) {
104
+ lock?.assert();
105
+ /** @type {Record<string, unknown>} */
106
+ const event = {
107
+ schemaVersion: PROTOCOL_SCHEMA_VERSION,
108
+ contractVersion: CONTRACT_VERSION,
109
+ at: state.updatedAt,
110
+ node: state.id,
111
+ sourceIdentity: state.sourceIdentity,
112
+ packetHash: state.packetHash,
113
+ from,
114
+ to,
115
+ phase: state.phase,
116
+ ...details,
117
+ };
118
+ if (state.attempt) event.attempt = state.attempt;
119
+ if (state.runtime?.id) event.runtime = state.runtime.id;
120
+ if (state.error?.code) event.error = state.error.code;
121
+ if (state.gate?.verdict) event.verdict = state.gate.verdict;
122
+ if (state.gate?.summary) event.summary = state.gate.summary;
123
+ if (state.revisions) event.revisions = state.revisions;
124
+ const invocation = state.invocations?.at(-1);
125
+ if (invocation?.id) event.invocationId = invocation.id;
126
+ validateEvent(event);
127
+ appendJsonl(join(runDir, "events.jsonl"), event);
128
+ }
129
+ /**
130
+ * @param {string} runDir
131
+ * @param {NodeSnapshot} state
132
+ * @param {import("../contract/index.mjs").ExecutionOverride} override
133
+ * @param {LockHandle} lock
134
+ */
135
+ export function recordExecutionOverride(runDir, state, override, lock) {
136
+ const entry = { ...override, at: override.at ?? new Date().toISOString() };
137
+ state.executionOverrides = [...(state.executionOverrides ?? []), entry];
138
+ writeNode(runDir, state, lock);
139
+ appendTransitionEvent(runDir, state, state.status, state.status, { override: entry, recovery: entry.decision }, lock);
140
+ }
141
+ /**
142
+ * @param {string} runDir
143
+ * @param {NodeSnapshot} state
144
+ * @param {LockHandle|null} [lock]
145
+ */
146
+ export function writeNode(runDir, state, lock = null) {
147
+ writeNodeSnapshot(runDir, state, lock);
148
+ }