Issue Tracker: Obsidian Vault
Tickets, specs, and wayfinding items live as flat markdown notes in $OBSIDIAN_VAULT_PATH/Library/. The vault is the tracker — no external API, no separate service — so every discovery is a deterministic filename glob across a flat namespace.
Conventions
File naming
All tracker notes use a uniform naming scheme. The number namespace is shared across issues, specs, and wayfinding items:
Library/TICKET-<NNNNNN>-<slug>.md
Examples:
| File | What it is |
|---|---|
TICKET-000001-add-login.md |
Issue |
TICKET-000002-auth-spec.md |
Spec |
TICKET-000003-explore-apis.md |
Wayfinder research |
TICKET-000004-prototype-ui.md |
Wayfinder prototype |
The <NNNNNN> is zero-padded to 6 digits so filenames sort numerically. The <slug> is a short kebab-case descriptor.
Library/ must remain flat. No subdirectories for tickets. Everything lives at one level — the flat structure is what makes glob-based discovery deterministic.
Frontmatter schema
Every tracker note uses the task tag and carries task_labels to distinguish its role:
| Property | Required | Description |
|---|---|---|
tags |
✅ | Always [task] (the tag schema that defines all task_* properties) |
task_ticket_id |
✅ | The numeric identifier (matches <NNNNNN> in filename) |
task_status |
✅ | Triage + execution state (see below) |
task_priority |
✅ | Default none |
task_blocked_by |
✅ | List of Obsidian wikilinks to tickets that block this one (default []) |
task_projects |
✅ | List of Obsidian wikilinks to parent maps or projects this ticket belongs to (default []) |
task_labels |
✅ | Unified classifier — includes ticket roles, wayfinder types, AND free-form classification tags (feature, bugfix, config, epic, commissions, ...). Replaces former task_tags. |
raised_by |
❌ | Who reported / requested |
task_due |
❌ | Due date |
task_scheduled |
❌ | Scheduled date |
task_completed |
❌ | Completion date (set when task_status: done) |
task_created |
❌ | Created datetime (linter-managed) |
task_modified |
❌ | Modified datetime (linter-managed) |
title |
❌ | Auto-inferred from H1 by linter — don't set manually |
Task status values (enum)
The task_status enum combines triage pipeline and execution tracks:
| Value | Meaning | Used by |
|---|---|---|
none |
Unset / not yet evaluated | Linter default |
needs-triage |
Needs maintainer evaluation | triage |
needs-info |
Waiting on reporter for more information | triage |
ready-for-agent |
Fully specified, ready for an agent to take | triage, to-tickets, to-spec |
ready-for-human |
Needs human judgment/access | triage |
wontfix |
Will not be actioned | triage |
done |
Completed (closed) | triage, wayfinder |
canceled |
Abandoned (closed) | triage, wayfinder |
bloqued |
Blocked by external dependency | execution |
sometime-soon |
Backlog | execution |
next-in-line |
Next up | execution |
Task labels (open-ended list)
| Value | When to use |
|---|---|
ticket |
This note is an issue/ticket |
spec |
This note is a specification document |
bug |
Category: something is broken (triage) |
enhancement |
Category: new feature or improvement (triage) |
wayfinder:map |
This note is a wayfinder investigation map |
wayfinder:research |
This note is a wayfinder research ticket |
wayfinder:prototype |
This note is a wayfinder prototype ticket |
wayfinder:grilling |
This note is a wayfinder grilling ticket |
wayfinder:task |
This note is a wayfinder task ticket |
feature |
Classification: new feature |
bugfix |
Classification: bug fix |
config |
Classification: configuration change |
support |
Classification: support request |
report |
Classification: report/query |
training |
Classification: training request |
issue |
Classification: general issue |
epic |
Classification: large multi-step effort |
commissions |
Classification: commissions-related |
| (any) | Free-form — no schema constraint |
A ticket normally carries exactly one category label (bug or enhancement) alongside ticket. A spec carries spec (and NOT ticket — it is a spec, not a bug report). A wayfinder item carries its wayfinder:* type.
Blocking edges
Use task_blocked_by for dependency edges. Values are Obsidian wikilinks pointing to the target ticket's full filename:
task_blocked_by:
- '[[TICKET-000012-payment-gateway]]'
- '[[TICKET-000014-auth-flow]]'
A ticket whose task_blocked_by is empty or [] has no blockers and can start.
Project membership (map → child)
Use task_projects for the wayfinder parent→child relationship. A wayfinder child lists its map as an Obsidian wikilink here:
task_projects:
- '[[TICKET-000005-sale-pipeline-spec]]'
This links a wayfinder research/prototype/grilling/task ticket to its map. For wayfinder, the map's task_projects stays empty (the map is the root — nothing points up from it).
Comments
Conversation history, triage notes, and agent briefs are appended at the end of the note body under a ## Comments heading. Every comment added by a triage session must start with:
This was generated by AI during triage.
Format:
## Comments
> *This was generated by AI during triage.*
**Triage Notes — 2026-07-09**
What we've established so far:
- ...
What we still need:
- ...
Cross-references in commit messages
When code-review or any other skill resolves issue references from commit messages (e.g. Closes TICKET-000042), strip the TICKET- prefix and leading zeros to get the bare number, then search Library/TICKET-000042-*.md by filename glob.
Frontmatter Templates
Full reference with every variant → references/templates.md. The schema table above defines all valid properties; templates are convenience presets.
When writing a note, open references/templates.md and pick the variant that matches, then fill in the task_ticket_id and slug.
Operations Reference (by consuming skill)
triage — state machine
- Discover un-triaged tickets: Search
Library/for notes wheretask_status: noneortask_status: needs-triage, taggedtask, withtask_labelscontainingticket. (Criterion: every match in the vault collected and presented — don't stop at the first hit.) - Check for reporter replies: When returning to a
needs-infoticket, read the## Commentssection. If new text appears after the last triage comment, the reporter has replied. (Criterion: confirmed yes/no on whether new text exists.) - Assign category: Set
task_labelsto includebugorenhancement. Replace, don't append — a ticket has exactly one category. (Criterion:task_labelsupdated on the note.) - Assign state: Set
task_statusto the target value. Remove the old state. (Criterion:task_statusupdated.) - Close: Set
task_status: done(completed) ortask_status: canceled(wontfix/abandoned). For completed tickets, also settask_completed: YYYY-MM-DD. - Add comment: Append under
## Commentswith the triage disclaimer. - Find by number: Glob
Library/TICKET-<NNNNNN>-*.mdor searchtask_ticket_id: <NNNNNN>in frontmatter. (Criterion: note open on screen or confirmed absent — don't guess.)
to-tickets — publish breakdown
- Create ticket files: Write each ticket to
Library/TICKET-<NNNNNN>-<slug>.mdwith the correct frontmatter. - Assign numbers: Allocate sequentially. The user will tell you the starting number or you can discover the next available by listing existing
TICKET-files and takingmax + 1, then zero-padding to 6 digits. - Wire blocking edges: For ticket A that depends on B, add
'[[TICKET-<B-NNNNNN>-<slug>]]'to A'stask_blocked_byfrontmatter. - Apply
ready-for-agent: Settask_status: ready-for-agenton each ticket. - Create in dependency order: Write blockers first so their numbers are known. In a linear chain that means TICKET-000001 first, TICKET-000002 second, etc. (Criterion for the whole section: every ticket in the breakdown has a file on disk with the correct frontmatter and slug.)
to-spec — publish specification
- Create spec file: Write to
Library/TICKET-<NNNNNN>-<slug>.md. - Frontmatter:
tags: [task],task_labels: [spec],task_status: ready-for-agent. - Body: Use the spec-template from the to-spec skill (Problem Statement, Solution, User Stories, etc.).
code-review — resolve issue references
- Parse commit messages: Match
TICKET-<NNNNNN>(padded),Closes TICKET-<NNNNNN>, and bare#<NN>patterns across every commit in scope. (Criterion: every commit parsed — one pass, no re-reading.) - Resolve: Glob
Library/TICKET-<NNNNNN>-*.mdfor the filename, or searchtask_ticket_id: <NNNNNN>in frontmatter. (Criterion: each reference resolved to a note or flagged absent.) - Read spec for Spec-axis review: Look for notes with
task_labelscontainingspecthat match the feature domain. - No match found? Fall back to checking
Library/PRD-<slug>.mdorLibrary/for feature-matching files (legacy format, being migrated to TICKET- convention).
wayfinder — charting and working the map
- Chart the map:
- Create the map note:
TICKET-<NNNNNN>-<slug>.mdwithtask_labels: [wayfinder:map]. - Create child tickets as separate
TICKET-<NNNNNN>-<slug>.mdfiles withtask_labels: [wayfinder:<type>]. - Wire child→map relationship via
task_projects: ['[[TICKET-<map-NNNNNN>-<slug>]]']on each child.
- Create the map note:
- Wire blocking edges between wayfinder tickets using
task_blocked_bywith wikilinks. - Find the frontier: Search
Library/for notes withwayfinder:*labels whosetask_blocked_byis[]andtask_statusis NOTdoneorcanceled. Unassigned = any such note. (Criterion: every match in the vault collected — don't return the first hit.) - Claim a ticket: Open the note and read its body. No formal claim mechanism in a file-based system — just pick one and start working.
- Resolve a ticket:
- Append the answer under
## Commentswith a## Resolutionheading. - Set
task_status: done. - Update the map note's
## Decisions so farsection. - Promote any fog that's now specifiable into new child tickets (new
TICKET-files linked viatask_projects).
- Append the answer under
- Find children of a map: Search for notes where
task_projectscontainsTICKET-<map-NNNNNN>-(the numeric prefix inside the wikilink is enough to glob-match).
Aliases and Migration
The following property aliases exist for backward compatibility with notes created under the old schema:
| Old name | New name | Status |
|---|---|---|
ticket_status |
task_status |
Active alias (property schema) |
ticket_priority |
task_priority |
Active alias (property schema) |
task_tags |
task_labels |
Merged (alias in property schema, bulk-renamed on next lint pass) |
task_board |
(removed) | Migrated to task_labels (July 2026) |
task_type |
(removed) | Migrated to task_labels (July 2026) |
The task_status enum was migrated July 2026:
| Old value | New value |
|---|---|
triage |
needs-triage |
ready |
ready-for-agent |
needs-human |
ready-for-human |
| (missing) | done (added) |