(args: CliArgs)
| 1198 | * optional maintenance. |
| 1199 | */ |
| 1200 | export async function runDream(args: CliArgs): Promise<StageResult> { |
| 1201 | const t0 = Date.now(); |
| 1202 | |
| 1203 | if (args.mode === "dry-run") { |
| 1204 | const root = repoRoot(); |
| 1205 | const sourceId = root ? deriveCodeSourceId(root) : null; |
| 1206 | return { |
| 1207 | name: "dream", |
| 1208 | ran: false, |
| 1209 | ok: true, |
| 1210 | duration_ms: 0, |
| 1211 | summary: sourceId |
| 1212 | ? `would: gbrain dream --source ${sourceId} (build this source's call graph)` |
| 1213 | : "would: gbrain dream (call-graph build)", |
| 1214 | }; |
| 1215 | } |
| 1216 | |
| 1217 | const localStatus = localEngineStatus({ noCache: false }); |
| 1218 | if (localStatus === "timeout") { |
| 1219 | warnProbeTimeout("dream"); // #1964: slow-but-healthy — proceed |
| 1220 | } else if (localStatus !== "ok") { |
| 1221 | return skipStageForLocalStatus("dream", localStatus, t0); |
| 1222 | } |
| 1223 | |
| 1224 | // Dedupe concurrent dreams across worktrees (lock-free path). |
| 1225 | if (!acquireDreamMarker()) { |
| 1226 | const pid = dreamMarkerPid(); |
| 1227 | return { |
| 1228 | name: "dream", |
| 1229 | ran: false, |
| 1230 | ok: true, |
| 1231 | duration_ms: Date.now() - t0, |
| 1232 | summary: `dream already running${pid !== null ? ` (pid ${pid})` : ""} — skipped`, |
| 1233 | }; |
| 1234 | } |
| 1235 | |
| 1236 | try { |
| 1237 | const dreamTimeoutMs = resolveStageTimeoutMs( |
| 1238 | process.env.GSTACK_SYNC_DREAM_TIMEOUT_MS, |
| 1239 | "GSTACK_SYNC_DREAM_TIMEOUT_MS", |
| 1240 | DEFAULT_DREAM_TIMEOUT_MS, |
| 1241 | ); |
| 1242 | |
| 1243 | // Scope the cycle to THIS worktree's code source: `gbrain dream --source <id>`. |
| 1244 | // Verified empirically (not just from `gbrain --help`): plain `gbrain dream` |
| 1245 | // cycles the brain's default source and never runs the source-scoped `extract` |
| 1246 | // phase for our code source, so the call graph for the pinned source stays |
| 1247 | // empty. `gbrain dream --source <id>` runs the per-source cycle (the form |
| 1248 | // `gbrain doctor` recommends for stale sources) and is what actually populates |
| 1249 | // code-callers/code-callees for this worktree. Falls back to plain `dream` |
| 1250 | // only when we can't derive the source id (not in a git repo). |
| 1251 | const root = repoRoot(); |
| 1252 | const sourceId = root ? deriveCodeSourceId(root) : null; |
| 1253 | const dreamArgs = sourceId ? ["dream", "--source", sourceId] : ["dream"]; |
| 1254 | |
| 1255 | // spawnGbrain seeds DATABASE_URL from gbrain's config via buildGbrainEnv. |
| 1256 | // |
| 1257 | // We CAPTURE output (pipe) rather than inherit because `gbrain dream` exits 0 |
no test coverage detected