BafGo Port
Agent guide

How an AI agent works with BafGo.

Point your agent at this page — or drop the same guide into your repo as AGENTS.md. It's everything the agent needs to draft rules, link work, and keep the record.

Install and set up

Prerequisite: Node.js 18 or newer.

# add to the repo's .npmrc (or your user .npmrc):
@bafgo:registry=https://npm.bafgo.com/
$ npm install -g @bafgo/cli
$ bafgo version # verify
$ bafgo init # create .bafgo/ (run at the repo root)

If you prefer npm config set, quote both arguments on Windows/PowerShell: npm config set "@bafgo:registry" "https://npm.bafgo.com/".

Run commands from anywhere inside the repo; the CLI walks up to find .bafgo/. Use --root <dir> to point at a specific repo instead.

Signing keys (optional, recommended):

$ bafgo keygen --name <you> # private key → ~/.bafgo/keys/<you>
$ bafgo keys
$ bafgo keys rotate --name <you>

Add the printed public key to .bafgo/keys.yaml, then approvals sign automatically. Verify the record anytime with bafgo manifest --verify, and open the local overview + review gate (approve/reject) with bafgo serve --port 4000.

Your role

When a ticket is handed to you, keep the record as you work:

  1. Draft the requirement as a rule.
  2. Specify it so it can be checked (when → then).
  3. Implement, then link the code and tests to the rule.
  4. Stage a demo and request review.
  5. A human approves; then the tests are verified.

You draft, specify, implement, link, and stage. You never impersonate the human: record the approver's name with --by, but the human is the one who approves.

The files

Everything lives in a .bafgo/ folder committed with the code:

  • rules/<id>.yaml — one requirement per file
  • changes/<id>.yaml — a unit of work touching one or more rules
  • decisions/<id>.yaml — decision log: how a requirement came about and what was decided
  • runners.yaml — optional test-runner registry (Playwright, custom filters)
  • events.jsonl — append-only, hash-chained record of what happened
  • links.json — the trace, regenerated
  • manifest.json — fingerprints of rules, changes, decisions, and linked files
  • keys.yaml — trusted public keys for signed approvals

Rules

A rule is one requirement written so it can be checked. For an executable rule, every when must hold and every then must pass. Design taste that can't be tested is --track exploratory: it is approved, never verified, and you don't generate tests for it.

Changes

A change is one unit of work touching one or more rules. Create one with claim and reference its tracker id with --tracker (Jira / Azure DevOps).

Triage, evidence & decisions

Record where a rule came from and why it exists — not just what it says.

  • Triage is the initial disposition of a rule or change: bafgo triage <id> --disposition accepted|declined|deferred records who, why, and when. Untriaged is the default.
  • Evidence is a list of file:line (or URL) references to where a requirement came from — e.g. docs/vision.md:42. Set it with --evidence on rule new, rule set, or claim.
  • Decisions are a standalone log of how a requirement came about. bafgo decide records the context, decision, rationale, and evidence, and links it to rules and changes.

Prose, comparison & legacy import

  • Prose: a rule can carry its requirement as free text instead of structured when → then — use --prose on rule new, rule import, or rule set. An executable rule needs when+then or prose.
  • Comparison status: how a ported rule lines up with the legacy system. --compare takes matches, fixed, defect, not-ported, differs-on-purpose, or needs-decision — it sits alongside the state machine, not in place of it.
  • Extra fields: --priority, --tags, --needed-by, --fix.
  • Legacy import: bafgo rule import creates a rule that already exists in a legacy system. It lands in implemented (code exists, not approved) and records one honest rule.imported event — it never fabricates approval history.

Test runners & filtering

verify picks a runner for each linked test file. Built-ins: node (*.js/*.mjs/*.cjs) and dotnet (*.csproj/*.fsproj/*.vbproj/*.sln). Add or override runners in .bafgo/runners.yaml:

# .bafgo/runners.yaml
runners:
  playwright:
    match: ["journeys/*.spec.ts"]
    cmd: npx
    args: ["playwright", "test", "{file}", "-c", "journeys/playwright.config.ts"]
    filter: "--grep {filter}"

{file} is the linked test path; {filter} is the value passed to bafgo verify ... --filter. The flag is defined by the runner, not BafGo, so no xUnit/Playwright contract is baked in — --filter "Rule=R-1" for dotnet, --filter "@BR-SENT-001" for Playwright.

Store a rule's filter with --filter on rule new / rule set / rule import. Then bafgo verify CH-1 on a change gathers every rule's filter, dedupes shared test files, and runs each file once with the filters OR-combined (Rule=R-1|Rule=R-2) — one dotnet test build covers all the change's rules instead of one per rule. If any rule links a file without a filter, that file runs unfiltered. An explicit --filter on bafgo verify overrides the stored filters.

State machines

Rule:   draft → specified → implemented → review → approved → verified
Change:  open → in-review → approved → verified
  • specify works only from draft; an executable rule needs a when and a then.
  • link with a code file moves a rule specified → implemented.
  • A rule reaches review by stage <rule-id> --demo <url>, or automatically when its change is requested for review.
  • stage on a change only sets the demo (it stays open); request-review moves it open → in-review and pushes its rules implemented → review.
  • approve moves review → approved (rule) or in-review → approved (change).
  • reject sends it back: review → implemented, in-review → open.
  • verify runs the linked tests and only reaches verified if every one passes. Run it on the change (it cascades to its rules) or on a rule already in approved — not on a rule in implemented.
  • How verify finds tests: it runs each linked --unit/--e2e file with the runner that matches its path (node, dotnet test, or a .bafgo/runners.yaml entry). Exit 0 is pass, non-zero is fail. --filter restricts a run to tagged tests.

Parallel branches & merging

The event log is one linear hash chain — every event points at the hash of the one before it — while state lives in per-rule and per-change files. Two consequences:

  • Parallel work is fine. Different rules and changes advance independently; their events interleave on the one chain in append order.
  • Approvals are content-signed. A signature covers the event's semantic fields (type, rule/change, by, reason) — never time, prev, or sig. So an approval stays valid wherever it lands in the log after a merge.

When two git branches both append to .bafgo/events.jsonl, git reports a conflict on that file. Resolve it like this (never hand-edit the log otherwise):

  1. Merge the per-rule / per-change YAML files with git as usual. If two branches changed the same rule or change, resolve the state conflict explicitly, like a code conflict — the merged file is the source of truth.
  2. For events.jsonl, keep every event from both branches. Don't drop, rewrite, or reorder by hand — just concatenate both branches' new events in chronological order (by time).
  3. Run bafgo chain to re-link each prev hash onto the merged order. This never invalidates a signature.
  4. Run bafgo manifest, then bafgo manifest --verify to confirm the chain and signatures are intact.

Don't approve the same rule or change concurrently from two branches — serialize approvals for a single entity. If it happens anyway, the log records both events honestly: resolve the YAML state conflict, and never edit the log to hide one.

Command reference

bafgo init bafgo keygen --name <you> bafgo keys [rotate --name <you>] bafgo status [id] [--json] bafgo rule new <id> --title <t> [--track executable|exploratory] [--when "a,b"] [--then "c,d"] [--prose "text"] [--source s] [--reason r] [--evidence "file:line,..."] [--priority p] [--tags "a,b"] [--needed-by d] [--fix f] [--compare status] [--filter f] [--by name] [--key path] bafgo rule import <id> --title <t> [--track executable|exploratory] [--when "a,b"] [--then "c,d"] [--prose "text"] [--compare status] [--filter f] [--state implemented] [--by name] [--key path] bafgo rule specify <id> [--by name] [--key path] bafgo rule set <id> [--title t] [--track x] [--when "a,b"] [--then "c,d"] [--prose "text"] [--source s] [--reason r] [--evidence "file:line,..."] [--priority p] [--tags "a,b"] [--needed-by d] [--fix f] [--compare status] [--filter f] [--by name] [--key path] bafgo link <id> --code a,b [--unit c,d] [--e2e e,f] [--demo url] bafgo triage <change-id|rule-id> --disposition accepted|declined|deferred [--reason text] [--by name] [--key path] bafgo stage <change-id> --demo <url> bafgo request-review <change-id> bafgo approve <change-id|rule-id> [--by name] [--reason text] [--key path] bafgo reject <change-id|rule-id> [--by name] [--reason text] [--key path] bafgo verify <change-id|rule-id> [--filter f] bafgo claim <change-id> --rule a,b [--tracker ref] [--evidence "file:line,..."] bafgo decide <id> --title t --decision d [--context c] [--rationale r] [--rule a,b] [--change c,d] [--evidence "file:line,..."] [--by name] [--key path] bafgo decisions [--json] bafgo regen [--json] bafgo graph [id] [--json] bafgo manifest [--verify] [--strict] bafgo chain bafgo serve [--port 4000]

Worked example — one ticket, end to end

The ticket: “Timer keeps running while the screen sleeps.”

$ bafgo rule new R-123 --title "Timer keeps running while the screen sleeps" --when "a timer is active,the display sleeps" --then "the countdown continues,the chime still fires at zero" --source "Jira BAFGO-42"
$ bafgo rule specify R-123
$ bafgo claim CH-42 --rule R-123 --tracker BAFGO-42
$ bafgo link R-123 --code timer.html --unit timer-core.test.js
$ bafgo stage CH-42 --demo https://preview.bafgo.com/r-123
$ bafgo request-review CH-42
# the human approves:
$ bafgo approve CH-42 --by dana --reason "fixes the chime"
$ bafgo verify CH-42
→ verified · tests green

Rules for you

  • Link only files that actually exist in the repo.
  • Record why a rule exists: --source, --reason, and --evidence (file:line refs).
  • Record a non-obvious requirement's origin with bafgo decide.
  • Triage before you build: bafgo triage <id> --disposition accepted.
  • Commit .bafgo/ changes alongside the code.
  • Sign approvals when a key is available: approve ... --key <path>.
  • Never invent a test result — verify runs the real tests.
  • Don't edit events.jsonl or links.json by hand; use the commands.
raw markdown (copy-paste)