v0.0.2
choom v0.0.2 — release notes draft
Milestone: v0.0.2 — 9 issues closed, 0 open.
v0.0.1 shipped a workspace you could capture into. v0.0.2's headline is /ai: you can now call
your AI assistant from inside a document, without leaving the editor — which is the whole premise
of this project arriving as an actual feature rather than a design goal. Around it, the release is
about what happens after capture: records can point at each other, tasks grew a body and can be
captured mid-sentence, and the whole TUI was re-laid-out around reading only what's on screen. The
project also picked up a new name.
Local AI assistant invocation (/ai)
choom has always been built on the assumption that a local AI assistant is reading the workspace.
This is the release where that assumption pays off from the inside: typing /ai <prompt> on its own
line in the editor and pressing enter saves the document, hands the prompt to whichever assistant
CLI you already have installed — Claude Code or GitHub Copilot — and drops the reply where the
command was. No integration, no API key, no second window.
- While it runs the command line shows
⋯, the editor is read-only, and the status bar shows a
randomly chosen corporate-jargon breadcrumb plusctrl+c to cancel. - Every failure restores the line exactly as typed — cancelled, non-zero exit, empty reply, no
assistant resolvable — each with a status-bar message naming the problem. A save failure is
reported and the assistant is never invoked. /aiis only a command when the line is entirely a command.Did you know you can type /ai in choom?, indented lines,/aim, and//aiare all plain text, never an error.- With exactly one supported assistant on
PATHand nothing configured,/aiworks with zero
setup. With two or none, the status bar names/config assistantas the way to choose. A
workspace with neither installed keeps every other feature unchanged. - Each assistant is invoked with a read-only permission flag (
--allowedTools "Read"for Claude
Code,--allow-tool "read"for Copilot) so it can read the document it was pointed at. Nothing
beyondReadis granted. - New config surface:
choom config assistant [<claude|copilot|none>] [--json](get or set,
reporting configured value, resolved value, source, and what's detected on this machine),
choom init --assistant <value>, and/config assistant <value>in the TUI command bar —
effective for the next/aiimmediately, no restart. Stored as[assistant].namein
.choom/config.toml; absent means "detect". - One deviation from the issue as written:
choom initdoes not prompt. The constitution
forbids the CLI blocking on input, so the choice is aninitargument, with/config assistant
as the interactive path.
It also shipped the reusable in-editor command framework (core.editor_commands) that /task and
/link were both built on later in this same release — a second, separate command grammar from the
list screen's command bar, where a line is a command only when it is entirely a command.
The project is now called choom
endpaper became choom. In Cyberpunk 2077 slang a choom is a good friend, which is the idea:
a tool that has your back, keeping you organized and your AI fed with context in a
corpo-restricted world.
- The CLI command is
choom(wasendpaper), and the PyPI distribution ischoom— reinstall
withuv tool install choom. - Breaking: the workspace marker directory moved from
.endpaper/to.choom/. An existing
workspace keeps working aftermv .endpaper .choomfrom its root. Nothing else about a
workspace's layout, frontmatter, or file contents changes.
Document links
Any record can now point at any other, and the workspace stays plain markdown while doing it. A
link is an ordinary CommonMark inline link, [text](path#id): the #id fragment is authoritative
and permanent, while the path is derived — computed by choom from the two real file locations, and
repaired whenever it goes stale.
- Hand-writable. Write
[Q3 planning](#meeting_20260728_a1b2c3d4)with no path at all and the
correct relative path appears on the next save. Move the target and the path is corrected the
same way. Nobody has to count../. - Nothing is indexed. Inbound links are computed by scanning on demand — measured at 155 ms
across 6,000 documents (50 MB) against a 500 ms budget, which is why there is no index and
nothing is written back into a link's target. - New CLI surface.
choom links <id> [--json] [--direction out|in|both]answers what a record
points at and what points at it.choom links checkreports stale (fixable) and dead
(needs a decision) as two distinct classes;choom links heal [--dry-run]repairs every stale
link and touches no dead one. - In the TUI.
/link <search terms>on its own line in the editor becomes a correct markdown
link to the one matching record; zero or several matches leave the line alone and report in the
status bar.btoggles a Links pane in both the full-screen preview and the list screen's
preview pane — outbound above, inbound below,enter/oto open. Clicking a rendered link
opens the record rather than a browser. - Task lines gained a
links:field, shaped exactly liketags:— comma-separated ids, never
paths. Field order is nowid,type,tags,links,created; a task with no links renders
exactly as before. - Breaking: id prefixes are now the full collection name —
meeting_,note_,task_
instead ofm_,n_,t_— so a new collection never needs a registry of abbreviations. Ids
written under the old scheme keep resolving; nothing is migrated and no existing file is
rewritten.
Inline task capture
Most followups are born mid-sentence. Typing /task <description> (or /task.<type> ...) on its
own line in the editor and pressing enter now captures a task without leaving the document: the
line becomes a checklist item linking to it —
- [ ] [call Terry about the renewal](../../../tasks.md#task_a1b2) — cursor at its end, nothing
else on screen moving. #tag tokens are extracted exactly as the command bar already does, and
prefixing an existing line with /task.followup promotes that line's own words into the task.
The checklist item is a control surface, not a copy. Tick it and save, or tick the task from
the tasks list, and the other side follows. Opening a document reconciles every mirror in it
against tasks.md first, so nothing shown is ever stale — no repair command, no background
process, and no cost for a document with no mirrors. A mirror is found by its #task_id fragment
alone, so rewording the link text or reindenting the line never loses it. Genuine disagreement is
reported rather than silently resolved: a mirror and task that both changed since they last agreed
surfaces a conflict, two disagreeing mirrors leave tasks.md untouched for that task, and a
mirror whose task no longer exists is left alone and reported.
Completing a task from the tasks list splices every mirror in the documents it links to without
stamping those documents' updated — ticking a box in a different collection is not an edit to
the meeting note.
choom task add "<description>" --link <id>(repeatable) records the same relationship from the
command line, validated before anything is written: an unresolvable id exits 1 and creates
nothing.choom task done/undone --jsongaindocuments_updatedandwarnings. A document that
couldn't be updated is a warning on stderr, never a non-zero exit — the task's own completion
already succeeded.
Task content editing
A task can now carry an optional markdown body — the detail, or a running log ("07-28 called, left
voicemail"), that a single checkbox line couldn't hold.
- Highlighting a task renders its body in the preview pane, the same way notes and meetings
already do; a task with no body clears the pane instead of showing the previous task's content. eon a highlighted task opens the editor scoped to that body alone (previously a no-op) —
empty for a task with none, pre-filled for one that has one.ctrl+o/ctrl+xsave,esc
discards with the existing confirm-only-if-dirty rule. A save that changes nothing writes
nothing.space(done/undone) leaves the body untouched.- On disk: indented continuation lines directly beneath the checkbox line in
tasks.md— no
sidecar file, no second store. One blank line separates them so the file stays valid CommonMark.
Indentation is dedented for display and re-applied on write using whatever prefix was observed.
A checkbox line indented under a task is still read as its own task, which is what keeps an
existing vault's task list unchanged on upgrade — and is the one deliberate limit (no nested
checklist inside a body). choom task show <id> [--json]prints one task and its body: the same columnstask list
prints, then the body verbatim after a blank line. Exit codes0found,1unknown id,2
ambiguous id (names the conflicting lines),3no workspace.task list --jsongains abody
key on every entry; every existing key keeps its name and meaning.
UI layout refresh
The whole TUI was re-laid-out, and the reading model changed underneath it — the app now reads only
the month it is showing instead of scanning a whole collection.
- Top collection bar.
Choom >> Tasks Notes Meetingsreplaces the vertical collection
menu;tab/shift+tabcycle and the panes refill immediately with focus on the list. The 14
columns the old menu used go back to the list/preview split, and the bar degrades to a compact
one-letter form on narrow terminals. The tool now opens on Tasks (was Meetings). - Month-scoped reading. For Notes and Meetings the left pane is a month list (
YYYY-MM,
most-recent-first, plus Unfiled for anything outside theYYYY/MMlayout). Opening a
collection or moving months reads only that month from disk. - Tasks: To-Do / Done in the left pane; the hidden
ashow-all binding is retired in favour
of selecting Done directly. - Editor-first.
eopens the editor straight from the list, and creating a note, meeting, or
daily note lands in the editor rather than a read-only preview. - Command bar. The
/prefix is permanent and can't be deleted.filter(aliasf) is now
an explicit verb — bare words are no longer silently treated as a search, and an unrecognised
first word is a visibleunknown command: '<word>'. An active filter searches every month, not
just the displayed one, reading each month at most once per session. /helpopens a pane listing every command and key binding without leaving the list screen,
and the running version renders in the bottom-right of every screen.
Internal
Nothing below changes what a user sees; it's here so the release record is complete.
Test suite retrofit and risk-based coverage
Constitution Principle VI was amended (v1.1.0) from "every acceptance criterion maps to at least
one test" to a risk-based rule, because the old one made the suite grow with spec verbosity rather
than with risk. The suite was then retrofitted against it: 361 tests / 6,347 lines → 337 tests /
5,538 lines with coverage of core and cli unchanged at 95% — the same 49 uncovered
statements, module for module.
Parallel _cli.py / _tui.py / _parity.py files for one behaviour collapsed into parametrized
tests where the parity check is the test, and each layer (contract/integration/unit/
performance) got a documented job. core's freedom from adapter imports is now enforced by ruff
(TID251) instead of a test that walked the AST, so it fails in the editor. Full-suite wall time
dropped from 118s to 62s serially and to ~8.5s under pytest -n auto — used by a new
tests.yml workflow, which is also the first to run the tests on push and pull request at all
(previously they ran only at release time or on manual dispatch).
Dependency pinning with a 48-hour cooldown
For supply-chain predictability, dependencies are pinned to exact versions rather than >=, and a
48-hour cooldown via uv's exclude-newer keeps a freshly published version from being adopted
before it has been in the world for two days. The ongoing maintenance target is that each release
bumps to the newest stable version older than 48 hours. Dependabot also bumped
actions/checkout 4 → 7 (#15) and
astral-sh/setup-uv 6 → 7 (#14).
Versioning and release rehearsal
internal · part of PR #30
__version__ is no longer a hardcoded literal — it's stamped into src/choom/_version.py at
build time by hatch-vcs, falling back to 0.0.0 for a source checkout or editable install. The
build hook is disabled by default precisely so an editable install doesn't stamp a development
version; real builds opt in with HATCH_BUILD_HOOKS_ENABLE=1. A new release-dry-run.yml
(workflow_dispatch) rehearses a release end to end — quality gate, build with a proposed
version, install, assert choom --version matches — and has no PyPI credentials, so it cannot
publish.
REQUIREMENTS.md retirement
REQUIREMENTS.md bootstrapped the project before GitHub issues took over that job. Its still-live
UX and design conventions were reshaped into a living conventions doc and moved to
docs/REQUIREMENTS.md, with references from other docs cleaned up.
Known gaps at release
- Manual terminal verification is outstanding across several features.
tasks.mdfor specs
005, 006, 007, and 008 each leave one task unchecked: confirming the new key bindings and
ctrl+ccancellation on Windows Terminal, iTerm2, macOS Terminal, PuTTY, and inside tmux. These
need a human at a real terminal on each platform and are a pre-release manual gate under the
constitution's own TUI verification rule. CHANGELOG.mdsays the Links pane toggles onl; the shipped binding isb(rebound
during #27 review becauselalready meant pane movement and read as1in most terminal
fonts). The changelog entry is stale — worth a one-word fix in a separate commit.docs/screenshots/execute-task.pngstill shows the old name. The assistant transcript in it
says "using the proper endpaper command" and uses[[wikilink]]syntax that predates document
links. It needs a re-recording, not an edit.