@sugar-crash-studios/vibe-forge 0.4.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 (201) hide show
  1. package/.claude/commands/clear-attention.md +63 -0
  2. package/.claude/commands/compact-context.md +52 -0
  3. package/.claude/commands/configure-vcs.md +102 -0
  4. package/.claude/commands/forge.md +171 -0
  5. package/.claude/commands/need-help.md +77 -0
  6. package/.claude/commands/update-status.md +64 -0
  7. package/.claude/commands/worker-loop.md +106 -0
  8. package/.claude/hooks/worker-loop.js +198 -0
  9. package/.claude/scripts/setup-worker-loop.sh +45 -0
  10. package/.claude/settings.local.json +46 -0
  11. package/LICENSE +21 -0
  12. package/README.md +238 -0
  13. package/agents/aegis/personality.md +294 -0
  14. package/agents/anvil/personality.md +276 -0
  15. package/agents/architect/personality.md +258 -0
  16. package/agents/crucible/personality.md +360 -0
  17. package/agents/ember/personality.md +291 -0
  18. package/agents/forge-master/capabilities.md +144 -0
  19. package/agents/forge-master/context-template.md +128 -0
  20. package/agents/forge-master/personality.md +138 -0
  21. package/agents/furnace/personality.md +340 -0
  22. package/agents/herald/personality.md +247 -0
  23. package/agents/loki/personality.md +108 -0
  24. package/agents/oracle/personality.md +283 -0
  25. package/agents/pixel/personality.md +113 -0
  26. package/agents/planning-hub/personality.md +320 -0
  27. package/agents/scribe/personality.md +251 -0
  28. package/agents/temper/personality.md +218 -0
  29. package/bin/cli.js +375 -0
  30. package/bin/dashboard/api/agents.js +333 -0
  31. package/bin/dashboard/api/dispatch.js +483 -0
  32. package/bin/dashboard/api/tasks.js +416 -0
  33. package/bin/dashboard/frontend/index.html +13 -0
  34. package/bin/dashboard/frontend/package.json +16 -0
  35. package/bin/dashboard/frontend/src/App.svelte +222 -0
  36. package/bin/dashboard/frontend/src/app.css +1777 -0
  37. package/bin/dashboard/frontend/src/lib/components/AgentCard.svelte +60 -0
  38. package/bin/dashboard/frontend/src/lib/components/AgentsPanel.svelte +57 -0
  39. package/bin/dashboard/frontend/src/lib/components/DispatchModal.svelte +180 -0
  40. package/bin/dashboard/frontend/src/lib/components/Footer.svelte +33 -0
  41. package/bin/dashboard/frontend/src/lib/components/Header.svelte +84 -0
  42. package/bin/dashboard/frontend/src/lib/components/IssueCard.svelte +33 -0
  43. package/bin/dashboard/frontend/src/lib/components/IssuesPanel.svelte +73 -0
  44. package/bin/dashboard/frontend/src/lib/components/KeyboardShortcutsModal.svelte +108 -0
  45. package/bin/dashboard/frontend/src/lib/components/MobileTabs.svelte +52 -0
  46. package/bin/dashboard/frontend/src/lib/components/NotificationCard.svelte +60 -0
  47. package/bin/dashboard/frontend/src/lib/components/NotificationsPanel.svelte +44 -0
  48. package/bin/dashboard/frontend/src/lib/components/TaskCard.svelte +63 -0
  49. package/bin/dashboard/frontend/src/lib/components/TasksPanel.svelte +82 -0
  50. package/bin/dashboard/frontend/src/lib/components/Toast.svelte +45 -0
  51. package/bin/dashboard/frontend/src/lib/stores/agents.js +34 -0
  52. package/bin/dashboard/frontend/src/lib/stores/issues.js +54 -0
  53. package/bin/dashboard/frontend/src/lib/stores/notifications.js +48 -0
  54. package/bin/dashboard/frontend/src/lib/stores/tasks.js +63 -0
  55. package/bin/dashboard/frontend/src/lib/stores/theme.js +33 -0
  56. package/bin/dashboard/frontend/src/lib/stores/toast.js +35 -0
  57. package/bin/dashboard/frontend/src/lib/stores/ui.js +25 -0
  58. package/bin/dashboard/frontend/src/lib/stores/voice.js +275 -0
  59. package/bin/dashboard/frontend/src/lib/stores/websocket.js +295 -0
  60. package/bin/dashboard/frontend/src/lib/utils/api.js +101 -0
  61. package/bin/dashboard/frontend/src/lib/utils/formatters.js +54 -0
  62. package/bin/dashboard/frontend/src/main.js +9 -0
  63. package/bin/dashboard/frontend/svelte.config.js +5 -0
  64. package/bin/dashboard/frontend/vite.config.js +20 -0
  65. package/bin/dashboard/public/assets/index-DnfVj9Ce.css +1 -0
  66. package/bin/dashboard/public/assets/index-Ze5h0kXQ.js +2 -0
  67. package/bin/dashboard/public/index.html +14 -0
  68. package/bin/dashboard/server.js +566 -0
  69. package/bin/forge-daemon.sh +463 -0
  70. package/bin/forge-setup.sh +645 -0
  71. package/bin/forge-spawn.sh +164 -0
  72. package/bin/forge.cmd +83 -0
  73. package/bin/forge.sh +533 -0
  74. package/bin/lib/agents.sh +177 -0
  75. package/bin/lib/colors.sh +44 -0
  76. package/bin/lib/config.sh +347 -0
  77. package/bin/lib/constants.sh +241 -0
  78. package/bin/lib/daemon/display.sh +128 -0
  79. package/bin/lib/daemon/notifications.sh +263 -0
  80. package/bin/lib/daemon/routing.sh +77 -0
  81. package/bin/lib/daemon/state.sh +115 -0
  82. package/bin/lib/daemon/sync.sh +95 -0
  83. package/bin/lib/database.sh +310 -0
  84. package/bin/lib/heimdall-setup.js +113 -0
  85. package/bin/lib/heimdall.js +265 -0
  86. package/bin/lib/json.sh +264 -0
  87. package/bin/lib/terminal.js +451 -0
  88. package/bin/lib/util.sh +126 -0
  89. package/bin/lib/vcs.js +349 -0
  90. package/config/agent-manifest.yaml +203 -0
  91. package/config/agents.json +168 -0
  92. package/config/task-template.md +159 -0
  93. package/config/task-types.yaml +106 -0
  94. package/context/agent-status/aegis.json +7 -0
  95. package/context/agent-status/anvil.json +7 -0
  96. package/context/agent-status/architect.json +7 -0
  97. package/context/agent-status/crucible.json +7 -0
  98. package/context/agent-status/ember.json +7 -0
  99. package/context/agent-status/furnace.json +7 -0
  100. package/context/agent-status/loki.json +7 -0
  101. package/context/agent-status/oracle.json +7 -0
  102. package/context/agent-status/pixel.json +7 -0
  103. package/context/agent-status/planning-hub.json +7 -0
  104. package/context/agent-status/scribe.json +7 -0
  105. package/context/agent-status/temper.json +7 -0
  106. package/context/feature-brainstorm.md +426 -0
  107. package/context/forge-state.yaml +19 -0
  108. package/context/modern-conventions.md +129 -0
  109. package/context/project-context-template.md +122 -0
  110. package/context/project-context.md +122 -0
  111. package/docs/TODO.md +150 -0
  112. package/docs/agents.md +409 -0
  113. package/docs/architecture/decisions/ADR-001-daemon-modularization.md +122 -0
  114. package/docs/architecture/vibe-lab-integration.md +684 -0
  115. package/docs/architecture.md +194 -0
  116. package/docs/bmad-gap-analysis-2026-03-31.md +444 -0
  117. package/docs/cleanup-workflow.md +329 -0
  118. package/docs/commands.md +451 -0
  119. package/docs/dashboard-mockup.html +989 -0
  120. package/docs/getting-started.md +261 -0
  121. package/docs/integration/forge-ownership-policy.md +112 -0
  122. package/docs/npm-publishing.md +132 -0
  123. package/docs/roadmap-2026.md +519 -0
  124. package/docs/security.md +144 -0
  125. package/docs/wireframes/dashboard-mvp.md +1164 -0
  126. package/docs/workflows/README.md +32 -0
  127. package/docs/workflows/azure-devops.md +108 -0
  128. package/docs/workflows/bitbucket.md +104 -0
  129. package/docs/workflows/git-only.md +130 -0
  130. package/docs/workflows/gitea.md +168 -0
  131. package/docs/workflows/github.md +103 -0
  132. package/docs/workflows/gitlab.md +105 -0
  133. package/docs/workflows.md +454 -0
  134. package/package.json +73 -0
  135. package/tasks/completed/ARCH-001-duplicate-agent-config.md +121 -0
  136. package/tasks/completed/ARCH-002-mixed-bash-node-implementation.md +88 -0
  137. package/tasks/completed/ARCH-003-worker-loop-hook-duplication.md +77 -0
  138. package/tasks/completed/ARCH-009-test-organization.md +78 -0
  139. package/tasks/completed/ARCH-011-jq-vs-nodejs-json.md +94 -0
  140. package/tasks/completed/ARCH-012-tmp-files-in-root.md +71 -0
  141. package/tasks/completed/ARCH-013-exit-code-constants.md +65 -0
  142. package/tasks/completed/ARCH-014-sed-incompatibility.md +96 -0
  143. package/tasks/completed/ARCH-015-docs-todo-tracking.md +83 -0
  144. package/tasks/completed/BUG-dash-001-tasks-filter-error.md +31 -0
  145. package/tasks/completed/BUG-dash-002-agents-unknown.md +41 -0
  146. package/tasks/completed/CLEAN-001.md +38 -0
  147. package/tasks/completed/CLEAN-002.md +43 -0
  148. package/tasks/completed/CLEAN-003.md +47 -0
  149. package/tasks/completed/CLEAN-004.md +56 -0
  150. package/tasks/completed/CLEAN-005.md +75 -0
  151. package/tasks/completed/CLEAN-006.md +47 -0
  152. package/tasks/completed/CLEAN-007.md +34 -0
  153. package/tasks/completed/CLEAN-008.md +49 -0
  154. package/tasks/completed/CLEAN-012.md +58 -0
  155. package/tasks/completed/CLEAN-013.md +45 -0
  156. package/tasks/completed/FEATURE-001a-dashboard-wireframes.md +162 -0
  157. package/tasks/completed/IMPL-007a-daemon-notifications-module.md +82 -0
  158. package/tasks/completed/IMPL-007b-daemon-sync-module.md +71 -0
  159. package/tasks/completed/IMPL-007c-daemon-state-module.md +80 -0
  160. package/tasks/completed/IMPL-007d-daemon-routing-module.md +77 -0
  161. package/tasks/completed/IMPL-007e-daemon-display-module.md +77 -0
  162. package/tasks/completed/IMPL-007f-daemon-integration.md +124 -0
  163. package/tasks/completed/PLAT-1-heimdall.md +420 -0
  164. package/tasks/completed/SEC-001-sql-injection-fix.md +58 -0
  165. package/tasks/completed/SEC-002-notification-injection-fix.md +45 -0
  166. package/tasks/completed/SEC-003-eval-injection-fix.md +54 -0
  167. package/tasks/completed/SEC-004-pid-race-condition-fix.md +49 -0
  168. package/tasks/completed/SEC-005-worker-loop-path-fix.md +51 -0
  169. package/tasks/completed/SEC-006-eval-agent-names.md +55 -0
  170. package/tasks/completed/SEC-007-spawn-escaping.md +67 -0
  171. package/tasks/completed/TASK-DASH-001-server-infrastructure.md +185 -0
  172. package/tasks/completed/TASK-anvil-001-dashboard-frontend.md +133 -0
  173. package/tasks/completed/review-bmad-aegis.md +89 -0
  174. package/tasks/completed/review-bmad-anvil.md +80 -0
  175. package/tasks/completed/review-bmad-crucible.md +81 -0
  176. package/tasks/completed/review-bmad-ember.md +90 -0
  177. package/tasks/completed/review-bmad-furnace.md +79 -0
  178. package/tasks/completed/review-bmad-pixel.md +82 -0
  179. package/tasks/completed/review-bmad-scribe.md +92 -0
  180. package/tasks/completed/review-bmad-sentinel.md +83 -0
  181. package/tasks/pending/ARCH-004-git-bash-detection-duplication.md +72 -0
  182. package/tasks/pending/ARCH-005-missing-src-directory.md +95 -0
  183. package/tasks/pending/ARCH-006-task-template-location.md +64 -0
  184. package/tasks/pending/ARCH-008-forge-master-vs-hub.md +81 -0
  185. package/tasks/pending/ARCH-010-missing-index-files.md +84 -0
  186. package/tasks/pending/CLEAN-009.md +31 -0
  187. package/tasks/pending/CLEAN-010.md +30 -0
  188. package/tasks/pending/CLEAN-011.md +30 -0
  189. package/tasks/pending/CLEAN-014.md +32 -0
  190. package/tasks/pending/DESIGN-dash-001-layout-review.md +45 -0
  191. package/tasks/pending/FEATURE-001-dashboard-mvp.md +268 -0
  192. package/tasks/review/ARCH-007-daemon-monolith.md +162 -0
  193. package/tasks/review/bmad-review-aegis.md +349 -0
  194. package/tasks/review/bmad-review-anvil.md +259 -0
  195. package/tasks/review/bmad-review-crucible.md +277 -0
  196. package/tasks/review/bmad-review-ember.md +307 -0
  197. package/tasks/review/bmad-review-furnace.md +285 -0
  198. package/tasks/review/bmad-review-pixel.md +329 -0
  199. package/tasks/review/bmad-review-scribe.md +361 -0
  200. package/tasks/review/bmad-review-sentinel.md +242 -0
  201. package/tasks/review/task-001.md +78 -0
@@ -0,0 +1,684 @@
1
+ # vibe-forge / vibe-lab Integration Architecture
2
+
3
+ **Status:** Proposed — pending vibe-lab review
4
+ **Date:** 2026-04-01
5
+ **Version:** 2.1 (blockers resolved)
6
+ **Authors:** vibe-forge architect, vibe-lab architect (synthesized)
7
+
8
+ ---
9
+
10
+ ## Platform Model
11
+
12
+ vibe-forge and vibe-lab are one platform delivered in two configurations:
13
+
14
+ ```
15
+ vibe-forge (standalone)
16
+ The interactive session layer.
17
+ For developers who want forge's agent model without a CI/CD pipeline.
18
+ Installs independently. Works without lab.
19
+
20
+ vibe-lab (includes forge)
21
+ The full platform.
22
+ Lab is the pipeline protocol layer.
23
+ Forge is the execution runtime.
24
+ Installing lab installs forge. Starting lab starts forge.
25
+ Lab without forge is not a supported configuration.
26
+ ```
27
+
28
+ The previous framing ("two systems with an integration boundary") is retired. There is one platform. The boundary that exists is between **protocol** (lab) and **execution** (forge), not between two optional peer systems.
29
+
30
+ ---
31
+
32
+ ## Why This Model
33
+
34
+ Lab's existing execution model — `claude -p` headless subprocesses — is opaque by design. Status indicators on a dashboard are not the same as visibility. The "stuck" feeling is structural: you cannot distinguish a working agent from a hung one without attaching to its process.
35
+
36
+ Forge terminal sessions are inherently observable. You can watch any worker in real time. You know in seconds whether it is making progress.
37
+
38
+ The platform model resolves this permanently: all story execution runs through forge workers. Lab provides orchestration, handoff protocol, and release management. Forge provides every agent that does work.
39
+
40
+ ---
41
+
42
+ ## Installation and Dependency
43
+
44
+ Lab declares forge as a versioned dependency:
45
+
46
+ ```json
47
+ // vibe-lab/package.json
48
+ {
49
+ "dependencies": {
50
+ "vibe-forge": "^1.0.0"
51
+ }
52
+ }
53
+ ```
54
+
55
+ `lab install` (or `npm install` in the lab directory) pulls forge. No separate forge install step for lab users. vibe-forge standalone users install forge directly via `npx vibe-forge` as today — unchanged.
56
+
57
+ **Version constraints:** The IPC protocol between lab's dispatcher and forge's daemon is versioned independently of both packages. On startup, lab and forge perform a handshake:
58
+
59
+ ```
60
+ lab → forge daemon: { "protocol": "ipc-v1", "project_id": "..." }
61
+ forge → lab: { "ok": true } | { "error": "protocol_mismatch", "supported": "ipc-v1" }
62
+ ```
63
+
64
+ Transport: `POST http://localhost:2800/api/handshake` — forge's dashboard server already runs HTTP at port 2800. IPC-1 must specify this explicitly. The port is configurable in `_vibe-chain/config.yaml` under `forge.dashboard_port`.
65
+
66
+ If versions are incompatible, lab falls back to degraded mode and logs a warning. Lab never fails to start because forge is on an unexpected version — it degrades gracefully.
67
+
68
+ **Upgrades:** Forge updates follow semver. Minor and patch versions are compatible. Major versions may break the IPC protocol — lab's package.json pins to a major version range (`^1.0.0`) and the CI matrix validates the combination before publishing.
69
+
70
+ ---
71
+
72
+ ## Deployment Architecture
73
+
74
+ When `lab start` runs, it starts two services:
75
+
76
+ ```
77
+ lab start
78
+ ├── sentinel (lab: filesystem watcher, pipeline state, hub sync)
79
+ ├── relay (lab: WebSocket fan-out for dashboard)
80
+ ├── hub (lab: API server, dashboard frontend)
81
+ └── forge-daemon (forge: worker lifecycle manager, task router)
82
+ ├── anvil (forge worker: frontend)
83
+ ├── furnace (forge worker: backend)
84
+ ├── crucible (forge worker: testing)
85
+ ├── sentinel (forge worker: code review)
86
+ ├── architect (forge worker: arch review)
87
+ └── pixel (forge worker: UX review)
88
+ ```
89
+
90
+ Forge workers run as **persistent background processes**, not terminal tabs that a human opens. The forge daemon owns worker lifetimes. Workers start with lab, run continuously, and stop with lab.
91
+
92
+ Workers run with `--dangerously-skip-permissions`. There is no user-required interaction during execution. Overnight runs, unattended runs, and active session runs are identical from the pipeline's perspective.
93
+
94
+ ### Visibility On Demand
95
+
96
+ Workers run in the background by default. When you want to observe:
97
+
98
+ ```bash
99
+ forge attach anvil # live terminal session, watch in real time
100
+ # detach with Ctrl+D, worker keeps running
101
+ ```
102
+
103
+ The session persists independently of whether you are watching it. This is the tmux attach model applied to agent workers.
104
+
105
+ ---
106
+
107
+ ## Security Model for Background Workers: Heimdall
108
+
109
+ Forge workers run with `--dangerously-skip-permissions`. In the terminal tab model, a human can observe and intervene. In background daemon mode there is no runtime human gate. The compensating control is **Heimdall** — a pre-tool hook interceptor that guards every action before it executes.
110
+
111
+ Heimdall is named for the Norse watchman of the gods, guardian of the Bifrost bridge between realms. The Bifrost here is the bridge between lab's pipeline and forge's workers. Heimdall sits at the crossing and decides what passes.
112
+
113
+ ```
114
+ bin/lib/heimdall.js
115
+ ```
116
+
117
+ ### How Heimdall Works
118
+
119
+ Claude Code's hook system fires before each tool execution. Heimdall is registered as a `PreToolUse` hook. It receives the tool name and input as JSON on stdin, checks against policy, and returns:
120
+
121
+ - Exit `0` — allow (optionally with audit log entry)
122
+ - Exit `2` — block, explanation written to stdout and fed back to the model as context
123
+
124
+ The model receives the block message and reasons about it, typically self-correcting without retrying the same action.
125
+
126
+ The forge daemon writes `.claude/settings.local.json` into each worker's working directory at startup:
127
+
128
+ ```json
129
+ {
130
+ "hooks": {
131
+ "PreToolUse": [
132
+ { "matcher": "Bash", "hooks": [{ "type": "command", "command": "node bin/lib/heimdall.js" }] },
133
+ { "matcher": "Write|Edit", "hooks": [{ "type": "command", "command": "node bin/lib/heimdall.js" }] }
134
+ ]
135
+ }
136
+ }
137
+ ```
138
+
139
+ ### What Heimdall Blocks (Hard Block)
140
+
141
+ **Catastrophic filesystem operations:**
142
+ - `rm -rf` targeting any path outside `worktree_path`
143
+ - `rm -rf /`, `rm -rf ~`, `rm -rf ..`
144
+ - `DROP TABLE`, `TRUNCATE` (without explicit `has_db_migration: true` flag)
145
+ - `DELETE FROM` without a WHERE clause
146
+
147
+ **Path escapes:**
148
+ - Any Write or Edit to a path outside `worktree_path`, `handoff_dir`, or `worker-inbox/`
149
+
150
+ **Credential access:**
151
+ - Any read of `.env`, `~/.ssh/*`, `~/.aws/credentials`, `**/secrets/**`
152
+ - Any bash command assigning or echoing `*TOKEN*`, `*SECRET*`, `*PASSWORD*`, `*API_KEY*`
153
+
154
+ **Dangerous git operations:**
155
+ - `git push --force` or `git push --force-with-lease`
156
+ - `git reset --hard HEAD~` (more than one commit back)
157
+ - `git clean -fd`
158
+ - Any git push to a branch other than the assigned story branch
159
+ - Any git operation targeting a remote other than `origin`
160
+
161
+ ### What Heimdall Logs But Allows
162
+
163
+ High-risk but legitimate operations — allowed with audit log entry:
164
+
165
+ - `npm install`, `pip install`, `cargo build` — dependency changes are high-signal
166
+ - `git commit --amend` — legitimate but noted
167
+ - Any `curl` or `wget` — destination logged
168
+ - DB migrations when `has_db_migration: true` — explicitly authorized
169
+ - Writes to `_vibe-chain-output/` — allowed, every write logged
170
+
171
+ ### Context File
172
+
173
+ The forge daemon writes a context file alongside each inbox task:
174
+
175
+ ```json
176
+ // _vibe-chain-output/worker-inbox/anvil/{story-id}.context.json
177
+ {
178
+ "story_id": "FORGE-3",
179
+ "agent": "anvil",
180
+ "worktree_path": "G:/dev/vibe-lab/.worktrees/forge-3-events-api",
181
+ "assigned_branch": "feature/forge-3-events-api",
182
+ "handoff_dir": "G:/dev/vibe-lab/_vibe-chain-output/handoffs",
183
+ "has_db_migration": false,
184
+ "has_api_changes": true,
185
+ "allowed_paths": [
186
+ "G:/dev/vibe-lab/.worktrees/forge-3-events-api",
187
+ "G:/dev/vibe-lab/_vibe-chain-output/handoffs",
188
+ "G:/dev/vibe-lab/_vibe-chain-output/worker-inbox"
189
+ ]
190
+ }
191
+ ```
192
+
193
+ Heimdall reads this on every invocation. Policy is derived from task context, not global config.
194
+
195
+ ### Escalation: Sounding the Gjallarhorn
196
+
197
+ If a worker accumulates 3 or more blocks in a single task (configurable), Heimdall writes a `{story-id}.escalation` signal file. The forge daemon detects it, logs `HEIMDALL SOUNDED`, and writes an escalation handoff. Lab's sentinel routes the story to a human review queue rather than retrying.
198
+
199
+ Audit log format:
200
+
201
+ ```
202
+ [2026-04-01T12:34:00Z] HEIMDALL BLOCKED anvil/FORGE-3: path escape — rm -rf ../
203
+ [2026-04-01T12:34:01Z] HEIMDALL BLOCKED anvil/FORGE-3: credential access — read .env
204
+ [2026-04-01T12:34:02Z] HEIMDALL SOUNDED anvil/FORGE-3: 3 violations, escalating to human review
205
+ ```
206
+
207
+ ### Story
208
+
209
+ **PLAT-1: Heimdall** is a prerequisite for IPC-7 (background worker daemon mode). Running workers in visible terminal tabs with a human present is an acceptable substitute. Running them as unattended background services is not — SEC-1 ships before IPC-7.
210
+
211
+ ```
212
+ PLAT-1: Heimdall — forge worker pre-tool hook interceptor
213
+ - bin/lib/heimdall.js
214
+ - Context file schema (.context.json alongside each inbox task)
215
+ - Daemon writes .claude/settings.local.json at worker startup
216
+ - Audit log in forge daemon output
217
+ - HEIMDALL SOUNDED escalation on N violations (default: 3, configurable)
218
+ - Policy config in _vibe-chain/config.yaml
219
+ Prerequisite for: IPC-7
220
+ Parallel with: IPC-1 through IPC-6
221
+ Note: SEC-1 is already assigned to "Relay server security hardening" in the lab pipeline.
222
+ PLAT-1 is intentionally in a new epic (PLAT) for cross-cutting platform stories.
223
+ ```
224
+
225
+ ### Trust Model
226
+
227
+ Workers are granted the same trust level as a human developer. This is a trust policy, not a technical capability limit. `--dangerously-skip-permissions` removes confirmation prompts — Heimdall replaces those prompts with automated policy enforcement. The underlying OS permissions are unchanged. The security gate at merge time (lab's review chain) remains the final control for output correctness.
228
+
229
+ ### Compromise Scenario
230
+
231
+ If a worker is compromised or misbehaving, blast radius is bounded by:
232
+ - `VIBE_LAB_FORGE_TOKEN` scope: `create_story` and `post_events` only, matching `project_id`
233
+ - Worktree isolation: changes are in a branch, not main
234
+ - Heimdall: blocks path escapes, credential access, and destructive operations at execution time
235
+ - Lab's review chain: output must pass arch review, code review, and tests before merge
236
+
237
+ ---
238
+
239
+ ## Execution Layer: IPC Inbox Mechanism
240
+
241
+ Lab's dispatcher does not spawn `claude -p` subprocesses for execution stages. It routes to running forge workers via the **IPC inbox mechanism**.
242
+
243
+ ### Inbox Location
244
+
245
+ Inbox files live in the project directory alongside other handoffs:
246
+
247
+ ```
248
+ _vibe-chain-output/
249
+ handoffs/ (existing — PR handoffs, review approvals, etc.)
250
+ worker-inbox/ (new — lab task files for forge workers)
251
+ anvil/
252
+ {story-id}.md
253
+ furnace/
254
+ {story-id}.md
255
+ sentinel/
256
+ {story-id}.md
257
+ ```
258
+
259
+ This keeps all pipeline state visible in one place, scoped to the project, with no global `~/.forge/` state. Two lab projects on the same machine cannot collide.
260
+
261
+ ### Inbox Task File Format
262
+
263
+ ```markdown
264
+ ---
265
+ story_id: FORGE-3
266
+ project_id: vibe-lab
267
+ worktree_path: G:/dev/vibe-lab/.worktrees/forge-3-events-api
268
+ project_root: G:/dev/vibe-lab
269
+ handoff_dir: G:/dev/vibe-lab/_vibe-chain-output/handoffs
270
+ agent: furnace
271
+ task_type: backend
272
+ ipc_protocol: ipc-v1
273
+ submitted_at: 2026-04-01T12:00:00Z
274
+ ---
275
+
276
+ [lab operational instructions for this story, including exact handoff frontmatter]
277
+ ```
278
+
279
+ The `worktree_path` field is mandatory — Anvil or Furnace must know which worktree to work in. This is IPC-3 territory and must be in the spec.
280
+
281
+ ### Routing Flow
282
+
283
+ ```
284
+ Lab dispatcher has a story to execute:
285
+
286
+ 1. GET http://localhost:2800/api/workers/available?task_type=backend
287
+ → { "available": true, "agent": "furnace", "idle_since": "..." }
288
+
289
+ 2. Write _vibe-chain-output/worker-inbox/furnace/{story-id}.md
290
+
291
+ 3. Forge worker's stop hook fires on next idle cycle (5-10s)
292
+ Worker checks worker-inbox/<self>/ before exiting
293
+ Finds {story-id}.md, picks it up
294
+
295
+ 4. Worker writes {story-id}.picked-up to signal acceptance
296
+
297
+ 5. Worker executes story in assigned worktree
298
+ Uses forge personality + lab operational instructions (hybrid model)
299
+
300
+ 6. Worker writes pr-handoff.md to _vibe-chain-output/handoffs/
301
+
302
+ 7. Lab sentinel detects the handoff, continues the pipeline
303
+ ```
304
+
305
+ ### Agent Availability API
306
+
307
+ The forge dashboard server exposes availability at a stable endpoint:
308
+
309
+ ```
310
+ GET http://localhost:2800/api/workers/available?task_type=frontend
311
+
312
+ Response:
313
+ {
314
+ "available": true,
315
+ "agent": "anvil",
316
+ "idle_since": "2026-04-01T12:00:00Z",
317
+ "current_task": null
318
+ }
319
+
320
+ Response (busy):
321
+ {
322
+ "available": false,
323
+ "agent": "anvil",
324
+ "idle_since": null,
325
+ "current_task": "FORGE-2",
326
+ "estimated_free": null
327
+ }
328
+
329
+ Response (no matching worker):
330
+ {
331
+ "available": false,
332
+ "agent": null,
333
+ "reason": "no_worker_configured"
334
+ }
335
+ ```
336
+
337
+ If no worker is available for the requested `task_type`, lab falls back to degraded spawn (spawn-per-story with forge personality adapter). The port `2800` is configurable in `_vibe-chain/config.yaml` under `forge.dashboard_port`. Defaults to `2800`.
338
+
339
+ ### Timeout and Orphan Policy
340
+
341
+ **Orphan** — a task file written to the inbox but not picked up by a worker within 5 minutes. Cause: worker is crashed, stuck, or not running.
342
+
343
+ **Hung task** — a task picked up (`.picked-up` signal present) but no handoff written within the stage timeout.
344
+
345
+ **Stage timeouts:**
346
+
347
+ | Stage | Timeout |
348
+ |---|---|
349
+ | Dev (frontend, backend, fullstack) | 30 minutes |
350
+ | Code review | 15 minutes |
351
+ | Arch review | 15 minutes |
352
+ | UX review | 10 minutes |
353
+ | Test runner | 20 minutes |
354
+
355
+ **On orphan (not picked up within 5 min):**
356
+ - Forge daemon writes `{story-id}.orphaned` signal file
357
+ - Lab sentinel detects it, removes the inbox task file, falls back to degraded spawn
358
+ - Degraded spawn proceeds identically to the Way 2 model
359
+
360
+ **On timeout (picked up but no handoff within stage timeout):**
361
+ - Forge daemon writes `{story-id}.timeout` signal file
362
+ - Lab sentinel detects it, marks the run as failed in pipeline.db
363
+ - Writes `{story-id}-error-handoff.md` with `status: failed, reason: worker_timeout`
364
+ - Dispatcher picks up the error handoff on next cycle and decides retry vs escalation (same policy as today's agent failures)
365
+
366
+ **Tracking:** Forge daemon maintains a `_vibe-chain-output/worker-inbox/.tracking.json` file with timestamps for each task: `submitted_at`, `picked_up_at`, `completed_at`. This is the daemon's orphan/timeout detection source. Sentinel reads it on each cycle.
367
+
368
+ **Inbox cleanup:** When a story completes — handoff written and confirmed by sentinel — the forge daemon is responsible for removing `{story-id}.md`, `{story-id}.picked-up`, and the tracking entry from `.tracking.json`. Lab sentinel writes a `{story-id}.confirmed` signal file after consuming the handoff; the forge daemon watches for this before cleaning up. Ownership: forge daemon cleans, sentinel signals. IPC-5 must specify this protocol explicitly.
369
+
370
+ ---
371
+
372
+ ## Worker Context: Benefit and Tradeoff
373
+
374
+ Workers accumulate codebase context across tasks within a session. A worker that just implemented a frontend feature already has the component structure loaded when the next frontend story arrives — no re-reading. This is a genuine velocity advantage.
375
+
376
+ **The tradeoff:** Accumulated context means accumulated assumptions. A worker finishing UX-3 brings UX-3's mental model into UX-4. In the cold-start model, each story starts clean.
377
+
378
+ **Mitigation:** The lab operational instructions delivered via the IPC inbox (IPC-3) must include a context-reset prompt at task start:
379
+
380
+ ```
381
+ Before reading this task, set aside any assumptions from previous tasks.
382
+ Treat this story as independent. Read the handoff file and the worktree
383
+ state to form your understanding of what is needed.
384
+ ```
385
+
386
+ This is a prompt instruction, not a process restart. It does not eliminate context bleed entirely but reduces the risk of a worker importing the wrong frame. Document this tradeoff in IPC-3's spec.
387
+
388
+ ---
389
+
390
+ ## Handoff Model: Hybrid Execution
391
+
392
+ Forge personality files contain forge-specific task execution patterns that have no meaning in a lab worktree. Lab's sentinel validates handoff frontmatter against a schema registry — a malformed `pr-handoff.md` stalls the pipeline.
393
+
394
+ **The split:** forge personalities handle the work, lab operational instructions handle the handoffs.
395
+
396
+ When the stop hook delivers a lab task, the prompt is structured as:
397
+
398
+ ```
399
+ [forge personality adapter — identity, principles, domain expertise, coding standards]
400
+ [explicit: forge task execution instructions are SUSPENDED for this task]
401
+
402
+ ---
403
+
404
+ [lab operational instructions — worktree_path, exact handoff frontmatter,
405
+ completion signal protocol, sprint-status update procedure]
406
+ ```
407
+
408
+ Personality adapter documents live at `_vibe-chain/agents/forge/<agent>.md` — maintained in the lab repo, containing Identity and Principles from each forge personality with forge-operational sections omitted.
409
+
410
+ ---
411
+
412
+ ## Fallback: Degraded Mode
413
+
414
+ When forge workers are unavailable (daemon not running, worker crashed, orphan timeout), lab falls back to spawn-per-story with forge personality adapters as prompt prefixes. This is the safety net, not the normal path.
415
+
416
+ In degraded mode:
417
+ - Lab spawns `claude -p` with the forge personality adapter prepended to the prompt
418
+ - Execution is headless — the visibility model does not apply
419
+ - Pipeline correctness is maintained — handoff format is identical to IPC mode
420
+ - Workers recovering in the background resume IPC routing automatically
421
+
422
+ Degraded mode is transparent from a pipeline correctness standpoint. It is visible in the forge dashboard (workers show as offline or degraded).
423
+
424
+ ---
425
+
426
+ ## Agent Roster Mapping
427
+
428
+ | Pipeline Stage | Forge Worker | Notes |
429
+ |---|---|---|
430
+ | Dev — frontend | anvil | Frontend specialist |
431
+ | Dev — backend | furnace | Backend/infra specialist |
432
+ | Dev — fullstack | anvil | Default to frontend lead |
433
+ | Code review | temper | Adversarial reviewer — NOT crucible |
434
+ | Arch review | architect | Structural and design review |
435
+ | UX review | pixel | Design and accessibility review |
436
+ | Test runner | crucible | Tester and coverage writer |
437
+ | Release manager | **lab agent-7** | Must stay lab — see below |
438
+
439
+ **Crucible → test runner, forge Sentinel → code review.** This mapping is fixed.
440
+
441
+ ### Why Release Manager Must Stay Lab
442
+
443
+ Lab's `agent-7-release-manager` is the pipeline termination agent. It is hardcoded in `SubagentManager.check_completed()`:
444
+
445
+ ```python
446
+ if agent.agent_type == "release-manager" and status == "completed":
447
+ log_pipeline_run_complete(...)
448
+ remove_story_worktree(project_path, agent.story_id)
449
+ ```
450
+
451
+ Any other agent in this slot means worktrees are never pruned. Stories pile up as orphans. Lab's release agent must stay lab's. Herald (forge's release agent) does not know this protocol and must not own this slot.
452
+
453
+ ---
454
+
455
+ ## Story Submission: Forge → Lab
456
+
457
+ > **Long-term transport note:** `create_story` and `get_story_status` currently use sentinel's local MCP server (stdio) as transport. This is intentional for now — forge is local, sentinel is local, no network hop required. The sentinel MCP surface is a candidate for deprecation as hub MCP matures. When hub MCP reaches parity on these two tools, forge should migrate to hub MCP as the stable external-facing transport. This migration is not in the current story set but should not be designed out — forge's MCP call sites should be abstracted behind a thin client so the transport swap is a one-line config change.
458
+
459
+ ```
460
+ MCP tool: create_story
461
+ Transport: stdio (local sentinel MCP server)
462
+
463
+ Required fields:
464
+ forge_task_id string task filename without extension
465
+ source string always "forge"
466
+ session_id string forge daemon session ID (correlation key)
467
+ task_type enum frontend | backend | arch | docs | test | devops | security
468
+ urgency enum low | normal | high
469
+ skip_reviews string[] default []
470
+ has_db_migration boolean default false
471
+ has_api_changes boolean default false
472
+
473
+ Optional fields:
474
+ estimated_scope enum small | medium | large (defaults to "medium" when absent)
475
+ ```
476
+
477
+ **urgency → priority mapping** (resolved in FORGE-1):
478
+
479
+ | urgency (forge) | priority (lab) |
480
+ |---|---|
481
+ | low | low |
482
+ | normal | medium |
483
+ | high | high |
484
+
485
+ `has_db_migration: true` — suppresses fast-lane, forces full arch review.
486
+ `has_api_changes: true` — forces code review even on small scope.
487
+ Both fields must be wired to Dispatcher routing logic at FORGE-1 time. Storing without acting is not sufficient.
488
+
489
+ ---
490
+
491
+ ## Event Stream: Forge → Lab
492
+
493
+ ```
494
+ HTTP POST /api/projects/:id/events
495
+ Auth: Bearer VIBE_LAB_FORGE_TOKEN
496
+
497
+ {
498
+ "source": "forge",
499
+ "type": "forge-session-started | forge-session-ended | forge-agent-started |
500
+ forge-agent-idle | forge-task-started | forge-task-completed |
501
+ forge-task-escalated",
502
+ "agent": "anvil",
503
+ "task_id": "task-042",
504
+ "session_id": "abc123",
505
+ "timestamp": "<ISO8601>",
506
+ "metadata": { "agent_display_name": "Anvil", "task_title": "Add login form" }
507
+ }
508
+ ```
509
+
510
+ Fire-and-forget. `|| true`. Never blocks worker execution.
511
+
512
+ ---
513
+
514
+ ## Status Feedback: Lab → Forge
515
+
516
+ **Story status query (FORGE-5)**
517
+
518
+ ```
519
+ MCP tool: get_story_status
520
+ Parameters: forge_task_id, project_path
521
+ Returns: { lab_story_id, found, status, title, assigned_to, started, completed, pr, branch }
522
+ ```
523
+
524
+ Called by the forge daemon at idle maintenance cycles. Non-fatal if lab is unreachable.
525
+
526
+ **Real-time relay subscription (FORGE-6)**
527
+
528
+ ```
529
+ WebSocket: wss://<relay-host>/subscribe?project=<project-id>
530
+ Auth: first-message → { "auth": "<VIBE_LAB_FORGE_TOKEN>" }
531
+
532
+ Forge subscribes to: story_transition, agent_completed, queue_summary
533
+ Forge ignores: heartbeat, agent_start
534
+
535
+ Relay event envelope (all forge-originated events):
536
+ {
537
+ "project_id": "vibe-lab", ← explicit project ID, not just "project" alias
538
+ "project": "vibe-lab", ← existing field, kept for relay compatibility
539
+ "event": "story_transition",
540
+ "story": "FORGE-3",
541
+ "forge_task_id": "FORGE-TASK-42",
542
+ "from_status": "in-progress",
543
+ "to_status": "review",
544
+ "timestamp": "<ISO8601>"
545
+ }
546
+
547
+ Both `project_id` and `forge_task_id` must be present on all events for forge-originated stories.
548
+ Thread through: DB → sentinel event → relay broadcast.
549
+ `project_id` is the authoritative cross-project discriminator. Forge uses it, not `story`, to correlate events.
550
+
551
+ Forge maintains one relay connection per lab project. If forge manages multiple lab projects simultaneously,
552
+ it opens one WebSocket subscription per project_id and routes incoming events by project_id before
553
+ dispatching to the appropriate local tracking entry.
554
+ ```
555
+
556
+ Relay validates `VIBE_LAB_FORGE_TOKEN` against hub DB on connect. Fail closed if hub unreachable.
557
+
558
+ ---
559
+
560
+ ## Authentication
561
+
562
+ **One credential: `VIBE_LAB_FORGE_TOKEN`**
563
+
564
+ - Environment variable only — never written to any committed file or log
565
+ - Injected into forge daemon process only, not passed to worker subprocesses
566
+ - Hub enforces scope: `create_story` and `post_events`, `source == "forge"`, matching `project_id` only. All other calls 403.
567
+ - Rate limit: 5 `create_story` per 10-second window per `session_id`. 429 + `Retry-After`. Forge retries on next sync cycle.
568
+ - Relay: validated against hub DB on connect, cached for session lifetime, re-validated on reconnect, fail closed.
569
+
570
+ ---
571
+
572
+ ## Story Ownership
573
+
574
+ Once a story enters the lab pipeline, **lab owns the execution lifecycle unconditionally.**
575
+
576
+ Forge creates a story and submits it. Lab routes it to forge workers for execution, sequences the review chain, merges, and releases. Forge workers cannot modify, reprioritize, or cancel an in-progress story.
577
+
578
+ Forge must not re-submit a story that already has an entry in `.forge/lab-stories.json`. The tracking file is keyed by `{project_id}/{forge_task_id}` — not by story ID alone — because vibe-lab manages multiple projects and story IDs are only unique within a project, not across them.
579
+
580
+ ```json
581
+ // .forge/lab-stories.json
582
+ {
583
+ "vibe-lab/FORGE-TASK-42": {
584
+ "project_id": "vibe-lab",
585
+ "forge_task_id": "FORGE-TASK-42",
586
+ "lab_story_id": "FORGE-3",
587
+ "status": "in-progress",
588
+ "last_checked": "2026-04-01T12:00:00Z"
589
+ },
590
+ "vibe-api/FORGE-TASK-07": {
591
+ "project_id": "vibe-api",
592
+ "forge_task_id": "FORGE-TASK-07",
593
+ "lab_story_id": "FORGE-3",
594
+ "status": "backlog",
595
+ "last_checked": "2026-04-01T11:00:00Z"
596
+ }
597
+ }
598
+ ```
599
+
600
+ Note both entries above have `lab_story_id: "FORGE-3"` — valid, because they are in different projects. The `{project_id}/{forge_task_id}` composite key is what makes them distinct.
601
+
602
+ ---
603
+
604
+ ## Full Story Set and Sequencing
605
+
606
+ ### FORGE Stories — pipeline plumbing
607
+
608
+ ```
609
+ FORGE-7 (ownership policy doc, co-authored)
610
+
611
+ FORGE-1 (create_story forge fields + DB migration + urgency mapping + routing wiring)
612
+
613
+ FORGE-5 (get_story_status MCP tool)
614
+
615
+ FORGE-2 (CORS + scoped forge token)
616
+
617
+ FORGE-3 (events API) ┐
618
+ FORGE-4 (forge sessions dashboard panel, relay push) ├── parallel
619
+ FORGE-6 (relay subscription + forge_task_id threading) ┘
620
+ ```
621
+
622
+ ### LAB-FORGE Stories — personality execution layer
623
+
624
+ ```
625
+ LAB-FORGE-1 (agent_personalities config schema) ─── start now
626
+ LAB-FORGE-2 (personality adapter docs: all agents) ─── parallel
627
+
628
+ LAB-FORGE-3 (SubagentManager personality_prefix + context-reset prompt)
629
+
630
+ LAB-FORGE-4 (Dispatcher personality routing)
631
+
632
+ LAB-FORGE-5 (integration test — forge personality end-to-end, validates handoff format)
633
+ ```
634
+
635
+ ### IPC Stories — persistent worker routing (the full vision)
636
+
637
+ *Starts after LAB-FORGE-5 confirms handoff format correctness.*
638
+
639
+ ```
640
+ IPC-1 (forge daemon: agent availability API at /api/workers/available)
641
+ IPC-2 (forge: worker-inbox/ directory structure + stop hook lab inbox check) ┐ parallel
642
+ IPC-3 (forge: inbox task file format, worktree_path field, context-reset prompt)┘
643
+
644
+ IPC-4 (lab: Dispatcher routing — availability check → inbox write vs degraded spawn)
645
+ IPC-5 (lab: sentinel inbox watcher + .picked-up detection + timeout policy + orphan fallback)
646
+
647
+ IPC-6 (lab: integration test — warm worker story end-to-end)
648
+
649
+ IPC-7 (forge: background worker lifecycle — daemon-managed processes, forge attach)
650
+ Spec note: `forge attach` on Windows requires explicit design. tmux is not native on Windows.
651
+ Candidates: Windows Terminal tab re-attach, named pipe, or Node.js/Bun PTY multiplexer.
652
+ IPC-7's spec must choose and document the mechanism before implementation starts.
653
+ ```
654
+
655
+ ```
656
+ PLAT-1 (Heimdall — pre-tool hook interceptor) ─── parallel with IPC-1 through IPC-6
657
+ prerequisite for IPC-7
658
+ Applicability note: Heimdall also applies to LAB-FORGE degraded spawn (which also runs
659
+ with --dangerously-skip-permissions). LAB-FORGE-3 (SubagentManager personality prefix
660
+ injection) must write .claude/settings.local.json to the worktree at spawn time, the same
661
+ as IPC workers. Heimdall is not IPC-7-only — it applies to every worker that runs
662
+ with --dangerously-skip-permissions, supervised or not.
663
+ ```
664
+
665
+ IPC-7 is the final piece: daemon-managed background workers replace human-spawned terminal tabs. After IPC-7, forge workers are always running as part of lab, with no human startup required.
666
+
667
+ ---
668
+
669
+ ## What Stays Separate
670
+
671
+ - **Codebases and repositories.** Deployment merger, not code merger. Forge's bash daemon, worker loop, personality files, and filesystem-as-state remain in the forge repo. Lab's sentinel, hub, relay, and review chain remain in the lab repo. Lab declares forge as an npm dependency.
672
+ - **vibe-forge standalone.** Continues to ship and work independently. Forge-only users install and use forge without lab. Unchanged.
673
+ - **Forge's internal task model.** `backlog/`, `in-progress/`, `completed/` filesystem is unchanged for forge-native tasks. Lab stories arrive via `worker-inbox/` — a separate path that does not disturb forge's own task queue.
674
+
675
+ ---
676
+
677
+ ## What This Is Not
678
+
679
+ - Forge is not being rewritten to fit lab's stack. Language boundary preserved.
680
+ - Lab is not adopting bash. Lab's sentinel, hub, and relay remain Python/TypeScript.
681
+ - The release manager is never a forge worker. It must remain `lab/agent-7-release-manager`.
682
+ - Forge workers in lab's pipeline are not headless subprocesses. They are real Claude Code sessions, observable on demand via `forge attach`.
683
+ - Human presence is not required for the pipeline to run. Workers run as background services. Human presence enables visibility, not operation.
684
+ - Background workers with `--dangerously-skip-permissions` are not ungated. Heimdall provides pre-execution policy enforcement at tool-call time. Lab's review chain provides output correctness validation at merge time. Both gates are active.