theslopmachine 0.7.5 → 0.7.6

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.
package/README.md CHANGED
@@ -170,6 +170,8 @@ Important details:
170
170
  - `--adopt` moves the current project files into `repo/`, preserves root workflow state in the parent workspace, and skips the automatic bootstrap commit
171
171
  - `--continue-from <PX>` is a smoother alias for existing-project bootstrap; it implies adoption mode and seeds the requested start phase in one step
172
172
  - if `--continue-from <PX>` is run while your current working directory is already the real project `repo/`, SlopMachine automatically treats `..` as the workspace root and writes the workflow state there instead of creating `repo/repo`
173
+ - when a later start phase is seeded for adoption or recovery, the Beads workflow phases before that requested phase are created and immediately marked completed so tracker state matches the seeded entry point
174
+ - in the `slopmachine-claude` path, if adopted or resumed later-phase work has no recoverable tracked Claude developer session yet, the owner must launch and orient the needed Claude lane first and only then continue the substantive work in that same session
173
175
  - `--phase <PX>` seeds the initial `current_phase` for adoption/recovery bootstrap; the owner should still fall back if the real repo evidence does not support that later phase
174
176
 
175
177
  ### `slopmachine set-token`
@@ -209,6 +209,7 @@ Maintain exactly one active developer session at a time.
209
209
  - the live Claude lane must run the installed Claude `developer` agent for normal work, and implementation-capable helper branches should stay developer-scoped when the environment supports explicit agent selection
210
210
  - launch Claude lanes with an explicit model choice rather than relying on the CLI default: use `opus` with `medium` effort for normal work, raise to `opus` with `xhigh` effort only when the planning/debugging/security difficulty genuinely justifies it, use `sonnet` with `medium` effort for documentation-heavy or otherwise simpler work, and keep helper subagents on `sonnet` by default unless there is a concrete reason to raise them too
211
211
  - do not create a fresh `develop-N` Claude session unless controlled replacement or explicit user direction actually requires it
212
+ - if adopted or resumed work needs Claude developer execution but no recoverable tracked Claude session exists yet, determine the correct lane for the current boundary, launch and orient that lane through `claude-worker-management`, persist the returned session id, and only then continue the substantive work
212
213
  - when `P7` begins, do not automatically switch away from `develop-N`
213
214
  - each fresh evaluation result decides the remediation lane:
214
215
  - `fail` -> route the issue list back to the latest `develop-N` Claude session and discard the working audit report file after triage
@@ -231,6 +232,8 @@ Maintain exactly one active developer session at a time.
231
232
 
232
233
  Do not launch the developer before clarification is complete and the workflow is ready to enter `P2`.
233
234
 
235
+ If later-phase adopted or repaired work reaches scaffold, development, verification, hardening, or evaluator remediation with no recoverable Claude session yet, do not stall there or treat the absence itself as a blocker. Launch the required live Claude lane first, complete its first orientation exchange, persist the session id and lane metadata, and then continue the bounded work in that same session.
236
+
234
237
  When the first develop developer session begins in `P2`, start it in this exact order through the live bridge:
235
238
 
236
239
  1. launch the live `develop-1` Claude `developer` lane
@@ -37,6 +37,23 @@ Use this skill whenever `slopmachine-claude` needs to launch, inspect, or messag
37
37
  - each substantive message should state the current engineering boundary, exact expected outcomes for that turn, the evidence required back, the important shortcuts that are not acceptable, and the stopping point
38
38
  - default to one bounded engineering objective per owner turn; if a request would naturally cross planning, scaffold, development, or gate-review boundaries, split it into separate turns
39
39
 
40
+ ## Session-presence rule
41
+
42
+ Before any Claude-backed developer work continues:
43
+
44
+ - determine whether the intended developer lane already has a recoverable live Claude session
45
+ - if the intended lane exists and its bridge state is recoverable, recover that same session and continue there
46
+ - if the current workflow boundary requires Claude developer work and no recoverable session is present yet, launch the necessary Claude lane first instead of treating the missing session as a handoff blocker
47
+ - do not send substantive work until the live lane exists, bridge registration has captured the Claude `session_id`, owner metadata and Beads comments have been synced, and the first session-start message for that lane has completed
48
+ - a missing Claude session during adoption, conservative phase repair, or other first-entry work is not itself an error; it means the owner must establish the correct live lane before continuing
49
+
50
+ Choose the first-launch action by boundary:
51
+
52
+ - `P2` planning entry with no Claude session yet -> launch `develop-1` and perform the planning handshake
53
+ - `P3` through `P6` entry with no recoverable `develop-N` session yet -> launch the appropriate `develop-N` lane, orient it to the current repo state, then continue with the bounded scaffold, development, verification, or hardening turn
54
+ - `P7` remediation routed to `develop-N` after a `fail` audit with no recoverable develop session yet -> launch the intended `develop-N` lane, orient it to the current delivered repo state and upcoming evaluator-driven remediation, then continue with the issue list
55
+ - `P7` remediation routed to `bugfix-N` after a `partial pass` audit -> launch the fresh `bugfix-N` lane and use the bugfix orientation handshake below
56
+
40
57
  ## Lane launch rule
41
58
 
42
59
  For a new bounded developer session slot:
@@ -80,11 +97,12 @@ The default pattern is to let the live lane start normally and then persist the
80
97
  For all later turns in the same bounded developer slot:
81
98
 
82
99
  ```bash
83
- printf '%s' "$PROMPT" | node ~/slopmachine/utils/claude_live_turn.mjs --runtime-dir <dir> --timeout-ms <turn-timeout>
100
+ printf '%s' "$PROMPT" | node ~/slopmachine/utils/claude_live_turn.mjs --runtime-dir <dir> --prompt-stdin 1 --timeout-ms <turn-timeout>
84
101
  ```
85
102
 
86
103
  - inject exactly one message at a time into the idle live lane
87
104
  - pass the prompt directly to the wrapper through stdin as the primary input path instead of requiring an owner-side prompt file
105
+ - when invoking the turn wrapper from the OpenCode Bash tool, pass `--prompt-stdin 1` explicitly so prompt detection does not depend on host stdin/TTY quirks
88
106
  - wait for `Stop` or `StopFailure` before sending the next message
89
107
  - do not bypass the bridge by calling the channel HTTP endpoint directly from owner logic
90
108
  - if turn execution fails, stop and recover explicitly instead of silently creating a new worker
@@ -286,6 +304,25 @@ Do not tell the developer worker to read files outside `repo/`.
286
304
  If project-lead artifacts outside `repo/` matter, restate their content directly in the message instead of passing file paths.
287
305
  Do not mention session names, slot labels, or workflow phase labels to the developer worker.
288
306
 
307
+ ### Adopted or repaired `develop-N` orientation handshake
308
+
309
+ When work enters scaffold, development, integrated verification, hardening, or `fail`-routed remediation without a recoverable `develop-N` Claude session yet:
310
+
311
+ 1. launch the live `develop-N` lane needed for that boundary
312
+ 2. use the first message only to orient that session to the current repo and delivered state
313
+ 3. make clear in plain engineering language that the codebase already exists and work is continuing from the current state rather than starting from zero
314
+ 4. say what kind of bounded follow-up work will come next, such as scaffold completion, a development slice, verification corrections, hardening, or evaluator-driven remediation
315
+ 5. wait for the first response and store the Claude session id from bridge `state.json`
316
+ 6. only after that orientation exchange, continue the same live lane with the first bounded work request
317
+
318
+ The orientation message should:
319
+
320
+ - identify the project and current repo as the working context
321
+ - summarize the current delivered state and the accepted constraints that matter immediately
322
+ - restate only the current engineering boundary and the next work type that will be requested after orientation
323
+ - avoid broad replanning unless the owner has intentionally reopened planning
324
+ - avoid mentioning workflow internals, phase labels, or session-lane labels
325
+
289
326
  ### `bugfix-N` orientation handshake
290
327
 
291
328
  When a fresh `partial pass` evaluation result opens the next remediation lane:
@@ -206,6 +206,8 @@ Keep `../metadata.json` focused on project facts and exported project metadata w
206
206
 
207
207
  - if session or phase records disagree, stop and repair the inconsistency before proceeding
208
208
  - if the current phase already has an active developer session, recover it instead of silently creating a replacement
209
+ - if an adopted, resumed, or conservatively repaired run reaches a developer-work boundary with no recoverable Claude session yet, treat that as a controlled first-launch case rather than a fatal inconsistency; choose the correct lane for the current boundary and hand off to `claude-worker-management` to launch and orient it before work continues
210
+ - if the current boundary requires Claude developer work but `active_developer_session_id` is still empty, do not continue substantive phase work until the missing-session case has been repaired by recovering or launching the intended lane
209
211
  - if an evaluator session is marked active, recover it before continuing the current `P7` cycle
210
212
  - treat live-lane recovery as deterministic recovery, not guesswork
211
213
  - if the active Claude developer session is marked `rate_limited`, do not replace it with owner-side coding; preserve it, record the blocked state, and auto-wait for reset or resume from the same session when the wait helper completes
@@ -8,9 +8,9 @@ import crypto from 'node:crypto'
8
8
  import { fileURLToPath } from 'node:url'
9
9
  import { spawn } from 'node:child_process'
10
10
 
11
- import { emitFailure, emitSuccess, extractRateLimitMetadata, parseArgs, readJsonFile, readPrompt, sleep, waitForRateLimitReset, writeFileIfNeeded, writeJsonIfNeeded } from './claude_worker_common.mjs'
11
+ import { emitFailure, emitSuccess, extractRateLimitMetadata, parseArgs, readJsonFile, readPrompt, readPromptInput, sleep, waitForRateLimitReset, writeFileIfNeeded, writeJsonIfNeeded } from './claude_worker_common.mjs'
12
12
 
13
- export { emitFailure, emitSuccess, parseArgs, readPrompt, sleep, waitForRateLimitReset, writeJsonIfNeeded }
13
+ export { emitFailure, emitSuccess, parseArgs, readPrompt, readPromptInput, sleep, waitForRateLimitReset, writeJsonIfNeeded }
14
14
 
15
15
  export const DEFAULT_LAUNCH_TIMEOUT_MS = 3600000
16
16
  export const DEFAULT_TURN_TIMEOUT_MS = 3600000
@@ -170,17 +170,56 @@ export async function waitFor(predicate, { timeoutMs, intervalMs = DEFAULT_POLL_
170
170
  throw new Error(errorMessage)
171
171
  }
172
172
 
173
- export async function maybeAcceptDevelopmentChannelsPrompt(sessionName, timeoutMs = 30000) {
173
+ function detectClaudeStartupPrompt(pane) {
174
+ if (!pane) {
175
+ return null
176
+ }
177
+
178
+ if (
179
+ pane.includes('Quick safety check:')
180
+ || pane.includes('Yes, I trust this folder')
181
+ || pane.includes('Accessing workspace:')
182
+ ) {
183
+ return 'workspace-trust'
184
+ }
185
+
186
+ if (
187
+ pane.includes('WARNING: Loading development channels')
188
+ || pane.includes('I am using this for local development')
189
+ ) {
190
+ return 'development-channels'
191
+ }
192
+
193
+ return null
194
+ }
195
+
196
+ export async function maybeAcceptClaudeStartupPrompts(sessionName, timeoutMs = 30000) {
174
197
  const deadline = Date.now() + timeoutMs
198
+ let lastAcceptedPrompt = null
199
+ let lastAcceptedAt = 0
200
+
175
201
  while (Date.now() < deadline) {
176
202
  const pane = await tmuxCapturePane(sessionName)
177
203
  if (pane.includes('Listening for channel messages from:')) {
178
204
  return false
179
205
  }
180
- if (pane.includes('I am using this for local development')) {
181
- await tmuxSendEnter(sessionName)
182
- return true
206
+
207
+ const promptKind = detectClaudeStartupPrompt(pane)
208
+ if (promptKind) {
209
+ const now = Date.now()
210
+ if (promptKind !== lastAcceptedPrompt || now - lastAcceptedAt >= 1500) {
211
+ lastAcceptedPrompt = promptKind
212
+ lastAcceptedAt = now
213
+ await tmuxSendEnter(sessionName)
214
+ }
215
+
216
+ await sleep(DEFAULT_POLL_INTERVAL_MS)
217
+ continue
183
218
  }
219
+
220
+ lastAcceptedPrompt = null
221
+ lastAcceptedAt = 0
222
+
184
223
  await sleep(DEFAULT_POLL_INTERVAL_MS)
185
224
  }
186
225
  return false
@@ -15,7 +15,7 @@ import {
15
15
  ensureRuntimeDirs,
16
16
  makeSuffix,
17
17
  makeToken,
18
- maybeAcceptDevelopmentChannelsPrompt,
18
+ maybeAcceptClaudeStartupPrompts,
19
19
  parseArgs,
20
20
  readJsonIfExists,
21
21
  resolveUtilsDir,
@@ -147,7 +147,7 @@ try {
147
147
  process.exit(1)
148
148
  }
149
149
 
150
- await maybeAcceptDevelopmentChannelsPrompt(tmuxSession, Math.min(launchTimeoutMs, 30000))
150
+ await maybeAcceptClaudeStartupPrompts(tmuxSession, launchTimeoutMs)
151
151
 
152
152
  const [{ event }, channelState] = await Promise.all([
153
153
  waitForHookEvent(paths, 0, new Set(['SessionStart']), launchTimeoutMs),
@@ -9,6 +9,7 @@ const target = path.resolve(process.cwd(), targetInput)
9
9
  const beadsCommand = process.env.BR_COMMAND || 'br'
10
10
 
11
11
  const ROOT_TITLE = 'SlopMachine Workflow'
12
+ const PHASE_KEYS = ['P1', 'P2', 'P3', 'P4', 'P5', 'P6', 'P7', 'P8', 'P9', 'P10']
12
13
  const PHASE_TITLES = [
13
14
  'P1 Clarification',
14
15
  'P2 Planning',
@@ -21,6 +22,7 @@ const PHASE_TITLES = [
21
22
  'P9 Submission Packaging',
22
23
  'P10 Retrospective',
23
24
  ]
25
+ const requestedStartPhase = process.env.SLOPMACHINE_REQUESTED_START_PHASE || null
24
26
 
25
27
  function log(message) {
26
28
  console.log(`[workflow-init] ${message}`)
@@ -115,10 +117,17 @@ async function ensureLifecycleSeeded() {
115
117
 
116
118
  log('Seeding workflow root and P1-P10 phases')
117
119
  const rootId = await createIssue(ROOT_TITLE)
120
+ const requestedStartIndex = requestedStartPhase ? PHASE_KEYS.indexOf(requestedStartPhase) : -1
121
+
122
+ if (requestedStartPhase && requestedStartIndex === -1) {
123
+ die(`Unsupported requested start phase '${requestedStartPhase}'. Use one of: ${PHASE_KEYS.join(', ')}`)
124
+ }
118
125
 
119
126
  let previousPhaseId = null
127
+ const phaseIds = []
120
128
  for (const title of PHASE_TITLES) {
121
129
  const phaseId = await createIssue(title)
130
+ phaseIds.push(phaseId)
122
131
 
123
132
  const parentResult = await runBeads(['update', phaseId, '--parent', rootId], { env: { ...process.env, CI: '1' } })
124
133
  if (parentResult.code !== 0) {
@@ -136,6 +145,17 @@ async function ensureLifecycleSeeded() {
136
145
 
137
146
  previousPhaseId = phaseId
138
147
  }
148
+
149
+ if (requestedStartIndex > 0) {
150
+ log(`Closing phases before requested start phase ${requestedStartPhase}`)
151
+ for (let index = 0; index < requestedStartIndex; index += 1) {
152
+ const closeResult = await runBeads(['close', phaseIds[index]], { env: { ...process.env, CI: '1' } })
153
+ if (closeResult.code !== 0) {
154
+ console.error(`${closeResult.stdout}${closeResult.stderr}`.trim())
155
+ die(`Failed to close seeded phase '${PHASE_TITLES[index]}' before '${requestedStartPhase}'.`)
156
+ }
157
+ }
158
+ }
139
159
  }
140
160
 
141
161
  async function main() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "theslopmachine",
3
- "version": "0.7.5",
3
+ "version": "0.7.6",
4
4
  "description": "SlopMachine installer and project bootstrap CLI",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/src/init.js CHANGED
@@ -315,6 +315,7 @@ async function runWorkflowBootstrap(paths, targetPath, trackerScript) {
315
315
  env: {
316
316
  ...process.env,
317
317
  BR_COMMAND: trackerCommand || '',
318
+ SLOPMACHINE_REQUESTED_START_PHASE: options.requestedStartPhase || '',
318
319
  HOME: paths.home,
319
320
  },
320
321
  })