diff --git a/README.md b/README.md index b0b51675..21c4952e 100644 --- a/README.md +++ b/README.md @@ -230,6 +230,25 @@ specs/auth/ [Read the complete spec format](site/src/content/docs/spec-format.md), [companion-file reference](site/src/content/docs/companion-files.md), and [workflow conventions](site/src/content/docs/workflow.md). +## Architecture + +One local binary does the work. Editors, the GitHub Action, MCP clients and agent skills all run +the same CLI. `check` is the drift check. `change` is the opt-in lifecycle that leaves its +evidence in Git. + +```mermaid +flowchart LR + users([Developers, coding agents,
VS Code, MCP clients, CI]) --> cli[[specsync]] + cli --> check[check
spec vs code drift] + cli --> change[change
new, approve, check,
review, finalize] + check --> repo[(specs/ + source)] + change -->|scoped sync| check + change --> evidence[(.specsync/changes/
then archive/)] +``` + +The [high-level design](docs/HLD.md) covers the components, the change state machine, sequence +diagrams for the key commands, the evidence files and digests, and CI, Trust and release. + ## Documentation | Start here | Reference and integration | diff --git a/docs/HLD.md b/docs/HLD.md new file mode 100644 index 00000000..0364f2f3 --- /dev/null +++ b/docs/HLD.md @@ -0,0 +1,846 @@ +# SpecSync high-level design + +This document explains how SpecSync works end to end: the binary, the validation engine, the +verified change lifecycle, the evidence it leaves in Git, and the CI, Trust and release machinery +around it. It describes **SpecSync 6.0.0** as it exists on `main`. Claims are traceable to files in this +repository, linked inline. Where something could not be established from the code, it says +**Unknown**. + +User-facing guides live elsewhere; this page ties them together. Start with the +[related documents](#related-documents) table if you are looking for a specific guide. + +Contents: + +1. [Purpose](#1-purpose) +2. [Context](#2-context) +3. [Components](#3-components) +4. [Key flows](#4-key-flows) +5. [Data](#5-data) +6. [Runtime and deployment](#6-runtime-and-deployment) +7. [Security and trust boundaries](#7-security-and-trust-boundaries) +8. [Failure modes and limits](#8-failure-modes-and-limits) +9. [Decisions](#9-decisions) +10. [Glossary](#10-glossary) + +--- + +## 1. Purpose + +SpecSync is a single Rust binary, `specsync`, that keeps Markdown module contracts (`*.spec.md`) +and source code honest with each other. `specsync check` compares every spec's Public API table +with the symbols the code actually exports, in both directions, and fails when a spec names +something the code no longer has. On top of that check sits an opt-in, verified change lifecycle +(`specsync change …`): a change is drafted through a deterministic interview, its scope is +approved once by a person, it is verified against the code, reviewed, and archived with its +evidence in the same pull request. Its users are developers, the coding agents working for them +(Claude Code, Cursor, Codex, Gemini CLI), and CI. The core never calls a model, needs no API key +and no hosted service ([`SCOPE.md`](../SCOPE.md), [`INTENT.md`](../INTENT.md)). + +## 2. Context + +```mermaid +flowchart LR + dev([Developer]) + agent([Coding agents
Claude Code, Cursor, Codex, Gemini]) + vscode([VS Code extension]) + mcpc([MCP clients]) + ci([GitHub Actions
CI, Trust, Release]) + + cli[[specsync binary]] + + subgraph repo[Adopting repository] + specs[(specs/**
spec + companions)] + src[(source files,
SQL migrations)] + dot[(.specsync/
config, policy,
changes, archive)] + git[(Git history)] + end + + ghapi[(GitHub REST API)] + raw[(Other repos'
spec registries)] + ext[(Jira / Confluence)] + + dev --> cli + agent -->|same CLI, skills, hooks| cli + vscode -->|spawns with --json| cli + mcpc -->|JSON-RPC over stdio| cli + ci -->|action.yml, cargo run| cli + + cli <-->|read, --fix, lifecycle writes| specs + cli -->|extract exports| src + cli <--> dot + cli -->|git CLI| git + + cli -.->|opt-in, GITHUB_TOKEN| ghapi + cli -.->|opt-in: resolve --remote| raw + cli -.->|opt-in: import| ext +``` + +Solid arrows are the default local path. Dotted arrows happen only when a command or flag asks +for them: + +| External system | Used by | How | Source | +|---|---|---|---| +| GitHub REST API (`api.github.com`) | `import github`, `issues`, MCP `specsync_issues`, `change ship-status`, `change ship --wait` | in-process `ureq`, needs `GITHUB_TOKEN` | [`src/github.rs`](../src/github.rs), [`src/commands/change.rs`](../src/commands/change.rs) | +| GitHub via `gh` CLI | `comment --pr`, `issues --create`, `check --create-issues` | `gh pr comment`, `gh issue create`, using gh's own login | [`src/commands/comment.rs`](../src/commands/comment.rs), [`src/github.rs`](../src/github.rs) | +| `raw.githubusercontent.com` | `resolve --remote` / `--verify` | `ureq`, optional `GITHUB_TOKEN` / `GH_TOKEN` | [`src/registry.rs`](../src/registry.rs) | +| Jira, Confluence | `import jira`, `import confluence` | `ureq`, `JIRA_*` / `CONFLUENCE_*` env vars | [`src/importer.rs`](../src/importer.rs) | +| Git | nearly everything that records evidence | the `git` CLI, bounded by [`src/lifecycle-validation-limits.json`](../src/lifecycle-validation-limits.json) | [`src/change.rs`](../src/change.rs), [`src/git_utils.rs`](../src/git_utils.rs) | + +A plain `specsync check` makes no network call. Its only network path is the opt-in +`--create-issues` flag ([`src/commands/check.rs`](../src/commands/check.rs)). + +## 3. Components + +### 3.1 Layers + +```mermaid +flowchart TB + subgraph entry[Entry points] + cli[main.rs + cli.rs
parse, dispatch,
recursion guard] + mcp[mcp.rs
MCP server] + watch[watch.rs
debounced watcher] + end + + subgraph cmds[commands/] + cmdcheck[check.rs] + cmdchange[change.rs] + cmdother[coverage, score, deps,
generate, init, resolve,
issues, import, comment ...] + end + + subgraph engine[Validation engine] + config[config.rs] + cache[hash_cache.rs] + validator[validator.rs] + parser[parser.rs] + schema[schema.rs] + exports[exports/
33 languages] + end + + change[change.rs
lifecycle, digests,
evidence, archive] + + subgraph adapters[Adapters] + gitcli[git CLI
git_utils.rs] + github[github.rs
comment.rs] + registry[registry.rs
importer.rs] + agents[agents.rs
hooks.rs] + end + + cli --> cmdcheck + cli --> cmdchange + cli --> cmdother + watch -->|spawns specsync check| cli + mcp --> validator + cmdcheck --> config + cmdcheck --> cache + cmdcheck --> validator + validator --> parser + validator --> schema + validator --> exports + cmdchange --> change + change -->|scoped spec-to-code sync| validator + change --> gitcli + cmdchange -->|ship-status| github + cmdother --> validator + cmdother --> github + cmdother --> registry + cmdother --> agents +``` + +The CLI is the only engine. The VS Code extension, the GitHub Action, MCP clients and the +installed agent skills all end up running the same commands and reading the same output +([`site/src/content/docs/architecture.md`](../site/src/content/docs/architecture.md)). + +### 3.2 Modules + +Every source file is claimed by one of the 62 module specs under [`specs/`](../specs/); this +repository checks itself at 100% file coverage. The table names the spec to read for detail. + +| Area | Files | Owns | Spec | +|---|---|---|---| +| Entry and grammar | [`src/main.rs`](../src/main.rs), [`src/cli.rs`](../src/cli.rs) | Global flags, subcommand grammar, default command (`check`), blocking lifecycle commands from re-entering themselves (`SPECSYNC_VERIFICATION_CONTEXT`) | `specs/cli`, `specs/cli_args` | +| Shared command plumbing | [`src/commands/mod.rs`](../src/commands/mod.rs) | Config loading and spec discovery for commands, spec filters, validation runs, exit codes | `specs/commands` | +| Configuration | [`src/config.rs`](../src/config.rs), [`src/types.rs`](../src/types.rs), [`src/manifest.rs`](../src/manifest.rs) | `.specsync/config.toml` and legacy fallbacks, shared types, manifest-aware source detection | `specs/config`, `specs/types`, `specs/manifest` | +| Spec parsing | [`src/parser.rs`](../src/parser.rs) | Frontmatter, required sections, Public API tables, the single `strip_frontmatter` | `specs/parser` | +| Validation | [`src/validator.rs`](../src/validator.rs), [`src/ignore.rs`](../src/ignore.rs), [`src/util.rs`](../src/util.rs) | Structural and API checks, coverage, `.specsyncignore` suppressions | `specs/validator`, `specs/ignore`, `specs/util` | +| Export extraction | [`src/exports/`](../src/exports/) (regex), [`src/exports/ast/`](../src/exports/ast/) (tree-sitter) | Language detection by extension and public-symbol extraction | `specs/exports` | +| Schema drift | [`src/schema.rs`](../src/schema.rs) | Replaying SQL migrations into a table/column snapshot | `specs/schema` | +| Dependency graph | [`src/deps.rs`](../src/deps.rs) | `depends_on` graph, cycles, undeclared imports, Mermaid/DOT output | `specs/deps` | +| Incremental cache | [`src/hash_cache.rs`](../src/hash_cache.rs), [`src/commands/rehash.rs`](../src/commands/rehash.rs) | `.specsync/hashes.json` and replayable findings | `specs/hash_cache`, `specs/rehash` | +| Quality | [`src/scoring.rs`](../src/scoring.rs) | 0–100 score over five 20-point components | `specs/scoring` | +| Change lifecycle | [`src/change.rs`](../src/change.rs), [`src/commands/change.rs`](../src/commands/change.rs) | Interview, approvals, deltas, verification, review, finalization, archive, recovery | `specs/change`, `specs/cmd_change` | +| Module maturity | [`src/commands/lifecycle.rs`](../src/commands/lifecycle.rs) | Spec `status` transitions (`draft → review → active → stable → deprecated → archived`) | `specs/cmd_lifecycle` | +| Authoring | [`src/generator.rs`](../src/generator.rs), `commands/new.rs`, `scaffold.rs`, `generate.rs`, `wizard.rs`, `init.rs` | Deterministic spec and companion scaffolds, project init | `specs/generator`, `specs/cmd_*` | +| Maintenance | [`src/compact.rs`](../src/compact.rs), [`src/archive.rs`](../src/archive.rs), [`src/merge.rs`](../src/merge.rs), [`src/changelog.rs`](../src/changelog.rs), [`src/view.rs`](../src/view.rs) | Changelog trimming, task archival, spec merge-conflict repair, spec changelogs, role views | matching `specs/*` | +| Cross-project | [`src/registry.rs`](../src/registry.rs), `commands/resolve.rs`, `commands/init_registry.rs` | `owner/repo@module` references and registry files | `specs/registry`, `specs/cmd_resolve` | +| GitHub | [`src/github.rs`](../src/github.rs), [`src/comment.rs`](../src/comment.rs), `commands/issues.rs`, `commands/import.rs`, [`src/importer.rs`](../src/importer.rs) | Issue links, drift issues, PR comments, imports | `specs/github`, `specs/comment`, `specs/importer` | +| Agents | [`src/agents.rs`](../src/agents.rs), [`src/hooks.rs`](../src/hooks.rs), [`src/mcp.rs`](../src/mcp.rs), [`src/mcp_tools_lock.rs`](../src/mcp_tools_lock.rs) | Native skills, instruction snippets, Git pre-commit hook, MCP server and its tool lock | `specs/agents`, `specs/hooks`, `specs/mcp` | +| Watch | [`src/watch.rs`](../src/watch.rs) | Debounced re-runs of `check` | `specs/watch` | + +The full module graph is generated from the specs themselves: `specsync deps --mermaid` prints it +(about 60 nodes, too many to draw legibly here). + +### 3.3 Command families + +Running `specsync` with no subcommand runs `check` ([`src/main.rs`](../src/main.rs)). Global flags +(`--strict`, `--require-coverage`, `--root`, `--format`, `--json`, `--enforcement`, status +filters) are defined once in [`src/cli.rs`](../src/cli.rs). The full reference is +[`site/src/content/docs/cli.md`](../site/src/content/docs/cli.md). + +| Family | Commands | +|---|---| +| Validate | `check` (default), `coverage`, `score`, `deps`, `diff`, `stale`, `report`, `rules`, `watch` | +| Author | `init`, `new`, `add-spec`, `scaffold`, `generate`, `wizard` | +| Lifecycle | `change …` (verified delivery), `lifecycle …` (module maturity) | +| Integrations | `mcp` (+ `mcp lock`, `mcp diff`), `agents`, `hooks`, `comment`, `issues`, `import` | +| Maintenance | `compact`, `archive-tasks`, `merge`, `changelog`, `rehash`, `migrate`, `view` | +| Cross-project | `init-registry`, `resolve` | + +**Enforcement.** `EnforcementMode` in [`src/types.rs`](../src/types.rs) is `warn`, `enforce-new` or +`strict`, and the default is `strict`: a bare `check` exits 1 on any error, while warnings still +pass unless `--strict` is given. `warn` always exits 0; `enforce-new` fails only when source files +have no spec. `--require-coverage N` is evaluated after the mode (`compute_exit_code`, +`exit_with_status` in [`src/commands/mod.rs`](../src/commands/mod.rs)). + +## 4. Key flows + +### 4.1 `specsync check` + +```mermaid +sequenceDiagram + autonumber + actor U as Developer / agent / CI + participant C as commands/check.rs + participant Cfg as config.rs + participant H as hash_cache.rs + participant V as validator.rs + participant P as parser.rs + participant X as exports/ + participant S as schema.rs + participant O as output.rs + + U->>C: specsync check [--strict] [--fix] [--force] + C->>Cfg: load config (.specsync/config.toml first) + C->>V: discover specs/**/*.spec.md + C->>H: load .specsync/hashes.json + H-->>C: unchanged specs (replay stored findings) + opt --fix + C->>C: rename near-miss headings, append undocumented exports + end + loop each changed spec + C->>V: validate_spec + V->>P: parse frontmatter, sections, Public API rows + V->>V: files exist, stay in root, required sections + V->>S: db_tables vs replayed migrations + V->>X: extract exports per source file + X-->>V: exported symbols + V->>V: spec-only symbol = error, code-only export = warning + end + C->>V: compute coverage (--require-coverage) + C->>H: save cache (only when there are no errors) + C->>O: render text / json / markdown / github / table / csv + O-->>U: findings + exit code +``` + +Stage by stage, per [`src/commands/check.rs`](../src/commands/check.rs) (`cmd_check`) and +[`src/validator.rs`](../src/validator.rs) (`validate_spec`): + +1. **Config.** The first file found among `.specsync/config.toml`, `.specsync/config.json`, + `.specsync.toml` and `specsync.json` wins. A config file that exists but cannot be loaded stops + the run instead of falling back to defaults. +2. **Discovery.** `*.spec.md` under `specs_dir` (default `specs`), skipping names that start with + `_` and recording skipped symlinks. +3. **Cache.** With no `--force`, `--strict`, `--fix` or spec filter, specs whose spec, companions, + sources and global inputs are all unchanged **replay** their stored findings; they do not + silently pass. +4. **Structure.** `module`, `version`, `status` and a non-empty `files` list are required. Every + listed file must exist inside the root. Frontmatter is read by a hand-written line parser, not a + YAML library, and duplicate keys are errors ([`src/parser.rs`](../src/parser.rs)). +5. **Sections.** The configured required sections (defaults: Purpose, Public API, Invariants, + Behavioral Examples, Error Cases, Dependencies, Change Log). Draft specs skip this. +6. **Schema.** When `schema_dir` is set, documented tables and columns are checked against the + migrations replayed in filename order. A documented column missing from the schema is an error; + an undocumented column or a type mismatch is a warning. +7. **API surface.** The first backtick span in the first cell of Public API table rows is compared + with the exports of the spec's `files`. A symbol the spec lists but the code lacks is an + **error** (the document is lying). An export the spec omits is a **warning** (the document is + incomplete), and `--strict` makes it fail. +8. **Dependencies.** `check` only verifies that each local `depends_on` path exists. + Graph-level checks (cycles, undeclared imports) belong to `specsync deps`; cross-project + references belong to `specsync resolve`. +9. **Coverage.** File and LOC coverage always runs and feeds `--require-coverage` and + `enforce-new`. + +`check` never reads `.specsync/changes/` or the archive. That is deliberate: the drift check and +the delivery lifecycle are separate questions (the comment in `cmd_check`, +[`src/commands/check.rs`](../src/commands/check.rs); `REQ-cmd-check-004` in +[`specs/cmd_check/requirements.md`](../specs/cmd_check/requirements.md)). + +**Export extraction.** All 33 languages have regex extractors. With `parse_mode = "ast"`, ten of +them (TypeScript, Python, Rust, C, C++, Scala, Erlang, Elixir, Perl, Lisp) use tree-sitter +and fall back to regex when the AST pass finds nothing ([`src/exports/mod.rs`](../src/exports/mod.rs)). + +### 4.2 The change lifecycle + +A change is one workspace under `.specsync/changes//` with a `state.json` whose `state` is +one of six values (`ChangeState` in [`src/change.rs`](../src/change.rs)). The diagram shows the +workflow-v2 path every new change takes, plus the audited recovery edges. Each arrow names the +command and the domain function that performs the transition. + +```mermaid +stateDiagram-v2 + [*] --> Draft: change new
create_change + Draft --> Draft: change answer
answer_question + Draft --> Approved: change approve
approve_definition + Approved --> Approved: change approve
re-approve changed scope + Approved --> Implementing: change check
materialize deltas into specs + Implementing --> Verifying: change check
record verification.json + Verifying --> Verifying: change check (re-verify)
change review, change approve + Implementing --> Implementing: change approve
re-approve changed scope + Verifying --> Implementing: change approve
before deltas were applied + Verifying --> Accepted: change finalize
closing + finalization evidence + Accepted --> Archived: same finalize call
move to dated archive + Accepted --> Verifying: change reopen
actor + reason + Archived --> Verifying: change reopen
un-archive, actor + reason + Archived --> [*] +``` + +How to read it, with the guards each command enforces: + +- **Draft.** `change new` mints the ID as a slug of the description and refuses a slug already + used by an active or archived change (the historical `CHG-NNNN` ordinals are only read, never + allocated). Interview answers are accepted only in `Draft`. Answering `public_contract yes` adds + the requirements and docs artifacts; `architecture_risk yes` adds research, design, plan, tasks + and testing (`answer_question_with_snapshot`). +- **Approved.** `change approve --actor ` validates the definition and delta files, checks + that declared paths have owners, and appends a `definition` approval carrying the scope digest + and a per-module digest of each semantic delta body to `approvals.json` + (`approve_definition_with_projection`). Approval is the first point a new session can resume + from. +- **Implementing.** The first `change check` materializes the approved semantic deltas into the + canonical specs (with a version bump and a Change Log row per module) and sets + `canonical_applied` (`materialize_change_deltas`). It refuses if a delta body changed since it + was approved, if dependencies are not yet accepted, if deltas conflict with another active + change, or if tasks are incomplete. In CI it refuses to write and asks for the result to be + committed first (`is_ci_project`). +- **Verifying.** The same `change check` then compares this change's specs to code in process and + records a `verification.json` bound to `HEAD`, the scope and execution digests and the + workspace digest (`verify_change_locked`). It does **not** run the project's tests: CI owns + those. A spec-to-code mismatch still lands in `Verifying`, with a failed record and the + findings; a missing evidence row in the change's artifacts fails before the comparison runs. +- **Review.** `change review --reviewer ` records `review.json` (and appends to + `review-attempts.json`) with an explicit pass or block verdict, bound to the implementation + commit and the same digests. It requires verification that is still current for the + checked-out commit (`record_scoped_review_with_verdict`). The reviewer may be the approver. +- **Accepted → Archived.** `change finalize` accepts and archives in one call + (`finalize_change`), so a workflow-v2 change is only observed as `Accepted` after an + interrupted finalization. It writes `finalization.json`, moves the workspace to + `.specsync/archive/changes/YYYY-MM-DD-/`, and writes a best-effort `lesson-bundle.md`. +- **Recovery.** `change reopen --actor --reason` moves stale accepted or archived evidence back + to `Verifying`, un-archiving if needed and restoring the archive if the reopen is refused + (`reopen_change`). The prior verification and closing approval stay in `approvals.json` as an + audit record. + +Workflow-v1 records (numeric `CHG-NNNN` IDs, from before 6.0) keep their own compatibility +commands: `start`, `verify`, `accept`, `archive`, `correct` and `correct-owner`. `finalize` +refuses a v1 record and names the `accept` → `archive` path instead. See +[`site/src/content/docs/workflow.md`](../site/src/content/docs/workflow.md) for the operator view +of both. + +A second, unrelated lifecycle exists for **module maturity**: `specsync lifecycle promote|demote|set` +moves a spec's frontmatter `status` through `draft → review → active → stable → deprecated → +archived` ([`src/commands/lifecycle.rs`](../src/commands/lifecycle.rs)). It does not interact with +change states. + +### 4.3 Draft to approval: `change new`, `change answer`, `change approve` + +```mermaid +sequenceDiagram + autonumber + actor A as Author (person or agent) + actor H as Scope owner (person) + participant CMD as commands/change.rs + participant D as change.rs + participant FS as .specsync/changes/id/ + + A->>CMD: change new "Add passkeys" --spec auth --path src/auth.rs + CMD->>D: create_change + D->>D: mint slug id, refuse if active or archived + D->>FS: state.json (Draft), change.md, deltas/, artifact stubs + CMD-->>A: record + next open question + prior lessons for the modules + loop until next_questions is empty + A->>CMD: change answer id question value + CMD->>D: answer_question_with_snapshot (takes change.lock) + D->>FS: state.json, change.md, newly selected artifact stubs + end + A->>FS: write selected artifacts and deltas/module.md + H->>CMD: change approve id --actor "Ada" + CMD->>D: approve_definition + D->>D: validate definition, delta files, path ownership + D->>D: scope digest + per-module delta body digests + D->>FS: append definition approval to approvals.json + D->>FS: state.json (Approved) + CMD-->>H: next action + Handoff line +``` + +The interview asks, only while unanswered: `acceptance_criteria`, `affected_specs` (skipped with +`--no-spec-change`), `affected_paths`, `public_contract` and `architecture_risk` +(`next_questions`). The artifacts start from the change kind (`adaptive_artifacts`): + +| Kind | Artifacts added to `context` | +|---|---| +| `feature` | requirements, plan, tasks, testing, docs | +| `bug_fix` | testing, tasks | +| `refactor` | plan, testing | +| `migration` | research, design, plan, tasks, testing, docs | +| `documentation` | docs | +| `operations` | plan, testing | + +More than one affected spec or more than four paths also adds design and tasks. `--artifact` adds +more. The answers can only add scrutiny, never remove it. + +### 4.4 Verification: `change check` and `change check --commit` + +```mermaid +sequenceDiagram + autonumber + actor A as Author + participant CMD as commands/change.rs + participant D as change.rs + participant V as spec-to-code sync + participant G as git + + A->>CMD: change check id --commit + CMD->>D: check_change (pass 1) + D->>D: take change.lock, recover any pending transaction + D->>D: approval current? delta bodies unchanged? deps accepted? tasks done? + alt deltas not yet in canonical specs + D->>D: apply deltas, bump version, add Change Log row (one transaction) + end + D->>V: compare this change's specs with code + V-->>D: findings + D->>D: verification.json bound to HEAD + digests, state Verifying + CMD->>G: git add -A, git commit "chore(lifecycle): materialize id" + CMD->>D: check_change (pass 2, against the committed tree) + D->>D: verification.json re-anchored to the new HEAD + CMD->>G: git add -A, git commit "chore(lifecycle): record id verification" + opt --push + CMD->>G: git push + end +``` + +Without `--commit`, `change check` stops after the first pass and commits nothing +(`run_checked_commit` in [`src/commands/change.rs`](../src/commands/change.rs)). Scope is the +modules in `affected_specs` plus the specs whose `files:` fall inside `affected_paths`; a declared +module with no spec on disk fails by name (contract 5 in +[`specs/change/change.spec.md`](../specs/change/change.spec.md)). + +**Staging warning.** Both commits use `git add -A` (`git_commit_all`). That stages every modified +and every untracked, non-ignored file in the working tree, not just the change's paths. Before +`--commit` (or `change ship --push`, which uses the same helper), make sure `git status` shows +nothing you do not mean to commit. + +### 4.5 Review and finalize (or `change ship`) + +```mermaid +sequenceDiagram + autonumber + actor R as Reviewer (person) + participant CMD as commands/change.rs + participant D as change.rs + participant FS as .specsync/ + participant GH as GitHub + + R->>CMD: change review id --reviewer "Ada" + CMD->>D: record_scoped_review_with_verdict (Verifying only) + D->>D: verification still current for HEAD? + D->>FS: review.json + append review-attempts.json (uncommitted) + Note over R,FS: no commit between review and finalize, or the review goes stale + R->>CMD: change finalize id (or change ship id) + CMD->>D: finalize_change + D->>D: require current verification and a passing, current review + D->>FS: closing approval, finalization.json, state Accepted + D->>FS: move to archive/changes/YYYY-MM-DD-id/, state Archived + D->>FS: lesson-bundle.md (best effort) + opt change ship --push [--wait] + CMD->>CMD: git add -A, commit "chore(lifecycle): archive id", git push + CMD->>GH: poll check-runs for HEAD every 10 s (needs GITHUB_TOKEN) + end + R->>GH: merge the PR once the archive tip is green +``` + +`change ship` first computes the same report as `change ship-status` and finalizes only when it +is ready: state `Verifying`, current verification, a current review and no blockers +(`ship_status_report`, `run_ship`). `ship-status` classifies the tip commit as `product`, +`review_only`, `archive_only` or `other` and, when `GITHUB_TOKEN` is set, reads GitHub check runs +for the commit (`build_ship_trust` → `fetch_commit_check_summary` in +[`src/github.rs`](../src/github.rs)). Only the first page of 100 check runs is read. + +The four ordering rules in [`AGENTS.md`](../AGENTS.md) follow from these bindings. Review and +ship are one step. Finalize one change at a time, because an archive write stales every other +active change. Do not batch reviews. Never merge while a change on the PR is still active. + +### 4.6 A pull request through CI and Trust + +```mermaid +sequenceDiagram + autonumber + actor Dev as Contributor + participant CI as ci.yml + participant T as trust.yml + participant GH as Branch protection + + Dev->>CI: push / open PR + Dev->>T: push / open PR + par CI + CI->>CI: classify changed paths (full, site, vscode, archive_only, review_only) + CI->>CI: preflight: verification commits exist and are ancestors of HEAD + CI->>CI: lifecycle gate: specsync change audit --strict + CI->>CI: test + clippy, fmt, hi check, audit, coverage, spec-check, site, vscode, action consumer + opt a verifying change needs review + CI->>CI: SpecSync scoped review (corvid-pet comment on the PR) + end + CI->>CI: SpecSync implementation ready + CI->>GH: Required CI gate + and Trust + T->>T: cargo build --release, checksummed local mirror + T->>T: specsync change audit --strict + T->>T: CorvidLabs Trust: contract (100% coverage), Augur risk, Attest provenance + end + GH-->>Dev: mergeable when Required CI gate is green +``` + +Job names and dependencies come from [`.github/workflows/ci.yml`](../.github/workflows/ci.yml) and +[`.github/workflows/trust.yml`](../.github/workflows/trust.yml). The path classifier is +[`.github/scripts/classify-ci-paths.sh`](../.github/scripts/classify-ci-paths.sh). A tip that +only adds a review or only moves one workflow-v2 change into the archive skips the heavy product +lane. Documentation-only paths still run the full lane, so every PR can report the required +gate. Branch protection on `main` requires the `Required CI gate` context. +[`docs/ci-confidence.md`](ci-confidence.md) explains who owns each check and why Trust does not +re-run the test suite. + +**Unknown:** what the pinned `CorvidLabs/trust` and `CorvidLabs/corvid-pet` actions do +internally. They live outside this repository; this page only describes how the workflows call +them. + +## 5. Data + +### 5.1 Specs and companions + +```text +specs// +├── .spec.md contract: frontmatter + required sections, checked against code +├── requirements.md stable REQ-- IDs, SHALL statements, acceptance criteria +├── context.md decisions, key files, lessons folded in after archival +├── tasks.md open work +├── testing.md requirement-to-test evidence +└── design.md optional, when design artifacts are enabled +``` + +Frontmatter fields: `module`, `version`, `status`, `files`, `db_tables`, `depends_on`, +`agent_policy`, `implements`, `tracks`, `lifecycle_log` ([`src/types.rs`](../src/types.rs)). The +format reference is [`site/src/content/docs/spec-format.md`](../site/src/content/docs/spec-format.md); +companions are described in [`site/src/content/docs/companion-files.md`](../site/src/content/docs/companion-files.md). + +### 5.2 The `.specsync/` directory + +| Path | Holds | Committed | Written by | +|---|---|---|---| +| `config.toml` | Project configuration (`specs_dir`, `source_dirs`, `required_sections`, `enforcement`, `parse_mode`, `schema_dir`, …) | yes | `init`, by hand | +| `version` | Layout stamp; 6.0 reads it only to detect a pre-4.0 project ([`MIGRATION.md`](../MIGRATION.md)) | yes | `init` | +| `sdd.json` | Change policy: `enabled`, meaningful and ignored paths, strict paths, legacy verification commands | yes | `init` (off), `change adopt` (on) | +| `registry.toml` | Specs this repo publishes to others | yes | `init-registry` | +| `hashes.json` | Incremental check cache | no (gitignored) | `check`, `rehash` | +| `change.lock` | Exclusive lifecycle lock (`fs2` `lock_exclusive`, not re-entrant) | no | every lifecycle mutation | +| `change-transaction.json` | Undo journal for multi-file writes | no | `write_prepared_files` | +| `change-sequence.json` | Historical `CHG-NNNN` ordinals; never allocated now, only raised to the committed value before lifecycle commits | yes | `floor_sequence_ledger_to_committed` | +| `workflow-v2-baseline.json` | Immutable cutoff commit for recognising workflow-v1 history | yes | `change adopt` or the first v2 `change new` | +| `bootstrap.json` | Protected paths created by bootstrap, exempt from coverage until edited | yes, when present | `init`, `adopt` | +| `mcp-tools.lock.json` | SHA-256 per MCP tool definition | yes | `mcp lock --write` | +| `agent-artifacts.json` | Digest of each installed agent skill/command file | yes | `agents install` | +| `changes//` | One active change workspace (5.3) | yes | `change …` | +| `archive/changes/YYYY-MM-DD-/` | Finalized changes, with `lesson-bundle.md` | yes | `change finalize` | + +This repository dogfoods the layout; see its own [`.specsync/`](../.specsync/) and +[`.specsync/sdd.json`](../.specsync/sdd.json). + +### 5.3 Change evidence + +```mermaid +erDiagram + CHANGE_RECORD ||--|| APPROVAL_LEDGER : "approvals.json" + APPROVAL_LEDGER ||--o{ APPROVAL : "definition, acceptance, finalization" + APPROVAL_LEDGER ||--o{ REOPEN : "audited reopenings" + CHANGE_RECORD ||--o| VERIFICATION : "verification.json" + CHANGE_RECORD ||--o{ VERIFICATION_ATTEMPT : "verification-attempts.json" + CHANGE_RECORD ||--o| SCOPED_REVIEW : "review.json" + CHANGE_RECORD ||--o{ REVIEW_ATTEMPT : "review-attempts.json" + CHANGE_RECORD ||--o| FINALIZATION : "finalization.json" + CHANGE_RECORD ||--o| CORRECTION_LEDGER : "corrections.json" + CHANGE_RECORD ||--o{ DELTA : "deltas/module.md" + CHANGE_RECORD ||--o{ ARTIFACT : "kind.md" + DELTA }o--|| SPEC : "materializes into" + + CHANGE_RECORD { + string id "slug" + string state "draft..archived" + int workflow_version + bool canonical_applied + string base_commit + list affected_specs + list affected_paths + list acceptance_criteria + } + APPROVAL { + string gate + string actor "claim, not identity" + string digest + map approved_delta_digests + } + VERIFICATION { + string commit + string contract_digest + string execution_digest + string workspace_digest + bool passed + } + SCOPED_REVIEW { + string reviewer + string verdict "pass or block" + string implementation_commit + string workspace_digest + } + FINALIZATION { + string implementation_commit + string closing_digest + string review_digest + string finalization_digest + } +``` + +Types are in [`src/change.rs`](../src/change.rs): `ChangeRecord`, `ApprovalLedger`, +`ApprovalRecord`, `ReopenRecord`, `VerificationRecord`, `ScopedReviewRecord`, +`FinalizationRecord`, `CorrectionRecord`. An archived package also carries +`accepted-state.json`, the authenticated accepted-state bytes staged during archival. + +### 5.4 Digests + +Every lifecycle digest is SHA-256 through `FramedDigest` in [`src/change.rs`](../src/change.rs): +the first frame is a domain tag, and each frame is written as length-prefixed tag and value, so two +different inputs cannot collide by concatenation. + +| Digest | Domain tag | Covers | Recorded on | +|---|---|---|---| +| Scope (workflow v2 contract) | `specsync.scope-digest.v1` | `ApprovedScopeV1`: id, title, description, kind, sorted specs, paths, criteria, dependencies, supersedes, no-spec rationale, effective answers | definition approval, verification, review, finalization | +| Execution (workflow v2) / definition (v1) | `specsync.definition-digest.v2` | The canonicalized record plus every selected artifact, every delta file, the principles file and corrections; CRLF folded to LF, `tasks.md` checkboxes blanked | verification, review | +| Workspace | `specsync.project-input-digest.v2` | Every Git-visible project file (`git ls-files --cached --others --exclude-standard`) except volatile paths such as `.specsync/changes/`, `.specsync/archive/`, `target/` and `node_modules/`: path, kind, mode, content | verification, review, finalization | +| Delta body | `specsync.approved-delta.v1` | Module name plus the delta body with CRLF folded to LF, nothing else normalized | `approved_delta_digests` on the definition approval | +| Closing | `specsync.closing-digest.v2` | id, contract, execution, workspace, commit, acceptance manifest, succession evidence | acceptance / finalization approval | +| Finalization | `specsync.finalization-digest.v2` | id, implementation commit and tree, contract, workspace, closing and review digests | `finalization.json` | + +Splitting scope from execution is what keeps one approval valid while the implementation moves. +Editing code, tests, evidence or materialized specs stales verification and review but not the +approval. Changing intent, acceptance criteria or affected scope stales the approval +(invariants 3–4 in [`specs/change/change.spec.md`](../specs/change/change.spec.md)). + +### 5.5 Hash cache + +`.specsync/hashes.json` maps paths to the SHA-256 of their raw bytes and stores per-spec snapshots +of prior findings with an input digest and an integrity digest +([`src/hash_cache.rs`](../src/hash_cache.rs)). It covers the spec, its companions, its `files:` +and global inputs (the config file and every file in `schema_dir`). An unknown format version or a +parse error loads as an empty cache. `--force` bypasses it and `rehash` rebuilds it. It is only +saved after a run with no errors. + +## 6. Runtime and deployment + +### 6.1 Build + +A Rust 2024 crate, toolchain pinned to 1.89.0 ([`rust-toolchain.toml`](../rust-toolchain.toml)), +one binary `specsync` ([`Cargo.toml`](../Cargo.toml)). The release profile uses LTO and strips +symbols. Main dependencies: `clap`, `serde`/`serde_json`, `toml`, `regex`, `walkdir`, `sha2`, +`fs2`, `cap-std`, `notify`, `ureq` and the tree-sitter grammars. Local tasks and lanes live in +[`fledge.toml`](../fledge.toml): + +| Lane | Steps | When | +|---|---|---| +| `pre-push` | `cargo fmt --check`, `cargo check`, strict spec check with 100% coverage | before every push ([`scripts/pre-push-gate.sh`](../scripts/pre-push-gate.sh)) | +| `verify` | fmt, clippy, check, `cargo test`, release build, spec-check, release-candidate test | before calling work done | +| `trust-lifecycle` | `cargo check` only | the Trust action's lifecycle step | +| `release-candidate` | `cargo test`, release build | the release workflow, per platform | +| `ci` / `repo` | everything, including site and VS Code builds | full local reproduction | + +### 6.2 Release + +```mermaid +flowchart LR + rc[annotated tag
vX.Y.Z-rc.N] --> resolve[resolve
check tag rulesets] + resolve --> validate[validate
ancestor of main,
Cargo version = tag] + validate --> qualify[qualify
Ubuntu + macOS
fledge lanes run release-candidate] + qualify --> record[record check run
SpecSync release candidate] + record --> dispatch{{workflow_dispatch
from main with rc_tag}} + dispatch --> promote[promote
create annotated vX.Y.Z] + promote --> build[build 5 targets
linux x86_64, x86_64-musl,
aarch64, macOS x86_64, aarch64] + build --> release[GitHub Release
tar.gz + .sha256] + release -.->|manual| crates[crates.io] + release -.->|manual| brew[Homebrew tap] +``` + +The operator runbook is [`docs/RELEASING.md`](RELEASING.md); the workflow is +[`.github/workflows/release.yml`](../.github/workflows/release.yml), with +[`.github/workflows/rc-assets.yml`](../.github/workflows/rc-assets.yml) for pre-release assets. +Windows is neither built nor published as of 6.0. Assets are integrity-checked with SHA-256 +sidecars; no signing or artifact attestation step exists in these workflows. What tag authority +does and does not enforce is written down in [`docs/ci-confidence.md`](ci-confidence.md#tag-authority-what-is-enforced-and-what-is-not). + +### 6.3 Distribution and integrations + +- **Binaries**: GitHub Releases, `cargo install specsync`, `brew install CorvidLabs/tap/spec-sync` + ([`README.md`](../README.md#install)). +- **GitHub Action** ([`action.yml`](../action.yml)): a composite action that downloads the + release tarball for the runner's OS and architecture, verifies its `.sha256`, runs + `specsync check --force` with the requested `strict` / `require-coverage` / `args`, optionally + runs `specsync lifecycle enforce --all`, and optionally posts or updates a PR comment. Linux and + macOS only. +- **VS Code extension** ([`vscode-extension/`](../vscode-extension/)): runs the binary with + `--root ` and `--json`, and shows diagnostics, a status bar item, score CodeLens and + coverage/score webviews. +- **MCP** ([`src/mcp.rs`](../src/mcp.rs)): JSON-RPC 2.0 over stdio. Read-only tools by default + (`specsync_check`, `specsync_coverage`, `specsync_list_specs`, `specsync_score`, + `specsync_issues`); `--allow-write` adds `specsync_generate` and `specsync_init`, which always + run at the server root. `mcp lock` / `mcp diff` pin the tool catalog in + `.specsync/mcp-tools.lock.json`. +- **Agents and hooks** ([`src/agents.rs`](../src/agents.rs), [`src/hooks.rs`](../src/hooks.rs)): + `agents install` writes `skills/spec-sync/SKILL.md` for Claude Code, Cursor, Codex and Gemini CLI + plus slash commands (not for Codex), tracked by digest in `.specsync/agent-artifacts.json`. + `hooks install` adds instruction snippets to `CLAUDE.md`, `.cursorrules`, + `.github/copilot-instructions.md` and `AGENTS.md`, a Claude Code post-edit hook, and a Git + pre-commit hook that runs `specsync check` when the binary is on `PATH`. + +### 6.4 Documentation and Pages + +- User documentation is published on the CorvidLabs hub at + . Its source in this repository is + [`site/src/content/docs/`](../site/src/content/docs/). +- The GitHub Pages site built by [`.github/workflows/pages.yml`](../.github/workflows/pages.yml) + from [`site/`](../site/) is a retired shell. Every page is a redirect stub to the hub + ([`site/src/lib/hub.ts`](../site/src/lib/hub.ts), + [`site/src/components/RedirectPage.astro`](../site/src/components/RedirectPage.astro)). +- Pages still serves the atlas badges rendered into `site/public/badges`, such as the README's + coverage badge. +- This HLD is not published to Pages. GitHub renders its Mermaid diagrams directly. + +## 7. Security and trust boundaries + +- **No inference and no credentials in the core.** SpecSync never selects a model, stores an + inference key or sends source anywhere to be generated. `generate` is deterministic. Agents + enrich specs under their own permissions ([`SCOPE.md`](../SCOPE.md), + [`specs/ai/retired.md`](../specs/ai/retired.md)). +- **Network is opt-in.** Section 2 lists every network path. Tokens are read from the environment + (`GITHUB_TOKEN`, `GH_TOKEN`, `JIRA_*`, `CONFLUENCE_*`); writes to GitHub go through the `gh` + CLI and its own login. +- **Approvals bind content, not identity.** `--actor` and `--reviewer` are recorded claims. The + digests prove what was approved, not who approved it. Identity needs hosted policy on top: branch + protection, the `SpecSync scoped review` check whose name is written into review provenance, and + signed provenance. The Trust configuration here uses Attest in `soft` mode + ([`.trust.toml`](../.trust.toml)), which the docs say is not a satisfied provenance policy on its + own ([`docs/ci-confidence.md`](ci-confidence.md)). +- **Fail closed.** Invalid policy, a changed delta body, stale verification, unreadable or corrupt + ledgers, and an unloadable config file all stop the command instead of being skipped. There is no + force or emergency transition (invariant 2 in [`specs/change/change.spec.md`](../specs/change/change.spec.md)). +- **Filesystem confinement.** Lifecycle paths are normalized to forward slashes and resolved inside + the project without following symlinks. The lock refuses a symlinked `.specsync`. The MCP server + opens its root as a `cap-std` directory capability and rejects traversal, symlink escapes and + `.git`. The agent installer refuses to overwrite or remove customized or untracked files, and the + hook installer refuses a symlinked hook. +- **Supply chain.** Workflow runtimes are pinned and validated + (`.github/scripts/validate-workflow-runtime-pins.py`). The Action and Trust verify SHA-256 + checksums of the binary they run. Final tags are protected by two immutable-tag rulesets. + Promotion itself is minted by the workflow's `GITHUB_TOKEN`, with no separate release identity. + [`docs/ci-confidence.md`](ci-confidence.md) records that as an accepted limit. +- **Reporting.** Vulnerabilities go through [`SECURITY.md`](../SECURITY.md). + +## 8. Failure modes and limits + +| Situation | What happens | Where | +|---|---|---| +| A spec lists a symbol the code lacks | `check` error, exit 1 under the default `strict` enforcement | `validate_spec` | +| Code exports a symbol the spec omits | Warning; fails with `--strict` | `validate_spec` | +| Config file present but unloadable | Command stops, no silent defaults | `refuse_unloadable_config` in `commands/mod.rs` | +| Process dies mid-write during a lifecycle mutation | The next lifecycle command takes `change.lock`, finds `change-transaction.json`, verifies its integrity envelope and restores the original bytes | `recover_pending_transaction` | +| Two lifecycle commands at once | The second blocks on the exclusive lock. Recursive calls (a verification child calling `check` or `change`) are refused by `SPECSYNC_VERIFICATION_CONTEXT` | `acquire_project_lock`, `main.rs` | +| Delta body edited after approval | Materialization and acceptance refuse and name the modules | invariant 38 | +| Code or tests change after verification | Verification is stale; `change check` again | `recorded_verification_is_current` | +| Implementation commit after review | Review is stale; review again. Only lifecycle JSON under the change directory may change between review and finalize | invariant 22 | +| Squash-merge, rebase or force-push drops the verification commit | CI preflight fails fast; recover with `change status`, then `change check` (or `reopen` for accepted evidence) | `ci.yml` preflight, [`site/src/content/docs/workflow.md`](../site/src/content/docs/workflow.md) | +| PR merged while a change is still active | The workspace is stranded on `main`; follow `change status` and `change audit` | [`AGENTS.md`](../AGENTS.md) rule 4 | +| Finalizing two changes in one sitting | Each archive write stales the other change's evidence | [`AGENTS.md`](../AGENTS.md) rule 2 | +| Approved deltas not materialized when CI runs | `change check` refuses to write in CI and asks for a local run and commit | `materialize_change_deltas` (`is_ci_project`) | +| `change check --commit` or `change ship --push` with unrelated files in the tree | They are committed too: `git_commit_all` runs `git add -A`. Clean or stash the tree first | `git_commit_all` in `commands/change.rs` | +| `--commit` second verification fails | The first `materialize` commit is already made; fix and rerun | `run_checked_commit` | +| Very large Git histories or outputs | Git calls are bounded: 8 MiB output, 30 s per call, at most 1000 descendant commits and 32 parents inspected for review freshness | [`src/lifecycle-validation-limits.json`](../src/lifecycle-validation-limits.json) | +| GitHub check runs for `ship-status` / `ship --wait` | One page of 100 runs, 10 s request deadline, `--wait` polls every 10 s up to 900 s by default; without `GITHUB_TOKEN` it falls back to local guidance | `fetch_commit_check_summary`, `wait_for_head_check_runs` | +| `lesson-bundle.md` cannot be written | Archival still succeeds | invariant 34 | + +Every change summary ends with a `Handoff:` verdict (`safe`, `conditional`, `not yet`) computed by +the pure function `classify_handoff`, so an agent knows whether clearing its context now would +lose anything the lifecycle has not recorded +([`site/src/content/docs/workflow.md`](../site/src/content/docs/workflow.md#clearing-context-between-steps)). + +## 9. Decisions + +| Decision | Summary | Record | +|---|---|---| +| Deterministic core | No embedded inference, providers or keys; agents use the CLI | [`SCOPE.md`](../SCOPE.md), [`specs/ai/retired.md`](../specs/ai/retired.md) | +| `check` is separate from the lifecycle | The drift check never reads changes or archives; exit status comes only from spec validation | `REQ-cmd-check-004`, [`specs/cmd_check/`](../specs/cmd_check/) | +| Strict by default in 6.0 | `enforcement` default moved from `warn` to `strict` | [`MIGRATION.md`](../MIGRATION.md) | +| One scope approval, one scoped review | Workflow v2 replaced v1's two approvals; the reviewer may be the approver; GitHub stays the merge authority | [`specs/change/change.spec.md`](../specs/change/change.spec.md) change log, [`site/src/content/docs/workflow.md`](../site/src/content/docs/workflow.md) | +| Same-PR finalization | New changes archive on the PR before merge (v1 was merge-then-archive) | [`MIGRATION.md`](../MIGRATION.md), [`AGENTS.md`](../AGENTS.md) | +| `change check` does not run tests | It is scoped spec-to-code sync; CI owns `cargo test` | contract 5 in [`specs/change/change.spec.md`](../specs/change/change.spec.md) | +| Slug IDs instead of allocated numbers | Avoids two clones minting the same `CHG-NNNN` | [`AGENTS.md`](../AGENTS.md) | +| Three 6.0 tolerance decisions | Scope to authored specs, exempt no-op changes, exempt metadata-only corrections | [`docs/6-0-tolerance-decisions.md`](6-0-tolerance-decisions.md) | +| CI vs Trust split | CI owns tests; Trust proves the PR binary, risk and provenance without re-running tests | [`docs/ci-confidence.md`](ci-confidence.md) | +| Release authority | Immutable RC and final tag rulesets; no release App; promotion uses `GITHUB_TOKEN` | [`docs/ci-confidence.md`](ci-confidence.md), [`docs/RELEASING.md`](RELEASING.md) | +| Standalone site retired | Docs moved to the CorvidLabs hub; Pages keeps redirects and badges | [`site/src/lib/hub.ts`](../site/src/lib/hub.ts) | +| Windows not a 6.0 target | Neither built nor published (#735) | [`CONTRIBUTING.md`](../CONTRIBUTING.md), [`docs/ci-confidence.md`](ci-confidence.md) | + +Module-level decisions live beside each module in `specs//context.md`. Product intent, +as plain criteria, lives in [`INTENT.md`](../INTENT.md) and [`hi/`](../hi/). + +## 10. Glossary + +| Term | Meaning | +|---|---| +| Spec | A `*.spec.md` module contract with frontmatter and required sections | +| Companion | `requirements.md`, `context.md`, `tasks.md`, `testing.md`, `design.md` beside a spec | +| Canonical spec | The living spec under `specs/`, as opposed to a change's proposed delta | +| Semantic delta | `deltas/.md` in a change: `## Added` / `## Modified` / `## Removed` items applied to the canonical spec on `change check` | +| Materialize | Apply approved deltas to canonical specs, bump `version`, add a Change Log row | +| Scope digest | Hash of a change's stable intent and boundary; what a person approves | +| Execution digest | Hash of the change's full definition and artifacts; moves with implementation | +| Workspace digest | Hash of every project input file; binds evidence to one tree | +| Scoped review | The single human implementation review recorded by `change review` | +| Finalize | Accept and archive a workflow-v2 change in the same PR | +| Workflow v1 / v2 | The pre-6.0 two-approval, merge-then-archive lifecycle / the current one | +| Handoff | Whether an agent may clear its context at this point: `safe`, `conditional`, `not yet` | +| Tip class | How `ship-status` and CI classify the last commit: product, review-only, archive-only | +| Trust | The CorvidLabs trust gate (contract, Augur risk, Attest provenance) run by `fledge trust verify` and `trust.yml` | +| hi | Human Intent: plain criteria with permanent IDs in `hi/`, checked by `hi check` | + +## Related documents + +| Document | What it covers | +|---|---| +| [`README.md`](../README.md) | Install, quick start, the one-minute tour | +| [`AGENTS.md`](../AGENTS.md) | Rules for agents and contributors: shipping path, ordering rules, pre-push gate | +| [`CONTRIBUTING.md`](../CONTRIBUTING.md) | Development setup and PR process | +| [`SCOPE.md`](../SCOPE.md) | What is in and out of scope | +| [`MIGRATION.md`](../MIGRATION.md) | Moving from 4.x / 5.x to 6.0 | +| [`docs/ADOPTING.md`](ADOPTING.md) | Adopting SpecSync in another repository, written to paste into an agent session | +| [`docs/ci-confidence.md`](ci-confidence.md) | CI vs Trust ownership, confidence tiers, tag authority | +| [`docs/RELEASING.md`](RELEASING.md) | Release operator runbook | +| [`site/src/content/docs/`](../site/src/content/docs/) | User documentation published on the hub: CLI, configuration, spec format, workflow, architecture overview, integrations | +| [`docs/6-0-release-review.md`](6-0-release-review.md), [`docs/6-0-confidence-report.md`](6-0-confidence-report.md), [`docs/6-0-confidence-checklist.md`](6-0-confidence-checklist.md), [`docs/6-0-verification-444f7b91.md`](6-0-verification-444f7b91.md) | 6.0 release review and verification evidence (historical) | +| [`docs/6-0-findings.md`](6-0-findings.md), [`docs/6-0-tolerance-decisions.md`](6-0-tolerance-decisions.md), [`docs/GOAL-6-fixes.md`](GOAL-6-fixes.md), [`docs/GOAL-6-taggable.md`](GOAL-6-taggable.md) | Problems found on the way to 6.0 and how they were decided | +| [`docs/6-0-overnight-brief.md`](6-0-overnight-brief.md), [`docs/6-0-overnight-prompt.md`](6-0-overnight-prompt.md), [`docs/6-0-overnight-journal.md`](6-0-overnight-journal.md), [`docs/SESSION-SUMMARY-6-0.md`](SESSION-SUMMARY-6-0.md) | Records of the 6.0 proving sessions (historical) | +| [`docs/superpowers/`](superpowers/) | Plans and design for the retired marketing site (historical) | +| [`examples/`](../examples/) | Runnable lifecycle examples that create a throwaway repo and drive the real CLI |