Skip to content

v0.2.1

Latest

Choose a tag to compare

@jtaylorcpp jtaylorcpp released this 20 Oct 06:18
· 107 commits to main since this release
62c1932

Task loops judge and delegate more reliably, especially on small models. This release fixes the five issues found running the task loop on another project (#5–#9).

0.2.1 is the first published 0.2 release. The v0.2.0 tag's run failed before publishing anything (npm read release/<name>.tgz as GitHub shorthand, fixed in #12), so there is no 0.2.0 on npm. 0.2.1 is the same content plus that workflow fix.

Highlights

  • The judge is no longer misled about who said what. Feedback carried from an earlier iteration was labelled "The principal's answer or earlier feedback", so judges read it as the principal's own claim. On one finished task with passing checks, qwen3.5 9B judged it unmet 10/10 times; it now judges it met 10/10. Haiku 4.5's escalations to the principal went from 3/10 to 0/10.
  • The judge sees what the loop has changed, not just this call (#8). A finished task no longer runs the loop out of budget just because a later iteration had nothing left to do.
  • Delegates get the change's contract (#9). The agent that writes the code now sees the proposal, design and spec deltas, not only the plan's paraphrase of them.
  • Tool-less steps are told the truth about their environment (#5, #6, #7).

Changes

Task loop (@specsesh/agent/task)

  • New optional seed changed. A task script that declares changed: mutable string gets the files changed since the loop started, one per line, before each iteration. The baseline is a snapshot of the workspace taken at the loop's first iteration. It's kept across resume and answered escalations, and seeded as (unknown) when the workspace can't snapshot. It leaves out tasks.md and the loop's database (TaskLoopOptions.excludeChanged). Scripts that don't declare it are unaffected. (#8)
  • loop-ended.changed is measured from the same baseline, so after resume it lists everything the loop changed, not only what changed since the resume.
  • The task preamble. Inside a task loop, every delegate called with run gets a fixed preamble before its instructions: the change and task, the rules (change only what the task needs, never tick boxes, never edit the spec), then the full proposal, design and deltas. It's stored in the run's durable input, so a resumed delegate gets the same text, and it goes on stdin only. A delegate offered as a step tool gets none. Exported as taskPreamble. (#9)
  • The bundled task and task-delegate scripts:
    • The system block is role-neutral, and each step's prompt states its role. Tool-less steps say "You have no tools: decide only from the text below." task.agent's act step carries its own workspace framing. (#7)
    • The judge sees the plan as "The instructions the delegate was given (for the delegate, not for you)", so "run the tests" in a plan isn't read as a job for the judge. (#7)
    • The carried label is neutral: "Feedback carried in (from an earlier iteration or the principal's answer)".
    • The judges show changed beside this iteration's file count, and are told that next_action is ignored once the task is met. A judge that changed 0 files in a loop that has changed nothing is told the work has not been done, so a delegate's claim that it "already exists" isn't taken on trust.
  • Passing checks as the definition of done is documented as a host policy (a trailing logic-only step) in guides/07-task-loop.md. The bundled scripts keep the judge.

Durable runs (@specsesh/agent/durable)

  • A structured step's result section reads "Finish by submitting a structured result matching this JSON Schema", naming no tool. Claude Code exposes the tool as StructuredOutput, not submit_result. (#5)
  • RunInput.delegatePreamble and ActionContext.delegatePreamble carry the preamble to actions called with run.

Claude Code provider (@specsesh/agent/claude-code)

  • Claude Code adds its own environment block (working directory, git status, platform) even with --system-prompt, and no flag removes it (checked on Claude Code 2.1.304). Every reasoning step's system prompt now ends with a note that those details describe the process, not the project. (#6)
  • Each step runs in a fresh empty directory, removed afterwards, instead of os.tmpdir(). A new cwd option on ClaudeCodeReasoningPortOptions and ClaudeCodeProviderOptions overrides it. (#6)

Fixes

  • specsesh change close and --amend no longer fail with EACCES when a change directory is read-only. The trial fold's temp copy is made writable before it's archived into and removed.
  • The release workflow publishes ./release/<tarball>. A bare release/<tarball> made npm publish look up a GitHub repository instead (#12), and both packages' trusted publishers now allow npm publish.

Upgrading

  • No migration. Stored loops take their baseline at their next iteration, so changes made before the upgrade aren't counted in changed. Only upgrading has been tested; don't read 0.2.0 databases with 0.1.0.
  • Custom task scripts copied from the old bundled template still say "in a real workspace" in their system block. Update them as docs/language/system.md describes, and declare changed if your judge should see it.
  • TaskLoopResult is now a type alias (JsonObject & {…}) rather than an interface.
  • Delegates inside a task loop receive more input: the change's full text, on every call. A delegate on a small-context model may need a larger window (#11).

Follow-ups

#11 tracks the public type change, temp directories left by crashed steps, untested downgrades, and an opt-out for the preamble's size.

Full changelog: ed23731...v0.2.1