devlog · git:20260827.96da65a · 2026-08-27 · sha256 ecd99b6458c99024

devlog git:20260827.96da65aA

Immutable. This exact content is served forever at /api/v1/blob/ecd99b6458c99024.

---
name: devlog
description: Use when finishing a feature, fixing a notable bug, wrapping up a work session, or discovering something non-obvious (a gotcha, undocumented behavior, a workaround that took several tries, a constraint you only noticed after hitting it). Write the entry immediately, in the same turn - don't ask permission, don't offer, just do it. Do NOT use for routing, connecting, or moving notes (that's process-inbox), and do NOT use for one-off external-source capture (that's second-brain:capture).
allowed-tools:
  - Read(~/.claude/vaults/**/CLAUDE.md)
  - Read(~/.claude/vaults/**/*.md)
  - Write(~/.claude/vaults/**/*.md)
  - Edit(~/.claude/vaults/**/*.md)
  - Bash(npx @techpickles/sb:*)
  - Bash(${CLAUDE_PLUGIN_ROOT}/scripts/diagnose-sb.sh)
hooks:
  PreToolUse:
    - matcher: "Bash"
      hooks:
        - type: command
          command: "python3 \"${CLAUDE_PLUGIN_ROOT}\"/hooks/check-sb-before-call.py"
---

# Devlog

## Overview

Devlog is continuous, point-in-time work commentary: what happened, why,
and anything surprising, captured as it happens instead of reconstructed
from memory at the end of a session. One narrative session note
accumulates every entry from the current session — no separate
"learnings" system, no til/cursed split, everything worth remembering goes
in the same place.

Processing (routing, connecting, daily-linking) happens later via
`/second-brain:process-inbox`. This skill only ever appends to the inbox.

**Write the entry now, in the same turn as whatever triggered it.** Don't
ask "should I log this?" and don't offer to — just write it. Asking is
exactly the failure mode that made the old `distill-conversation` flow get
skipped: by the time a session ends, you're mentally done, and a skip-able
step gets skipped.

## When to write

- Finished a feature or fixed a notable bug
- Wrapping up a work session
- Discovered something non-obvious: a gotcha, undocumented behavior, a
  workaround that took several tries, a constraint you only noticed after
  hitting it

## When not to write

- Routing, connecting, or moving existing notes — that's
  `/second-brain:process-inbox`
- Reading an external source and writing it up — that's
  `second-brain:capture`
- Trivially obvious or one-off details you'd never reach for again
- Project-specific context (a config value, a repo convention) — that
  belongs in Claude Code's own memory system, not the vault

## Step 1: Load Configuration

Don't preflight-check sb availability — just run it:

```bash
npx @techpickles/sb config default && npx @techpickles/sb config vaults
```

If this fails, run `${CLAUDE_PLUGIN_ROOT}/scripts/diagnose-sb.sh` and follow
its guidance rather than re-deriving the checks by hand.

If no default vault is configured:
```
Second brain not configured. Run /second-brain:setup first.
```

## Step 2: Find or create this session's devlog note

Every entry from the current session lands in ONE note. Never create a
second devlog note for a session that already has one.

1. Collect session context:
   - Current repo and branch, if in a worktree
   - Current bean ID, if applicable
   - Short session id (first 8 characters of the session UUID)
   - Source string format: `claude-code session {short-id} ({repo}, branch {branch}, bean {id})`
     (omit fields that don't apply, matching the format the retired
     `distill-conversation` command used)

2. **Check your own memory first.** If you already created or appended to
   this session's devlog note earlier in this same conversation, you
   already have its path in context — go straight to Step 2a and append.
   No CLI search needed: this is the same mechanism
   `second-brain:insight` uses for its own accumulating session note
   ("if the session file already exists, was created in an earlier call
   in this same conversation, append").

3. **If this is the first devlog entry this session,** go to Step 2b to
   create the note, and keep its path in mind for the rest of the
   session.

4. **Fallback, only if you've genuinely lost track of the note's path**
   (e.g. a context compaction wiped your memory of this session) —
   resolve a real path instead of guessing:
   - `npx @techpickles/sb vault obsidian` — parse the JSON for the
     top-level `inbox` field (the inbox subfolder name, e.g. `📫 Inbox`).
     This is the precomputed field `sb note create` itself resolves the
     inbox folder from — don't parse `app.newFileFolderPath` directly,
     since that skips the Zettelkasten Prefixer plugin's folder override
     (`inbox` is `zkPrefixer?.folder ?? app?.newFileFolderPath`) and can
     point at the wrong folder.
   - `npx @techpickles/sb inbox list --detail` — parse the JSON, look for
     the entry with `type: session-notes` whose `source` field contains
     the current short session id, and take its `filename`
   - Combine: `~/.claude/vaults/{vault-name}/{inbox}/{filename}` is the
     real, readable/editable path. `inbox list --detail` only ever
     returns a bare filename, never a resolvable path on its own.
   - With the resolved path in hand, go to Step 2a and append. If the
     `inbox list --detail` search finds no entry matching the current
     session id (genuinely nothing to recover), fall through to Step 2b
     and create the note instead.

### Step 2a: Append to the existing note

Read the note, append one new bullet to the end of its body (format
below) using the Edit tool. Don't touch its frontmatter.

### Step 2b: Create the note

Staged and written in one unsandboxed Bash call:

```bash
STAGE=$(mktemp)
cat > "$STAGE" <<'EOF'
- {HH:MM} {entry}
EOF
npx @techpickles/sb note create \
  --title "Session: {topic}" \
  --source "claude-code session {short-id} ({repo}, branch {branch}, bean {id})" \
  --content "$(cat "$STAGE")"
rm -f "$STAGE"
```

Run this with the Bash tool's `dangerouslyDisableSandbox: true` set from
the start, not as a retry. Two reasons, same as the retired
distill-conversation flow documented: vault paths symlink outside the
sandbox's writable allowlist (a sandboxed attempt always EPERMs), and
`$TMPDIR` resolves to a *different path* under
`dangerouslyDisableSandbox: true` than under the normal sandbox — staging
and reading have to happen in the same call under the same mode or the
read can miss the file. The staged file must be a fresh `mktemp` path,
not a fixed filename: `$TMPDIR` is shared across every Claude Code
session on the machine, and a fixed-name file can be overwritten or read
by a stale write from a different session.

`sb note create` prints JSON with a `path` field. If stdout has anything
before the `{` (e.g. a stray `npm warn ...` line from the caller's own
`.npmrc`), extract the JSON substring starting at the first `{` rather
than parsing the whole stdout blob.

`sb note create` only ever writes `captured`, `source`, `repo`, `branch`,
`commit` to frontmatter — it has no `--type`/`--status` flag, and `repo`/
`branch` come out as the literal `none`/`none` (devlog's `--source` format
doesn't match `sb`'s own `conversation:repo=...,branch=...` parsing
convention). Read the file back at the path just parsed to preserve the
exact frontmatter sb wrote, then Edit it:
- add `status: raw`
- add `type: session-notes`
- replace the `repo: none` line sb wrote with `repo: {repo-name}` (from
  git context if available) — don't add a second `repo:` line
- replace the `branch: none` line sb wrote with `branch: {branch-name}`
  (from git context if available) — don't add a second `branch:` line
- add `bean: {bean-id}` (from git context if available)
- keep `source` as sb wrote it — the `claude-code session ...` string
  already covers provenance for devlog's format
- keep `captured` as sb wrote it

Without this fixup, the note is invisible both to this skill's own
"check memory / find existing note" logic (Step 2, above) and to
`process-inbox`'s stage 3a filter, which requires exactly `status: raw` +
`type: session-notes` to ever pick the note up.

## Entry format

One bullet per entry, each with a wall-clock timestamp:

```markdown
- {HH:MM} {what happened, why, anything surprising — one to three
  sentences, standalone enough to make sense without the surrounding
  conversation}
```

Prose, not a title. Write it like a short journal line, not a commit
message.

## Constraints

- One note per session — append, never create a second one for the same
  session.
- Never ask before writing, never present a summary and wait for a
  selection.
- Don't route, connect, move the note, or touch the daily note. That's
  entirely `process-inbox`'s job, later.
- Frontmatter stays `status: raw`, `type: session-notes` for the life of
  the session. `process-inbox` owns every status transition after that.