@rungs/cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +196 -0
- package/modules/README.md +116 -0
- package/modules/adr/files/{{path}}/README.md +52 -0
- package/modules/adr/files/{{path}}/TEMPLATE.md +55 -0
- package/modules/adr/fragments/AGENTS.md +8 -0
- package/modules/adr/gates/adr.toml +65 -0
- package/modules/adr/module.toml +67 -0
- package/modules/audit/files/{{criteria_path}} +43 -0
- package/modules/audit/fragments/AGENTS.md +8 -0
- package/modules/audit/gates/audit.toml +48 -0
- package/modules/audit/module.toml +72 -0
- package/modules/audit/skills/assess/SKILL.md +62 -0
- package/modules/backlog/files/docs/{{root}}/BACKLOG.md +52 -0
- package/modules/backlog/files/docs/{{root}}/README.md +110 -0
- package/modules/backlog/files/docs/{{root}}/TEMPLATE.md +56 -0
- package/modules/backlog/files/docs/{{root}}/archive/README.md +13 -0
- package/modules/backlog/files/docs/{{root}}/items/README.md +13 -0
- package/modules/backlog/fragments/AGENTS.md +9 -0
- package/modules/backlog/gates/ids.toml +106 -0
- package/modules/backlog/module.toml +150 -0
- package/modules/backlog/rules/work-items.md +44 -0
- package/modules/backlog/skills/backlog-summary/SKILL.md +65 -0
- package/modules/backlog/skills/work-item/SKILL.md +96 -0
- package/modules/ci/files/{{workflow_path}} +43 -0
- package/modules/ci/gates/ci.toml +44 -0
- package/modules/ci/module.toml +74 -0
- package/modules/concurrency/files/docs/concurrent-sessions.md +87 -0
- package/modules/concurrency/fragments/AGENTS.md +10 -0
- package/modules/concurrency/fragments/gitattributes +15 -0
- package/modules/concurrency/gates/concurrency.toml +54 -0
- package/modules/concurrency/module.toml +113 -0
- package/modules/design-sync/fragments/AGENTS.md +8 -0
- package/modules/design-sync/gates/design.toml +70 -0
- package/modules/design-sync/module.toml +82 -0
- package/modules/design-sync/skills/design-align/SKILL.md +51 -0
- package/modules/design-sync/skills/design-pull/SKILL.md +50 -0
- package/modules/doc-authority/files/{{registry_path}} +59 -0
- package/modules/doc-authority/files/{{rules_path}} +51 -0
- package/modules/doc-authority/fragments/AGENTS.md +9 -0
- package/modules/doc-authority/gates/authority.toml +127 -0
- package/modules/doc-authority/module.toml +111 -0
- package/modules/findings/files/docs/{{backlog.root}}/FINDINGS.md +56 -0
- package/modules/findings/fragments/AGENTS.md +7 -0
- package/modules/findings/gates/findings.toml +68 -0
- package/modules/findings/module.toml +100 -0
- package/modules/findings/skills/record-finding/SKILL.md +63 -0
- package/modules/gates/files/.ai/gates.toml +49 -0
- package/modules/gates/fragments/AGENTS.md +8 -0
- package/modules/gates/fragments/gitignore +4 -0
- package/modules/gates/gates/structural.toml +127 -0
- package/modules/gates/module.toml +143 -0
- package/modules/gates/skills/harden-rule/SKILL.md +82 -0
- package/modules/instructions/files/.ai/rules/README.md +53 -0
- package/modules/instructions/files/AGENTS.md +104 -0
- package/modules/instructions/files/CLAUDE.md +11 -0
- package/modules/instructions/gates/core.toml +114 -0
- package/modules/instructions/module.toml +127 -0
- package/modules/release/fragments/AGENTS.md +8 -0
- package/modules/release/gates/release.toml +85 -0
- package/modules/release/module.toml +97 -0
- package/modules/release/skills/cut-release/SKILL.md +81 -0
- package/modules/session/files/{{archive}}/README.md +28 -0
- package/modules/session/files/{{path}} +56 -0
- package/modules/session/fragments/AGENTS.md +6 -0
- package/modules/session/gates/session.toml +63 -0
- package/modules/session/module.toml +72 -0
- package/modules/session/skills/close-session/SKILL.md +59 -0
- package/modules/skills/fragments/AGENTS.md +8 -0
- package/modules/skills/gates/skills.toml +94 -0
- package/modules/skills/module.toml +89 -0
- package/modules/skills/rules/skill-authoring.md +71 -0
- package/modules/specs/files/{{path}}/README.md +63 -0
- package/modules/specs/files/{{path}}/TEMPLATE.md +62 -0
- package/modules/specs/fragments/AGENTS.md +8 -0
- package/modules/specs/gates/specs.toml +119 -0
- package/modules/specs/module.toml +107 -0
- package/modules/workflows/fragments/AGENTS.md +8 -0
- package/modules/workflows/gates/workflows.toml +65 -0
- package/modules/workflows/module.toml +75 -0
- package/modules/workflows/rules/planning-tiers.md +46 -0
- package/modules/workflows/rules/reuse-decision.md +50 -0
- package/modules/workflows/skills/decompose/SKILL.md +75 -0
- package/package.json +42 -0
- package/src/add.ts +331 -0
- package/src/check.ts +216 -0
- package/src/cli.ts +411 -0
- package/src/detect.ts +246 -0
- package/src/engines.ts +260 -0
- package/src/engines2.ts +297 -0
- package/src/engines3.ts +194 -0
- package/src/glob.ts +101 -0
- package/src/lifecycle.ts +268 -0
- package/src/manifest.ts +108 -0
- package/src/render.ts +222 -0
- package/src/substitute.ts +78 -0
- package/src/types.ts +110 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Gate table for the `audit` module.
|
|
2
|
+
|
|
3
|
+
# ── audit-output-is-rows ──────────────────────────────────────────────────────
|
|
4
|
+
#
|
|
5
|
+
# Refuses a per-subject audit document tree from re-forming. Deliberately does not fire on a small
|
|
6
|
+
# number: one or two written-up assessments are a legitimate thing to have. It fires on the shape
|
|
7
|
+
# that scales — a document per subject, accumulating.
|
|
8
|
+
|
|
9
|
+
[file_population]
|
|
10
|
+
scan = ["docs/**/audits/**/*.md", "docs/**/*-audit-*.md", "docs/**/*-readiness-*.md"]
|
|
11
|
+
exclude = ["docs/**/archive/**"]
|
|
12
|
+
warn_at = 6
|
|
13
|
+
fail_at = 15
|
|
14
|
+
exempt_marker = "audit-doc-ok:" # ignored unless it states a reason
|
|
15
|
+
message = """
|
|
16
|
+
{count} per-subject audit documents. This is the shape that reached 268 files in one repo, where
|
|
17
|
+
there is still no way to say which findings are open — the prompt was good enough to run 268
|
|
18
|
+
times, the output form was the defect. Audit output belongs in the findings register as rows.
|
|
19
|
+
Existing documents are history and can stay; mark them with `<!-- audit-doc-ok: <reason> -->` or
|
|
20
|
+
move them to an archive.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
# ── self-tests ────────────────────────────────────────────────────────────────
|
|
24
|
+
|
|
25
|
+
[[self_test]]
|
|
26
|
+
gate = "audit-output-is-rows"
|
|
27
|
+
expect = "pass"
|
|
28
|
+
fixture = { matching_files = 3 }
|
|
29
|
+
|
|
30
|
+
[[self_test]]
|
|
31
|
+
gate = "audit-output-is-rows"
|
|
32
|
+
expect = "fail"
|
|
33
|
+
fixture = { matching_files = 40 }
|
|
34
|
+
|
|
35
|
+
[[self_test]]
|
|
36
|
+
gate = "audit-output-is-rows"
|
|
37
|
+
expect = "pass"
|
|
38
|
+
fixture = { matching_files = 40, location = "docs/archive/audits/" }
|
|
39
|
+
|
|
40
|
+
[[self_test]]
|
|
41
|
+
gate = "audit-output-is-rows"
|
|
42
|
+
expect = "pass"
|
|
43
|
+
fixture = { matching_files = 40, exempt = "audit-doc-ok: regulatory evidence, retained per policy" }
|
|
44
|
+
|
|
45
|
+
[[self_test]]
|
|
46
|
+
gate = "audit-output-is-rows"
|
|
47
|
+
expect = "fail"
|
|
48
|
+
fixture = { matching_files = 40, exempt = "audit-doc-ok:" }
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
[module]
|
|
2
|
+
name = "audit"
|
|
3
|
+
version = "1.0.0"
|
|
4
|
+
rung = 2
|
|
5
|
+
summary = "A repeatable assessment whose output is rows in the findings register, never a document per subject."
|
|
6
|
+
|
|
7
|
+
[requires]
|
|
8
|
+
modules = ["findings"]
|
|
9
|
+
|
|
10
|
+
[conflicts]
|
|
11
|
+
modules = []
|
|
12
|
+
|
|
13
|
+
# The dependency is the whole point, and it is declared because a repo violated
|
|
14
|
+
# it: an audit with nowhere to land produces unactioned prose.
|
|
15
|
+
|
|
16
|
+
[params.subject]
|
|
17
|
+
description = "What gets audited, one at a time — package, service, surface, endpoint…"
|
|
18
|
+
default = "package"
|
|
19
|
+
|
|
20
|
+
[params.criteria_path]
|
|
21
|
+
description = """
|
|
22
|
+
The criteria list, as a file the repo edits rather than a parameter. A list substituted into a
|
|
23
|
+
skill body is a list nobody maintains, and criteria are the part that changes most.
|
|
24
|
+
"""
|
|
25
|
+
default = "docs/audit-criteria.md"
|
|
26
|
+
|
|
27
|
+
[[gates]]
|
|
28
|
+
id = "audit-output-is-rows"
|
|
29
|
+
kind = "declared"
|
|
30
|
+
engine = "file-population"
|
|
31
|
+
table = "gates/audit.toml"
|
|
32
|
+
tier = "fast"
|
|
33
|
+
why = """
|
|
34
|
+
hexguard produced 268 per-subject audit documents and there is still no way to say which findings
|
|
35
|
+
are open. The audit prompt was good enough to run 268 times; the output form was the defect. This
|
|
36
|
+
gate refuses the document tree from re-forming.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
[skills.assess]
|
|
40
|
+
# No extensions. Read-only, and useful for a model to reach for when it notices
|
|
41
|
+
# a subject has never been assessed.
|
|
42
|
+
|
|
43
|
+
[detect]
|
|
44
|
+
# A dedicated audits directory only. The first draft also matched
|
|
45
|
+
# `docs/**/*-audit-*.md` and `*-readiness-*` anywhere, and on a real repo it hit
|
|
46
|
+
# `WI-108-lol-mechanics-fidelity-audit-epic.md` and a launch-readiness epic —
|
|
47
|
+
# work items, not audit documents. Under-detection is the correct bias: a repo
|
|
48
|
+
# with scattered audit files gets a second directory it can see, while a false
|
|
49
|
+
# positive would have the CLI believe it owns somebody's backlog.
|
|
50
|
+
paths = ["docs/**/audits/**/*.md", "docs/**/audit/**/*.md"]
|
|
51
|
+
markers = ["rungs:begin audit"]
|
|
52
|
+
|
|
53
|
+
[[detect.adopt_as]]
|
|
54
|
+
kind = "audit-documents"
|
|
55
|
+
note = """
|
|
56
|
+
An existing per-subject audit tree is the failure this module exists to prevent, already present.
|
|
57
|
+
Adopt means: leave the documents alone as history, register the criteria, and report how many
|
|
58
|
+
subjects have documents with no corresponding open finding — which is the number that says how much
|
|
59
|
+
of that work is currently actionable. Never bulk-convert; that would fabricate findings nobody
|
|
60
|
+
verified.
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
[provenance]
|
|
64
|
+
sources = ["hexguard", "axiom-mesh"]
|
|
65
|
+
patterns = ["audit-to-register", "lifecycle-verbs"]
|
|
66
|
+
incident = """
|
|
67
|
+
hexguard ran /assess-package-readiness across its catalogue twice, producing 268 dated documents
|
|
68
|
+
plus a global audit, a triage doc and a methodology doc — and no aggregate state. To know the
|
|
69
|
+
current condition of the catalogue you read 268 files. There was also no work-item object for a
|
|
70
|
+
finding to become, so the assessments could not close. axiom-mesh got this right by logging every
|
|
71
|
+
confirmed defect as a stable AD-### row before any remediation was deferred.
|
|
72
|
+
"""
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: assess
|
|
3
|
+
description: >-
|
|
4
|
+
Assess one subject — a package, service, surface or endpoint — against this repo's audit criteria,
|
|
5
|
+
and record each failure as a row in the findings register. Use when asked to "audit", "assess",
|
|
6
|
+
"review the readiness of", "check whether X is production-ready", or before promoting, releasing
|
|
7
|
+
or publicising something. Also use to work through a backlog of unassessed subjects, one at a
|
|
8
|
+
time. It records observations; it does not fix them and does not open work items — that is
|
|
9
|
+
/record-finding's triage and /work-item.
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Assess a subject
|
|
13
|
+
|
|
14
|
+
Read the criteria file first. It is repo-owned and changes; the version in your memory does not
|
|
15
|
+
count.
|
|
16
|
+
|
|
17
|
+
## 1. One subject at a time
|
|
18
|
+
|
|
19
|
+
Assessing several at once produces a comparison, which is a different and less useful artifact.
|
|
20
|
+
Name the subject explicitly before starting, and its boundary — what is in it and what is merely
|
|
21
|
+
adjacent.
|
|
22
|
+
|
|
23
|
+
## 2. Check each criterion against the artifact
|
|
24
|
+
|
|
25
|
+
**Read the thing, not the documentation about the thing.** A README claiming the errors are typed
|
|
26
|
+
is a claim; the code is the fact. This is the failure that makes an audit worthless: an assessment
|
|
27
|
+
built from the subject's own self-description confirms it.
|
|
28
|
+
|
|
29
|
+
For each criterion, reach one of three verdicts:
|
|
30
|
+
|
|
31
|
+
- **passes** — record nothing
|
|
32
|
+
- **fails** — one row in the findings register
|
|
33
|
+
- **cannot tell** — say so, and say what would settle it. A "cannot tell" recorded honestly is
|
|
34
|
+
more useful than a pass you did not earn
|
|
35
|
+
|
|
36
|
+
## 3. Write rows, not a document
|
|
37
|
+
|
|
38
|
+
Each failure is one row: severity from the criteria table, what specifically fails, **evidence**
|
|
39
|
+
(a path, a symbol, a command), when to act, how to fix.
|
|
40
|
+
|
|
41
|
+
**Do not produce an audit document.** One repo in the corpus generated 268 of them and still cannot
|
|
42
|
+
say which findings are open — the prompt was fine, the output form was the defect. `rungs check`
|
|
43
|
+
refuses the document tree from re-forming.
|
|
44
|
+
|
|
45
|
+
## 4. Report the run itself
|
|
46
|
+
|
|
47
|
+
Say how many criteria were checked, how many subjects, and **how many files you actually read**.
|
|
48
|
+
|
|
49
|
+
An audit that found nothing and an audit that read nothing produce the same output, and only one of
|
|
50
|
+
them is good news. A report that cannot fail loudly is not a measurement.
|
|
51
|
+
|
|
52
|
+
## 5. Stop
|
|
53
|
+
|
|
54
|
+
Do not fix what you found. Do not open work items. Do not rank. Those are decisions made against
|
|
55
|
+
the register later, deliberately, by someone weighing them against everything else — which is the
|
|
56
|
+
whole reason the register exists.
|
|
57
|
+
|
|
58
|
+
## What this cannot tell you
|
|
59
|
+
|
|
60
|
+
- **That a subject is good.** It passed the criteria that were written down.
|
|
61
|
+
- **That the criteria are right.** They are this repo's opinion, in a file you can edit — and a
|
|
62
|
+
criterion nothing has ever failed is worth suspecting.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
**Authoritative for:** which items exist and what state each is in, and the next free id.
|
|
2
|
+
**Not authoritative for:** why any item exists or how to do it — that is the item file.
|
|
3
|
+
|
|
4
|
+
# Backlog
|
|
5
|
+
|
|
6
|
+
The board. One row per live work item, grouped by status. Items live in
|
|
7
|
+
[`items/`](items/); finished work moves to [`archive/`](archive/).
|
|
8
|
+
|
|
9
|
+
<!-- NEXT-ID: {{id_prefix}}-001 -->
|
|
10
|
+
<!-- Claim from this marker and bump it on your own branch. `rungs check` refuses a duplicate. -->
|
|
11
|
+
|
|
12
|
+
## In progress
|
|
13
|
+
|
|
14
|
+
| Id | Title | Type | Branch |
|
|
15
|
+
| --- | --- | --- | --- |
|
|
16
|
+
| — | *nothing in flight* | | |
|
|
17
|
+
|
|
18
|
+
## Review
|
|
19
|
+
|
|
20
|
+
| Id | Title | Type | Branch |
|
|
21
|
+
| --- | --- | --- | --- |
|
|
22
|
+
| — | | | |
|
|
23
|
+
|
|
24
|
+
## Planned
|
|
25
|
+
|
|
26
|
+
| Id | Title | Type |
|
|
27
|
+
| --- | --- | --- |
|
|
28
|
+
| — | | |
|
|
29
|
+
|
|
30
|
+
## Accepted
|
|
31
|
+
|
|
32
|
+
| Id | Title | Type |
|
|
33
|
+
| --- | --- | --- |
|
|
34
|
+
| — | | |
|
|
35
|
+
|
|
36
|
+
## Proposed
|
|
37
|
+
|
|
38
|
+
| Id | Title | Type |
|
|
39
|
+
| --- | --- | --- |
|
|
40
|
+
| — | | |
|
|
41
|
+
|
|
42
|
+
## Deferred
|
|
43
|
+
|
|
44
|
+
| Id | Title | Revisit when |
|
|
45
|
+
| --- | --- | --- |
|
|
46
|
+
| — | | |
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
Rejected and completed items keep their files — see [`archive/`](archive/). A rejection is part of
|
|
51
|
+
the record, with its reason; deleting one loses the answer to "why not?" and invites the same
|
|
52
|
+
proposal again.
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
**Authoritative for:** how work is proposed, decided, planned, executed and reviewed, and the meaning of each status.
|
|
2
|
+
**Not authoritative for:** what any individual item contains, and what the code should look like — see the engineering guides.
|
|
3
|
+
|
|
4
|
+
# Delivery methodology & backlog
|
|
5
|
+
|
|
6
|
+
How work is proposed, decided, planned, executed, and reviewed here — and where it is tracked.
|
|
7
|
+
**Mandatory for all contributors, including AI agents.**
|
|
8
|
+
|
|
9
|
+
This tracks **units of work and their state**. It does not describe behaviour (that is a spec) or
|
|
10
|
+
record decisions (that is an ADR).
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## 1. Lifecycle
|
|
15
|
+
|
|
16
|
+
Every unit of work is a **work item** with a stable id (`{{id_prefix}}-###`) and a status:
|
|
17
|
+
|
|
18
|
+
```mermaid
|
|
19
|
+
stateDiagram-v2
|
|
20
|
+
[*] --> proposed
|
|
21
|
+
proposed --> accepted: decision
|
|
22
|
+
proposed --> rejected: decision
|
|
23
|
+
accepted --> planned: requirements + impacts
|
|
24
|
+
accepted --> deferred: not now
|
|
25
|
+
deferred --> planned: revisited
|
|
26
|
+
planned --> in_progress: branch + build
|
|
27
|
+
in_progress --> review: executed + tested
|
|
28
|
+
review --> planned: changes requested
|
|
29
|
+
review --> done: approved + merged
|
|
30
|
+
done --> [*]
|
|
31
|
+
rejected --> [*]
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
| Status | Meaning |
|
|
35
|
+
| --- | --- |
|
|
36
|
+
| `proposed` | An idea captured with **rationale**; awaiting a decision |
|
|
37
|
+
| `accepted` | Approved to pursue; not yet planned in detail |
|
|
38
|
+
| `rejected` | Declined; kept for the record **with the reason** |
|
|
39
|
+
| `deferred` | Accepted in principle but intentionally **not now**, with a revisit trigger |
|
|
40
|
+
| `planned` | Requirements, impacts, approach and acceptance criteria written; ready to build |
|
|
41
|
+
| `in_progress` | Being executed on its branch — **the "current" work** |
|
|
42
|
+
| `review` | Executed and tested; under review against the acceptance criteria |
|
|
43
|
+
| `done` | Approved, merged to `{{integration_branch}}`, acceptance criteria met |
|
|
44
|
+
|
|
45
|
+
## 2. Workflow
|
|
46
|
+
|
|
47
|
+
1. **Propose** — open an item (`proposed`) stating the problem and **why it matters**. Anyone may
|
|
48
|
+
propose.
|
|
49
|
+
2. **Decide** — accept, reject, or defer. Record the decision **and its reason** in the item. A
|
|
50
|
+
rejection is final unless re-proposed with new information.
|
|
51
|
+
3. **Plan** — for an `accepted` item write **requirements**, **impacts**, **approach** (with
|
|
52
|
+
options and trade-offs where relevant), **acceptance criteria**, and **out of scope**.
|
|
53
|
+
Significant design choices get an ADR. Status → `planned`.
|
|
54
|
+
4. **Execute + test** — branch, build, test. Status → `in_progress`, then `review`.
|
|
55
|
+
5. **Review** — verify against the acceptance criteria, reconcile any specs or ADRs, merge.
|
|
56
|
+
Status → `done`. Changes requested → back to `planned` / `in_progress`.
|
|
57
|
+
|
|
58
|
+
**Planning artifacts ride `{{integration_branch}}`.** Proposals, decisions and plans precede
|
|
59
|
+
execution and do not need a branch; only *code* does.
|
|
60
|
+
|
|
61
|
+
## 3. Ids
|
|
62
|
+
|
|
63
|
+
Ids are permanent and never reused, including after archiving. `BACKLOG.md` carries the
|
|
64
|
+
`NEXT-ID` marker; claim from it and bump it **on your own branch**.
|
|
65
|
+
|
|
66
|
+
An id spent in a branch name or a commit subject without bumping the marker is invisible to the
|
|
67
|
+
check, which reads the working tree. Bump first.
|
|
68
|
+
|
|
69
|
+
## 4. Branches
|
|
70
|
+
|
|
71
|
+
`{{branch_prefix}}/{{id_prefix}}-###-slug`, cut from `{{integration_branch}}` and merged back on
|
|
72
|
+
review. Use `chore/`, `docs/` and `spike/` for non-feature work. **Delete the branch on merge.**
|
|
73
|
+
|
|
74
|
+
## 5. Definition of done
|
|
75
|
+
|
|
76
|
+
- Acceptance criteria met and demonstrated
|
|
77
|
+
- Tests written and passing; existing tests not weakened
|
|
78
|
+
- Docs updated where public behaviour or developer workflow changed
|
|
79
|
+
- Status is `done` **and the branch is merged** — the two must agree
|
|
80
|
+
|
|
81
|
+
## 6. Scope discipline
|
|
82
|
+
|
|
83
|
+
**Never scope-creep an item.** Work discovered mid-flight becomes a *new* item, or a finding if it
|
|
84
|
+
is only an observation. An item that grew a second purpose should have been two.
|
|
85
|
+
|
|
86
|
+
## 7. Epics and sub-items
|
|
87
|
+
|
|
88
|
+
An epic (`type: epic`) has no branch of its own. Membership is a **two-way link**: the epic lists
|
|
89
|
+
`children`, each child names its `epic`. Both directions or neither.
|
|
90
|
+
|
|
91
|
+
## 8. Archiving
|
|
92
|
+
|
|
93
|
+
`items/` should hold only work that can still change. Move finished items to `archive/` with
|
|
94
|
+
`rungs backlog archive` — it recomputes every link repo-wide, so archived ids still resolve and
|
|
95
|
+
stay permanently spent.
|
|
96
|
+
|
|
97
|
+
**Never edit an archived item.** If archived work turns out to be wrong, that is a *new* item.
|
|
98
|
+
|
|
99
|
+
## 9. What keeps this true
|
|
100
|
+
|
|
101
|
+
Three gates, run by `rungs check`:
|
|
102
|
+
|
|
103
|
+
| Gate | Refuses |
|
|
104
|
+
| --- | --- |
|
|
105
|
+
| `backlog-ids` | A duplicate id, or a citation to an id that does not exist |
|
|
106
|
+
| `backlog-stale-blocker` | A document claiming it is blocked on work that has finished — it reads as a live constraint and the next session plans around a wall that came down months ago |
|
|
107
|
+
| `backlog-merged-status` | An item whose branch is merged but whose status is still pre-review. One-directional: a merged branch with a pre-review status is always wrong; an unmerged one is not. A genuinely phased item says why with `branch-merged-ok: <reason>` |
|
|
108
|
+
|
|
109
|
+
The `status` field is typed by a person and "did it land" is a fact in git. When they disagree the
|
|
110
|
+
typed field is what every board and plan reads, so the gate exists to keep it honest.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: {{id_prefix}}-000
|
|
3
|
+
title: Short imperative title
|
|
4
|
+
type: feature # feature | chore | docs | spike | epic
|
|
5
|
+
status: proposed # proposed | accepted | rejected | deferred | planned | in_progress | review | done
|
|
6
|
+
branch: # {{branch_prefix}}/{{id_prefix}}-000-slug — set when execution starts; epics have none
|
|
7
|
+
created: YYYY-MM-DD
|
|
8
|
+
updated: YYYY-MM-DD
|
|
9
|
+
related: [] # {{id_prefix}}-###, ADR-####, spec ids
|
|
10
|
+
epic: # parent {{id_prefix}}-### — sub-items only
|
|
11
|
+
children: [] # child {{id_prefix}}-### — epics only
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Proposal (rationale)
|
|
15
|
+
|
|
16
|
+
The problem, and why it matters. Expected value, or the cost of not doing it.
|
|
17
|
+
|
|
18
|
+
## Decision
|
|
19
|
+
|
|
20
|
+
`accepted` | `rejected` | `deferred` — date, who, and the reason. Empty until decided.
|
|
21
|
+
|
|
22
|
+
## Plan
|
|
23
|
+
|
|
24
|
+
> Filled once `accepted`. Significant design choices get an ADR — link it.
|
|
25
|
+
|
|
26
|
+
### Requirements
|
|
27
|
+
|
|
28
|
+
- Explicit and testable.
|
|
29
|
+
|
|
30
|
+
### Impacts
|
|
31
|
+
|
|
32
|
+
- Specs and ADRs touched, code areas, data and contracts, migration or ops, risks.
|
|
33
|
+
|
|
34
|
+
### Approach
|
|
35
|
+
|
|
36
|
+
- The chosen approach; options considered and their trade-offs; decisions still open.
|
|
37
|
+
|
|
38
|
+
### Acceptance criteria / tests
|
|
39
|
+
|
|
40
|
+
- How we will know it is done and correct.
|
|
41
|
+
|
|
42
|
+
### Out of scope
|
|
43
|
+
|
|
44
|
+
- **Required — answer, do not delete.** What this item explicitly does *not* cover, plus the
|
|
45
|
+
follow-up item id if there is one, or an explicit `nothing deferred`.
|
|
46
|
+
>
|
|
47
|
+
> A blank line here is an unfinished plan, exactly like blank acceptance criteria. This section is
|
|
48
|
+
> the one that prevents an item quietly growing a second purpose.
|
|
49
|
+
|
|
50
|
+
## Execution
|
|
51
|
+
|
|
52
|
+
Branch, key commits, notes, and any deviation from the plan **with its reason**.
|
|
53
|
+
|
|
54
|
+
## Review
|
|
55
|
+
|
|
56
|
+
Verification against each acceptance criterion. What was checked, and how.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
**Authoritative for:** how finished items are archived and why they are never edited afterwards.
|
|
2
|
+
**Not authoritative for:** the content of any archived item, which is fixed at the moment it was archived.
|
|
3
|
+
|
|
4
|
+
# Archive
|
|
5
|
+
|
|
6
|
+
Finished work items, moved here so [`../items/`](../items/README.md) holds only work that can still
|
|
7
|
+
change.
|
|
8
|
+
|
|
9
|
+
Move them with `rungs backlog archive`, never by hand — it recomputes every link repo-wide, and
|
|
10
|
+
archived ids stay indexed so citations to them keep resolving and the ids stay permanently spent.
|
|
11
|
+
|
|
12
|
+
**Never edit an archived item.** If archived work turns out to be wrong, that is a new item. The
|
|
13
|
+
value of an archived record is that it says what was decided at the time.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
**Authoritative for:** what lives in this directory and when an item leaves it.
|
|
2
|
+
**Not authoritative for:** the lifecycle itself, which is the methodology one level up.
|
|
3
|
+
|
|
4
|
+
# Items
|
|
5
|
+
|
|
6
|
+
One file per work item: `{{id_prefix}}-###-slug.md`, from
|
|
7
|
+
[`../TEMPLATE.md`](../TEMPLATE.md).
|
|
8
|
+
|
|
9
|
+
This directory holds **only work that can still change.** Finished items move to
|
|
10
|
+
[`../archive/`](../archive/) with `rungs backlog archive`, which recomputes every link repo-wide so
|
|
11
|
+
archived ids still resolve and stay permanently spent.
|
|
12
|
+
|
|
13
|
+
Never edit an archived item. If archived work turns out to be wrong, that is a new item.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<!-- rungs:begin backlog@1.0.0 -->
|
|
2
|
+
## Work tracking
|
|
3
|
+
|
|
4
|
+
Non-trivial work is a **work item** (`{{id_prefix}}-###`) under
|
|
5
|
+
[`docs/{{root}}/`](docs/{{root}}/README.md) — that file is the methodology and is mandatory reading
|
|
6
|
+
before your first item. Branch `{{branch_prefix}}/{{id_prefix}}-###-slug` off
|
|
7
|
+
`{{integration_branch}}`; claim ids from `NEXT-ID` on your own branch; never scope-creep an item.
|
|
8
|
+
Status must agree with git, and `rungs check` enforces it. Execute one with **`/work-item`**.
|
|
9
|
+
<!-- rungs:end backlog -->
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# Gate table for the `backlog` module.
|
|
2
|
+
#
|
|
3
|
+
# Consumed by two engines the CLI provides — `id-integrity` and `git-status-reconcile`. No script
|
|
4
|
+
# is written into the repo (ADR-0002). Everything here is data; anything that would need an
|
|
5
|
+
# expression belongs in a `command` gate instead (ADR-0003 revisit trigger 3).
|
|
6
|
+
|
|
7
|
+
# ── backlog-ids ───────────────────────────────────────────────────────────────
|
|
8
|
+
|
|
9
|
+
[kinds.item]
|
|
10
|
+
format = "{{id_prefix}}-\\d{1,4}"
|
|
11
|
+
marker = { file = "docs/{{root}}/BACKLOG.md", pattern = "NEXT-ID: ({{id_prefix}}-\\d{1,4})" }
|
|
12
|
+
sources = ["docs/{{root}}/items/**/*.md", "docs/{{root}}/archive/**/*.md"]
|
|
13
|
+
# The archive is indexed so archived ids still resolve and stay permanently spent.
|
|
14
|
+
|
|
15
|
+
[citations]
|
|
16
|
+
# Where a reference to an id may appear, and must resolve.
|
|
17
|
+
roots = ["docs", "AGENTS.md", "CLAUDE.md", ".ai"]
|
|
18
|
+
extensions = [".md", ".toml"]
|
|
19
|
+
|
|
20
|
+
# ── backlog-stale-blocker ─────────────────────────────────────────────────────
|
|
21
|
+
#
|
|
22
|
+
# Refuses a document claiming it waits on work that has finished.
|
|
23
|
+
#
|
|
24
|
+
# The vocabulary is deliberately narrow, and that was measured: rift-forge's first draft matched
|
|
25
|
+
# `until {{id_prefix}}-###` and hit 29 lines that were almost all true history in the repo's own
|
|
26
|
+
# voice. `until` and `once … lands` were dropped, and past tense is a record rather than a claim.
|
|
27
|
+
|
|
28
|
+
[stale_blocker]
|
|
29
|
+
phrases = [
|
|
30
|
+
"blocked on",
|
|
31
|
+
"blocked by",
|
|
32
|
+
"waiting on",
|
|
33
|
+
"waiting for",
|
|
34
|
+
"pending",
|
|
35
|
+
]
|
|
36
|
+
# Checked against a preceding-context window, so a sentence explaining a retired claim is not
|
|
37
|
+
# itself refused — a guard that also refuses its own fix is one people disable.
|
|
38
|
+
negation_window = 60
|
|
39
|
+
past_tense_ok = ["was blocked on", "were blocked on", "had been blocked on", "used to be blocked"]
|
|
40
|
+
exempt_marker = "owner-ok:" # ignored unless it states a reason
|
|
41
|
+
scope_exclude = ["docs/{{root}}/archive/**"]
|
|
42
|
+
|
|
43
|
+
# ── backlog-merged-status ─────────────────────────────────────────────────────
|
|
44
|
+
#
|
|
45
|
+
# One-directional by design: a merged branch with a pre-review status is always wrong; an unmerged
|
|
46
|
+
# branch at any status is not. The gate can only see items whose branch still exists, so deleting a
|
|
47
|
+
# branch immediately after merge costs you the check — which is why the methodology says to delete
|
|
48
|
+
# it *on* merge, after the status moves.
|
|
49
|
+
|
|
50
|
+
[merged_status]
|
|
51
|
+
branch_field = "branch"
|
|
52
|
+
status_field = "status"
|
|
53
|
+
pre_review_statuses = ["proposed", "accepted", "planned", "in_progress"]
|
|
54
|
+
integration_branch = "{{integration_branch}}"
|
|
55
|
+
exempt_marker = "branch-merged-ok:" # ignored unless it states a reason
|
|
56
|
+
|
|
57
|
+
# ── self-test ─────────────────────────────────────────────────────────────────
|
|
58
|
+
#
|
|
59
|
+
# A gate whose rules are all currently satisfied is indistinguishable from a gate that matches
|
|
60
|
+
# nothing, so each engine runs these fixtures and asserts BOTH directions.
|
|
61
|
+
|
|
62
|
+
[[self_test]]
|
|
63
|
+
gate = "backlog-stale-blocker"
|
|
64
|
+
expect = "fail"
|
|
65
|
+
input = "The mapper is blocked on {{id_prefix}}-001, which is done."
|
|
66
|
+
|
|
67
|
+
[[self_test]]
|
|
68
|
+
gate = "backlog-stale-blocker"
|
|
69
|
+
expect = "pass"
|
|
70
|
+
input = "The mapper was blocked on {{id_prefix}}-001 until it landed."
|
|
71
|
+
|
|
72
|
+
[[self_test]]
|
|
73
|
+
gate = "backlog-stale-blocker"
|
|
74
|
+
expect = "pass"
|
|
75
|
+
input = "Do not write that a doc is blocked on finished work. <!-- owner-ok: quotes the retired rule to explain it -->"
|
|
76
|
+
|
|
77
|
+
[[self_test]]
|
|
78
|
+
gate = "backlog-merged-status"
|
|
79
|
+
expect = "fail"
|
|
80
|
+
input = "status: in_progress, branch merged into {{integration_branch}}"
|
|
81
|
+
|
|
82
|
+
[[self_test]]
|
|
83
|
+
gate = "backlog-merged-status"
|
|
84
|
+
expect = "pass"
|
|
85
|
+
input = "status: in_progress, branch not merged"
|
|
86
|
+
|
|
87
|
+
# ── backlog-ids self-tests ────────────────────────────────────────────────────
|
|
88
|
+
#
|
|
89
|
+
# The marker case is the one worth pinning: a NEXT-ID pointing at an id that is
|
|
90
|
+
# already spent hands the next session a collision, and the archive is indexed
|
|
91
|
+
# precisely so an archived id still counts as taken.
|
|
92
|
+
|
|
93
|
+
[[self_test]]
|
|
94
|
+
gate = "backlog-ids"
|
|
95
|
+
expect = "fail"
|
|
96
|
+
fixture = { items = ["{{id_prefix}}-001-a.md", "{{id_prefix}}-001-b.md"] }
|
|
97
|
+
|
|
98
|
+
[[self_test]]
|
|
99
|
+
gate = "backlog-ids"
|
|
100
|
+
expect = "pass"
|
|
101
|
+
fixture = { items = ["{{id_prefix}}-001-a.md", "{{id_prefix}}-002-b.md"] }
|
|
102
|
+
|
|
103
|
+
[[self_test]]
|
|
104
|
+
gate = "backlog-ids"
|
|
105
|
+
expect = "fail"
|
|
106
|
+
fixture = { items = ["{{id_prefix}}-001-a.md"], marker = "{{id_prefix}}-001" }
|