canary

package module
v0.3.7 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 2, 2026 License: Apache-2.0 Imports: 4 Imported by: 0

README ΒΆ

CANARY

Agentic-Coding-Friendly Requirement Tracking System

License Go Version PRs Welcome Go Reference Version

CANARY is a requirement tracking system that embeds tokens directly into source code, enabling precise tracking of features, tests, benchmarks, and documentation. This bridges the gap between requirements and implementation, ensuring that an agent coding system has not only the ability to be precise in the specification and planning phases but the outputs of which can be measured and verified automatically.

The CANARY system is designed with autonomous AI agents in mind, providing slash commands and structured data to facilitate agent workflows. It also prescribes a test-first development approach through constitutional principles (guidance the tooling verifies via evidence, not a technical gate that blocks writing code), ensuring that quality is prioritized over speed.

Quick Start

Installation

Canary publishes .deb packages to the APT repository at apt.codepros.org (same pattern as void).

# Import the Codepros APT signing key
curl -fsSL https://apt.codepros.org/codepros-keyring.gpg | sudo tee /usr/share/keyrings/codepros-archive-keyring.gpg > /dev/null

# Add the Codepros APT repository
echo "deb [signed-by=/usr/share/keyrings/codepros-archive-keyring.gpg] https://apt.codepros.org/ stable main" | sudo tee /etc/apt/sources.list.d/codepros.list

# Update and install
sudo apt update
sudo apt install canary
From source
# Install from source
go install devnw.dev/canary/cmd/canary@latest

# Or clone and build
git clone https://gitlab.com/devnw/codepros/oss/canary
cd canary
make build

Repository (for the pages site)

The code is hosted on GitLab; github.com/devnw/canary is a read-only mirror.

Initialize Your Project
# Create a new project
canary init my-project

# This creates:
# .canary/
#   β”œβ”€β”€ memory/constitution.md      # Project principles
#   β”œβ”€β”€ templates/                   # Spec and plan templates
#   β”œβ”€β”€ specs/                       # Individual requirements
#   └── canary.db                    # Token database
# GAP_ANALYSIS.md                    # Requirement tracking
Your First Requirement
# Create a specification (AI agent)
/canary.specify Add user authentication with JWT tokens

/canary.plan CBIN-001

/canary.implement

# The primary functions like specify, plan, and implement can be run
# via the CLI but won't really do much for a user. They are designed
# for AI agents to call programmatically.

# User's can find all of the command options through the --help flag

# Build database and query progress
canary index
canary show CBIN-001
canary status CBIN-001

How It Works

CANARY Tokens

Tokens are structured comments that track requirements:

// CANARY: REQ=CBIN-105; FEATURE="UserAuth"; ASPECT=Security; STATUS=TESTED; TEST=TestUserAuth; UPDATED=2026-08-29
func AuthenticateUser(creds *Credentials) (*Session, error) {
    // implementation
}
Legacy Tokens & Normalization

For backward compatibility the scanner accepts older token patterns and normalizes them:

Legacy Form Normalized
CBIN-5 CBIN-005
CBIN-42 CBIN-042
REQ-7 REQ-007
REQ-12 REQ-012
REQ-GQL-4 REQ-GQL-004

Rules:

  1. Bare ID segments (e.g. REQ-7) inside a CANARY line are accepted.
  2. When both a bare legacy ID and a canonical REQ= key appear, the REQ= value wins.
  3. Only the final numeric segment is zero‑padded to three digits.

Use the canonical format in new code:

// CANARY: REQ=CBIN-005; FEATURE="Parser"; ASPECT=Engine; STATUS=IMPL; UPDATED=2025-10-18

Legacy forms are supported for historical tokens but should not be added to new implementations.

Token Lifecycle:

STUB β†’ IMPL β†’ TESTED β†’ BENCHED
  • STUB: Placeholder, not yet implemented
  • IMPL: Implementation exists, tests missing
  • TESTED: Declared tested β€” verification requires a passing evidence record for every declared test at the current commit
  • BENCHED: Declared benchmarked β€” verification requires a bench evidence record for every declared benchmark at the current commit
Architecture Aspects

CANARY organizes code by architectural concerns:

  • API - Public interfaces, exported functions
  • CLI - Command-line interfaces
  • Engine - Core algorithms and business logic
  • Storage - Databases, persistence, repositories
  • Security - Authentication, authorization, encryption
  • Docs - Documentation files
  • Wire - Serialization, protocols, networking
  • Planner - Planning and scheduling
  • Bench - Performance benchmarks
  • FrontEnd - User interface
  • Dist - Distribution and deployment
Dependency Management (CBIN-147)

Express dependencies between requirements:

## Dependencies

### Full Dependencies (entire requirement needed)

- CBIN-146 (Multi-Project Support - required for token namespacing)

### Partial Dependencies (specific features/aspects)

- CBIN-140:GapRepository,GapService (only gap storage needed)
- CBIN-133:Engine (only Engine aspect required)

Features:

  • Circular dependency detection using DFS algorithm
  • Transitive dependency resolution
  • Evidence-based satisfaction (a dependency is satisfied by verified evidence, not by declared status)
  • Reverse dependency queries
  • ASCII tree visualization
canary deps check CBIN-147        # Check if dependencies satisfied
canary deps graph CBIN-147 --status  # Visualize dependency tree
canary deps reverse CBIN-146      # What depends on this?
canary deps validate              # Check entire graph for cycles
Verification Gates

Prevent overclaiming with evidence-based verification. A declared STATUS=TESTED or STATUS=BENCHED is only an author's claim; canary verify checks it against recorded evidence, not against the declared status:

# Scan codebase for tokens
canary scan --out status.json --csv status.csv

# Verify claims in GAP_ANALYSIS.md against recorded evidence
canary verify --root . --claims GAP_ANALYSIS.md
# Exits 0 if every claimed requirement has a passing evidence record, at the
# current commit, for every TEST=/BENCH= it declares. Exits 1 otherwise
# (including an empty claims file, and a dirty working tree β€” pass
# --allow-dirty to accept one).

Evidence is produced by running the real test/benchmark suite and recording the result, not by editing a token by hand. The trusted path is canary evidence run-go-test: it always runs canary's own resolved Go toolchain itself (never a caller-supplied executable), retains the raw go test -json event stream as a digest-named artifact under .canary/artifacts/, and derives every emitted record by reparsing that retained artifact. It is the only command allowed to mark its output origin: "executed" β€” but it only does so when the toolchain it ran was operator-named via --toolchain-path (or config evidence.toolchain_path); a toolchain merely resolved from the caller's own GOROOT/PATH yields origin: "imported" instead, the same level canary verify treats as untrusted by default (see the trust boundary below):

# Run tests under canary's own supervision, with an operator-named
# toolchain, emit an evidence file on stdout, then merge it into the store
canary evidence run-go-test --project <KEY> --toolchain-path "$(go env GOROOT)/bin/go" -- -count=1 -json ./... > evidence.json
canary evidence ingest --in evidence.json --out .canary/evidence.json

# Now verification can find that evidence
canary verify --root . --claims GAP_ANALYSIS.md

canary evidence run executes an arbitrary caller-supplied command instead of a fixed toolchain; because canary cannot know that command was a real test runner, every record it emits is marked origin: "imported", and canary verify refuses those by default β€” pass --allow-imported to accept them. If you already have a go test -json stream you did not produce with run-go-test (e.g. captured from a CI job's logs), from-go-test maps it to evidence records the same way β€” also "imported":

go test -count=1 -json -bench=. -benchtime=1x ./... > gotest.json
canary evidence from-go-test --project <KEY> < gotest.json > evidence.json
canary evidence ingest --in evidence.json --out .canary/evidence.json

# --allow-imported is required here: these records are origin "imported"
canary verify --root . --claims GAP_ANALYSIS.md --allow-imported

from-go-test derives the commit from --root's git HEAD and refuses a dirty working tree unless --allow-dirty is given; it validates schema and matches the record to that commit, but β€” like any imported evidence β€” does not by itself authenticate who produced the underlying test-event stream (see the trust boundary below). canary evidence ingest never relabels a record's origin on trust, though: an "executed" record it cannot independently re-derive from a local artifact (missing artifact file, digest mismatch, or the artifact not actually proving that test/package passed) is demoted to "imported" before it reaches the store, and canary verify performs that same re-derivation again at verification time.

Trust boundary

canary evidence run-go-test is the only producer that may label a record origin "executed" β€” but only when the toolchain it ran was operator-named via --toolchain-path (or config evidence.toolchain_path), never merely resolved from the caller's own environment. canary always runs the real go test binary itself, so the caller can smuggle test arguments but never the executable; WHICH go that is still matters. Without --toolchain-path, resolution falls back to GOROOT/bin/go or a PATH lookup for convenience β€” both of which honor values the caller's own environment supplies (a GOROOT env var, PATH itself) β€” so records from that fallback are labeled "imported" instead, the same trust level canary evidence run and from-go-test always produce. -exec, -toolexec, and -overlay are refused, GOFLAGS is scrubbed, and GOROOT itself is scrubbed from the child's environment, so a caller cannot substitute the runner, its inputs, or the toolchain a nested go invocation would resolve.

Every emitted record binds three things canary verify re-checks: the commit it was produced against, a source digest computed immediately before and after the child process (verify drops any executed record whose source digest no longer matches the working tree β€” the source changed since the record was produced), and the resolved toolchain's identity (path and digest). evidence ingest independently re-derives any "executed" label against a local artifact and demotes it to "imported" when it cannot; canary verify performs that same re-derivation again at verification time and its verdict carries a receipt (policy, overrides, commit, source digest) β€” a receipt is a binding, not a signature.

Executed evidence is filesystem-trust by default. Even a fully operator-named toolchain only proves the record was produced by a real go test run somewhere on this filesystem β€” it does not authenticate WHO ran it or on WHOSE machine. A local adversary who controls the whole environment (and can therefore invoke canary evidence run-go-test with their own --toolchain-path) is not defeated by any of the above: nothing stops them from building whatever code they like and recording that it passed. Filesystem trust means "whoever has write access to this filesystem is trusted," not "the reported result reflects some other party's honest run."

Adversarial or cross-machine trust requires the ed25519 attestation mechanism pkg/attest provides, with the signing key held OUTSIDE the workspace it signs for:

# Once, on a machine the workspace adversary does not control:
canary evidence keygen --out signing.key --pub signing.pub
# Move signing.key off this filesystem; distribute only signing.pub.

canary evidence run-go-test --project <KEY> --toolchain-path "$(go env GOROOT)/bin/go" --sign-key signing.key -- -count=1 -json ./... > evidence.json
canary evidence ingest --in evidence.json --out .canary/evidence.json --require-attestation --trusted-keys signing.pub
canary verify --root . --claims GAP_ANALYSIS.md --require-attestation --trusted-keys signing.pub

--sign-key signs an attestation binding producer, toolchain, source, and commit for that run's executed records; verify --require-attestation and ingest --require-attestation refuse any executed record whose attestation does not verify against --trusted-keys. This is a real trust upgrade ONLY when the signing key is kept outside the workspace it describes β€” a key committed to the repo, or dropped next to the evidence it signs, protects nothing, since a workspace-writer can read and reuse it exactly like the evidence itself. Turning --require-attestation on with an in-workspace key is security theater, not a fix (see pkg/attest's package doc for the full honesty boundary).

Peer resolution (the peers: list in .canary/project.yaml, consulted when a requirement id belongs to a sibling project) inherits the same boundary: by default it trusts whatever verified/verification a peer's status.json reports, exactly as forgeable as this project's own evidence store would be without attestation β€” anyone who can write to peer.Root can write whatever receipt they like. Configuring evidence.trusted_keys upgrades peer trust to "a key I actually trust vouched for this project, this commit, this exact claims list" (the receipt's signature, its ClaimsDigest, and its PolicyHash are all checked) β€” but even then, a peer entry names only a filesystem path (name/root), not an expected project identifier, so a trusted signature only proves SOME project the signer vouches for, not specifically the project that peer entry names. A peer entry pointing at the wrong directory, or a compromised second peer reusing a trusted key, is not caught today; that needs a per-peer project field (tracked in GAP_ANALYSIS.md).

None of this defeats a local adversary who controls the whole environment an evaluator runs the binary in β€” a binary running inside a workspace an adversary controls cannot authenticate a party that same adversary also controls. What v0.3.7 actually provides is a strong default against accidental overclaiming (toolchain, source, and argv are pinned and re-checked, so an ordinary run cannot silently mint evidence for the wrong code) plus a genuine cryptographic upgrade for teams willing to hold a signing key somewhere the workspace cannot reach. Neither substitutes for running canary's evidence pipeline in an environment you already trust.

GAP_ANALYSIS.md Format:

# Requirements Gap Analysis

## Claimed Requirements

βœ… CBIN-101 - Scanner Core
βœ… CBIN-102 - Verify Gate

## Gaps

- [ ] CBIN-103 - Status JSON (needs tests)

Core Commands

Query and Inspection
canary show CBIN-105          # Display all tokens for a requirement
canary files CBIN-105         # List implementation files
canary status CBIN-105        # Show progress summary
canary grep "Authentication"  # Search tokens by pattern
canary list --status TESTED --aspect API  # Filtered listing
Workflow Automation
canary next                   # Get next priority requirement
canary next --prompt          # Generate AI agent prompt
canary implement CBIN-105     # Get implementation guidance
canary implement fuzzy        # Fuzzy match requirement
Specification Management
canary specify add-user-auth  # Create new specification (requires a feature description)
canary specify update CBIN-105  # Modify existing spec
canary plan CBIN-105          # Generate implementation plan
Documentation Tracking
canary doc status CBIN-105              # Check doc currency
canary doc update CBIN-105              # Update doc hashes (every feature under the requirement)
canary doc report --show-undocumented   # Coverage report
Dependency Management
canary deps check CBIN-147         # Check dependency satisfaction
canary deps graph CBIN-147 --status  # Show dependency tree
canary deps reverse CBIN-146       # Show reverse dependencies
canary deps validate               # Detect circular dependencies
Multi-Project Support (CBIN-146)
# Build or rebuild the index (always resolved under --root; default is
# .canary/canary.db, there is no --local flag on `index`)
canary index

# Project management (subcommand is singular: `project`)
canary project register my-app ./my-app   # Register a project
canary project list                       # List all registered projects
canary project switch my-app              # Change the active project
canary project current                    # Show the active project

Peer projects: sibling repos can be declared under peers: in .canary/project.yaml so one project's requirement graph can resolve IDs another project owns (see docs/user/ticket-sources-guide.md). Peer resolution requires a fresh, non-degraded verification receipt from the peer's own canary scan export β€” one that names the peer's project, its commit, and the evidence policy it was judged under. A bare verified array with no receipt, a degraded receipt (produced under a relaxed policy), or a receipt with no commit is not proof and resolves unknown, never satisfied; only a requirement listed in a receipt-backed, strict peer export resolves satisfied.

Complete Workflow

For AI Agents
1. Agent runs: /canary.next
2. System returns next priority requirement with:
   - Full specification
   - Implementation plan
   - Constitution principles
   - Test-first guidance
3. Agent implements following RED-GREEN-REFACTOR
4. Agent places CANARY tokens in code
5. Agent updates token STATUS as work progresses
6. Agent verifies with /canary.scan
7. Repeat from step 1
For Human Developers
# Morning routine
canary next                   # See what's next

# Review requirement
cat .canary/specs/CBIN-105-fuzzy-search/spec.md
cat .canary/specs/CBIN-105-fuzzy-search/plan.md

# Implement with test-first
# 1. Write failing test (RED)
# 2. Implement minimum code to pass (GREEN)
# 3. Refactor (REFACTOR)
# 4. Add CANARY tokens
# 5. Update STATUS field

# Verify progress
canary status CBIN-105
canary verify --claims GAP_ANALYSIS.md

# Check what's next
canary next

Key Features

🎯 Test-First Discipline

Constitutional principles direct agents to write tests before implementation; canary verify then checks the recorded evidence:

## Article IV: Test-First Imperative

All features SHALL be implemented using test-first development (TDD).
Tests MUST be written before implementation code.
πŸ“Š Real-Time Progress Tracking
canary status CBIN-105
# Output:
# Requirement: CBIN-105 (Fuzzy Search)
# Total tokens: 8
# Status breakdown:
#   TESTED: 6 (75%)
#   IMPL: 1 (12.5%)
#   STUB: 1 (12.5%)
# Incomplete work:
#   - FuzzyRanking (Engine): IMPL β†’ needs tests
#   - FuzzyConfig (API): STUB β†’ not implemented
canary grep Authentication
# Searches across:
# - Requirement IDs
# - Feature names
# - Aspects
# - Owners
# - Files
# Returns tokens with file locations and line numbers
πŸ“š Documentation Currency

Track documentation status with cryptographic hashes:

// CANARY: REQ=CBIN-105; FEATURE="FuzzySearch"; ASPECT=Engine; STATUS=TESTED; TEST=TestFuzzySearch; DOC=user:docs/user/search-guide.md; DOC_HASH=a3f5b8c2e1d4a6f9; UPDATED=2026-08-29
canary doc status CBIN-105
# Status: DOC_CURRENT (hash matches)

# After editing docs/user/search-guide.md:
canary doc status CBIN-105
# Status: DOC_STALE (hash mismatch)

# doc update takes only a REQ-ID -- it recalculates DOC_HASH for every
# feature/token under that requirement, not one named feature at a time.
canary doc update CBIN-105
# Recalculates and updates DOC_HASH
πŸ”— Dependency Tracking

Full dependency graph with cycle detection:

canary deps graph CBIN-147 --status
# Output:
# CBIN-147 (Specification Dependencies)
# β”œβ”€β”€ βœ… CBIN-146 (Multi-Project Support)
# β”‚   └── βœ… CBIN-129 (Database Migrations)
# └── βœ… CBIN-140:GapRepository,GapService
#     β”œβ”€β”€ βœ… CBIN-133:Engine
#     └── ❌ CBIN-135:Storage (STATUS=IMPL, needs tests)
#
# Summary: 3 satisfied, 1 blocking
πŸ€– AI Agent Integration

Slash commands for autonomous workflows:

  • /canary.next - Get next priority with full context
  • /canary.show <req-id> - Display requirement tokens
  • /canary.status <req-id> - Check progress
  • /canary.implement <req-id> - Get implementation guidance
  • /canary.scan - Verify token placement
  • /canary.specify - Create new requirement
  • /canary.plan <req-id> - Generate implementation plan
πŸš€ GitHub Copilot Integration

CANARY automatically configures GitHub Copilot with project-specific instructions:

canary init my-project
# Creates .github/instructions/ with CANARY workflow guidance

What Gets Configured:

  • Repository-wide instructions - CANARY token format, test-first development, constitutional principles
  • Path-specific guidance - Context-aware help for specs, tests, and .canary/ directory
  • Automatic discovery - Works with both GitHub Copilot CLI and VS Code Copilot Chat

Instruction Files Created:

.github/instructions/
β”œβ”€β”€ repository.md              # CANARY workflow fundamentals
β”œβ”€β”€ .canary/
β”‚   β”œβ”€β”€ instruction.md        # CANARY directory guidelines
β”‚   └── specs/
β”‚       └── instruction.md    # Specification writing (WHAT/WHY, not HOW)
└── tests/
    └── instruction.md        # Test-first development guidelines

Verification:

# Using GitHub Copilot CLI
gh copilot suggest "What is the CANARY token format?"

# Using VS Code Copilot Chat
# Ask: "@workspace What is the CANARY token format?"

Features:

  • βœ… Zero manual configuration required
  • βœ… Preserves custom instructions on re-init
  • βœ… Project key substitution in templates
  • βœ… Compatible with Copilot CLI and VS Code

Re-initialization Safe:

# Customize your instructions
echo "# Custom Rule" >> .github/instructions/repository.md

# Re-run init - your customizations are preserved
canary init --local
# ⏭️  Skipping existing instruction file: repository.md

Bootstrap is journaled and rolled back on failure: everything canary init writes below the initial project-directory/key setup β€” the .canary/ tree, agent files, slash commands, Copilot instructions β€” is one journaled operation. Every write is snapshotted first, so a failure partway through (a bad template, a malformed gated section, a mid-copy error) rolls the whole run back instead of leaving a mixed-version tree behind.

Agent-context updates run outside that rollback boundary: The core bootstrap (.canary tree, generated files) is journaled and rolled back atomically on failure. The subsequent agent-context phase (CLAUDE.md, CURSOR.md, AGENTS.md, Copilot instructions, AGENT_CONTEXT.md) runs OUTSIDE that rollback boundary as a best-effort phase: it attempts each file independently, reports a per-file result (created/updated/kept/failed), and RETAINS successfully applied files even if another fails. A non-zero init result in this phase means "bootstrap succeeded; some agent files applied, some failed" (see the itemized output) β€” NOT "nothing changed". Inspect the per-file results before retrying.

Documentation

User Documentation
Developer Documentation
Architecture Documentation

Project Structure

canary/
β”œβ”€β”€ cmd/canary/              # Main CLI application
β”‚   β”œβ”€β”€ main.go             # CLI entry point and command registration
β”‚   └── *_test.go           # Command tests
β”œβ”€β”€ cli/                    # Root command assembly (Commands())
β”œβ”€β”€ pkg/
β”‚   β”œβ”€β”€ canaryscan/         # The scanner and token parser
β”‚   β”œβ”€β”€ specs/              # Specification and dependency engine
β”‚   β”œβ”€β”€ storage/            # SQLite database layer + migrations
β”‚   β”œβ”€β”€ cmds/               # One package per CLI subcommand
β”‚   β”œβ”€β”€ sources/            # Requirement-ID source configuration
β”‚   β”œβ”€β”€ ticket/             # Ticket-destination integrations
β”‚   └── ...                 # evidence, drift, upgrade, migrate, etc.
β”œβ”€β”€ mcp/                    # MCP server for AI-assistant integration
β”œβ”€β”€ gate/                   # Managed-marker engine (init doc updates)
β”œβ”€β”€ .canary/
β”‚   β”œβ”€β”€ memory/
β”‚   β”‚   └── constitution.md          # Project principles
β”‚   β”œβ”€β”€ templates/
β”‚   β”‚   β”œβ”€β”€ spec-template.md         # Requirement template
β”‚   β”‚   └── plan-template.md         # Implementation plan template
β”‚   └── specs/
β”‚       └── CBIN-XXX-feature/        # Individual requirements
β”‚           β”œβ”€β”€ spec.md
β”‚           └── plan.md
β”œβ”€β”€ docs/
β”‚   β”œβ”€β”€ user/               # User-facing documentation
β”‚   β”œβ”€β”€ architecture/       # Architecture decision records
β”‚   └── *.md                # Various docs
β”œβ”€β”€ GAP_ANALYSIS.md         # Requirement tracking
β”œβ”€β”€ CLAUDE.md               # AI agent guide
└── README.md               # This file

Development

Build
make build          # Build binary
make test           # Run tests
make bench          # Run benchmarks
make fuzz           # Run fuzz targets
make verify         # Self-verify with CANARY: scan, produce evidence, canary verify
Self-Canary

CANARY uses itself for requirement tracking:

# Scan the codebase
canary scan --root . --out status.json --csv status.csv

# Produce evidence from the real test run (this is what `make verify` does),
# then verify claims against it
canary evidence run-go-test --project <KEY> -- -count=1 -json ./... > evidence.json
canary evidence ingest --in evidence.json --out .canary/evidence.json
canary verify --claims GAP_ANALYSIS.md

# Check dependencies
canary deps validate

canary scan --verify is a deprecated spelling that now delegates entirely to canary verify β€” same checks, same verdict, same exit code. Use canary verify directly.

Testing
# Unit tests
go test ./...

# Package tests (scanner, specs engine, storage, ...)
go test ./pkg/specs/...

# Benchmarks
go test ./pkg/... -bench=. -benchmem

# Audit acceptance tests
go test ./internal/audit/...

MCP Server

CANARY ships an MCP (Model Context Protocol) server so AI assistants can drive it through tools rather than shelling out:

canary mcp
  • Bind address: the server listens on 127.0.0.1 (loopback) by default, so it is not reachable off the host unless you deliberately expose it.
  • Auth model: mutating tools require a bearer token; read-only tools can be gated behind a separate read token. On a loopback bind with no token configured, requests from localhost are accepted.
  • Tool list: the authoritative, generated list of exposed tools lives in docs/MCP_TOOLS.md. See docs/MCP_QUICK_START.md for IDE wiring.

Contributing

We welcome contributions! Please:

  1. Check existing requirements: canary list
  2. Create a specification: canary specify
  3. Follow test-first development
  4. Place CANARY tokens in your code
  5. Update documentation with DOC= fields
  6. Verify before submitting: canary verify --claims GAP_ANALYSIS.md

See CONTRIBUTING.md for detailed guidelines.

License

Licensed under the terms found in LICENSE.

Acknowledgments

CANARY was inspired by:

  • spec-kit methodology for requirement-first development
  • Test-Driven Development (TDD) principles
  • Evidence-based claims β€” inspired by formal verification (CANARY does not perform formal verification)
  • Zero-trust verification β€” inspiration from security engineering; see the trust boundary below for what is and is not authenticated

Built with love by Developer Network.

  • Claude Code - AI coding assistant with CANARY integration
  • spec-kit - Specification-driven development methodology

Getting Help


Ready to start? β†’ Getting Started Guide

For AI Agents β†’ CLAUDE.md

For API Documentation β†’ pkg.go.dev


CANARY: Making every feature claim searchable, verifiable, and traceable.

Documentation ΒΆ

Index ΒΆ

Constants ΒΆ

This section is empty.

Variables ΒΆ

This section is empty.

Functions ΒΆ

func FormatFilesList ΒΆ

func FormatFilesList(fileGroups map[string][]*storage.Token) string

FormatFilesList converts fileGroups (map[file][]tokens) into a human-readable summary grouped by aspect.

func FormatGrepResults ΒΆ

func FormatGrepResults(tokens []*storage.Token) string

FormatGrepResults returns human readable list output for grep tokens.

func FormatGrepResultsByRequirement ΒΆ

func FormatGrepResultsByRequirement(tokens []*storage.Token) string

FormatGrepResultsByRequirement groups grep tokens by requirement.

func FormatTokensTable ΒΆ

func FormatTokensTable(tokens []*storage.Token, groupBy string) string

FormatTokensTable renders grouped tokens (used by show command).

func GrepTokens ΒΆ

func GrepTokens(db *storage.DB, projectID, pattern string, limit int) ([]*storage.Token, error)

CANARY: REQ=ENG-4323; FEATURE="ContextCaps"; ASPECT=API; STATUS=IMPL; UPDATED=2026-08-28 GrepTokens returns tokens whose feature/file/test/bench/reqID match pattern (case-insensitive substring), bounded by limit (<=0 uses the storage layer's own small default; see storage.DefaultSearchLimit).

NOTE: SearchTokens' SQL already matches keywords, feature, req_id, file_path, test, and bench columns (bounded by LIMIT), so a single bounded call covers every column this function's contract advertises. Previously this loaded the entire token table via db.ListTokens to catch file/test/bench matches that the old (narrower) SearchTokens couldn't produce; that full-table union is no longer needed now that SearchTokens covers those columns itself. projectID scopes the search; "" spans every project in the database.

func GroupTokens ΒΆ

func GroupTokens(tokens []*storage.Token, groupBy string) map[string][]*storage.Token

GroupTokens groups tokens by aspect/status (default aspect).

Types ΒΆ

This section is empty.

Directories ΒΆ

Path Synopsis
cmd
canary command
CANARY: REQ=CBIN-CLI-104; FEATURE="CanaryCLI"; ASPECT=CLI; STATUS=IMPL; OWNER=canary; UPDATED=2025-10-16
CANARY: REQ=CBIN-CLI-104; FEATURE="CanaryCLI"; ASPECT=CLI; STATUS=IMPL; OWNER=canary; UPDATED=2025-10-16
pkg
attest
Package attest implements the CANARY attestation mechanism: ed25519 signing and verification of the claims one `canary evidence run-go-test` run makes about itself.
Package attest implements the CANARY attestation mechanism: ed25519 signing and verification of the claims one `canary evidence run-go-test` run makes about itself.
buildinfo
Package buildinfo holds the resolved binary version as a leaf value any package may read without importing cmd/canary or cobra.
Package buildinfo holds the resolved binary version as a leaf value any package may read without importing cmd/canary or cobra.
canaryscan
CANARY: REQ=CP-268; FEATURE="MermaidRefs"; ASPECT=Engine; STATUS=TESTED; TEST=TestCANARY_CBIN_202_ExtractDiagramRefs,TestCANARY_CBIN_202_ScanDiagramRefsSkipsOversizedFile; UPDATED=2026-08-31
CANARY: REQ=CP-268; FEATURE="MermaidRefs"; ASPECT=Engine; STATUS=TESTED; TEST=TestCANARY_CBIN_202_ExtractDiagramRefs,TestCANARY_CBIN_202_ScanDiagramRefsSkipsOversizedFile; UPDATED=2026-08-31
cmds/drift
Package drift wires the pkg/drift engine into `canary drift`: it reads the token index built by `canary index` and reports, per requirement, whether the tree still matches the baseline β€” CURRENT, DRIFTED, or UNKNOWN β€” decided by content hash and git availability, never by a token's UPDATED= date.
Package drift wires the pkg/drift engine into `canary drift`: it reads the token index built by `canary index` and reports, per requirement, whether the tree still matches the baseline β€” CURRENT, DRIFTED, or UNKNOWN β€” decided by content hash and git availability, never by a token's UPDATED= date.
cmds/evidence
Package evidence implements `canary evidence`: the commands that produce and accumulate the passing-test records `canary verify` consumes.
Package evidence implements `canary evidence`: the commands that produce and accumulate the passing-test records `canary verify` consumes.
cmds/gap
CANARY: REQ=ENG-4317; FEATURE="GapCLI"; ASPECT=CLI; STATUS=IMPL; UPDATED=2025-10-17
CANARY: REQ=ENG-4317; FEATURE="GapCLI"; ASPECT=CLI; STATUS=IMPL; UPDATED=2025-10-17
cmds/implement
CANARY: REQ=CP-253; FEATURE="RequirementLookup"; ASPECT=API; STATUS=TESTED; UPDATED=2026-08-29
CANARY: REQ=CP-253; FEATURE="RequirementLookup"; ASPECT=API; STATUS=TESTED; UPDATED=2026-08-29
cmds/next
CANARY: REQ=CP-252; FEATURE="NextPriorityCommand"; ASPECT=CLI; STATUS=BENCHED; TEST=TestCANARY_CBIN_132_CLI_NextPrioritySelection,TestCANARY_CBIN_132_CLI_DBAndScanAgreeOnOrder,TestCANARY_CBIN_132_CLI_Candidate51IsFound,TestCANARY_CBIN_132_CLI_PromptResolvesUnderRoot,TestCANARY_C502_Next_ImportedEvidenceDoesNotUnblockByDefault; BENCH=BenchmarkCANARY_CBIN_132_CLI_PriorityQuery; OWNER=canary; UPDATED=2026-09-01
CANARY: REQ=CP-252; FEATURE="NextPriorityCommand"; ASPECT=CLI; STATUS=BENCHED; TEST=TestCANARY_CBIN_132_CLI_NextPrioritySelection,TestCANARY_CBIN_132_CLI_DBAndScanAgreeOnOrder,TestCANARY_CBIN_132_CLI_Candidate51IsFound,TestCANARY_CBIN_132_CLI_PromptResolvesUnderRoot,TestCANARY_C502_Next_ImportedEvidenceDoesNotUnblockByDefault; BENCH=BenchmarkCANARY_CBIN_132_CLI_PriorityQuery; OWNER=canary; UPDATED=2026-09-01
cmds/onboard
Package onboard analyzes a codebase that has few or no CANARY tokens and produces the agent hand-off needed to begin adoption: a language histogram, top-level directory layout, best-effort entry-point detection, any existing CANARY tokens, pre-seeded CANARY:MIGRATE guidance, the configured requirement-ID sources, the next available flatfile ID, and a next_steps checklist.
Package onboard analyzes a codebase that has few or no CANARY tokens and produces the agent hand-off needed to begin adoption: a language histogram, top-level directory layout, best-effort entry-point detection, any existing CANARY tokens, pre-seeded CANARY:MIGRATE guidance, the configured requirement-ID sources, the next available flatfile ID, and a next_steps checklist.
cmds/project
CANARY: REQ=ENG-4319; FEATURE="ProjectCLI"; ASPECT=CLI; STATUS=IMPL; UPDATED=2025-10-18
CANARY: REQ=ENG-4319; FEATURE="ProjectCLI"; ASPECT=CLI; STATUS=IMPL; UPDATED=2025-10-18
cmds/specify
CANARY: REQ=ENG-4314; FEATURE="SpecModification"; ASPECT=CLI; STATUS=IMPL; DOC=user:docs/user/spec-modification-guide.md; DOC_HASH=676eb2a18c9d002a; UPDATED=2025-10-17
CANARY: REQ=ENG-4314; FEATURE="SpecModification"; ASPECT=CLI; STATUS=IMPL; DOC=user:docs/user/spec-modification-guide.md; DOC_HASH=676eb2a18c9d002a; UPDATED=2025-10-17
cmds/ticket
Package ticket wires pkg/ticket into `canary ticket sync`: computing a codified ticket-source synchronization plan from indexed tokens and the configured `sources:` registry, and β€” only when JIRA credentials are present and --apply is set β€” applying it via the JIRA REST client and writing a completed plan plus a remap map for `canary upgrade --map`.
Package ticket wires pkg/ticket into `canary ticket sync`: computing a codified ticket-source synchronization plan from indexed tokens and the configured `sources:` registry, and β€” only when JIRA credentials are present and --apply is set β€” applying it via the JIRA REST client and writing a completed plan plus a remap map for `canary upgrade --map`.
cmds/upgrade
Package upgrade wires the pkg/upgrade legacy-token rewriter into the CLI.
Package upgrade wires the pkg/upgrade legacy-token rewriter into the CLI.
cmds/verify
Package verify implements `canary verify`: the evidence-backed answer to "are this project's claims true right now?".
Package verify implements `canary verify`: the evidence-backed answer to "are this project's claims true right now?".
cmds/view
Package view aggregates everything known about one requirement β€” tokens, files, tests, dependencies, spec/plan, diagrams, ticket link β€” into one bounded, agent-friendly answer.
Package view aggregates everything known about one requirement β€” tokens, files, tests, dependencies, spec/plan, diagrams, ticket link β€” into one bounded, agent-friendly answer.
config
CANARY: REQ=ENG-4317; FEATURE="ProjectConfig"; ASPECT=Storage; STATUS=IMPL; UPDATED=2026-09-01
CANARY: REQ=ENG-4317; FEATURE="ProjectConfig"; ASPECT=Storage; STATUS=IMPL; UPDATED=2026-09-01
contract
Package contract carries the two facts a refused CLI contract needs on both sides of the command boundary: the sentinel a command returns once it has printed its JSON refusal, and the exit status main gives it.
Package contract carries the two facts a refused CLI contract needs on both sides of the command boundary: the sentinel a command returns once it has printed its JSON refusal, and the exit status main gives it.
drift
Package drift decides, per requirement, whether the tree in front of it still matches the index baseline `canary index` recorded.
Package drift decides, per requirement, whether the tree in front of it still matches the index baseline `canary index` recorded.
evidence
Package evidence holds strict parsing of CANARY evidence records and the single completion function ("has every claimed requirement been proven at the current commit") that downstream verification consumes.
Package evidence holds strict parsing of CANARY evidence records and the single completion function ("has every claimed requirement been proven at the current commit") that downstream verification consumes.
external
Package external resolves whether a CANARY requirement ID is satisfied as an external dependency β€” one owned by a ticket-source (e.g.
Package external resolves whether a CANARY requirement ID is satisfied as an external dependency β€” one owned by a ticket-source (e.g.
gap
CANARY: REQ=ENG-4317; FEATURE="GapService"; ASPECT=Engine; STATUS=IMPL; UPDATED=2025-10-17
CANARY: REQ=ENG-4317; FEATURE="GapService"; ASPECT=Engine; STATUS=IMPL; UPDATED=2025-10-17
gotest
Package gotest turns passing tests/benches (as parsed by the leaf package pkg/gotestevents) into evidence records, independent of the `canary evidence` command package.
Package gotest turns passing tests/benches (as parsed by the leaf package pkg/gotestevents) into evidence records, independent of the `canary evidence` command package.
gotestevents
Package gotestevents parses `go test -json` event streams into passing tests/benches and re-derives whether an evidence.Record is actually backed by them.
Package gotestevents parses `go test -json` event streams into passing tests/benches and re-derives whether an evidence.Record is actually backed by them.
matcher
CANARY: REQ=CP-253; FEATURE="FuzzyMatcher"; ASPECT=Engine; STATUS=TESTED; TEST=TestCANARY_CBIN_133_Engine_Levenshtein; OWNER=canary; UPDATED=2026-08-29
CANARY: REQ=CP-253; FEATURE="FuzzyMatcher"; ASPECT=Engine; STATUS=TESTED; TEST=TestCANARY_CBIN_133_Engine_Levenshtein; OWNER=canary; UPDATED=2026-08-29
migrate
CANARY: REQ=ENG-4313; FEATURE="MigrateFrom"; ASPECT=CLI; STATUS=IMPL; OWNER=canary; UPDATED=2025-10-16
CANARY: REQ=ENG-4313; FEATURE="MigrateFrom"; ASPECT=CLI; STATUS=IMPL; OWNER=canary; UPDATED=2025-10-16
reqid
CANARY: REQ=ENG-4316; FEATURE="AspectIDGenerator"; ASPECT=Engine; STATUS=IMPL; UPDATED=2025-10-16
CANARY: REQ=ENG-4316; FEATURE="AspectIDGenerator"; ASPECT=Engine; STATUS=IMPL; UPDATED=2025-10-16
safewrite
Package safewrite is the single way this tool replaces a file on disk.
Package safewrite is the single way this tool replaces a file on disk.
sources
Package sources resolves requirement-ID prefixes to their origin: a local flatfile series (e.g.
Package sources resolves requirement-ID prefixes to their origin: a local flatfile series (e.g.
specs
CANARY: REQ=ENG-4314; FEATURE="ExactIDLookup"; ASPECT=Engine; STATUS=IMPL; UPDATED=2025-10-16
CANARY: REQ=ENG-4314; FEATURE="ExactIDLookup"; ASPECT=Engine; STATUS=IMPL; UPDATED=2025-10-16
storage
CANARY: REQ=ENG-4319; FEATURE="ContextManagement"; ASPECT=Engine; STATUS=IMPL; UPDATED=2025-10-18
CANARY: REQ=ENG-4319; FEATURE="ContextManagement"; ASPECT=Engine; STATUS=IMPL; UPDATED=2025-10-18
storage/testutil
CANARY: REQ=ENG-4319; FEATURE="TestInfrastructure"; ASPECT=Storage; STATUS=IMPL; UPDATED=2025-10-18
CANARY: REQ=ENG-4319; FEATURE="TestInfrastructure"; ASPECT=Storage; STATUS=IMPL; UPDATED=2025-10-18
ticket
Package ticket computes and applies ticket-source synchronization plans: comparing a requirement's rollup CANARY status against its owning non-flatfile source's remote status (proposing "transition" actions), and codifying flatfile-to-ticket promotion as paired "create_issue" + "remap" actions.
Package ticket computes and applies ticket-source synchronization plans: comparing a requirement's rollup CANARY status against its owning non-flatfile source's remote status (proposing "transition" actions), and codifying flatfile-to-ticket promotion as paired "create_issue" + "remap" actions.
upgrade
Package upgrade rewrites legacy on-disk CANARY token shapes into the current parseable form: unicode hyphens inside IDs into ASCII hyphens, unpadded flatfile IDs into zero-padded IDs, bare legacy ID segments into keyed REQ= tokens, bug tokens missing FEATURE= into scan-parseable single lines, STATUS=FIXED into STATUS=REMOVED, missing UPDATED= into stamped tokens, the old multi-line bug-create continuation shape into one line, and (when an ID map is supplied) old requirement IDs into new ones across both CANARY tokens and GAP_ANALYSIS.md "βœ… <ID>" claim lines.
Package upgrade rewrites legacy on-disk CANARY token shapes into the current parseable form: unicode hyphens inside IDs into ASCII hyphens, unpadded flatfile IDs into zero-padded IDs, bare legacy ID segments into keyed REQ= tokens, bug tokens missing FEATURE= into scan-parseable single lines, STATUS=FIXED into STATUS=REMOVED, missing UPDATED= into stamped tokens, the old multi-line bug-create continuation shape into one line, and (when an ID map is supplied) old requirement IDs into new ones across both CANARY tokens and GAP_ANALYSIS.md "βœ… <ID>" claim lines.
tools
canary command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL