-
Notifications
You must be signed in to change notification settings - Fork 0
Concepts
The core ideas behind SpecGit and how they fit together. For practical usage see Getting Started; for commands see the CLI Reference.
evidence not assertion — acceptance is derived from git, PR, and CI facts
fail-closed not hopeful — anything unverifiable is "unknown", never "accepted"
git is the contract — the branch, the PR, and the checks are the deliverable
SpecGit does not manage plans, specs, or task lists. By the time SpecGit looks at your work, the work already exists as a branch, issues, a pull request, and CI checks. SpecGit's only job is to verify — from real evidence — that the delivery is complete. A checklist that says "done" is not evidence; a green required check on the PR head commit is.
Every delivery is one aggregate, declared in a single record file (.specgit.yaml) plus the facts it points at:
| Part | What it is | Where it lives |
|---|---|---|
| Delivery | A kebab-case id for the work (add-login-flow) |
record |
| Execution context | Where the work happens: a branch, or a linked worktree plus its branch | live git |
| Issues | 1..N forge issue numbers the delivery closes | record → forge |
| Pull request | Exactly one PR (or MR) that merges the delivery | record → forge |
| Required checks | CI check names that must pass at the PR head | policy → forge |
The record declares; git and the forge substantiate. Acceptance means every part was verified against the real thing.
One PR may close N issues. issues is a list: a single PR can close any number of issues (Closes #123, Fixes #124, …). Every bound issue must be closed by the PR's closing references — acceptance fails with the missing numbers listed if even one is absent.
One issue = one independently verifiable WHY. If a deliverable cannot be verified on its own evidence, split it before binding. One delivery binds N issues to one PR that closes them all — but each issue must stand alone as a verifiable reason for the work.
The execution context is resolved from live git at evaluation time — never passed as a flag, never taken from the record alone.
-
kind: branch— the record names a branch. Any checkout currently on that branch satisfies it. -
kind: worktree— the record names a portable label plus a branch. The current checkout must be a linked worktree whose label resolves (viagit worktree list) to that branch.
If HEAD is detached, the live branch disagrees with the record, or the worktree label does not resolve, context gates fail with precise codes. There is no "trust me" mode.
repo root/
├── spec_git/
│ ├── policy.yaml # required_checks — shared by every delivery
│ └── providers.yaml # optional platform declaration (declared GitLab host)
└── .specgit.yaml # this delivery's binding — committed on the delivery branch
| Tier | Files | Regeneration / lifecycle |
|---|---|---|
| Authoritative |
spec_git/policy.yaml, .specgit.yaml, spec_git/providers.yaml
|
Hand-owned; versioned like code |
| Derived harness |
.github/workflows/specgit-accept.yml, managed AGENTS.md/CLAUDE.md block |
Regenerate with specgit init --force; never hand-edit |
| Local integration | Guard hooks, setup entry points |
Agent conveniences, merged non-destructively, never acceptance inputs |
-
Policy (
spec_git/policy.yaml): the project-level contract — the list of CI check names every delivery must pass. Created byspecgit init. -
Record (
.specgit.yaml): the delivery-level binding. Written byspecgit issue(script alias:bind), removed byspecgit unbind. Unknown keys are preserved on rewrite.
No other state exists. No artifact directories, no caches, no global stores. Root discovery is git rev-parse --show-toplevel — SpecGit runs only inside a git repository, at its root.
Delivery states — unbound, draft, bound, accepted, rejected, unknown — are computed on every invocation. Nothing is persisted, so a state can never drift from reality.
Evaluation runs eleven gates in order, from cheapest local facts to live forge evidence:
- Record present and valid
- Policy present and valid
- Completeness (≥1 issue, exactly 1 PR)
- Context matches live git
- Origin resolves to a forge repository
- Provider reachable and authenticated
- Issues exist (and are issues, not PRs)
- Sequence — when
ordered_issuesis on, no open issue with a smaller number precedes this delivery - PR exists, open or merged, head matches the context branch, same repository
- PR body closes every bound issue
- Every required check is green at the PR head commit
Gates short-circuit across gates; within a gate, every failure is collected and reported.
Every piece of evidence is either gathered successfully or it is a failure — there is no silent skip.
- All evidence gathered, at least one gate failed → rejected (exit 1) with complete evidence.
- Evidence could not be gathered (missing/invalid record or policy, no provider CLI, no auth, transport error, not a git repo) → unknown (exit 3).
- Every gate passed with evidence → accepted (exit 0). Nothing else.
Consequences worth knowing:
-
specgit finish(aliasaccept) offline can returnunknown, neveraccepted. Acceptance requires the provider by design. - A dirty working tree is reported as evidence but never fails acceptance — acceptance is about the PR head, not your local edits.
- If your local HEAD differs from the PR head, you get an informational
local_head_stalewarning; checks are evaluated at the PR head commit, because that is what will be merged.
- Not a spec framework: there are no proposal, spec, design, or task artifacts.
- Not a git wrapper: it reads git facts; it never rewrites history or touches your branches.
- Not an issue tracker or CI system: it verifies against the forge and your existing CI.
- Not a plugin platform:
initcreates only the policy and harness.
Chinese version: Concepts-zh