gy — good,yes
English | 日本語
A Rust CLI that manages decisions, questions, needs, requirements, acceptance criteria, and continuation gates as a graph of Markdown files. AI agents operate through the CLI or MCP; people read the ledger generated by render and individual records with show.
lint checks the graph's internal consistency. Users verify whether the ledger matches the code, GitHub, and production. gy does not call the GitHub API.
The initial command set, including requirement compression, is implemented.
Installation and first records
Install from crates.io:
Initialization creates .gy-dir, docs/ledger/gy.toml, and the scope directories, and appends a ledger reference to an existing AGENTS.md. Commands can discover the ledger from subdirectories. Reads cover all scopes by default. To create a node, run inside its scope directory or supply --scope. Commands that update an existing ID write to that node's scope.
To install from a source checkout, run cargo install --path crates/gy --locked.
Commands
| Command | Result |
|---|---|
init <scope> |
Create a ledger and scope |
need add / need file |
Create a need tied to acceptance criteria and associate it with a requirement |
question add / question close |
Search across scopes before registering a question; close it in one of three ways |
q "one sentence" |
Capture a quick human note; lint fails until the required information is supplied |
decide |
Record a decision with its applicability conditions and close specified questions |
link <source> <label> <target> |
Record a relationship in both nodes' frontmatter |
req add / req advance |
Register requirements and record state transitions with evidence |
req compress <issue> |
Check that constraints have become decisions, then compress an archived record into six items |
criterion add / criterion satisfy |
Create acceptance criteria and record evidence of satisfaction |
gate add |
Create a gate for deciding whether an approach should continue |
node set / node submit |
Author attributes or body text; validate and snapshot a configured record |
find / show |
Search attributes and text; display nodes and their relationships |
next |
List needs whose prerequisites have been resolved |
lint / handover |
Check consistency and the records needed for a handover |
stats |
Report satisfied acceptance criteria and question arrival rates from git history |
render |
Generate paginated Markdown or DOT |
import <directory> |
Import ADRs while preserving their IDs |
cheatsheet / completions <shell> |
Print a workflow reference or shell completions |
skills install <dir> / mcp serve |
Install agent instructions or start the MCP server |
All commands accept --json, --quiet, --verbose, -C <dir>, and --scope. With --json, results go to standard output and diagnostics go to standard error as JSON. --quiet suppresses ordinary result output while retaining JSON results and errors.
Exit codes are 0 for success, 1 for a failed check, 2 for missing or invalid input, nonexistent references, or guard violations, and 3 for ledger parsing failures or corruption. handover also returns 1 when next-transition evidence, responsibility, or referenced nodes are missing.
Attributes and relationships
The five required frontmatter attributes are id, type, title, scope, and created. Unknown attributes survive reads and writes. Set values with node set --set key=value; values that parse as JSON are stored as arrays, numbers, booleans, or objects.
Search operators are =, !=, >=, <=, >, <, and ~ (substring matching). Numeric values are compared numerically; other values are compared as strings. ISO-format dates sort chronologically as strings. Results identify matching sections, including Context, Decision, and applicability conditions.
| Attribute | Meaning |
|---|---|
decision_scope |
Conditions under which a decision applies; supplied through --scope-note at creation |
decider / options |
The decision-maker and an array of distinct options for a question |
bundle / bundle-rationale |
A question bundle and why the same intervention resolves its questions |
waiting-on / unresolved |
Arrays of IDs referenced as unresolved |
raised-by |
Requirements that raised a question; multiple origins are allowed |
belongs-to |
A question's owning node IDs; L4 allows at most one distinct owner |
bearer_count |
The stated number of needs supporting an acceptance criterion; compared with actual targets links |
parent_issue |
A confirmed parent Issue number; compared with the configured value |
status |
One of 11 requirement states, or open / closed for questions |
pr_url / pr_base / pr_files |
The PR URL, base branch, and changed-file count recorded for a requirement |
remaining_work |
A legacy remaining-work count or array; checked for consistency with residual when present |
summary / contracts_changed |
A one-sentence outcome and free-form text or a list of changed contracts |
artifacts |
pr, merge_commit, and base_branch: pointers to deliverables |
production |
An object containing production measurements and verification results, or none if there was no production work |
deviations / residual |
Design deviations and additional decisions; destinations of transferred work. Explicitly use none when absent |
compressed / compressed_from |
Compression date and archive Issue comment URL; not counted among the six items |
next_evidence / responsible |
Evidence for the next transition and the responsible party for an active requirement |
constraints |
An array of downstream constraints, each with text and decision |
constraints_reviewed |
A record that each constraint has been reviewed |
satisfied / satisfied_at |
Acceptance criterion satisfaction and its recorded timestamp |
The CLI, generated views, and bundled agent instructions use English. Requirement states use the values listed below; none explicitly records the absence of production work, deviations, or residual work.
Relationships have the following directions and inverse attribute names. link writes both sides; lint checks symmetry and node types.
| Direction | Label | Inverse attribute |
|---|---|---|
| question → decision | closes |
closes |
| decision → decision | narrows / widens / supersedes / completes |
narrowed-by / widened-by / superseded-by / completed-by |
| need / requirement → criterion | targets |
targeted-by |
| need → decision | spawned-by |
spawns |
| need → requirement | filed-as |
filed-from |
| need → need | depends-on |
needed-by |
| requirement → decision | relies-on |
relied-on-by |
| requirement → question | raised |
raised-by |
| gate → question | measured-by |
measures |
For narrows and supersedes, use --mark to identify the affected passage in the older decision. The stored body is unchanged; show and render annotate it at display time. If the passage cannot be found, it is displayed separately, so a marker cannot silently move to an unrelated passage.
Requirement state transitions
Use req advance --evidence to record what was checked. gy enforces the following 11 states and the main guards, rather than a complete transition table:
unfiled / defining / awaiting-design / awaiting-approval / awaiting-implementation /
awaiting-audit / awaiting-pr / awaiting-merge / awaiting-production / awaiting-cleanup / complete
defining means the requirement is being defined. Optional context can follow a state in parentheses, such as awaiting-implementation (phase 2). Ledgers created with the earlier Japanese values must have those state values and the explicit absence value updated before use; gy does not migrate them automatically.
To enter awaiting-merge, the user supplies confirmed values through --reported-base and --reported-files; gy compares them with frontmatter. It does not query the PR or its diff.
Transitions to awaiting-production (awaiting production work), awaiting-cleanup (awaiting cleanup), or complete (complete) require --data-migration true|false and --production-only true|false. If migration or production-only verification is needed and production_done is not recorded, the state is awaiting production work. If production work is unnecessary or complete, but cleanup is unconfirmed or work remains, the state is awaiting cleanup. Completion requires --cleanup-done true and no unassigned remaining work.
Completed requirements must explicitly record deviations and residual. Set residual to none or to existing destination IDs of the form N-xx, Q-xx, or #Issue. If remaining_work is still present, it must be zero or an empty array.
Before archiving constraints, associate every constraints entry with a decision that states its applicability conditions, and add a relies-on link. constraints_reviewed=true records the user's review. gy cannot determine whether constraints have been omitted from the record.
Compressing completed requirements
First associate each downstream constraint with a decision, then record the six retained items in frontmatter. summary describes the outcome in one sentence on one line, separately from the requirement title. contracts_changed is free-form; gy does not normalize it against project-specific quality-gate tables.
The user posts the full contents of archive.md as a comment on the corresponding Issue, then supplies that comment's URL. gy does not fetch the archive, so the user must check that the full record was archived. If the original requirement changes after archiving, archive its latest contents again.
Without --evidence, the command checks the record and outputs the original file in full without changing it. With --evidence, it still returns the original text to standard output and replaces the body with six items. JSON output contains the full text in archive and the compressed record in node. The update rechecks the constraints and all six items.
Compression retains the ID, required attributes including created, both sides of graph edges, and unknown attributes. The example retains the existing requirement ID spelling, #6006. The six items remain searchable attributes, and show / render generate the body from them. Relationships such as acceptance criteria are not duplicated in the body. Direct targets links from requirements also retain their inverse links; L2 counts only needs as supporting an acceptance criterion.
Compression removes these known transient attributes: constraints, constraints_reviewed, remaining_work, transitions, record_history, next_evidence, responsible, pr_url, pr_base, pr_files, data_migration, production_only, production_done, cleanup_done, evidence, quality_gates, design_proposal, and audit_records. They remain in the archive along with the original body. Other extension attributes are preserved. A compressed record cannot be compressed again to overwrite its original archive pointer.
For transferred work, use a value such as residual=[{"id":"N-2","note":"Transferred performance improvements"},"Q-3","#6010"]. Each destination must be an existing node. Blank values, null, and empty arrays are distinct from the explicit none and are reported by L11.
Configured workflow records
Optional [workflow.records] schemas and [workflow.guards] in gy.toml require structured reports at selected states. They can check design approvals, explicit waivers, contract/gate coverage, and declared file scope. gy node submit <ID> --record <name> --evidence <record> validates a report without advancing state; submissions and transitions retain the checked inputs and schemas.
gy lint reports missing or inconsistent inputs under workflow. gy handover includes the effective configuration. These checks validate reported records, not external facts or approver identity.
See the workflow guide, configuration example, and matching illustrative records. Current profiles govern ongoing work and new actions. Completed requirements retain their history without retroactive record requirements; saved snapshots are checked against their saved schemas.
Importing existing ADRs
gy import docs/adr --scope demo preserves IDs, body text, and existing frontmatter attributes. To populate decision_scope from a body section, configure its heading title in gy.toml:
[]
= "Applicability"
= ["Not recorded during migration"]
Use the heading title from your repository, in any language. The mapper recognizes ATX headings (## Title), includes nested subsections, stops at the next heading of the same or higher level, and ignores headings inside fenced code blocks. Repeated matching headings reject the import as ambiguous. An existing nonempty decision_scope takes precedence.
A missing, empty, or configured placeholder-only section leaves decision_scope unfilled and visible to L7. Placeholder matching is exact after trimming surrounding whitespace; gy does not assess whether free text describes meaningful applicability conditions. Without a section mapping, only existing frontmatter provides decision_scope.
The result includes import_summary with counts and IDs for missing decision_scope, plus source/relationship/target records for missing marks. The command also prints the counts as a warning. These omissions do not abort the import; run gy lint afterward.
Imported narrows and supersedes frontmatter entries carry imported: true on each relationship. L6 identifies missing marks on these entries as migration work; its severity and configuration remain unchanged. Review the older decision and run gy link <new-ID> <relationship> <old-ID> --mark "<affected passage>" to record the mark on both sides. A subsequent gy link replaces that relationship's import provenance with the current operation.
Import preserves frontmatter relationships; it does not infer relationships or marks from prose or Markdown links. Relations added later through gy link are ordinary operations, even when their endpoints were imported.
Configuring lint and render
# docs/ledger/gy.toml
= 6000
[]
= 6000
[]
= "error"
= "warn"
= { = true, = "error" }
# Use false or "off" to disable an individual rule.
[]
= "{scope}/README.md"
= 100
| Rule | Check |
|---|---|
| L1 | Closed questions still referenced as unresolved |
| L2 | Stated bearer counts differ from actual supporting needs |
| L3 | Needs without acceptance criteria |
| L4 | Questions with multiple owners |
| L5 | Unfinished requirements relying on superseded decisions |
| L6 | Missing marks for narrowed or superseded passages |
| L7 | Decisions without applicability conditions |
| L8 | Questions without a decision-maker |
| L9 | Questions with fewer than two distinct options |
| L10 | Requirement states outside the allowed set |
| L11 | Missing or inconsistent completion and compression records |
| L12 | Question bundles without a rationale |
| L13 | References to nonexistent nodes |
L1 checks explicit waiting-on and unresolved attributes. L2 compares the optional nonnegative integer bearer_count with supporting needs. Neither rule infers declarations from body text.
Completed requirements retain their relies-on history. Superseded historical dependencies appear in show and handover.historical_superseded_dependencies, without triggering L5. Reopening restores current dependency checks. Broken links and completion records remain errors. See ledger semantics for the authority and lifecycle contracts.
L6 defaults to warn; the others default to error. The additional edges rule checks inverse links, edge types, and matching marks. Incomplete questions created with q report missing information as errors regardless of the L8/L9 settings.
render splits each scope into pages of the configured node count and creates a README index when multiple pages are needed. Output paths must be relative to the ledger directory and cannot point into the directories containing source nodes.
stats --days 7 reports new question counts, daily rates, and changes for the most recent seven days and the preceding seven days. It counts the first addition of each ID across all git refs; body edits are not new arrivals. Uncommitted questions are excluded. If the previous period had no arrivals, the decay fraction is null. Record acceptance criterion satisfaction with criterion satisfy --evidence.
MCP and bundled skills
MCP exchanges one JSON-RPC message per line over standard input and output. Configure the client to run gy with arguments mcp serve -C /absolute/project/path. The server exposes 18 tools, including gy_find, gy_show, gy_question, and gy_decide. Each tool accepts an args array containing arguments after its corresponding CLI command. For example, gy_find accepts {"args":["delivery","--where","type=decision"]}.
The three bundled skills are gy-ledger, gy-question, and gy-decide. If a destination skill has been edited, installation refuses to overwrite it and requests a different destination.
Development and distribution checks
gy-core provides storage, operations, validation, and views; gy provides the clap CLI and MCP interface. Both reads and writes acquire a ledger-wide file lock. Multi-file updates are journaled before replacing individual files, and an interrupted update is completed on the next startup. Commit .gy-ids.json with the ledger: it records allocated numbers to prevent reuse after deletion. Do not commit .gy.lock.
CI is configured to test and install on macOS, Linux, and Windows. The pre-commit hook entry is in .pre-commit-hooks.yaml.