waypoint-codex 0.18.0 → 0.18.2

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
@@ -189,6 +189,15 @@ The continuity story matters:
189
189
  - `.waypoint/context/RECENT_THREAD.md` helps the agent retain the important
190
190
  parts of the previous conversation
191
191
 
192
+ Waypoint defaults to Codex transcript discovery.
193
+ If you use Pi instead, set this in `.waypoint/config.toml`:
194
+
195
+ ```toml
196
+ coding_agent = "pi"
197
+ ```
198
+
199
+ Supported values are `"codex"` and `"pi"`.
200
+
192
201
  ## Best fit
193
202
 
194
203
  Waypoint is most useful when you want:
package/dist/src/core.js CHANGED
@@ -143,6 +143,7 @@ function buildWaypointConfig(projectRoot, existingConfig, options) {
143
143
  return {
144
144
  version: existingConfig?.version ?? defaults.version,
145
145
  profile: options.profile,
146
+ coding_agent: existingConfig?.coding_agent ?? defaults.coding_agent,
146
147
  workspace_file: existingConfig?.workspace_file ?? defaults.workspace_file,
147
148
  docs_dirs: configuredRootDirs(projectRoot, existingConfig?.docs_dirs, existingConfig?.docs_dir, DEFAULT_DOCS_DIR).map((dir) => path.relative(projectRoot, dir).split(path.sep).join("/")),
148
149
  plans_dirs: configuredRootDirs(projectRoot, existingConfig?.plans_dirs, existingConfig?.plans_dir, DEFAULT_PLANS_DIR).map((dir) => path.relative(projectRoot, dir).split(path.sep).join("/")),
@@ -30,6 +30,7 @@ export function defaultWaypointConfig(options) {
30
30
  return {
31
31
  version: 1,
32
32
  profile: options.profile,
33
+ coding_agent: "codex",
33
34
  workspace_file: ".waypoint/WORKSPACE.md",
34
35
  docs_dirs: [".waypoint/docs"],
35
36
  plans_dirs: [".waypoint/plans"],
@@ -44,6 +45,7 @@ export function renderWaypointConfig(config) {
44
45
  const renderedConfig = {
45
46
  version: config.version,
46
47
  profile: config.profile,
48
+ coding_agent: config.coding_agent,
47
49
  workspace_file: config.workspace_file,
48
50
  docs_dirs: config.docs_dirs,
49
51
  plans_dirs: config.plans_dirs,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "waypoint-codex",
3
- "version": "0.18.0",
3
+ "version": "0.18.2",
4
4
  "description": "Make Codex better by default with stronger planning, code quality, reviews, tracking, and repo guidance.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -102,6 +102,7 @@ Plans document your understanding. Include what matters for this task:
102
102
  - **Current State**: What exists today — relevant files, data flows, constraints, existing patterns
103
103
  - **Changes**: Every file to create/modify/delete, how changes connect
104
104
  - **Decisions**: Why this approach, tradeoffs, assumptions
105
+ - **Scope checklist**: Concrete implementation items that can be marked done or not done
105
106
  - **Acceptance criteria**: What must be true when each step is "done"
106
107
  - **Test cases**: For behavioral changes, include input -> expected output examples
107
108
  - **Non-Goals**: Explicitly out of scope to prevent implementation drift
@@ -125,6 +126,8 @@ Before presenting the plan, verify against real code:
125
126
  - No "we'll figure it out during implementation"
126
127
  - No literal code unless the user explicitly wants it
127
128
  - No pretending you verified something you didn't
129
+ - Approved scope must be explicit enough to act as an execution contract after user approval
130
+ - If the user approves the plan, do not silently defer or drop checklist items later; discuss any proposed scope change first
128
131
 
129
132
  If the change touches durable project behavior, include docs/workspace updates in the plan.
130
133
  Write or update the durable plan doc under `.waypoint/plans/` as part of the skill, not as an optional follow-up.
@@ -140,9 +143,10 @@ A good durable plan doc usually includes:
140
143
  1. Current state
141
144
  2. Proposed changes
142
145
  3. Decisions and tradeoffs
143
- 4. Acceptance criteria
144
- 5. Verification
145
- 6. TL;DR
146
+ 4. Scope checklist
147
+ 5. Acceptance criteria
148
+ 6. Verification
149
+ 7. TL;DR
146
150
 
147
151
  ## Final Response
148
152
 
@@ -152,6 +156,7 @@ When the plan doc is written:
152
156
  - include the doc path
153
157
  - call out any unresolved decisions that still need the user's input
154
158
  - if there are no unresolved decisions and the user approves the plan, treat that approval as authorization to execute the plan end to end rather than asking again at each obvious next step
159
+ - once approved, use the plan's checklist and acceptance criteria to decide whether the work is actually done; if anything approved is skipped, report that as partial work or ask to change scope instead of calling it complete
155
160
 
156
161
  ## Quality Bar
157
162
 
@@ -49,10 +49,13 @@ If something important lives only in your head or in the chat transcript, the re
49
49
  - Read code before editing it.
50
50
  - Follow the repo's documented patterns when they are healthy.
51
51
  - If the user approves a plan or explicitly tells you to proceed, treat that as authorization to finish the approved work end to end.
52
- - When the user shows a bug, screenshot, or broken behavior, investigate first. Lead with what is happening, why it is likely happening, what you checked, and what you are doing next.
52
+ - Once a plan is approved, treat its scope and acceptance criteria as the execution contract. Do not silently narrow, defer, or drop approved work because the system feels good enough, the remaining work looks less valuable, or you would prefer a smaller PR. If the scope should change, discuss that with the user first unless a real blocker, hidden-risk decision, or explicit user redirect forces the change.
53
+ - When the user shows a bug, screenshot, or broken behavior, investigate first. Lead with what is happening, why it is likely happening, the important options or tradeoffs if they matter, what you checked, and what you are doing next.
54
+ - After investigation, explain the diagnosis before jumping into implementation whenever the cause, tradeoffs, or solution shape are not already obvious.
53
55
  - Fix underlying causes instead of papering over symptoms. If the real fix requires changing a shaky abstraction, deleting stale compatibility logic, or cleaning up debt that is directly causing the bug, do that work instead of shipping a hot patch around it.
54
56
  - Do not stop at the first local patch that makes the symptom disappear if the root cause is still obviously in place.
55
57
  - Do not lead with readiness disclaimers such as "I can't call this done yet" unless the user explicitly asked whether the work is ready, shippable, or complete.
58
+ - Keep communication concise by default. Lead with the answer, diagnosis, decision, or next step, and include only the most important supporting detail unless the user asks for more.
56
59
  - Honesty means accurate diagnosis, explicit uncertainty, and clear verification limits. It does not mean substituting process language for investigation.
57
60
  - Before making meaningful frontend or backend decisions, inspect the available user-scoped and project-scoped `AGENTS.md` guidance. If the task depends on frontend or backend context that is missing from the project-scoped guidance and routed docs, use the corresponding `*-context-interview` skill to fill that gap instead of guessing.
58
61
  - Update the user-scoped `AGENTS.md` when you learn a durable preference, workflow rule, or default that should apply across projects and your environment allows you to edit it.
@@ -96,6 +99,7 @@ Pause only when:
96
99
 
97
100
  - a real blocker prevents forward progress
98
101
  - a hidden-risk or non-obvious decision would materially change scope, behavior, cost, or data safety
102
+ - you want to change approved scope or defer approved work
99
103
  - the user explicitly redirects, pauses, or narrows the work
100
104
 
101
105
  If none of those are true, keep going.
@@ -143,6 +147,7 @@ Deliberate closeout review is available when you want a second pass for ship-rea
143
147
  - No silent assumptions
144
148
  - No fake verification
145
149
  - No hiding behind process language when a useful diagnosis is possible
150
+ - No silent scope reduction after plan approval
146
151
  - No skipping docs or workspace updates when they matter
147
152
  - No broad scope creep under the banner of "while I'm here"
148
153
 
@@ -116,7 +116,7 @@ function renderPullRequestBlock(result, emptyMessage) {
116
116
  return result.stdout || emptyMessage;
117
117
  }
118
118
 
119
- const SESSION_DIR_NAMES = ["sessions", "archived_sessions"];
119
+ const CODEX_SESSION_DIR_NAMES = ["sessions", "archived_sessions"];
120
120
  const SECRET_PATTERNS = [
121
121
  /npm_[A-Za-z0-9]+/g,
122
122
  /github_pat_[A-Za-z0-9_]+/g,
@@ -132,6 +132,29 @@ function codexHome() {
132
132
  return process.env.CODEX_HOME || path.join(os.homedir(), ".codex");
133
133
  }
134
134
 
135
+ function piAgentHome() {
136
+ return process.env.PI_AGENT_HOME || path.join(os.homedir(), ".pi", "agent");
137
+ }
138
+
139
+ function loadCodingAgent(projectRoot) {
140
+ const configPath = path.join(projectRoot, ".waypoint", "config.toml");
141
+ if (!existsSync(configPath)) {
142
+ return "codex";
143
+ }
144
+
145
+ const configText = readFileSync(configPath, "utf8");
146
+ const match = configText.match(/^\s*coding_agent\s*=\s*"(codex|pi)"\s*$/m);
147
+ return match?.[1] || "codex";
148
+ }
149
+
150
+ function codingAgentLabel(codingAgent) {
151
+ return codingAgent === "pi" ? "Pi" : "Codex";
152
+ }
153
+
154
+ function codingAgentHome(codingAgent) {
155
+ return codingAgent === "pi" ? piAgentHome() : codexHome();
156
+ }
157
+
135
158
  function redactSecrets(text) {
136
159
  return SECRET_PATTERNS.reduce((current, pattern) => current.replace(pattern, "[REDACTED]"), text);
137
160
  }
@@ -181,7 +204,7 @@ function collectSessionFiles(rootDir) {
181
204
  return files;
182
205
  }
183
206
 
184
- function extractMessageText(content) {
207
+ function extractCodexMessageText(content) {
185
208
  if (!Array.isArray(content)) {
186
209
  return "";
187
210
  }
@@ -192,6 +215,17 @@ function extractMessageText(content) {
192
215
  .trim();
193
216
  }
194
217
 
218
+ function extractPiMessageText(content) {
219
+ if (!Array.isArray(content)) {
220
+ return "";
221
+ }
222
+ return content
223
+ .filter((block) => block?.type === "text")
224
+ .map((block) => (typeof block?.text === "string" ? block.text : ""))
225
+ .join("")
226
+ .trim();
227
+ }
228
+
195
229
  function isBootstrapNoise(role, text) {
196
230
  return role === "user" && text.startsWith("# AGENTS.md instructions for ");
197
231
  }
@@ -213,11 +247,33 @@ function mergeConsecutiveTurns(turns) {
213
247
  return merged;
214
248
  }
215
249
 
216
- function parseSession(sessionFile, projectRoot) {
250
+ function finalizeParsedSession(sessionFile, projectRoot, sessionId, sessionCwd, sessionStartedAt, rawTurns, compactionBoundaries) {
251
+ if (!sessionCwd || !isWithinPath(sessionCwd, projectRoot)) {
252
+ return null;
253
+ }
254
+
255
+ const selectedFromPreCompaction = compactionBoundaries.length > 0;
256
+ const relevantTurns = selectedFromPreCompaction ? rawTurns.slice(0, compactionBoundaries.at(-1)) : rawTurns;
257
+ const turns = mergeConsecutiveTurns(relevantTurns);
258
+ if (turns.length === 0) {
259
+ return null;
260
+ }
261
+
262
+ return {
263
+ path: sessionFile,
264
+ sessionId,
265
+ sessionCwd,
266
+ turns,
267
+ compactionCount: compactionBoundaries.length,
268
+ selectedFromPreCompaction,
269
+ sessionStartedAt,
270
+ };
271
+ }
272
+
273
+ function parseCodexSession(sessionFile, projectRoot) {
217
274
  let sessionId = null;
218
275
  let sessionCwd = null;
219
276
  let sessionStartedAt = null;
220
- let compactionCount = 0;
221
277
  const rawTurns = [];
222
278
  const compactionBoundaries = [];
223
279
 
@@ -250,7 +306,6 @@ function parseSession(sessionFile, projectRoot) {
250
306
  }
251
307
 
252
308
  if (parsed.type === "compacted") {
253
- compactionCount += 1;
254
309
  compactionBoundaries.push(rawTurns.length);
255
310
  continue;
256
311
  }
@@ -264,7 +319,7 @@ function parseSession(sessionFile, projectRoot) {
264
319
  continue;
265
320
  }
266
321
 
267
- const text = redactSecrets(extractMessageText(parsed.payload?.content));
322
+ const text = redactSecrets(extractCodexMessageText(parsed.payload?.content));
268
323
  if (!text || isBootstrapNoise(role, text)) {
269
324
  continue;
270
325
  }
@@ -277,37 +332,90 @@ function parseSession(sessionFile, projectRoot) {
277
332
  });
278
333
  }
279
334
 
280
- if (!sessionCwd || !isWithinPath(sessionCwd, projectRoot)) {
281
- return null;
282
- }
335
+ return finalizeParsedSession(sessionFile, projectRoot, sessionId, sessionCwd, sessionStartedAt, rawTurns, compactionBoundaries);
336
+ }
283
337
 
284
- const selectedFromPreCompaction = compactionBoundaries.length > 0;
285
- const relevantTurns = selectedFromPreCompaction ? rawTurns.slice(0, compactionBoundaries.at(-1)) : rawTurns;
286
- const turns = mergeConsecutiveTurns(relevantTurns);
287
- if (turns.length === 0) {
288
- return null;
338
+ function parsePiSession(sessionFile, projectRoot) {
339
+ let sessionId = null;
340
+ let sessionCwd = null;
341
+ let sessionStartedAt = null;
342
+ const rawTurns = [];
343
+ const compactionBoundaries = [];
344
+
345
+ for (const line of readFileSync(sessionFile, "utf8").split("\n")) {
346
+ if (!line.trim()) {
347
+ continue;
348
+ }
349
+
350
+ let parsed;
351
+ try {
352
+ parsed = JSON.parse(line);
353
+ } catch {
354
+ continue;
355
+ }
356
+
357
+ if (parsed.type === "session") {
358
+ if (typeof parsed.id === "string") {
359
+ sessionId = parsed.id;
360
+ }
361
+ if (typeof parsed.cwd === "string") {
362
+ sessionCwd = parsed.cwd;
363
+ }
364
+ if (typeof parsed.timestamp === "string") {
365
+ sessionStartedAt = parsed.timestamp;
366
+ }
367
+ continue;
368
+ }
369
+
370
+ if (parsed.type === "compaction") {
371
+ compactionBoundaries.push(rawTurns.length);
372
+ continue;
373
+ }
374
+
375
+ if (parsed.type !== "message") {
376
+ continue;
377
+ }
378
+
379
+ const role = parsed.message?.role;
380
+ if (role !== "user" && role !== "assistant") {
381
+ continue;
382
+ }
383
+
384
+ const text = redactSecrets(extractPiMessageText(parsed.message?.content));
385
+ if (!text || isBootstrapNoise(role, text)) {
386
+ continue;
387
+ }
388
+
389
+ rawTurns.push({
390
+ role,
391
+ text,
392
+ timestamp: parsed.timestamp || parsed.message?.timestamp || null,
393
+ messageCount: 1,
394
+ });
289
395
  }
290
396
 
291
- return {
292
- path: sessionFile,
293
- sessionId,
294
- sessionCwd,
295
- turns,
296
- compactionCount,
297
- selectedFromPreCompaction,
298
- sessionStartedAt,
299
- };
397
+ return finalizeParsedSession(sessionFile, projectRoot, sessionId, sessionCwd, sessionStartedAt, rawTurns, compactionBoundaries);
300
398
  }
301
399
 
302
- function latestMatchingSession(projectRoot, threadIdOverride = null) {
400
+ function latestMatchingSession(projectRoot, codingAgent, threadIdOverride = null) {
303
401
  const matches = [];
304
- for (const dirName of SESSION_DIR_NAMES) {
305
- for (const sessionFile of collectSessionFiles(path.join(codexHome(), dirName))) {
306
- const parsed = parseSession(sessionFile, projectRoot);
402
+
403
+ if (codingAgent === "pi") {
404
+ for (const sessionFile of collectSessionFiles(path.join(piAgentHome(), "sessions"))) {
405
+ const parsed = parsePiSession(sessionFile, projectRoot);
307
406
  if (parsed) {
308
407
  matches.push(parsed);
309
408
  }
310
409
  }
410
+ } else {
411
+ for (const dirName of CODEX_SESSION_DIR_NAMES) {
412
+ for (const sessionFile of collectSessionFiles(path.join(codexHome(), dirName))) {
413
+ const parsed = parseCodexSession(sessionFile, projectRoot);
414
+ if (parsed) {
415
+ matches.push(parsed);
416
+ }
417
+ }
418
+ }
311
419
  }
312
420
 
313
421
  const requestedThreadId = threadIdOverride || process.env.CODEX_THREAD_ID || null;
@@ -331,7 +439,10 @@ function latestMatchingSession(projectRoot, threadIdOverride = null) {
331
439
 
332
440
  function writeRecentThread(contextDir, projectRoot, threadIdOverride = null) {
333
441
  const filePath = path.join(contextDir, "RECENT_THREAD.md");
334
- const snapshot = latestMatchingSession(projectRoot, threadIdOverride);
442
+ const codingAgent = loadCodingAgent(projectRoot);
443
+ const agentLabel = codingAgentLabel(codingAgent);
444
+ const agentHome = codingAgentHome(codingAgent);
445
+ const snapshot = latestMatchingSession(projectRoot, codingAgent, threadIdOverride);
335
446
  const generatedAt = new Date().toString();
336
447
 
337
448
  if (!snapshot) {
@@ -342,7 +453,7 @@ function writeRecentThread(contextDir, projectRoot, threadIdOverride = null) {
342
453
  "",
343
454
  `Generated by \`${path.relative(projectRoot, fileURLToPath(import.meta.url))}\` on ${generatedAt}.`,
344
455
  "",
345
- "No matching local Codex session was found for this repo yet.",
456
+ `No matching local ${agentLabel} session was found for this repo yet.`,
346
457
  "",
347
458
  ].join("\n"),
348
459
  "utf8"
@@ -358,10 +469,10 @@ function writeRecentThread(contextDir, projectRoot, threadIdOverride = null) {
358
469
  "",
359
470
  `Generated by \`${path.relative(projectRoot, fileURLToPath(import.meta.url))}\` on ${generatedAt}.`,
360
471
  "",
361
- `- Source session: \`${path.relative(codexHome(), snapshot.path)}\``,
472
+ `- Source session: \`${path.relative(agentHome, snapshot.path)}\``,
362
473
  `- Session cwd: \`${snapshot.sessionCwd}\``,
363
474
  `- Compactions in source session: ${snapshot.compactionCount}`,
364
- "- No compaction was found in the latest matching local Codex session, so there is nothing to restore into startup context yet.",
475
+ `- No compaction was found in the latest matching local ${agentLabel} session, so there is nothing to restore into startup context yet.`,
365
476
  "",
366
477
  ].join("\n"),
367
478
  "utf8"
@@ -370,7 +481,7 @@ function writeRecentThread(contextDir, projectRoot, threadIdOverride = null) {
370
481
  }
371
482
 
372
483
  const selectedTurns = snapshot.turns.slice(-MAX_RECENT_TURNS);
373
- const relSessionPath = path.relative(codexHome(), snapshot.path);
484
+ const relSessionPath = path.relative(agentHome, snapshot.path);
374
485
  const lines = [
375
486
  "# Recent Thread",
376
487
  "",
@@ -435,6 +546,8 @@ function main() {
435
546
 
436
547
  const docsIndexPath = writeDocsIndex(projectRoot);
437
548
  const { outputPath: tracksIndexPath, activeTracks } = writeTracksIndex(projectRoot);
549
+ const codingAgent = loadCodingAgent(projectRoot);
550
+ const codingAgentLabelText = codingAgentLabel(codingAgent);
438
551
 
439
552
  const currentTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
440
553
  const currentLocalDatetime = new Date().toString();
@@ -577,7 +690,7 @@ function main() {
577
690
  `- \`${path.relative(projectRoot, uncommittedChangesPath)}\` — uncommitted change summary`,
578
691
  `- \`${path.relative(projectRoot, recentCommitsPath)}\` — recent commits`,
579
692
  `- \`${path.relative(projectRoot, prsPath)}\` — open and recently merged pull requests`,
580
- `- \`${path.relative(projectRoot, recentThreadPath)}\` — latest meaningful turns from the local Codex session for this repo`,
693
+ `- \`${path.relative(projectRoot, recentThreadPath)}\` — latest meaningful turns from the local ${codingAgentLabelText} session for this repo`,
581
694
  `- \`${path.relative(projectRoot, docsIndexPath)}\` — current docs index`,
582
695
  `- \`${path.relative(projectRoot, tracksIndexPath)}\` — current tracker index`,
583
696
  `- \`${path.relative(projectRoot, activeTrackersPath)}\` — active tracker summary`,
@@ -69,18 +69,20 @@ If some uncertainty still remains after checking persisted context and interview
69
69
 
70
70
  Prefer existing persisted context over re-interviewing the user.
71
71
 
72
- If the user approves a plan or explicitly tells you to proceed, treat that as authorization to execute the work end to end. Do not stop mid-implementation for incremental permission unless a real blocker, hidden-risk decision, or explicit user redirect requires a pause.
72
+ If the user approves a plan or explicitly tells you to proceed, treat that as authorization to execute the work end to end. An approved plan is the active execution contract: do not silently narrow, defer, or drop planned work because the system feels good enough, the remaining work feels less important, or you would prefer a smaller PR. If you believe the approved scope should change, pause and discuss that change with the user before proceeding. Only change approved scope without that discussion when a real blocker, hidden-risk decision, or explicit user redirect requires it.
73
73
  When work is in flight elsewhere — reviewer agents, subagents, CI, automated review, external jobs, or other waiting periods — wait as long as required. There is no fixed waiting limit, and slowness alone is not a reason to interrupt or abandon the work.
74
74
  When you use a browser, app, or other interactive UI to inspect, reproduce, or verify something, send the user screenshots of the relevant states so they can see what you saw. If screenshots are not possible in the current environment, say so explicitly.
75
75
  When an explanation is clearer visually, use Mermaid diagrams directly in chat for flows, architecture, state, and plans.
76
76
 
77
77
  Delivery expectations:
78
- - Before you start, decide what "done" means for the task. Set your own finish line and use it as your checklist while you work.
78
+ - Keep communication concise by default. Lead with the answer, diagnosis, decision, or next step, and include only the most important supporting detail unless the user asks for more.
79
+ - For planned work, define done from the approved scope and acceptance criteria, not from your own sense that the system is already good enough.
79
80
  - When you report back to the user, explain the result in plain, direct language. Say what you changed, what happened, and anything the user actually needs to know, but do not lean on jargon, low-level implementation detail, or code-heavy narration unless the user asks for it.
80
81
  - Write for a smart person who is not looking at the code. The goal is clarity, not technical performance.
81
82
  - This communication rule applies to how you explain the work, not to how you do it. Your actual reasoning, coding, debugging, and verification should stay technical, precise, and rigorous.
82
83
  - When the user shows a bug, broken behavior, or a screenshot of something wrong, investigate before discussing readiness.
83
- - Lead with the useful truth: what is happening, the likely cause, what you checked, and what you are doing next.
84
+ - After investigation, explain the problem to the user before jumping into implementation whenever the diagnosis, tradeoffs, or solution shape are not already obvious.
85
+ - Lead with the useful truth: what is happening, the likely cause, the important options or tradeoffs if they matter, what you checked, and what you are doing next.
84
86
  - Fix the underlying problem, not only the visible symptom. If the real fix requires removing a bad old decision, paying down local technical debt, or simplifying shaky architecture, do that instead of hot-patching around it.
85
87
  - Do not ship a bug fix that knowingly leaves the real cause in place behind a cosmetic patch unless the user explicitly asked for a temporary workaround.
86
88
  - Do not lead with refusal or readiness-disclaimer language like "I can't call this done yet" unless the user explicitly asked for a ship/readiness judgment.
@@ -90,6 +92,7 @@ Delivery expectations:
90
92
  - Use representative or real inputs when practical instead of toy examples, so the check tells you something meaningful about the actual request.
91
93
  - If there are realistic edge cases, failure modes, or recovery paths you can exercise without turning the task into a science project, do that too.
92
94
  - If something looks wrong, incomplete, or unproven, keep going. Fix it, rerun the check, and only report completion once the result matches the request.
95
+ - Do not call work done while approved scope or acceptance criteria remain unfinished. If any approved item was skipped or deferred, report that plainly as partial work or a scope-change proposal, not as completion.
93
96
  - The point of this is to keep iteration off the user's shoulders. Return finished work when possible, not a first pass that still depends on the user to spot-check it for you.
94
97
  - Only come back before that if you hit a genuine blocker you cannot clear with the codebase, tools, or available context. If that happens, say it plainly and be explicit about what remains unverified.
95
98