Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ section headers and bullet shapes intact. -->

## What changed

<bullet list of substantive deltas; group by package (parser/lint/mcp/cli) or by surface (spec/commands/taskgrind)>
<bullet list of substantive deltas; group by package (parser/lint/mcp/cli) or by surface (spec/commands)>

## Vision trace

Expand Down
15 changes: 1 addition & 14 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ supporting tools that make the format useful for humans and agents:
- `packages/cli` provides the `tasks` command-line interface.
- `commands/` contains the shared `/next-task` and `/lint-tasks` command variants
for Claude Code, Codex, Cursor, Devin, Gemini CLI, and Windsurf.
- `taskgrind/` contains the autonomous-grind prompt template and enforcement
scripts adopted by downstream repos.

## Repo Layout

Expand All @@ -40,7 +38,6 @@ tasks.md/
| +-- lint/ # @tasks-md/lint and tasks-lint binary
| +-- mcp/ # tasks-mcp server
| +-- cli/ # @tasks-md/cli and tasks binary
+-- taskgrind/ # Long-running autonomous session guardrails
```

## Development
Expand Down Expand Up @@ -125,7 +122,7 @@ shifts:
After changing a repeated term or command step, run a targeted search such as:

```bash
grep -r "<changed-term>" commands/ examples/ taskgrind/ README.md spec.md
grep -r "<changed-term>" commands/ examples/ README.md spec.md
```

### Spec Propagation
Expand All @@ -138,16 +135,6 @@ grep -r "<changed-term>" commands/ examples/ taskgrind/ README.md spec.md
- Examples must remain valid and should demonstrate new format features when
they would help agents learn the pattern.

### Taskgrind Propagation

Scripts in `taskgrind/scripts/` are canonical for adopting repos. For behavior
or interface changes, update the script header comment and `taskgrind/README.md`
with adoption notes. Pure bug fixes can say that no downstream action is needed.

Changes to `taskgrind/prompt-template.md` hard rules are policy changes. Update
the corresponding enforcement script when applicable and document the concrete
failure mode in `taskgrind/README.md`.

## Agentfile And MCP

`Agentfile.yaml` declares repo-local agent integrations:
Expand Down
23 changes: 0 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -311,29 +311,6 @@ Add more tasks → ...keeps draining the queue

You're always adding to the queue. The agent is always draining it. This is the core loop — planning is your job, execution is the agent's.

## Taskgrind — overnight / 24h autonomous sessions

For unsupervised runs longer than a single coding session, the
`/next-task` loop alone isn't enough. Without guardrails, agents
eventually find micro-doc-drift to "fix" once the real queue is
exhausted, generate single-finding PRs, exceed admin-merge volume on
shared branches, and ignore orchestrator stop signals.

[`taskgrind/`](taskgrind/) provides a canonical rule set + 4
enforcement scripts that prevent these failure modes:

| File | What it does |
|---|---|
| [`prompt-template.md`](taskgrind/prompt-template.md) | 10 hard rules — copy to your repo's `taskgrind.md`, fill in placeholders |
| [`scripts/check-zero-ship-streak.mjs`](taskgrind/scripts/check-zero-ship-streak.mjs) | Pre-flight `STOP`/`CONTINUE` check — already wired into the `next-task` skill |
| [`scripts/check-admin-merge-rate.mjs`](taskgrind/scripts/check-admin-merge-rate.mjs) | Counts admin self-merges in trailing 24h, exits non-zero at ≥5 |
| [`scripts/safe-admin-merge.sh`](taskgrind/scripts/safe-admin-merge.sh) | Wrapper around `gh pr merge --admin` that runs the rate check first |
| [`scripts/lint-pr-shape.mjs`](taskgrind/scripts/lint-pr-shape.mjs) | CI gate — refuses single-finding doc-only PRs without `closes <task-id>` |

See [`taskgrind/README.md`](taskgrind/README.md) for adoption options
(copy / symlink / future npx) and the lessons that motivated each
rule.

## Tooling

### CLI
Expand Down
2 changes: 0 additions & 2 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ This file is the root-level milestone summary that the `load-project-context` ru
| **Per-agent variants** — Claude Code, Codex, Cursor, Devin, Gemini CLI, Windsurf | ✅ Shipped | [`commands/{claude,codex,cursor,devin,gemini,windsurf}/`](commands/) |
| **Plan-first workflow** — agents write `docs/plans/<task-id>.md` before non-trivial work, validated by a reviewer subagent | ✅ Stable | [`docs/plans/next-task-plan-first-workflow.md`](docs/plans/next-task-plan-first-workflow.md), [`docs/templates/plan-template.md`](docs/templates/plan-template.md) |
| **Workspace mode** — `next-task` aggregates TASKS.md files across nested repos in one or more workspaces | 🟡 In progress | [`TASKS.md` § "Workspace mode"](TASKS.md) |
| **Taskgrind** — long-running autonomous-session enforcement scripts adopted by downstream repos | ✅ Shipped | [`taskgrind/`](taskgrind/) |
| **Site / docs hub** — public website at tasksmd.github.io | ✅ Live | [tasksmd.github.io/tasks.md](https://tasksmd.github.io/tasks.md/) |

## Active focus
Expand All @@ -43,7 +42,6 @@ When a new agent vendor ships first-class `/next-task` support, that's a milesto
| User stories | [`docs/user-stories/`](docs/user-stories/) (numbered) | New US per shipped capability |
| Spec definition | [`spec.md`](spec.md) | Conservative; breaking changes are spec-version bumps |
| Architecture | [`ARCHITECTURE.md`](ARCHITECTURE.md) + [`AGENTS.md`](AGENTS.md) | Updated in same commit as behavior changes |
| Long-running session enforcement | [`taskgrind/prompt-template.md`](taskgrind/prompt-template.md) | Hardened from real session learnings |

## Out-of-scope (won't do)

Expand Down
171 changes: 11 additions & 160 deletions TASKS.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@

Today `/next-task` reads `./TASKS.md` only — there's no first-class way to (a) pick the highest-priority unblocked task across one workspace, OR (b) pick across all workspaces on the host. Friction scales with workspace count × repo count.

The right answer is a workspace mode in the canonical tasks.md tooling, since the spec + parser + MCP + CLI are the load-bearing dependencies every other tool (agentbrew, dotfiles, minsky-observer, taskgrind) consumes. The design MUST support **multiple workspaces on one host** as a first-class concept — single-workspace mode is just N=1.
The right answer is a workspace mode in the canonical tasks.md tooling, since the spec + parser + MCP + CLI are the load-bearing dependencies every other tool (agentbrew, dotfiles, minsky-observer) consumes. The design MUST support **multiple workspaces on one host** as a first-class concept — single-workspace mode is just N=1.

**Outcome-shaped** (what a workspace-aware operator sees):

Expand Down Expand Up @@ -91,120 +91,6 @@

## P2

- [ ] Add mid-session main sync (or per-session sync) option to taskgrind
- **ID**: taskgrind-per-session-sync
- **Tags**: taskgrind, sync, queue, duplicate-work, cross-repo
- **Blocked**: needs-user-approval — the deliverable is a code
change in `/Users/fivanishche/apps/taskgrind` (`bin/taskgrind`,
`tests/git-sync.bats`, `README.md`, `man/taskgrind.1`), and
landing it requires pushing a feature branch to that remote and
opening an upstream PR — a cross-repo public write outside the
current `tasks.md` workspace that requires explicit
current-session operator approval. Additionally, the taskgrind
repo is being actively modified right now: 8 files dirty on
`main` including `bin/taskgrind`, `tests/features.bats`,
`tests/signals.bats`, `README.md`, `man/taskgrind.1`,
`docs/architecture.md`, `docs/user-stories.md`, and `TASKS.md`,
with a `bats tests/signals.bats` run in flight (PID 38645+) and
`.taskgrind-state` reporting `status=running session=4`. Two of
the three target files (`bin/taskgrind`, `README.md`) overlap
with the concurrent agent's WIP — picking this up from a
`tasks.md` session would race their edits. The previous
`**Blocked**` line on this task was unintentionally removed by
the squash-merge of #45 (which re-included session 4's pre-block
snapshot of TASKS.md); restoring it here.
- **Details**: With `TG_SYNC_INTERVAL=5` (default), sessions 1-4
work off the same stale `main`. During the 2026-05-02 tech-lead
run, the operator merged session 1's PRs (which removed 5 task
blocks) but session 2 still saw the old TASKS.md and re-claimed
one of the just-completed tasks (`reconcile-session-28-30-followups`),
redoing the cherry-picks and conflicts on a fresh branch. Add an
option `TG_SYNC_INTERVAL=1` (or change the default) and document
the trade-off — frequent syncs add fetch overhead but prevent
duplicate-work episodes when an external operator is merging
PRs in parallel. Better: detect when local main has diverged
from origin/main between sessions and force a sync regardless of
interval.
- **Files** (in taskgrind repo): `bin/taskgrind`,
`lib/constants.sh`, `tests/git-sync.bats`, `README.md`,
`man/taskgrind.1`
- **Acceptance**: Default sync interval is 1 OR a "concurrent
operator" mode is available that forces `git fetch && rebase`
between every session. Documented behavior change. Test added.
- **Research**: 2026-05-02 — implementation sketch
Sync behavior in taskgrind currently lives in three call sites:
1. `lib/constants.sh:35` defines `DVB_DEFAULT_SYNC_INTERVAL="5"`.
2. `bin/taskgrind:673` reads
`sync_interval="${DVB_SYNC_INTERVAL:-$DVB_DEFAULT_SYNC_INTERVAL}"`
(the `TG_*` → `DVB_*` translation table around line 231 maps
`TG_SYNC_INTERVAL` to `DVB_SYNC_INTERVAL`).
3. The sync gate at `bin/taskgrind:5413` triggers when
`sync_interval == 0` OR `session % sync_interval == 0`, with
a `_dvb_slot >= 1` early-return so only slot 0 syncs and a
`git_sync skipped (interval=…, session=…)` log line at
`bin/taskgrind:5586` for the non-trigger path.
Help/doc surfaces that mention the default: `bin/taskgrind:94`
(header help block), `README.md:248` (env-var table row), and
`man/taskgrind.1` (the same env-var entry — concurrent agent is
rewriting this file as part of the `TG_STALL_EXIT` consolidation).
Smallest-change option (acceptance "default sync interval is 1"):
flip `DVB_DEFAULT_SYNC_INTERVAL` from `"5"` to `"1"`, then update
the three doc surfaces above. Existing tests in
`tests/git-sync.bats` already parameterize
`DVB_SYNC_INTERVAL=0|2|3` (lines 42-131), so a new bats case can
drop the `DVB_SYNC_INTERVAL` export entirely and assert that
every loop iteration logs `git_sync` (not `git_sync skipped`).
Divergence-detection option (acceptance "concurrent-operator
mode that forces sync"): keep the default at 5 but add a cheap
probe before the interval gate at line 5413 — `git fetch
--quiet origin "$_default_branch"` followed by `git rev-list
HEAD..origin/$_default_branch --count`. If the count is non-zero,
log `git_sync forced reason=diverged ahead=N` and run the
existing stash/checkout/fetch/rebase block; otherwise fall
through to the interval logic. Bats coverage stages a remote one
commit ahead and asserts `git_sync forced` appears even with
`DVB_SYNC_INTERVAL=99`. Either option must keep the
`_dvb_slot >= 1` early-return so only slot 0 syncs.
Concurrent-agent note: the live README diff in taskgrind is the
`TG_STALL_EXIT` consolidation (collapsing `TG_NO_STALL_EXIT`,
`TG_EXIT_ON_STALL`, and `TG_EARLY_EXIT_ON_STALL` into a single
`TG_STALL_EXIT={never|first|second}` knob) — it rewrites the
same env-var table neighborhood as the `TG_SYNC_INTERVAL` row,
so this work should land after that branch merges (or be
rebased onto it) to avoid a textual conflict. The taskgrind
state file `.taskgrind-state` showed `status=running session=4`
during this enrichment, so the operator should also wait for
that grind to finish (or pause it) before unblocking.
- **Last-enriched**: 2026-05-02

- [ ] File Bosun follow-up: deliver orphan `main` commit `924f8f14`
- **ID**: bosun-orphan-main-commit-924f8f14
- **Tags**: bosun, delivery, cross-repo
- **Blocked**: needs-user-approval — this task lives in a different
repo (`/Users/fivanishche/apps/bosun`) and would require pushing a
feature branch to that remote and opening a Bosun PR. Cross-repo
publication outside the current `tasks.md` workspace requires
explicit current-session operator approval. Another agent is also
actively working in Bosun, so the operator should coordinate the
handoff before unblocking.
- **Details**: `/Users/fivanishche/apps/bosun` local `main` is one
commit ahead of `origin/main` with `924f8f14 refactor(skills): apply
3 improvement themes to user persona AIFN-720` (authored
2026-05-02 12:33). Direct commits on `main` violate the Bosun policy
`<!-- policy: Never commit directly on main — create a feature branch
first. -->` from `bosun/TASKS.md`. The proper delivery path is to
move the commit to a feature branch and open a PR. Another agent is
actively working in Bosun (saw branch flips and staged `bin/bosun`
changes during this taskgrind run), so coordinate before rewriting
local `main`.
- **Files** (in bosun repo):
`skill-plugins/orchestrator/orchestrator-user/SKILL.md`
- **Acceptance**: Either (a) commit is delivered via a Bosun PR with
`AIFN-720` suffix and bosun's `cd orchestrator && npm run verify`
passes, or (b) the commit is reverted on local main if it duplicates
work already on origin/main.
- **Last-enriched**: 2026-05-02

## P3

- [ ] Set up custom domain for GitHub Pages
Expand Down Expand Up @@ -234,41 +120,9 @@
Generated by `companion-task-groom` on 2026-05-21.
Lint status: pass — `npx -y @tasks-md/lint TASKS.md` exits 0
with 0 errors. Total tasks before this append: 3 (P2: 2,
P3: 1). Findings (3):

1. [probable-dead] `taskgrind-per-session-sync` (P2, line
13). All 5 paths in `**Files**` live in
`/Users/fivanishche/apps/taskgrind/` (`bin/taskgrind`,
`lib/constants.sh`, `tests/git-sync.bats`, `README.md`,
`man/taskgrind.1`), but that directory does not exist
on this host. The in-repo `taskgrind/` subdir at
`/Users/fivanishche/apps/tooling/tasks.md/taskgrind/`
only contains `prompt-template.md`, `README.md`, and
`scripts/` — none of the referenced paths match. The
task's `**Research**` field also pins line numbers in
the same dead paths (`bin/taskgrind:673`,
`bin/taskgrind:5413`, `lib/constants.sh:35`, etc.).
Last-enriched 2026-05-02. Action: confirm with operator
whether the taskgrind tool still exists at a different
path, then either refresh `**Files**` / `**Research**`
against the new location or retire the task with a
commit message noting the repo is gone.

2. [probable-dead] `bosun-orphan-main-commit-924f8f14` (P2,
line 99). `**Files**` references
`/Users/fivanishche/apps/bosun/skill-plugins/orchestrator/orchestrator-user/SKILL.md`,
but `/Users/fivanishche/apps/bosun/` does not exist
anywhere under `/Users/fivanishche/apps/` on this host
(verified with `find /Users/fivanishche/apps -maxdepth 4
-name bosun` returning empty). The orphan commit
`924f8f14` was on that repo's local `main`, so if the
repo is gone the deliverable is presumably also gone.
Last-enriched 2026-05-02. Action: confirm whether bosun
was renamed, archived, or retired; if the orphan commit
no longer needs delivery, remove the task with a commit
message noting the bosun repo is gone.
P3: 1). Findings (1):

3. [worker-fixable] `set-up-github-pages-custom-domain`
1. [worker-fixable] `set-up-github-pages-custom-domain`
(P3, line 129). Carries `**ID**`, `**Tags**`,
`**Blocked**`, `**Details**`, `**Research**`, `**Files**`,
and `**Last-enriched**`, but no `**Acceptance**` line.
Expand All @@ -282,23 +136,20 @@
what "done" looks like.

Suggested resolution path: worker (or operator) reviews
each finding and either updates the underlying task,
the finding and either updates the underlying task,
removes it with reasoning in the commit message, or
explicitly defers. Once all three findings are addressed,
explicitly defers. Once the finding is addressed,
remove THIS grooming task in the same commit and confirm
`npx -y @tasks-md/lint TASKS.md` still passes.

Bucket counts: worker-fixable=1, probable-dead=2,
Bucket counts: worker-fixable=1, probable-dead=0,
stale-claim=0, duplicate=0, spec-violation=0.

Companion-sweep notes:
- Both probable-dead tasks already carry
`**Blocked**: needs-user-approval` for cross-repo writes,
so they were already unpickable. The dead-files finding
just promotes them from "blocked-but-someday" to
"blocked AND likely obsolete".
- All three tasks were last-enriched 2026-05-02 (19 days
before this sweep), so they have cleared the 7-day
- Archived project task entries have been removed in a prior
commit.
- The remaining task was last-enriched 2026-05-02 (19 days
before this sweep), so it has cleared the 7-day
enrichment cooldown from spec.md § "Enriching blocked
tasks". No fresh-cooldown skips this round.
- No `(@agent-id)` claims appear on any task, so no
Expand All @@ -308,7 +159,7 @@
so all findings are batched inline here instead of
staged to a separate audit file.
- **Files**: `TASKS.md`
- **Acceptance**: Each of the three findings above is
- **Acceptance**: The finding above is
addressed — task updated in-place, removed with reasoning
in the commit message, or explicitly deferred with a
documented rationale appended to the task. After
Expand Down
6 changes: 1 addition & 5 deletions spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -518,11 +518,7 @@ When a task is done, the agent removes it from the file — the task line, its m

Top-level tasks should never be marked `[x]`. The `[x]` checkbox is only for sub-tasks tracking progress on a parent. When a top-level task is complete, remove the entire block — don't check the box. Linters should flag `[x]` on top-level tasks as a warning.

Taskgrind-powered repos may also require the completion commit to include
`closes <task-id>` in the commit message, using lowercase `closes` followed by
the task's exact kebab-case **ID**. This token lets
`taskgrind/scripts/lint-pr-shape.mjs` distinguish a doc-only commit that closes
a queued task from untasked doc drift:
Some repos may also require the completion commit to include `closes <task-id>` in the commit message, using lowercase `closes` followed by the task's exact kebab-case **ID**. This convention helps distinguish a doc-only commit that closes a queued task from untasked doc drift:

```text
docs: update setup notes
Expand Down
Loading
Loading