fix(work): emit completion signal to stop sandcastle agent loops

Sandcastle re-invokes agents up to maxIterations even when the work is
already done — the decomposer was looping 4x re-writing the same epic
on every dispatch. Two halves to the fix:

- Pass completionSignal: "<promise>COMPLETE</promise>" explicitly on
  all three run() calls (decompose, implementer, reviewer). Makes the
  contract visible alongside maxIterations instead of relying on
  sandcastle's default.
- Append a "Signal completion (required)" section to each prompt
  telling the agent to emit the literal marker as its final line when
  the work is genuinely done, plus a "do NOT emit if..." list to
  discourage premature signaling.
This commit is contained in:
2026-05-13 19:11:44 +02:00
parent d6bf2f638f
commit eadbb7ebd9
5 changed files with 47 additions and 0 deletions

View File

@@ -73,3 +73,15 @@ When done, tell the human the epic folder path and offer them a chance to review
- If the PRD's status is not `approved`, refuse to decompose and tell the human to flip it first.
- **Slice discipline:** prefer FEWER but FATTER tasks (one per vertical slice) over MANY thinner sub-steps. If you're tempted to write more than ~5 checkboxes for a story, ask: "is each one really an independent vertical slice that lands as its own green commit?" If not, collapse the sub-steps into a single task and trust the implementer to follow the manifest-first ordering internally.
- **Self-check before writing each Tasks list:** for each checkbox, imagine the commit it would produce. Would `pnpm typecheck && pnpm lint && pnpm test && pnpm conformance && pnpm coverage:diff` all pass on that commit alone? If no, the checkbox isn't a slice — merge it with its neighbours.
## Signal completion (required)
When the epic folder + story files are written and committed (or you have determined the work is truly done — including the case where you decided not to write anything and reported the reason), emit the literal string `<promise>COMPLETE</promise>` as the final line of your response.
Sandcastle uses this marker to stop the iteration loop. Without it, the orchestrator will re-invoke you up to `maxIterations` times even when the work is already done — every redundant iteration costs subscription quota and time.
Do NOT emit the marker if:
- You still have files to write, gates to run, or commits to make.
- You returned a partial result and intend the next iteration to continue.
- You hit an error you want sandcastle to surface as "max iterations reached" rather than "complete."