# SkillFoundry v5.11.0 — FolderFlow

**Released:** 2026-05-08

---

## What's in This Release

### FolderFlow — Deterministic Story State via the Filesystem

Story state in SkillFoundry was implicit. The agent decomposed PRDs into
stories under `docs/stories/<feature>/STORY-XXX.md`, then updated `- [ ]`
→ `- [x]` from memory and frequently forgot. This produced two recurring
failures: stories appearing unfinished after the gates had passed, and
`/go` re-running work that was already complete.

v5.11.0 ships the FolderFlow PRD end-to-end (Phases 1–3). State is now
a function of the filesystem, not LLM memory:

- Checkboxes are reconciled against real artifacts on disk.
- Stories physically move between `todo/`, `in-progress/`, `blocked/`,
  `done/` folders as gates pass and fail.
- `git mv` preserves the audit trail across renames.
- No daemons, no watchers — synchronous and Git-visible.

PRD: [`genesis/2026-05-08-folder-state-and-checkbox-reconciler.md`](../genesis/2026-05-08-folder-state-and-checkbox-reconciler.md)

---

### Phase 1 — Story Checkbox Reconciler

`scripts/reconcile-story-checkboxes.sh` walks story files, dispatches
artifact-tagged checkboxes to handlers, and flips `- [ ]` → `- [x]`
when the artifact passes.

**Tag a checkbox:**

```markdown
- [ ] Build artifact present <!-- artifact: file-exists:.artifacts/STORY-042/build.ok -->
- [ ] Log says PASS          <!-- artifact: grep:.artifacts/STORY-042/log.txt:^PASS$ -->
- [ ] Manual review approved
```

**After the gate runs, the reconciler flips:**

```markdown
- [x] Build artifact present <!-- artifact: file-exists:.artifacts/STORY-042/build.ok -->
```

**Properties:**

- **Idempotent** — Running twice in a row with no new artifacts produces
  zero changes (FR-007).
- **Non-destructive** — A `- [x]` line is never reverted to `- [ ]`.
  If a previously-checked artifact has since vanished, a regression
  warning is logged but the box stays checked. Regression detection
  is the orchestrator's job (FR-008).
- **Untagged checkboxes are sacred** — A `- [ ]` line without an
  `<!-- artifact: ... -->` tag is never modified by the reconciler
  (FR-005).
- **Path-safe** — Absolute paths, `..` traversal, and symlinks resolving
  outside the repo root are all rejected before any handler runs.
- **No content leakage** — Logs include artifact paths and pass/fail
  booleans only. Secrets in artifact files cannot leak through reconciler
  output.

**Strict mode:** `--strict` (or `SF_RECONCILER_STRICT=1`) exits non-zero
if any artifact-tagged `- [ ]` remains. Used by `/layer-check` and
`move-story.sh` to enforce that gate verdict and checkbox state agree
before a story can be marked done.

**Self-test:** `scripts/reconcile-story-checkboxes.sh --self-test`
runs built-in fixtures and exits 0 on a healthy install.

---

### Phase 2 — Story Folder State Machine

```
docs/stories/<feature>/
├── INDEX.md                ← auto-regenerated; never edit by hand
├── todo/
├── in-progress/
├── blocked/
│   └── STORY-XXX.BLOCKED.md  ← auto-generated sibling: gate, reason, timestamp
└── done/
```

**`scripts/move-story.sh <story-path> <target-state>`**

| Target | Effect |
|--------|--------|
| `todo` | Move to `todo/`. Cleans up any leftover `STORY-XXX.BLOCKED.md`. |
| `in-progress` | Move to `in-progress/`. |
| `blocked` | Move to `blocked/` and write `STORY-XXX.BLOCKED.md` with `--blocked-gate` and `--reason`. |
| `done` | Run reconciler in `--strict` mode first; **refuse the move (rc=3) if any artifact-tagged `- [ ]` remains**. Then move to `done/`. |

`git mv` is used when inside a git tree, so `git log --follow` keeps the
audit trail across renames.

**`scripts/migrate-stories-to-folders.sh`** — one-time, idempotent
migration. Walks `docs/stories/<feature>/`, classifies each flat story
by checkbox state, and `git mv`s into the right folder:

- All artifact-tagged `[x]`, no tagged `[ ]` → `done/`
- Any tagged `[ ]` → `todo/`
- No artifact tags at all → `todo/` (ambiguous; printed in the
  manual-review list)

Safe to re-run.

**`scripts/lib/story-index.sh`** — INDEX.md regeneration. Deterministic
output (sorted by ID, no timestamps). Parses `# STORY-XXX — Title`
headings and YAML `depends_on:` frontmatter (inline + block list).
Only re-writes the file when content actually changes.

---

### Phase 3 — JSON Artifact Handlers

The reconciler now parses standardized JSON artifacts emitted by Anvil
gates and CI tools. Schemas are frozen in PRD §5.2:

| Tag | Schema | Pass when |
|-----|--------|-----------|
| `test:` | `{"passed": <bool>, "failed": <number>, "total": <number>}` | `passed == true && failed == 0` |
| `lint:` | `{"violations": <number>, "files_scanned": <number>}` | `violations == 0` |
| `layer-check:` | `{"status": "pass"\|"fail", "database": ..., "backend": ..., "frontend": ...}` | `status == "pass"` |

```markdown
- [ ] Unit tests pass     <!-- artifact: test:.artifacts/STORY-042/test.json -->
- [ ] Lint clean          <!-- artifact: lint:.artifacts/STORY-042/lint.json -->
- [ ] Layer-check pass    <!-- artifact: layer-check:.artifacts/STORY-042/layer-check.json -->
```

**Strict schema enforcement:** Malformed JSON, missing required fields,
or required fields with the wrong type all produce `invalid` (rc=2).
The checkbox stays unchecked and a clear error is logged. This is the
explicit defense against a half-broken gate producing a vacuous pass.

---

### `/go` and `/layer-check` Integration

**`/go`** now skips any story already in `done/`. This is the explicit
fix for the "re-runs completed work" failure described in the PRD's §1.1.
At each gate transition, `/go` calls `move-story.sh`:

```
PRD decomposed → todo/
Story selected → move-story.sh <s> in-progress
Architect → Coder → Tester → Gate-Keeper
APPROVED → move-story.sh <s> done
REJECTED → move-story.sh <s> blocked --blocked-gate <id> --reason "<reason>"
```

**`/layer-check`** runs the reconciler as its final iteration gate
(new "Checkbox Reconcile" row in the verdict box). On stories already
inside the folder layout, an APPROVED verdict drives `→ done/`; a
REJECTED verdict drives `→ blocked/`.

---

### Test Coverage

Two shell-based test suites:

| Suite | Cases | Covers |
|-------|-------|--------|
| `tests/scripts/test-reconciler.sh` | 43 | All 5 handlers (pass, fail-by-data, fail-by-missing-file, malformed-JSON, missing-required-field, wrong-type), path safety (absolute paths, traversal, symlink escape), CLI semantics, dispatch routing |
| `tests/scripts/test-folder-state.sh` | 40 | INDEX regeneration, all state-transition validation, BLOCKED sibling lifecycle, migrator classification (3 cases × 4 outcomes), idempotency, dry-run, regression check that Phase 1 still passes |

**Total: 83 cases green.** Plus an in-script `--self-test` mode (10+
fixture assertions). All run in < 2 seconds.

---

## Migration Notes

**No breaking changes.** All Phase-1 reconciler installs (which were
not yet shipped publicly) keep working — the JSON handlers that
previously returned a "deferred" code (4) now route to real handlers,
so any pre-existing artifact tags using `test:`, `lint:`, or
`layer-check:` will start working as soon as the emitting gate
produces JSON.

**To opt a feature into the folder layout:**

```bash
# Dry-run first to see classifications
scripts/migrate-stories-to-folders.sh --dry-run docs/stories/<feature>

# Migrate
scripts/migrate-stories-to-folders.sh docs/stories/<feature>
```

Idempotent and reversible (`git mv` preserves history). Features still
on the flat layout continue to work — `/go` and `/layer-check` detect
and graceful-degrade.

**Sourced libraries (`scripts/lib/*.sh`)** deliberately do **not**
enable `set -u` or `set -e` — those leak into the caller's shell.
Entry-point scripts set their own strict modes.

---

## Documented Deviations from PRD

1. **`bats` → plain shell tests.** PRD §10.1 specified `bats` for unit
   tests; `bats` is not in the project's existing test toolchain.
   Substituted plain-shell tests under `tests/scripts/` matching the
   existing pattern (`tests/run-tests.sh`). No new system dependency.

2. **Phase prerequisites waived.** PRD §9.1 stated Phase 2 should ship
   only "after Phase 1 proves valuable on at least one feature," and
   Phase 3 should ship only "when Anvil gates emit standardized JSON
   artifacts." Both prerequisites were waived at user direction; all
   three phases shipped back-to-back. Phase 3 is forward-looking —
   handlers are wired and tested against synthetic JSON, ready for
   the first gate that emits the frozen schema.

---

## Documentation

- [`docs/story-checkbox-reconciler.md`](./story-checkbox-reconciler.md)
  — Phases 1 + 3 (reconciler + JSON handlers).
- [`docs/story-state-folders.md`](./story-state-folders.md)
  — Phase 2 (folder layout, CLI reference, exit codes, manual override).
- [`genesis/2026-05-08-folder-state-and-checkbox-reconciler.md`](../genesis/2026-05-08-folder-state-and-checkbox-reconciler.md)
  — The full PRD.

---

## Previous Release

[v5.10.0 — Test Cartographer](./V5.10.0-RELEASE-NOTES.md)
