session: pickup and wrap, four stores, one disposable handoff (V-445)

The pickup skill reads HANDOFF.md and the Vikunja task, then restates
the assumption set and stops. That pause is the point: every wasted
session here started with an agent that inferred the goal.

The wrap skill runs the tests, updates the durable docs, commits in
slices, pushes, records state in Vikunja, and leaves a handoff only if
work remains.

HANDOFF.md is gitignored and injected by a SessionStart hook. It holds
what the next agent needs to resume and nothing else. Anything that
would still matter next week goes to Vikunja, CLAUDE.md or docs/.

.claude/ was ignored wholesale. The workflow itself is now tracked,
because how a session behaves should be reviewed like code.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
kami
2026-08-02 03:18:48 +04:00
parent 54245f188a
commit 8d1209c392
6 changed files with 389 additions and 2 deletions
+45
View File
@@ -213,3 +213,48 @@ data pans on a phone. Local preview + headless screenshot recipe is in `AGENTS.m
This repo is project **Maven** (ID 2) in Vikunja. MCP: `http://localhost:9100/mcp` (or
`http://192.168.1.104:9100/mcp` from workpc). Feature/bug/deploy tasks go there.
Vikunja is the durable task store. A task holds the goal, the constraints and the
assumption ledger. Work without a task id is work nobody can resume, so a session that
has no id asks for one before it starts.
## Session workflow
Start with `/pickup`, end with `/wrap`. One task per session, and wrap at roughly half
context rather than letting the session compact.
Four stores, and each one owns something the others must not hold:
| Store | Holds | Lifetime |
|---|---|---|
| Vikunja task | goal, constraints, assumption ledger, status | durable |
| `CLAUDE.md`, `AGENTS.md` | what an agent must know before touching code | durable |
| `docs/` | design, measurements, decisions | durable |
| `HANDOFF.md` | only what the next agent needs to resume this task | one session |
`HANDOFF.md` is gitignored, injected at session start, and deleted the moment it is
consumed. If a line in it would still matter next week, it is in the wrong file.
Docs are tiered by path, so staleness is visible from the filename. Files directly under
`docs/` are living and carry a `Last verified: <date> @ <sha>` line. Files under
`docs/evals/` are dated measurements and are never edited after the day — a newer number
is a new file. Files under `docs/archive/` are dead and read by nobody by default.
## Git flow
Hooks live in `.githooks/` and are tracked, wired with `core.hooksPath`. Fresh clone:
```sh
git config core.hooksPath .githooks
```
- Branch per task: `scripts/task-branch.sh <vikunja-id> <slug>`. It records the parent
branch under `.git/maven-parent/`, because git does not track where a branch came from
and the PR needs the base.
- `pre-commit` refuses master, and refuses more than 300 changed lines in non-markdown
files. Markdown is exempt and may land as one batch.
- `commit-msg` requires the subject to end with `(V-<id>)`. `V-` and not `#`, because
Gitea autolinks `#123` to a Gitea issue, which is the wrong tracker.
- `pre-push` opens the PR with `tea` against the recorded parent. It never blocks a push.
`--no-verify` exists. Using it means saying why in the commit body.