Requirement: CP-252 (originally CBIN-132) Status: Complete Last Updated: 2026-08-30
canary next picks the highest-priority requirement that is actually ready to
be worked on, and — with --prompt — generates the implementation guidance for
it. It answers one question, "what should be started now?", and it names the
evidence it answered from.
Key properties:
- Automated prioritization: declared
PRIORITYfirst, then unstarted work before work in progress. - Evidence-gated dependencies: a dependency is only complete when passing
evidence at the current commit says so.
STATUS=TESTEDis a claim, not proof. - Honest about its source: every answer says whether it came from the token
index (
database) or from a direct scan of the tree (filesystem). - Read-only:
nextnever creates or updates the index, the evidence store, or your tokens.
- CANARY CLI installed.
- A git repository: freshness and evidence are both bound to the current
commit, so a tree with no readable
HEADcan prove nothing. - (Recommended) a current index:
canary index. - (Optional)
.canary/memory/constitution.mdand.canary/templates/next-prompt-template.mdfor--promptoutput.
canary next # what to work on
canary next --prompt # full implementation guidance for itnext uses the token index only while that index still describes the tree in
front of it. The index is used when it exists, was built from this root, at
this commit, by this binary's token grammar. Otherwise the tree is scanned
directly with the same scanner, ignore rules, and token grammar canary scan
uses, so the two can never disagree about what the repository contains.
"source" |
Meaning |
|---|---|
database |
Answered from a current index. |
filesystem |
The index was missing, unbuilt, stale, or built elsewhere, so the tree was scanned. |
Two consequences worth knowing:
- "All requirements completed" is only ever claimed from a current index. A filesystem scan that finds no candidate says exactly that and no more — it cannot see the whole project's history, so it never congratulates you.
- Nothing is written. An absent index is not created to answer a read.
canary nextOutput:
Next: CBIN-101 - ScannerCore (Priority: 1, Status: STUB)
Run with --prompt for full implementation guidance.
canary next --promptRenders .canary/templates/next-prompt-template.md with the requirement's
specification, the constitution, test-first guidance, token placement examples,
success criteria, and dependency information. --prompt-arg <file|name> makes
one extra prompt available to the template as {{.PromptArg}} /
{{.PromptContent}}.
The template is read only on this path: a --format json or --dry-run run
never fails on a template it was not going to use.
canary next --status STUB # only unstarted work
canary next --status IMPL # only work that still needs tests
canary next --aspect Engine # only Engine work
canary next --aspect CLIcanary next --dry-runOutput:
Next priority (dry run): CBIN-101 - ScannerCore
Priority: 1 | Status: STUB | Aspect: Engine
Location: a.go
Source: filesystem
canary next --format json--format takes json or text (default text). In JSON mode stdout carries
one JSON object and nothing else; notes and warnings go to stderr.
{
"req_id": "CBIN-101",
"feature": "ScannerCore",
"aspect": "Engine",
"status": "STUB",
"priority": 1,
"file_path": "a.go",
"updated": "2026-08-30",
"source": "filesystem"
}source is always present. The requirement fields are absent when there was
nothing to select, and message says why instead:
{
"source": "filesystem",
"message": "no actionable requirements found"
}priority is the requirement's declared PRIORITY, or 5 when its token
declared none — the same neutral default the index applies, and the same number
selection ranked it at.
JSON is a machine contract and outranks the human-facing modes: --format json --dry-run returns the JSON object, not prose.
Deprecated:
--jsonis still accepted as an alias for--format jsonand now prints a deprecation warning on stderr (stdout stays pure JSON). An explicit--formatalways wins over the alias. Use--format json.
A dependency owned by a configured ticket source or peers: project resolves to
satisfied, unsatisfied, or unknown. Only satisfied clears it: an
unresolvable dependency blocks selection, because handing an agent a requirement
whose prerequisite might not exist is a wrong answer rather than a graceful
degradation. The block is explained on stderr:
note: external dependency ENG-9 blocks selection: no cached ticket status
Refresh the cache (canary ticket status --refresh) or, to start anyway:
canary next --allow-unknown-externalnote: external dependency ENG-9 is unresolved (allowed): no cached ticket status
Next: CBIN-200 - Consumer (Priority: 5, Status: STUB)
A dependency that names nothing at all — an unconfigured prefix that no peer knows either — always blocks; there is no flag for that, because there is nothing to resolve.
# 1. Agent completes the current task and records evidence for it
# 2. Agent asks for the next one
canary next --prompt
# 3. Agent implements the requirement following the generated guidance
# 4. Agent places/updates CANARY tokens
# 5. Agent runs the tests and records evidence (canary evidence ...)
# 6. Repeat from step 2Recording evidence is part of the loop, not an afterthought: until a requirement has passing evidence at the current commit, everything that depends on it stays blocked.
canary index # make the index current for this commit
canary next # what's next
cat .canary/specs/CBIN-138-multiline-tokens/spec.md
canary next --prompt > implementation-guidance.mdcanary list --aspect API --status STUB
canary next --aspect API --prompt# Current state:
# CBIN-105 (PRIORITY=1, DEPENDS_ON=CBIN-104)
# CBIN-104 (PRIORITY=3, STATUS=STUB)
canary next
# CBIN-104 — CBIN-105 is blocked until CBIN-104 is proven complete
# Implement CBIN-104, run its tests, record the result, then:
canary verify
canary next
# CBIN-105 — now unblockedMarking CBIN-104 STATUS=TESTED is not what unblocks CBIN-105. A PASS evidence
record for every feature/aspect CBIN-104 declares, at this commit, is.
$ canary next --promptOutput (abridged):
# Implementation Guidance: CBIN-101 - ScannerCore
## Priority Information
- Requirement ID: CBIN-101
- Priority: 1 (Highest)
- Status: STUB
- Aspect: Engine
## Specification
[Full spec.md content loaded here...]
## Constitutional Principles
From .canary/memory/constitution.md:
**Article IV: Test-First Imperative**
All features SHALL be implemented using test-first development...
## Implementation Guidance
### Step 1: Write Tests (RED phase)
```go
// CANARY: REQ=CBIN-101; FEATURE="ScannerCore"; ASPECT=Engine; STATUS=STUB; TEST=TestCANARY_CBIN_101_Engine_BasicScan; UPDATED=2026-08-30
func TestCANARY_CBIN_101_Engine_BasicScan(t *testing.T) {
// Test implementation...
}
```Scenario: a current index, and every requirement complete.
$ canary nextOutput:
🎉 All requirements completed! No work available.
Suggestions:
• Run: canary verify
• Review completed requirements
• Consider creating new specifications
This wording is reachable only from a current index. Scanned directly, the same empty answer reads:
no actionable requirements found (source=filesystem)
The tree was scanned directly because no current index was available.
• Run: canary index (then re-run canary next)
Work that exists but is blocked is never completion, whatever the source:
$ canary nextOutput:
no unblocked requirements (1 blocked by unmet dependencies)
Every remaining candidate is waiting on a dependency:
• Run: canary deps check <REQ-ID> (which dependency, and in what state)
• A local dependency blocks until evidence at this commit proves it:
run its tests, record the result, then: canary verify
• Unresolved external/peer dependencies are noted on stderr above
$ canary next --format jsonOutput:
{
"req_id": "CBIN-101",
"feature": "ScannerCore",
"aspect": "Engine",
"status": "STUB",
"priority": 1,
"file_path": "a.go",
"updated": "2026-08-30",
"source": "filesystem"
}The answer is the same shape whichever source produced it; only "source"
differs. Run canary index to make later runs answer from the index (and to
let a genuinely finished project say so).
- Record evidence, then ask again — dependencies clear on evidence, not on status edits.
- Always use
--promptfor full context. - Follow test-first guidance — RED → GREEN → REFACTOR.
- Read
"source"before believing a completion claim. - Do not paper over a block —
--allow-unknown-externalis a deliberate risk, not a default.
- Start the day with
canary next. - Preview with
--dry-runbefore generating a prompt. - Filter by
--aspectto match what you are working in. - Re-run
canary indexafter committing, sonextcan use the fast path. - Adjust
PRIORITYfields (orcanary prioritize) when plans change.
- Use
--format json— stdout is JSON only. - Check exit codes — 0 = success (including "no work"), non-zero = error.
- Branch on
"source"— treat afilesystemanswer as "index not current". - Handle
"message"— the requirement fields are absent when nothing was selected.
PRIORITY=1 (Highest priority)
...
PRIORITY=5 (The default applied to a token that declares none)
...
PRIORITY=10 (Lowest priority)
Lower numbers are selected first. Both sources honor the declaration: the index
stores it, and a filesystem scan carries it out of the token. A token that
declares no PRIORITY is ranked — and reported — at 5.
Among candidates of equal priority:
- STUB — not yet implemented (selected first)
- IMPL — implemented, still needs tests
- TESTED / BENCHED — not actionable, skipped unless
--statusasks for them
A candidate whose dependencies are not all complete is passed over, and the blocking dependency is normally selected instead. "Complete" means:
- Local dependency: every feature/aspect it declares has a PASS evidence record for this project at the current commit.
- External/peer dependency: it resolved
satisfied.unsatisfiedblocks;unknownblocks unless--allow-unknown-externalis passed.
Remaining ties are broken so that two runs over an unchanged tree pick the same
requirement: the index orders by priority ASC, updated_at DESC (most recently
touched first), and a filesystem scan orders by priority, then status, then
requirement id, feature, and aspect.
The tree was scanned and held nothing selectable. Check, in order:
- Tokens exist and parse:
canary scan --root . --out status.json .canaryignoreis not excluding them.- You are in the project root.
- Hidden paths (tests, templates, agent directories) are excluded by design.
That claim comes from a current index, so the index and the tree have diverged
in a way next could not detect, or the work is filtered out:
canary list --status STUB— is it there at all?canary list --include-hidden --status STUB— is it in a hidden path?- Commit your changes and re-run
canary index— an index is current for the commit it was built at.
canary deps check <REQ-ID>— which dependency, in what state.- For a local dependency: run its tests, record evidence, then
canary verify. - For an external/peer dependency:
canary ticket status --refresh, or scan the peer so itsstatus.jsoncarries a current verification export.
Error: render prompt: read template: ...
.canary/templates/next-prompt-template.mdmust exist.- The specification path referenced by the requirement must exist.
- Run without
--promptto test selection independently.
Q: Can I override the selection?
A: Adjust PRIORITY in the tokens (or canary prioritize <REQ-ID> <feature> <priority>),
narrow with --status/--aspect, or pick one explicitly with canary implement <REQ-ID>.
Q: Does the command modify anything?
A: No. canary next is read-only — it does not even create an index it finds
missing.
Q: Does it work without an index?
A: Yes. It scans the tree with the same scanner canary scan uses and reports
"source": "filesystem". The scan is slower on a large tree, and a filesystem
answer never claims the project is finished.
Q: Why is a dependency still blocking after I marked it TESTED?
A: Because a declaration is not proof. Run its tests, record the result in
.canary/evidence.json, and confirm with canary verify.
Q: How does this integrate with slash commands?
A: /canary.next runs canary next --prompt and feeds the result to the agent
as implementation guidance.
- canary list — viewing all requirements
- canary implement — manual requirement selection
- canary prioritize — adjusting priorities
- Ticket sources & peers — external dependency resolution
- Constitutional Principles — project governance
name: Auto-implement next requirement
on:
schedule:
- cron: '0 */6 * * *' # Every 6 hours
jobs:
auto-implement:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install canary
run: go install ./cmd/canary
- name: Get next requirement
id: next
run: |
canary index
canary next --format json > next.json
echo "req_id=$(jq -r '.req_id // empty' next.json)" >> $GITHUB_OUTPUT
echo "source=$(jq -r '.source' next.json)" >> $GITHUB_OUTPUT
- name: Implement with AI agent
if: steps.next.outputs.req_id != ''
run: |
# Call your AI agent here with steps.next.outputs.req_id#!/bin/bash
# .git/hooks/post-commit
canary next --dry-runUser: /canary.next
Claude: [Reads generated prompt]
Claude: I'll implement CBIN-138 (MultilineTokens) following the specification...
[Claude implements, tests, records evidence, and updates tokens]
User: continue
Claude: [Runs /canary.next again for the next task]
Implementation status: BENCHED (tested and benchmarked)