task-journal
Original:🇺🇸 English
Translated
2 scripts
Append-only task record convention for Loom. Use when reading or writing per-task records under `.agents/tasks/<task-id>/`, when another skill needs to log a lifecycle milestone (saved, decision, readiness, blocker, done), when the user asks about an existing task's status or progress, or when checking task records for drift via the linter. Do NOT trigger for creating new tasks (that is `task-brief`'s job), for general docs/planning unrelated to a task directory, or for historical questions about the retired `task-state-management` system.
11installs
Sourcefreeacger/loom
Added on
NPX Install
npx skill4agent add freeacger/loom task-journalTags
Translated version includes tags in frontmatterSKILL.md Content
View Translation Comparison →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 file.
journal.mdThe 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 (see Tier table)
journal.md - 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 entry; do not move the directory)
done:
Do NOT use this skill when:
- the user wants to create a new task — that is 's job
task-brief - the user is asking a historical question about (the retired state machine)
task-state-management - 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: . Flat — no
<repo>/.agents/tasks/oractive/partitions.completed/ - Git tracking: default off. includes
.gitignore. The user may.agents/tasks/an individual file deliberately.git add - pointer: optional single-file pointer with the active task id (no newline required, trailing newlines tolerated). Written by
.currenton creation. Other skills may read it as a fallback when no task id was passed explicitly.task-brief
Task ID Format
YYYY-MM-DD-<slug>-<rand>- : ISO date of creation.
YYYY-MM-DD - : ASCII kebab-case, ≤ 6 words, ≤ 60 characters. Proposed by
<slug>from the user goal; the user may override.task-brief - : 2 lowercase hex characters (
<rand>–00), generated viaffthen formatted asrandom.randint(0, 255). Re-roll on directory collision. Never reused after deletion (collisions are rare and easy to detect).%02x
Example: .
2026-04-26-task-journal-redesign-a3Brief Immutability
brief.md- Before the first entry: the brief may be overwritten freely (the "uncommitted brief" window).
- After the first entry: do not edit . Record any goal drift, scope change, or refinement as a new journal entry, e.g.:
brief.mdmarkdown## 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 lines and an optional short body.
key: valuemarkdown
## <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 or
##), thenZ(em dash, space-padded), then the skill name.— - At least one line is required immediately under the heading.
key: value - 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 and reference it in the body.
artifacts/ - 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 |
|---|---|---|
| A persistent artifact was written or updated. Value names what was saved. | |
| A bounded design decision was made. Value names the decision; body may carry rationale. | |
| Readiness status of the design or plan. Value is | |
| A blocker was raised. Value is a short label; body describes what is blocked and why. | any skill |
| The task reached its completion state. Value names the result ( | |
Other keys (, , , , …) are free-form. The linter does not check them.
refinednotehandofflinkCompletion Semantics
Only code tasks have a real notion of completion (Option 2 from the design):
- Primary path: appends
finishing-a-development-branchafter a successful merge/ship.done: <result> - Fallback: the user states the task is done; the next skill (often ) appends the
task-briefentry.done:
Non-code tasks never get a entry. Activity is judged by the timestamp of the last entry; the linter may flag long-stale tasks as a soft hint.
done:Do not migrate the directory when a task is done. The task stays under . The presence of a entry is the marker.
.agents/tasks/<task-id>/done:Reopen is just appending a new entry after a entry. The linter detects this pattern and emits a warning so the human notices it.
done: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 | | Sole entry point that creates |
| B — appends on milestones | | Append entries at meaningful lifecycle moments (saved, decision, readiness, done). |
| C — appends conditionally | | Append only when a task id is in scope. |
| D — never appends | | 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.
- pointer — read
.current; if it names an existing directory, treat that as the active task.<repo>/.agents/tasks/.current - 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
journal.pyscripts/journal.pybash
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"bash
python3 ${SKILL_DIR}/scripts/journal.py read \
--tasks-dir .agents/tasks \
--task-id <task-id> \
[--last N]SKILL_DIR~/.claude/skills/task-journalskills/task-journalThe helper is stdlib-only, ~100 lines, and never blocks. It does not validate keys (that is the linter's job).
Linter: lint_tasks.py
lint_tasks.pyscripts/lint_tasks.py.agents/tasks/- error — corrupt heading, missing line, malformed reserved-key value, unparseable timestamp, missing
key: value. Returns exit code 1.brief.md - warn — body > 15 lines, entry written after a 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.
done:
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.
bash
python3 ${SKILL_DIR}/scripts/lint_tasks.py --tasks-dir .agents/tasksFlags:
- : required; path to the tasks root.
--tasks-dir <path> - : treat warnings as errors (exit 1 on any warning).
--strict - : lint a single task instead of the whole tree.
--task-id <id>
Anti-Patterns
These are the failure modes the design explicitly rejects. Avoid them.
- Do not edit after the first journal entry. Record changes as a
brief.mdentry.refined: - Do not move task directories on completion. A entry is the marker.
done: - 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,blockerfit, use them.done - 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 for the canonical list.
docs/design-decisions/2026-04-26-task-journal-replaces-task-state-management.md| 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 ( |
| 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 () was built without observed failures.
task-state-managementExamples
Tier B append (design-structure has just saved a design tree)
markdown
## 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 contractTier C conditional append (systematic-debugging on a tracked task)
markdown
## 2026-04-26T14:05:00+08:00 — systematic-debugging
note: reproduced the lost-entry case under concurrent appends
linked artifact: artifacts/concurrent-append-trace.mdReserved decision key
markdown
## 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 YAGNIDone marker (does not move directory)
markdown
## 2026-04-26T18:12:00+08:00 — finishing-a-development-branch
done: merged
PR #142 merged into main; cleanup branch deleted