# Long-running progress file

Version: 1.0
Updated: 2026-10-02
Method: https://suhailmohebi.com/notes/handoff-packets/

Purpose: let an agent that starts every session blank resume multi-session work from recorded state, not guesswork.

Output: a progress log, a JSON feature list and an init script that a fresh session can read and run in minutes.

## Use when

Work will span many sessions or context windows, and each session must start where the last one stopped — without rebuilding the state or declaring the job done early.

## Quick start

1. In the first session, create the three files in section 1 and commit them.
2. Open every later session with the start ritual and close it with the end ritual.
3. Mark a feature passing only after its end-to-end steps were run and observed.

One feature per session. Never delete or weaken a feature entry to make the work look finished.

## 1. Files and their jobs

| File | Job | Format | Edited by |
|---|---|---|---|
| `progress.txt` | What is true after each session, with evidence and the next step | Key-value entries, newest last | Every session, append only |
| `features.json` | Every end-to-end behaviour the work must deliver | JSON array; every entry starts `"passes": false` | First session writes it; later sessions change `passes` only |
| `init.sh` | Starts the app and drives one basic user path, for example with a headless-browser script | Executable, safe to re-run; non-zero exit on failure | First session; later only to repair it |
| git history | Recoverable states with descriptive messages | One commit per verified step, then one log commit | Every session |

Keep the feature list in JSON: Anthropic found its model less likely to inappropriately change or overwrite JSON than Markdown.

## 2. Feature list

Order entries by priority; the first entry with `"passes": false` is next.

```json
[
  {
    "id": "F01",
    "category": "functional",
    "description": "A visitor sends the contact form and sees a confirmation",
    "steps": [
      "Open /contact",
      "Fill in every required field and press Send",
      "See the confirmation message",
      "Find the new message in the data store"
    ],
    "passes": false
  },
  {
    "id": "F02",
    "category": "functional",
    "description": "A visitor who leaves the email field empty sees an inline error",
    "steps": [
      "Open /contact",
      "Leave the email field empty and press Send",
      "See an error beside the email field and no new stored message"
    ],
    "passes": false
  }
]
```

## 3. Progress entry

Record state and evidence, not activity:

```text
SESSION: [n] | [YYYY-MM-DD HH:MM UTC] | [feature id]
STATE: [what is true now, not what was done]
EVIDENCE: [the check that was run and what was observed]
DECIDED: [choice - reason - revisit if], or none
COMMITS: [short hashes]
BROKEN OR UNKNOWN: [what fails or is unverified, or none]
NEXT: [the next feature and the first check before starting it]
```

## 4. Session start

```text
Before any new work:
1. Run pwd and git status. Work only in this directory. If the tree holds changes you did
   not make, stop and report.
2. Read the latest entries in progress.txt and the last 20 commits (git log --oneline -20).
3. Read features.json and choose the single highest-priority feature with "passes": false.
4. Run ./init.sh. If its basic check fails, fix that first and log it; never build a new
   feature on a broken base.
5. Before editing, state the feature id, its steps and the evidence that will prove it.
```

Read-only bearings, run from the project root:

```bash
pwd
git status --short
git log --oneline -20
tail -n 30 progress.txt
python3 -m json.tool features.json > /dev/null && python3 - <<'EOF'
import json
features = json.load(open("features.json"))
todo = [f["id"] for f in features if not f["passes"]]
print(len(features) - len(todo), "of", len(features), "passing; next:", todo[0] if todo else "none")
EOF
```

## 5. Session end

```text
Before ending the session:
1. Run the feature's steps end to end, as a user would, and record what you observed.
2. Only if every step passed, set that feature's "passes" to true. Change nothing else in
   features.json, except setting a feature you saw fail back to false (log why).
3. Run ./init.sh again to confirm that its basic end-to-end check still passes.
4. Commit the work locally with a message that names the feature id. Do not push, deploy or publish.
5. Append a progress entry: state, evidence, decisions, the commit hashes from step 4, broken or unknown, next.
6. Commit progress.txt on its own ("log: session [n]"), so git status shows nothing to commit.
```

## 6. Rules

- One feature per session. If one is too big for a session, propose a split in `progress.txt`.
- Agents change only `passes`. Adding, splitting, rewording or removing entries is a human decision.
- `"passes": true` needs the end-to-end steps run and observed. A unit test, an HTTP 200 or a clean exit is not enough.
- If a passing feature breaks, set it back to `false` and log why.
- Never remove or edit tests to make work pass.

## 7. Progress file or handoff packet

| | Progress file | Handoff packet |
|---|---|---|
| Job | Continuous log of one work stream | Transfer of ownership |
| Written | Every session, appended | Once, at the boundary |
| Ends with | The next feature to pick | One bounded next action and a return point |
| Reader | The next session of the same setup: same files, no memory of the conversation | A person or agent new to the work and its files |

Write both when ownership changes, work pauses for days, or the next step needs new authority. The packet then points to these files instead of retelling them.

## Worked example

```text
SESSION: 1 | 2026-10-01 09:10 UTC | setup
STATE: init.sh starts the app and loads the home page; features.json has 12 entries, all false.
EVIDENCE: ./init.sh exited 0 and printed "basic check: ok".
DECIDED: none
COMMITS: abc1234
BROKEN OR UNKNOWN: none
NEXT: F01; first check: /contact renders.

SESSION: 2 | 2026-10-01 14:30 UTC | F01
STATE: the form stores the message, but no confirmation appears; the redirect drops it.
EVIDENCE: new message found in the data store; the browser shows an empty form after Send.
DECIDED: none
COMMITS: def5678
BROKEN OR UNKNOWN: F01 still failing.
NEXT: F01; first check: does the redirect carry the confirmation state?

SESSION: 3 | 2026-10-02 08:05 UTC | F01
STATE: F01 passes end to end; F02 untouched.
EVIDENCE: browser run of all four F01 steps: confirmation shown, message stored; init.sh exit 0.
DECIDED: carry the confirmation in a query parameter, not the session; the redirect drops session state; revisit if Send stops redirecting.
COMMITS: 9fe2a10 "F01: keep the confirmation state across the redirect after Send"
BROKEN OR UNKNOWN: none
NEXT: F02; first check: send with the email field empty.
```

In session 3 the only change to `features.json` is F01’s `"passes": false` becoming `true`. The bearings now report 1 of 12 passing, next F02.

## Stop if

- `init.sh` fails for a reason outside the feature: environment, credentials or an upstream service.
- A feature cannot be checked end to end with the tools available: leave it `false` and log why.
- The next step needs a push, deploy, publish, deletion or new credentials.
- The feature list looks wrong or incomplete: changing it is a human decision.
- The tree holds changes this session did not make, or the same feature has failed three sessions running.

## Sources

- Anthropic: Effective harnesses for long-running agents — https://www.anthropic.com/engineering/effective-harnesses-for-long-running-agents (checked 2026-10-02)
