task-journal
Task Journal
Overview
This skill is a convention reference, not a tool. It defines how every task in this repository records its life as an append-only journal of plain markdown. There is no slash command, no CLI, and no central writer process — every skill that touches a task appends entries directly to the same journal.md file.
The job of this skill is to give other skills (and humans reading the file) a stable contract about:
- where task records live,
- what an entry looks like,
- which keys are reserved,
- what the linter enforces,
- what completion means,
- and what signals justify upgrading away from this design.
Truth lives in the file system + git + memory. The convention exists to keep that truth readable.
When to Use
Use this skill when:
- a skill is at a lifecycle milestone and needs to append to
journal.md(see Tier table) - the user asks about an existing task's status, progress, or recent activity
- the user wants to check task records for drift (run the linter)
- you are about to mark a task done (write a
done:entry; do not move the directory)
Do NOT use this skill when:
- the user wants to create a new task — that is
task-brief's job - the user is asking a historical question about
task-state-management(the retired state machine) - the request is about general docs, design, or planning that has no task directory
- the request is a one-off action that does not warrant a task at all (e.g., "rename this variable")
Data Layout
<repo>/.agents/tasks/
├── .current # single-line pointer: <task-id>
└── <task-id>/
├── brief.md # immutable after first journal entry
├── journal.md # append-only log
└── artifacts/ # private to this task; long bodies go here
- Location:
<repo>/.agents/tasks/. Flat — noactive/orcompleted/partitions. - Git tracking: default off.
.gitignoreincludes.agents/tasks/. The user maygit addan individual file deliberately. .currentpointer: optional single-file pointer with the active task id (no newline required, trailing newlines tolerated). Written bytask-briefon creation. Other skills may read it as a fallback when no task id was passed explicitly.
Task ID Format
YYYY-MM-DD-<slug>-<rand>
YYYY-MM-DD: ISO date of creation.<slug>: ASCII kebab-case, ≤ 6 words, ≤ 60 characters. Proposed bytask-brieffrom the user goal; the user may override.<rand>: 2 lowercase hex characters (00–ff), generated viarandom.randint(0, 255)then formatted as%02x. Re-roll on directory collision. Never reused after deletion (collisions are rare and easy to detect).
Example: 2026-04-26-task-journal-redesign-a3.
Brief Immutability
brief.md is frozen the moment the first journal entry is written.
- Before the first entry: the brief may be overwritten freely (the "uncommitted brief" window).
- After the first entry: do not edit
brief.md. Record any goal drift, scope change, or refinement as a new journal entry, e.g.:## 2026-04-26T11:20:00+08:00 — task-brief refined: goal narrowed to logging-only path scope was widening to cover dashboards; user confirmed pulling that out
This rule exists so the brief stays a stable reference point. The journal is where evolution lives.
Journal Entry Format
Each entry is a level-2 markdown heading followed by one or more key: value lines and an optional short body.
## <ISO8601-timestamp> — <skill-name>
<key>: <value>
[<additional key>: <value>]
[free-form markdown body, ≤ 15 lines]
Rules:
- Heading must start with
##, then an ISO 8601 timestamp (date+time, with timezone orZ), then—(em dash, space-padded), then the skill name. - At least one
key: valueline is required immediately under the heading. - Keys are lowercase ASCII, may contain hyphens and underscores. Values are free text on the same line.
- Body is optional, ≤ 15 lines. If you need more, write to
artifacts/and reference it in the body. - Entries are appended only. Never rewrite existing entries.
- Entries should be separated by a blank line for readability (the linter does not require it).
Reserved Core Keys
These five keys are reserved and linted. Use them when their meaning fits — do not invent synonyms.
| Key | Meaning | Typical writer |
|---|---|---|
saved |
A persistent artifact was written or updated. Value names what was saved. | design-structure, writing-plans, executing-plans |
decision |
A bounded design decision was made. Value names the decision; body may carry rationale. | decision-evaluation, design-decision-audit |
readiness |
Readiness status of the design or plan. Value is ready or not-ready; body explains gaps. |
design-readiness-check |
blocker |
A blocker was raised. Value is a short label; body describes what is blocked and why. | any skill |
done |
The task reached its completion state. Value names the result (merged, shipped, cancelled, …). |
finishing-a-development-branch, user-initiated close |
Other keys (refined, note, handoff, link, …) are free-form. The linter does not check them.
Completion Semantics
Only code tasks have a real notion of completion (Option 2 from the design):
- Primary path:
finishing-a-development-branchappendsdone: <result>after a successful merge/ship. - Fallback: the user states the task is done; the next skill (often
task-brief) appends thedone:entry.
Non-code tasks never get a done: entry. Activity is judged by the timestamp of the last entry; the linter may flag long-stale tasks as a soft hint.
Do not migrate the directory when a task is done. The task stays under .agents/tasks/<task-id>/. The presence of a done: entry is the marker.
Reopen is just appending a new entry after a done: entry. The linter detects this pattern and emits a warning so the human notices it.
Skill Tiers
This is the contract for which skills should append to journal and which should not. Cross-reference each skill's own SKILL.md "Journal Integration" section for exact entry shape.
| Tier | Skills | Behavior |
|---|---|---|
| A — creates tasks | task-brief (only on user confirmation) |
Sole entry point that creates .agents/tasks/<id>/. Writes brief.md and the first journal entry (task_created: …). |
| B — appends on milestones | design-structure, design-refinement, design-decision-audit, decision-evaluation, design-readiness-check, writing-plans, executing-plans, subagent-driven-development, verification-before-completion, finishing-a-development-branch |
Append entries at meaningful lifecycle moments (saved, decision, readiness, done). |
| C — appends conditionally | bug-investigation, systematic-debugging, requesting-code-review |
Append only when a task id is in scope. |
| D — never appends | design-orchestrator, dispatching-parallel-agents, using-git-worktrees, test-driven-development, writing-clearly-and-concisely, receiving-code-review |
Methodology / routing / meta layer. They do not own task state. |
Task ID Propagation Between Skills
Pick the first that applies:
- Explicit parameter — the caller (user or upstream skill) names the task id directly. Preferred.
.currentpointer — read<repo>/.agents/tasks/.current; if it names an existing directory, treat that as the active task.- Otherwise — do not guess. Ask the user, or skip the journal append for this turn.
Do not introduce ambient state, environment variables, daemons, or file locks for this purpose.
Helper Script: journal.py
scripts/journal.py is a thin Python helper for skills that prefer a function call over hand-writing markdown. It is optional — appending markdown directly is equally valid.
python3 ${SKILL_DIR}/scripts/journal.py append \
--tasks-dir .agents/tasks \
--task-id <task-id> \
--skill <skill-name> \
--kv saved=design-tree.md \
--body "expanded core branches"
python3 ${SKILL_DIR}/scripts/journal.py read \
--tasks-dir .agents/tasks \
--task-id <task-id> \
[--last N]
SKILL_DIR resolves to wherever this skill is installed (~/.claude/skills/task-journal or the repo skills/task-journal). Both paths work.
The helper is stdlib-only, ~100 lines, and never blocks. It does not validate keys (that is the linter's job).
Linter: lint_tasks.py
scripts/lint_tasks.py walks .agents/tasks/ and reports drift. Severity rules:
- error — corrupt heading, missing
key: valueline, malformed reserved-key value, unparseable timestamp, missingbrief.md. Returns exit code 1. - warn — body > 15 lines, entry written after a
done:entry without a clear reopen marker, task with no entry for > 30 days, unknown skill name (heuristic only). Returns exit code 0 if no errors.
The linter never blocks LLM real-time writes. Whether the loom repo's pre-push hook calls it is a separate decision and not part of this contract.
python3 ${SKILL_DIR}/scripts/lint_tasks.py --tasks-dir .agents/tasks
Flags:
--tasks-dir <path>: required; path to the tasks root.--strict: treat warnings as errors (exit 1 on any warning).--task-id <id>: lint a single task instead of the whole tree.
Anti-Patterns
These are the failure modes the design explicitly rejects. Avoid them.
- Do not edit
brief.mdafter the first journal entry. Record changes as arefined:entry. - Do not move task directories on completion. A
done:entry is the marker. - Do not use journal as RPC between skills. The journal is write-many, read-rarely. If skill B needs a value from skill A, pass it as a parameter or read the underlying artifact (plan file, design tree, etc.), not by parsing journal entries.
- Do not invent synonyms for reserved keys. If
saved,decision,readiness,blocker,donefit, use them. - Do not embed long bodies in the journal. > 15 lines goes to
artifacts/. - Do not rewrite or delete past entries. Append a correction entry instead.
- Do not introduce ambient state (env vars, daemons, locks). Pass the task id explicitly or use
.current.
Upgrade Signals
This design intentionally stays small. Each of the failure scenarios below has a documented signal and a documented upgrade direction. See the ADR docs/design-decisions/2026-04-26-task-journal-replaces-task-state-management.md for the canonical list.
| Failure scenario | Signal | Upgrade direction |
|---|---|---|
| Concurrent writers from multiple agents | interleaved or lost entries observed | file lock or single-writer protocol |
| Frequent cross-task index queries | > 50 tasks and weekly "which tasks reference X?" needs | index file or SQLite |
| Single task with very long journal | > 200 lines or reading feels slow | rolling archive (journal-2026-Q1.md) |
| Frequent status grep queries | same grep ≥ 5×/week | structured summary command |
| Real second-person collaboration | a second human starts using it | switch to git-tracked + add an ownership field |
| Actual journal corruption | ≥ 1 corruption incident in practice | backup / checkpoint mechanism |
| Stable automated invocation needs | recurring CI/hook integration demands | thin CLI, one command at a time |
Meta-gate. Any PR that proposes "add the state machine back" must:
- point to one of the seven scenarios above,
- cite the number of times the signal has fired, and
- limit the upgrade to a single failure scenario.
Otherwise the PR is rejected. This rule exists because the previous design (task-state-management) was built without observed failures.
Examples
Tier B append (design-structure has just saved a design tree)
## 2026-04-26T10:42:11+08:00 — design-structure
saved: docs/design-tree/2026-04-26-task-journal-redesign.md
expanded the core branches; pending refinement on linter contract
Tier C conditional append (systematic-debugging on a tracked task)
## 2026-04-26T14:05:00+08:00 — systematic-debugging
note: reproduced the lost-entry case under concurrent appends
linked artifact: artifacts/concurrent-append-trace.md
Reserved decision key
## 2026-04-26T15:30:00+08:00 — decision-evaluation
decision: stdlib-only helper, no third-party deps
considered ruamel.yaml for richer parsing; rejected for footprint and YAGNI
Done marker (does not move directory)
## 2026-04-26T18:12:00+08:00 — finishing-a-development-branch
done: merged
PR #142 merged into main; cleanup branch deleted
More from freeacger/loom
writing-plans
Use when you have a spec or requirements for a multi-step task, before touching code
23writing-clearly-and-concisely
Use when writing prose humans will read—documentation, commit messages, error messages, explanations, reports, or UI text. Applies Strunk's timeless rules for clearer, stronger, more professional writing.
22systematic-debugging
Use when debugging bugs, test failures, build failures, performance regressions, or unexpected behavior and you need root-cause investigation before proposing fixes. Trigger on requests to debug, investigate why something broke, or find the source of a technical issue.
22subagent-driven-development
Use when executing implementation plans with independent tasks in the current session
19dispatching-parallel-agents
Use when facing 2+ independent tasks that can be worked on without shared state or sequential dependencies
19using-git-worktrees
Use when starting feature work that needs isolation from current workspace or before executing implementation plans - creates isolated git worktrees with smart directory selection and safety verification
19