feature-tracker-mcp
feature-tracker-mcp — "oz"
A project-agnostic tracker for the case where one person directs several AI coding sessions at once and has become the bottleneck.
The problem is not "track features." It is: several tracks are in flight, each session is blind to the others, decisions surface constantly, and reading everything is the job. Most of those decisions are mundane and delegable. A few are genuinely yours. Nothing separates them, so you read all of it.
Each project gets its own Postgres instance in a .oz folder — the project owns its tracker outright.
Table of Contents
The model
Goals are objectives, not filing labels. Each carries what is being achieved, what done means, what is deliberately excluded, and what constrains it. A worker reads its goal before starting and declines work that falls inside the non-goals — that is the difference between executing a queue and pursuing an objective.
Features belong to exactly one goal. Ids are allocated atomically (DIP-17, GLG-4) and never change, so anything citing one in a commit message keeps resolving. They carry acceptance criteria, dependencies, and a parity record.
Gates are decisions a session must not take alone — architectural, install, spend, irreversible. Raising one banks the question and moves on; the principal answers a round of them in one sitting.
Parity is tracked across eight dimensions, because work is only done when it is done in all of them: code, unit tests, functional tests, documentation, screens, builds, infrastructure, features.
Studies are investigations whose deliverable is a document and a decision. Pre-registered, and allowed to conclude "do not build this".
Conferrals are the record of Claude working a judgement out with a peer model — ChatGPT, a harder-thinking Claude, DeepSeek. Every goal, feature, study, checkpoint and finished diff requires one, enforced by the tools rather than requested by the docs.
Commands
Driven through the /oz skill.
Working
Command | Does |
| What can be worked now — not done, nothing it depends on outstanding |
| What is waiting, and on what |
| Drive a goal's features to done, here |
| Spawn the backlog maintainer and a worker per goal, each in its own worktree |
| Cross-track state, including uncommitted work per worktree |
| Broadcast to every worker, including ones not yet running |
| Wind down leaving it resumable: stash every worktree, record where. |
| Work interrupted by a shutdown, and putting it back |
| Who is alive, how long since each was heard from, what each holds |
Shaping the work
Command | Does |
| Both passes in order — goals, then the features under them. The order matters: criteria written under an unrefined goal are written against nothing |
| Draft and record every unrefined goal in one pass; gate what the docs cannot settle |
| Give every open feature acceptance criteria in one pass |
| Define a new goal — the conversation produces the name |
| Work through a derived checklist a page at a time |
| Project-wide coverage analysis — what nothing tracks, including goals that should exist. The scoped forms bound what is filed, not what is examined |
| What the project is not |
| Invention, ending in one checkpoint |
| Investigate; deliverable is a document and a recommendation |
| Record something that would otherwise be lost |
| Record a dependency |
Decisions and reporting
Command | Does |
| Watches, and prompts you with the pending gates as a pickable checklist. The only path from a raised gate to a decision |
| The gate queue |
| Counts, items, the append-only record |
| Coverage, and the cheap probe that populates it |
| Goals and hygiene |
| Dump, list, and restore the tracker — take one before any bulk pass |
The peer
One reasoner cannot find its own blind spot, and the principal cannot always supply one. When the question is a derivation they would have to take on trust, asking them is not review — it is handing over an unread liability. That is the problem this exists to solve.
A conferral is an exchange, not a verdict. It runs rounds: put the actual draft and reasoning to the peer, settle what it says it lacked, check what it asserts, disagree where you disagree, keep going while it is still converging, stop when circling. How it ends decides who hears about it.
Outcome | Consequence |
| Recorded. The principal is not involved |
| The peer moved Claude and the change was applied. Recorded with what changed |
| Promoted to the principal, carrying both positions |
Soft gates never reach the principal. architectural and install forks are raised into the loop — gate_raise refuses one with no conferral, because escalating instead of thinking is what made the queue unreadable. spend and irreversible are always the principal's: two models agreeing is not authorisation to spend someone's money or to take an action with no undo.
The peer drafts where it should. Study pre-registration is written by the peer and attacked by Claude, because a method is attacked best by whoever did not write it, and the falsifier is the question a single reasoner is least able to ask itself.
A feature cannot reach done until its diff has been through a peer — checked against the database, not asserted in the same call that closes the work.
Enforcement is mechanical because prose failed. The actor field has accepted the value chat since the first commit and across 76 recorded events has never once held it. A mechanism that recommends conferral is a mechanism that gets skipped.
The framing is kept verbatim. Claude chooses what the peer sees, so agreement can be manufactured with a leading question — and from outside, that is indistinguishable from independent agreement. asked and the peer's own account of what it lacked are what make it distinguishable.
Agents
Agent | Does |
| One per goal, own worktree. Claims, builds, evidences, releases. Refines its own items and gates only genuine forks |
| What a worker structurally cannot: dependencies across goals, cross-goal duplication, staleness |
| Per turn, cheap model. Records what a turn implied that would otherwise be lost |
| Dedups; splits what peers can settle from what only the principal can |
| Investigates, writes it up, links what it spawned and killed |
No agent may call gate_decide. An agent approving its own gate makes the mechanism theatre.
Install
git clone https://github.com/spe-investigator/feature-tracker-mcp.git
cd feature-tracker-mcp
.\install.ps1Builds, copies the /oz skill and the agents into ~/.claude, and registers the server. Requires Node 18+ and Docker; the database is created in the project's .oz folder on first use, and Docker is started automatically if it is not running.
Design notes
The database is the state, and the bus. Sessions cannot see each other, so coordination goes through Postgres rather than messages: a message is lost if its recipient is not running, and unauditable afterwards. Messaging is at most a doorbell.
Ids are allocated atomically. Two sessions reading a file, seeing the next free number and both taking it is how duplicate migration numbers happen. A counter incremented inside the inserting transaction makes it impossible.
Claims are leases, not flags. A worker that dies holding a flag blocks its feature forever; a lease expires and the work returns to the pool.
Counts are two numbers, never a ratio. The numerator is what converged; a growing denominator means real work was found. 12/16 → 12/20 reads as 75% → 60% — a drop — when nothing regressed and two genuine issues were named.
Standup reads working trees, not just commits. Measured here: every worktree reported zero commits ahead while two held real uncommitted work — a new crate in one, four untracked files in another. A standup built on git history calls active tracks idle.
Rejection means two things. "Not for this goal" is a routing error and the feature is moved; "not wanted" is a decision about the project. Only the second suppresses re-derivation. Every refusal keeps its rationale, and accumulated they define what the project is not — the difference between "no" and "no, because we decided X".
Drafting is derivable; decisions are not. Refinement is a batch pass over everything, with gates raised only for what the documents cannot settle. A flow that asks a question per item spends attention on the part that needed no input.
Studies are pre-registered. Method, decisive evidence and falsifier are recorded before execution, because a method chosen after seeing the data can be selected for the answer it produces. Revisions are counted rather than silent.
Thresholds are measured, not guessed. Duplicate detection uses trigram similarity at 0.30 — real restatements score 0.45–0.63, unrelated titles under 0.10, and an initial guess of 0.45 let a genuine duplicate through at 0.449.
Backups are verified, not just written. A dump is read back before it is reported as taken — the expected tables must be present and the file non-trivial. pg_dump can exit zero having written almost nothing, and an unverified dump is a file rather than a backup.
The schema has no migrations. All DDL lives in src/db.ts as idempotent statements applied on every connect, so a schema change ships with the code that needs it and lands the moment any session connects. The cost is that nothing records when a change landed, there is no down-migration, and anything destructive — dropping a column, narrowing a type — has no safe expression in this pattern and needs a real migration path first.
An objective is lifted once, not re-derived. Goals carry doc_ref — the file and heading their objective came from — and refined_commit, the tree it was read at. Without the pointer a refine pass has to re-read the whole source document to locate each goal's section, once per goal, and afterwards nothing distinguishes an objective taken from the document from one invented where the document was thin.
Incorporation, not annotation. converged_after_revision requires stating what changed, and the tool refuses the outcome without it. Feedback logged beside an unchanged draft changes nothing and reads as diligence — which is worse than not conferring, because it looks like the mechanism worked.
Derivation is goal-scoped; gap analysis is not. derive_features asks what one objective implies, so it can only find work inside a goal somebody already wrote. Work between two goals, or outside all of them, is invisible to it by construction — and a missed feature inside a goal surfaces the next time anyone reads that goal, while a missing goal never surfaces at all. /oz gaps is the only thing looking from far enough back to find one, and a missing goal is a hard gate because gate_reopen cannot unwind a goal that already has work routed into it.
A gate blocks work, and answering one has to unblock it. gate.blocks was written from the first commit and read by nothing, so a feature whose blocking question was unanswered was still handed out and coded straight past. feature_ready now skips those and reports them rather than hiding them; feature_unblocked is what a resuming worker calls, because answering a gate changes a gate row and nothing else.
What is not built
/oz starthas never been executed. The spawn line is documented but background spawning is untested here. Try/oz taskon one goal first.The interactive flows are unrun.
/oz refine,/oz design,/oz studyand/oz goalare written and their tools are tested individually; the conversations are not.Parity probes exist for
documentationandcodeonly.buildsandinfrastructureneed a real check — a build, an applied migration — and a reference search would only pretend to answer them.screensis switched off.No test suite. The conferral enforcement has 11 passing checks; nothing else does.
The conferral loop has never been run for real. The enforcement is tested against fabricated conferrals, which is exactly what it cannot detect. Whether the exchanges actually happen is a property of the sessions, not of this code.
The markdown renderer that would regenerate a human-readable tracker from the database does not exist, so a migrated markdown tracker cannot be diffed against the database to prove nothing was lost.