journal · v1.0.0 · 2026-09-11 · sha256 4956951532c6a00d
journal v1.0.0A
Immutable. This exact content is served forever at /api/v1/blob/4956951532c6a00d.
---
name: journal
description: "Write a focused engineering journal entry - retrospective on what just shipped or post-mortem on what just broke. Use after vd:ship, vd:cook, or right after an incident while context is fresh. Saves to the injected Journals path (personal dev log, not project docs)."
license: MIT
argument-hint: "[topic] [--incident] [--quick] [--since <ref>]"
metadata:
author: vanducng
version: "1.0.0"
---
# Journal
Journal **records** - one markdown file in the injected `Journals:` path. It does not redesign, retest, or roll back. If writing the entry surfaces a real bug - stop, kick to `vd:fix` or `vd:cook`, then come back to journal once the fact pattern stabilises.
## Modes
| Mode | Voice | Use when |
|---|---|---|
| _(default)_ **retro** | Calm, structured. What shipped, what was tricky, what's next. | After `vd:ship`, `vd:cook`, end of session. |
| `--incident` | Brutal-honest, 2am-developer voice. Root cause without euphemism. | A failure happened - outage, data loss, broken migration, repeated test failure, security finding. |
| `--quick` | 3-5 lines, no formal structure. | Drive-by note - small fix, minor decision, surprising bit of context. Pairs with either voice. |
## Hard rules
1. **Specificity beats vibe.** Every entry names at least one concrete artifact: commit SHA, PR #, file path, error string, metric. "We had perf issues" is not a journal entry.
2. **Decision, not narration.** If a choice was made, name the alternatives that lost and why. Otherwise the future reader can't reverse-engineer the call.
3. **Brutal in `--incident`, not performative.** Honesty about root cause is the point - not theatrics. "We shipped without testing the migration" beats both "an oversight occurred" *and* "this is a fucking disaster".
4. **No new design decisions.** Journal records what already happened. New decisions belong in `vd:brainstorm` or `vd:plan`.
5. **One file per event.** Don't append to yesterday's entry. New event → new file. Cross-link if related.
6. **Stop at one page.** Retro: 200-400 words. Incident: 300-600 words. If it's longer, it's a post-mortem doc - link to the journal entry from there, don't bloat the journal.
7. **Unslop the entry.** Final `vd:unslop` pass before saving - no AI tells, no em dashes, no chatbot sign-offs.
## Arguments
| Flag | Effect |
|------|--------|
| `[topic]` | Free-text title hint. If omitted, derived from branch + recent commits. |
| `--incident` | Switch to incident voice + structure. Default is retro. |
| `--quick` | Skip the full structure - write a 3-5 line note instead. |
| `--since <ref>` | Scope change analysis to commits since `<ref>` (default: last journal entry or branch divergence point). |
| `--no-subagent` | Force inline writing in main context. Default: delegate to `journal-writer` subagent if available. |
## Workflow
### 1. Gather facts (do not write yet)
- `git log --oneline <since>..HEAD` - what landed
- `git diff --stat <since>..HEAD` - surface area
- Read `## Plan Context` from the hook injection - if a plan dir exists, scan `plan.md` for phase status and the most recent phase file
- For `--incident`: also collect the specific error string, failed test name, log line, or metric that triggered this entry
If `<since>` isn't given:
```
1. Look in the injected Journals path for the most recent file → use its date
2. Otherwise, find the merge-base with the default branch
3. Otherwise, last 20 commits
```
### 2. Decide mode (if not flagged)
- Failure / red CI / rollback / security finding → `--incident`
- Successful ship / completed plan / end-of-session wrap → retro (default)
- One-line context worth saving → `--quick`
### 3. Pick the writer
| Condition | Writer |
|---|---|
| Default, `journal-writer` subagent available | Delegate to `journal-writer` via `Agent` tool - keeps main context clean |
| `--no-subagent`, or subagent unavailable | Write inline using the templates below |
| `--quick` | Always inline - subagent overhead > entry size |
If delegating, pass: mode, topic hint, `<since>` ref, plan dir (if any), and the relevant facts already gathered. **Do not** pass full git output - let the subagent re-gather scoped to what it needs.
### 4. Write the file
Path: write to the injected `Journals:` path. Filename: `journal-{YYYYMMDD-HHMM}-{slug}.md`. Use the naming pattern from the session hook (`## Naming` block) when present.
If the hook context has no `Journals:` path, do not search `$HOME`, inspect sibling features, or guess the artifact folder. Load `vd:workbench`, resolve from the current git root, and use its `journals` field exactly:
```bash
workbench_script='<loaded-workbench-skill-dir>/scripts/workbench.py'
VD_SESSION_ID="${VD_SESSION_ID:-}" python3 "$workbench_script" resolve --json
```
Resolve `<loaded-workbench-skill-dir>` from the `vd:workbench` skill location in this runtime - never a hardcoded `$HOME/skills`. Use only the hook-exported `VD_SESSION_ID`; no runtime-specific substitute unless that runtime's hooks document the same state key. When it is absent, accept branch-based resolution only if the returned feature matches the active branch or plan - otherwise stop and ask instead of constructing a destination.
> Journals are a personal dev log - what *I* learned, decided, or broke - not project documentation. `./docs/` is for artifacts shared with the team (architecture, code standards, changelog).
Final handoff must include an openable entry location, such as
`[journal-entry.md](/absolute/path/to/journal-entry.md)` or
`file:///absolute/path/to/journal-entry.md`, not just the basename.
### 5. Cross-link
- If a `plan.md` exists, append a one-line link to this entry under a "Journal" section at the bottom of `plan.md`.
- If the entry references a PR, drop the PR # in the frontmatter.
- Do **not** auto-edit `CHANGELOG.md` - that's `vd:ship`'s job.
## Templates
### Retro (default)
```markdown
---
date: YYYY-MM-DD HH:mm
mode: retro
branch: <branch>
pr: <#N or n/a>
plan: <plan-dir or n/a>
---
# {Title - what shipped, in 6 words or less}
## What shipped
- {bullet} ({commit-sha or PR#})
- ...
## Why this shape
{1 short paragraph. The decision and the alternatives rejected. If the plan covered this, link the phase file instead of repeating it.}
## What was harder than expected
- {one or two specific snags - file/error/metric}
## What the next dev should know
- {non-obvious fact, gotcha, or convention introduced}
## Next steps
- {actionable, owned, optional date}
```
### Incident
```markdown
---
date: YYYY-MM-DD HH:mm
mode: incident
severity: critical | high | medium | low
component: <system/feature>
status: ongoing | mitigated | resolved
branch: <branch>
---
# {Title - what broke, in 6 words or less}
## What happened
{Factual, terse. When, where, blast radius.}
## The brutal truth
{Root cause, no euphemism. The mistake, the missed signal, the bad assumption.}
## Technical detail
{The error string. The failed test. The metric. The query. At least one concrete artifact.}
## What we tried
- {attempt} → {why it didn't work}
## Lesson
{One sentence a future dev can change behaviour from. Not "be more careful". Something specific - "add a migration dry-run step before ship", "alert on queue depth > 1k".}
## Next steps
- {action} - {owner} - {by when}
```
### Quick
```markdown
---
date: YYYY-MM-DD HH:mm
mode: quick
---
# {Title}
{3-5 lines. One concrete artifact. Move on.}
```
Template discipline: delete any section with nothing concrete to say, and "root cause unclear - see follow-up issue #N" beats invented certainty. One file write, no review loop - the next entry corrects yesterday's wrong take if it matters.