@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,107 @@
|
|
|
1
|
+
[module]
|
|
2
|
+
name = "specs"
|
|
3
|
+
version = "1.0.0"
|
|
4
|
+
rung = 2
|
|
5
|
+
summary = "Behaviour specifications with stable feature and story ids, per-story status, a mandatory scope section, and a gate against overclaiming."
|
|
6
|
+
|
|
7
|
+
[requires]
|
|
8
|
+
modules = []
|
|
9
|
+
|
|
10
|
+
[conflicts]
|
|
11
|
+
modules = []
|
|
12
|
+
|
|
13
|
+
# Orthogonal to `backlog`, and installing one does not give you the other. Specs
|
|
14
|
+
# answer *what is in scope*; a backlog answers *what is happening*. hexguard-templates
|
|
15
|
+
# has excellent specs and cannot say what is in flight.
|
|
16
|
+
|
|
17
|
+
[params.path]
|
|
18
|
+
description = "Directory holding the specs"
|
|
19
|
+
default = "docs/specs"
|
|
20
|
+
|
|
21
|
+
[params.feature_format]
|
|
22
|
+
description = "Feature id shape, per surface prefix"
|
|
23
|
+
default = "<PREFIX>-F##"
|
|
24
|
+
|
|
25
|
+
[params.story_format]
|
|
26
|
+
description = "Story id shape"
|
|
27
|
+
default = "<PREFIX>-US-###"
|
|
28
|
+
|
|
29
|
+
[params.split_lines]
|
|
30
|
+
description = """
|
|
31
|
+
Split a spec into spec.md + stories/ only past this many lines. A threshold, not a preference —
|
|
32
|
+
it is what stops structure being created ahead of content.
|
|
33
|
+
"""
|
|
34
|
+
default = 600
|
|
35
|
+
|
|
36
|
+
[[gates]]
|
|
37
|
+
id = "specs-status-evidence"
|
|
38
|
+
kind = "declared"
|
|
39
|
+
engine = "register-schema"
|
|
40
|
+
table = "gates/specs.toml"
|
|
41
|
+
tier = "fast"
|
|
42
|
+
why = """
|
|
43
|
+
A story marked done must name what closed it. "Demo is not done": an endpoint that exists to
|
|
44
|
+
showcase a capability in isolation is partial with a note, never complete. A spec that overclaims
|
|
45
|
+
integration is worse than no spec, because it is read as verified.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
[[gates]]
|
|
49
|
+
id = "specs-scope-section"
|
|
50
|
+
kind = "declared"
|
|
51
|
+
engine = "sections"
|
|
52
|
+
table = "gates/specs.toml"
|
|
53
|
+
tier = "fast"
|
|
54
|
+
why = "Out-of-scope is what stops an agent silently expanding a surface while implementing an unrelated story."
|
|
55
|
+
|
|
56
|
+
[[gates]]
|
|
57
|
+
id = "specs-ids-unique"
|
|
58
|
+
kind = "declared"
|
|
59
|
+
engine = "id-integrity"
|
|
60
|
+
table = "gates/specs.toml"
|
|
61
|
+
tier = "fast"
|
|
62
|
+
why = "Story ids are cited from commits, plans and work items; two stories sharing one make every citation ambiguous."
|
|
63
|
+
|
|
64
|
+
[[gates]]
|
|
65
|
+
id = "specs-split-threshold"
|
|
66
|
+
kind = "declared"
|
|
67
|
+
engine = "file-budget"
|
|
68
|
+
table = "gates/specs.toml"
|
|
69
|
+
tier = "fast"
|
|
70
|
+
why = "A spec past the threshold is one nobody reads to the end, and its later sections are where the unverified claims collect."
|
|
71
|
+
|
|
72
|
+
[detect]
|
|
73
|
+
paths = ["docs/specs/**/*.md", "docs/**/spec.md", "specs/**/*.md"]
|
|
74
|
+
markers = ["rungs:begin specs"]
|
|
75
|
+
|
|
76
|
+
# Authority-docs-by-topic is a different way to answer "what is true", and it is
|
|
77
|
+
# not a spec tree. Never auto-adopted.
|
|
78
|
+
[[detect.paradigm]]
|
|
79
|
+
id = "authority-docs"
|
|
80
|
+
paths = [".ai/context/doc_ownership.md", "docs/architecture/project_scope.md"]
|
|
81
|
+
compare = "docs/research/synthesis.md#37-specs-separate-tree-or-work-item-plans"
|
|
82
|
+
note = """
|
|
83
|
+
axiom-mesh routes truth by topic through a doc-ownership registry rather than by surface through
|
|
84
|
+
story specs. Both answer "what is in scope"; only the spec tree carries per-story status. They
|
|
85
|
+
coexist — `doc-authority` is the module for the other shape — but installing specs over an
|
|
86
|
+
authority tree creates a second place to look, which is the failure that registry exists to
|
|
87
|
+
prevent.
|
|
88
|
+
"""
|
|
89
|
+
|
|
90
|
+
# An inference rule was declared here and removed after running it. It matched
|
|
91
|
+
# `[A-Z]{2,8}-US-\d` and proposed `AUTH` for `story_format`, whose value is a
|
|
92
|
+
# *shape* (`<PREFIX>-US-###`) — so it proposed a prefix for a parameter that
|
|
93
|
+
# wants a pattern, and a repo with several surfaces has several prefixes by
|
|
94
|
+
# design. Nothing in this module is inferable without judgement, so it infers
|
|
95
|
+
# nothing. That is a legitimate answer, and a knob wired to a wrong value is
|
|
96
|
+
# worse than no knob.
|
|
97
|
+
|
|
98
|
+
[provenance]
|
|
99
|
+
sources = ["hexguard-templates", "rift-forge"]
|
|
100
|
+
patterns = ["spec-ids-and-status", "mandatory-scope-section", "demo-not-done", "split-threshold", "reference-implementation-pointer", "adoption-guide"]
|
|
101
|
+
incident = """
|
|
102
|
+
hexguard-templates states the rule this module gates: "Some endpoints exist purely to showcase a
|
|
103
|
+
package in isolation. Mark these partial with an explicit note, never complete — a spec that
|
|
104
|
+
overclaims integration is worse than no spec." The rule exists because overclaiming had already
|
|
105
|
+
happened, and it was enforced by review only. Its own platform spec carries the status line
|
|
106
|
+
"stable, enforcement gaps noted".
|
|
107
|
+
"""
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<!-- rungs:begin workflows@1.0.0 -->
|
|
2
|
+
## Planning
|
|
3
|
+
|
|
4
|
+
Handed something larger than one change? **`/decompose`** — split into technical concerns, decide
|
|
5
|
+
reuse per concern, pick a planning tier, sequence by dependency. The decision tables are in
|
|
6
|
+
[`.ai/rules/`](.ai/rules/README.md) and replace judgement rather than supplementing it.
|
|
7
|
+
**Tier 0 — no document — is a real answer.**
|
|
8
|
+
<!-- rungs:end workflows -->
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Gate table for the `workflows` module.
|
|
2
|
+
|
|
3
|
+
# ── workflows-plan-tier-declared ──────────────────────────────────────────────
|
|
4
|
+
|
|
5
|
+
[[frontmatter_schema]]
|
|
6
|
+
id = "plans"
|
|
7
|
+
scan = ["{{plan_path}}/**/*.md"]
|
|
8
|
+
exclude = ["{{plan_path}}/README.md"]
|
|
9
|
+
required = ["tier", "status", "created"]
|
|
10
|
+
enum = { tier = [1, 2], status = ["draft", "active", "done", "abandoned"] }
|
|
11
|
+
message = """
|
|
12
|
+
{file} has no `tier`. A plan that does not declare its tier cannot be checked against that tier's
|
|
13
|
+
required sections, and tiering degrades back into "write whatever felt right" — the judgement call
|
|
14
|
+
the table exists to replace. Tier 0 work has no document, so a document here is tier 1 or 2.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
# ── workflows-plan-sections ───────────────────────────────────────────────────
|
|
18
|
+
|
|
19
|
+
[[sections]]
|
|
20
|
+
when = { tier = 1 }
|
|
21
|
+
scan = ["{{plan_path}}/**/*.md"]
|
|
22
|
+
required = ["Why", "Decisions", "Phases", "Validation", "Follow-up"]
|
|
23
|
+
non_empty = true
|
|
24
|
+
|
|
25
|
+
[[sections]]
|
|
26
|
+
when = { tier = 2 }
|
|
27
|
+
scan = ["{{plan_path}}/**/*.md"]
|
|
28
|
+
required = [
|
|
29
|
+
"Why", "Decisions", "Phases", "Validation", "Follow-up",
|
|
30
|
+
"Boundary decision", "Reuse table", "Proof-of-reuse gate", "Verification gate",
|
|
31
|
+
"Explicitly out of scope",
|
|
32
|
+
]
|
|
33
|
+
non_empty = true
|
|
34
|
+
message = """
|
|
35
|
+
{file} is tier 2 and has no non-empty `{section}`. A tier-2 proposal missing its proof-of-reuse
|
|
36
|
+
gate is a tier-1 plan wearing a heavier label — and the label is what other work will trust.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
# ── self-tests ────────────────────────────────────────────────────────────────
|
|
40
|
+
|
|
41
|
+
[[self_test]]
|
|
42
|
+
gate = "workflows-plan-tier-declared"
|
|
43
|
+
expect = "fail"
|
|
44
|
+
fixture = { frontmatter = { status = "active", created = "2026-01-01" } }
|
|
45
|
+
|
|
46
|
+
[[self_test]]
|
|
47
|
+
gate = "workflows-plan-tier-declared"
|
|
48
|
+
expect = "pass"
|
|
49
|
+
fixture = { frontmatter = { tier = 1, status = "active", created = "2026-01-01" } }
|
|
50
|
+
|
|
51
|
+
[[self_test]]
|
|
52
|
+
gate = "workflows-plan-tier-declared"
|
|
53
|
+
expect = "fail"
|
|
54
|
+
fixture = { frontmatter = { tier = 0, status = "active", created = "2026-01-01" } }
|
|
55
|
+
# Tier 0 has no document by definition, so a tier-0 file is a contradiction rather than a plan.
|
|
56
|
+
|
|
57
|
+
[[self_test]]
|
|
58
|
+
gate = "workflows-plan-sections"
|
|
59
|
+
expect = "fail"
|
|
60
|
+
fixture = { tier = 2, sections = ["Why", "Decisions", "Phases", "Validation", "Follow-up"] }
|
|
61
|
+
|
|
62
|
+
[[self_test]]
|
|
63
|
+
gate = "workflows-plan-sections"
|
|
64
|
+
expect = "pass"
|
|
65
|
+
fixture = { tier = 1, sections = ["Why", "Decisions", "Phases", "Validation", "Follow-up"] }
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
[module]
|
|
2
|
+
name = "workflows"
|
|
3
|
+
version = "1.0.0"
|
|
4
|
+
rung = 2
|
|
5
|
+
summary = "Decision procedures: how to decompose work, whether to reuse or build, and how much planning a piece of work earns."
|
|
6
|
+
|
|
7
|
+
[requires]
|
|
8
|
+
modules = ["instructions"]
|
|
9
|
+
|
|
10
|
+
[conflicts]
|
|
11
|
+
modules = []
|
|
12
|
+
|
|
13
|
+
[params.path]
|
|
14
|
+
description = "Where the authority document lives"
|
|
15
|
+
default = "docs/workflows"
|
|
16
|
+
|
|
17
|
+
[params.upstream_repo]
|
|
18
|
+
description = """
|
|
19
|
+
A sibling or upstream repository this one consumes from. Enables the extend-upstream branch of the
|
|
20
|
+
reuse decision. Blank means the reuse table has three branches instead of four.
|
|
21
|
+
"""
|
|
22
|
+
default = ""
|
|
23
|
+
|
|
24
|
+
[params.plan_path]
|
|
25
|
+
description = "Where tier-1 and tier-2 plan documents land"
|
|
26
|
+
default = "docs/plans"
|
|
27
|
+
|
|
28
|
+
[[gates]]
|
|
29
|
+
id = "workflows-plan-tier-declared"
|
|
30
|
+
kind = "declared"
|
|
31
|
+
engine = "frontmatter-schema"
|
|
32
|
+
table = "gates/workflows.toml"
|
|
33
|
+
tier = "fast"
|
|
34
|
+
why = """
|
|
35
|
+
A plan document that does not say which tier it is cannot be checked against that tier's required
|
|
36
|
+
sections, and tiering silently degrades into "write whatever felt right", which is the judgement
|
|
37
|
+
call the table exists to replace.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
[[gates]]
|
|
41
|
+
id = "workflows-plan-sections"
|
|
42
|
+
kind = "declared"
|
|
43
|
+
engine = "sections"
|
|
44
|
+
table = "gates/workflows.toml"
|
|
45
|
+
tier = "fast"
|
|
46
|
+
why = "A tier-2 proposal missing its proof-of-reuse gate is a tier-1 plan wearing a heavier label."
|
|
47
|
+
|
|
48
|
+
[skills.decompose]
|
|
49
|
+
# No extensions. Deciding how to break work down is exactly what a model should
|
|
50
|
+
# reach for unprompted when handed something larger than one change.
|
|
51
|
+
|
|
52
|
+
[detect]
|
|
53
|
+
paths = ["docs/plans/**/*.md", "docs/**/plan-*.md", "docs/.ai/backlog/plan-*.md", ".github/instructions/*workflow*.md"]
|
|
54
|
+
markers = ["rungs:begin workflows"]
|
|
55
|
+
|
|
56
|
+
[provenance]
|
|
57
|
+
sources = ["hexguard-templates", "hexguard", "rift-forge"]
|
|
58
|
+
patterns = [
|
|
59
|
+
"reuse-decision-table",
|
|
60
|
+
"second-consumer-threshold",
|
|
61
|
+
"doc-tier-selection",
|
|
62
|
+
"per-concern-decomposition",
|
|
63
|
+
"multi-story-batching",
|
|
64
|
+
"numbered-workflow-steps",
|
|
65
|
+
"phase-checklist",
|
|
66
|
+
"lifecycle-verbs",
|
|
67
|
+
"controlled-performance-comparison",
|
|
68
|
+
]
|
|
69
|
+
incident = """
|
|
70
|
+
hexguard-templates wrote the best decision procedure in the corpus — a nine-step workflow whose
|
|
71
|
+
tier table is introduced with "Use this table instead of 'use judgment'" — and shipped it with no
|
|
72
|
+
invocation surface at all. Its AGENTS.md prompts table reads, verbatim: "No custom prompts or
|
|
73
|
+
agents defined yet for this repo. This table is a placeholder for future additions." The procedure
|
|
74
|
+
loads only when a matching file is edited, which is after the decisions it governs have been made.
|
|
75
|
+
"""
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: >-
|
|
3
|
+
How much planning a piece of work earns — tier 0 (none), tier 1 (light plan), tier 2 (heavy
|
|
4
|
+
proposal) — and the triggers that move work up a tier. Loads when writing or reviewing a plan.
|
|
5
|
+
paths:
|
|
6
|
+
- "{{plan_path}}/**/*.md"
|
|
7
|
+
enforcement: gated
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# How much planning
|
|
11
|
+
|
|
12
|
+
**Use this table instead of judgement.**
|
|
13
|
+
|
|
14
|
+
| Tier | When | Artifact |
|
|
15
|
+
| --- | --- | --- |
|
|
16
|
+
| **0 — no document** | One piece of work, every concern resolves to use-as-is or one trivial hand-roll, one consumer, no cross-repo change | Implement directly. A task list is enough |
|
|
17
|
+
| **1 — light plan** | Several concerns, or one extend/hand-roll branch, but bounded to one surface | `{{plan_path}}/plan-*.md` with frontmatter `tier: 1` and: **Why · Decisions · Phases · Validation · Follow-up** |
|
|
18
|
+
| **2 — heavy proposal** | An upstream change is taken, or a genuinely new shared primitive is proposed | `{{plan_path}}/plan-*.md` with `tier: 2` and, additionally: **Boundary decision · Per-concern reuse table · Proof-of-reuse gate · Verification gate · Explicitly out of scope** |
|
|
19
|
+
|
|
20
|
+
## Tier 0 has to exist
|
|
21
|
+
|
|
22
|
+
A process that always demands a document is one people route around, and the routing is invisible —
|
|
23
|
+
the work still happens, just with no record. **Tier 0 is what makes tiers 1 and 2 credible**,
|
|
24
|
+
because choosing them then means something.
|
|
25
|
+
|
|
26
|
+
## Triggers that move work up
|
|
27
|
+
|
|
28
|
+
Any one of these is enough:
|
|
29
|
+
|
|
30
|
+
- a cross-repo change (at least tier 1, usually tier 2)
|
|
31
|
+
- more than about two distinct concerns
|
|
32
|
+
- more than one consumer affected
|
|
33
|
+
- a hand-roll that looks likely to cross the second-consumer threshold soon — **write it down now
|
|
34
|
+
rather than rediscovering it later**
|
|
35
|
+
|
|
36
|
+
## Tier is declared, not inferred
|
|
37
|
+
|
|
38
|
+
Every plan carries `tier:` in its frontmatter. `rungs check` reads it and checks that tier's
|
|
39
|
+
required sections are present and non-empty. A plan with no tier cannot be checked, and tiering
|
|
40
|
+
degrades back into "write whatever felt right" — which is the judgement call this table replaces.
|
|
41
|
+
|
|
42
|
+
## Plans are not specs and not work items
|
|
43
|
+
|
|
44
|
+
A plan says *how we intend to do this*. It does not define behaviour, and it does not track state.
|
|
45
|
+
When a plan and a spec disagree the spec wins; when a plan and a work item's status disagree, the
|
|
46
|
+
work item wins. A plan that has started carrying status is one that should have been closed.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: >-
|
|
3
|
+
How to decide, per technical concern, whether to use what exists, extend it locally, extend it
|
|
4
|
+
upstream, or hand-roll. Loads when planning work or writing a plan document.
|
|
5
|
+
paths:
|
|
6
|
+
- "{{plan_path}}/**/*.md"
|
|
7
|
+
- "{{path}}/**/*.md"
|
|
8
|
+
enforcement: review-only
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# The reuse decision
|
|
12
|
+
|
|
13
|
+
Run this **per concern, not per story.** A piece of work with four distinct technical concerns runs
|
|
14
|
+
the loop four times before anything is sequenced. The most common decomposition failure is
|
|
15
|
+
resolving a story against one existing capability and calling it planned.
|
|
16
|
+
|
|
17
|
+
## The four branches
|
|
18
|
+
|
|
19
|
+
| Branch | When | Where the work lands |
|
|
20
|
+
| --- | --- | --- |
|
|
21
|
+
| **Use as-is** | The existing API fully covers the concern, there is no repo-specific gap, and an existing consumer already demonstrates the same usage shape | Wiring only, in the consuming surface |
|
|
22
|
+
| **Extend — locally** | The primitive exists, but the composition around it does not | Here |
|
|
23
|
+
| **Extend — upstream** | The capability is genuinely missing from `{{upstream_repo}}`, not merely unconsumed here, **and** it is general-purpose rather than specific to one consumer | Upstream first, consumed here after |
|
|
24
|
+
| **Hand-roll** | Nothing exists anywhere, and the need is not general enough to justify a shared primitive yet | Here, local only |
|
|
25
|
+
|
|
26
|
+
*(With no `upstream_repo` configured, the third branch does not apply and the table has three.)*
|
|
27
|
+
|
|
28
|
+
## The second-consumer threshold
|
|
29
|
+
|
|
30
|
+
**Hand-roll once. On the second consumer, stop and extract.**
|
|
31
|
+
|
|
32
|
+
The first hand-roll is cheap and correct — you do not yet know the shape. The second is the
|
|
33
|
+
decision point, and the negative form matters as much as the positive one: **do not build a second
|
|
34
|
+
local copy.** Two copies is where the divergence starts, and by the third nobody can tell which is
|
|
35
|
+
canonical.
|
|
36
|
+
|
|
37
|
+
This cuts both ways. Extracting on the *first* consumer produces a shared primitive designed
|
|
38
|
+
against one use case, which is the other way to get an abstraction nobody can use.
|
|
39
|
+
|
|
40
|
+
## When a concern spans a batch
|
|
41
|
+
|
|
42
|
+
If several pieces of work are being planned together, run the decision across **all** of them
|
|
43
|
+
first and produce **one combined concern table**. A concern shared by two stories should be caught
|
|
44
|
+
once, not solved twice — and the second solution is usually a worse version of the first written
|
|
45
|
+
by someone who did not know the first existed.
|
|
46
|
+
|
|
47
|
+
## What this does not decide
|
|
48
|
+
|
|
49
|
+
Sequencing, ownership, and whether the work is worth doing. Those come after, and a reuse decision
|
|
50
|
+
that has quietly answered them is one that skipped a conversation.
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: decompose
|
|
3
|
+
description: >-
|
|
4
|
+
Break a request into technical concerns, decide reuse per concern (use as-is / extend locally /
|
|
5
|
+
extend upstream / hand-roll), pick the planning tier, and sequence by dependency. Use when handed
|
|
6
|
+
something larger than a single change, when asked to "plan this", "how should we approach this",
|
|
7
|
+
"break this down", "scope this out", or before starting work whose shape is not obvious. Also use
|
|
8
|
+
across a batch of related work to catch shared concerns once. Executing a tracked item is
|
|
9
|
+
/work-item; recording an out-of-scope observation is /record-finding.
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Decompose
|
|
13
|
+
|
|
14
|
+
Nine numbered steps. Other documents cite them by number — *"resolved at step 4 as extend-local"* —
|
|
15
|
+
so the numbering is stable and worth keeping.
|
|
16
|
+
|
|
17
|
+
## 1. Anchor
|
|
18
|
+
|
|
19
|
+
Name the narrowest concrete thing this touches: a file, a symbol, a route, a failing test. If you
|
|
20
|
+
cannot, the request is not yet a piece of work and the next move is a question, not a plan.
|
|
21
|
+
|
|
22
|
+
## 2. Split into concerns
|
|
23
|
+
|
|
24
|
+
List the distinct **technical** concerns, not the user-visible steps. "Persist the draft",
|
|
25
|
+
"debounce the input", "authorise the caller" are three concerns; "add the editor" is one story
|
|
26
|
+
containing them.
|
|
27
|
+
|
|
28
|
+
**Steps 3–4 run per concern.** A story with four concerns runs them four times.
|
|
29
|
+
|
|
30
|
+
## 3. Look up what exists
|
|
31
|
+
|
|
32
|
+
For each concern, search before deciding: the shared primitives, the reference implementation, the
|
|
33
|
+
upstream repository if there is one. **Search with the concern's vocabulary, not the story's** —
|
|
34
|
+
this is where a capability that already exists gets missed and rebuilt.
|
|
35
|
+
|
|
36
|
+
## 4. Decide the branch
|
|
37
|
+
|
|
38
|
+
Apply the reuse decision table. Record the branch **and the reason**, per concern. A concern whose
|
|
39
|
+
branch is "hand-roll" needs the second-consumer check: is this the first time, or the second?
|
|
40
|
+
|
|
41
|
+
## 5. Pick the tier
|
|
42
|
+
|
|
43
|
+
Apply the planning-tier table. **Tier 0 is a real answer** — if every concern resolved to
|
|
44
|
+
use-as-is, write nothing and implement.
|
|
45
|
+
|
|
46
|
+
## 6. Sequence
|
|
47
|
+
|
|
48
|
+
Order by **dependency only**, not by size or by what is interesting. If two concerns can proceed
|
|
49
|
+
independently, say so — that is a scheduling fact somebody will want.
|
|
50
|
+
|
|
51
|
+
For a batch: produce one combined concern table across all the work first, so a shared concern is
|
|
52
|
+
caught once.
|
|
53
|
+
|
|
54
|
+
## 7. Name what this does not cover
|
|
55
|
+
|
|
56
|
+
Explicitly, before starting. Out-of-scope written after the fact is a description of what you ran
|
|
57
|
+
out of time for.
|
|
58
|
+
|
|
59
|
+
## 8. Implement
|
|
60
|
+
|
|
61
|
+
Following the repo's engineering guidance, not this skill.
|
|
62
|
+
|
|
63
|
+
## 9. Close
|
|
64
|
+
|
|
65
|
+
Reconcile: does the spec still describe what was built? Did any concern's branch change during
|
|
66
|
+
execution, and is that recorded with its reason? A plan that silently stopped matching reality
|
|
67
|
+
reads as verified.
|
|
68
|
+
|
|
69
|
+
## Two failure modes to watch for
|
|
70
|
+
|
|
71
|
+
- **Resolving the story instead of the concerns.** One search, one answer, four concerns
|
|
72
|
+
unexamined. This is the common one.
|
|
73
|
+
- **Deciding reuse from a memory of the codebase.** Search at decision time. A recollection of what
|
|
74
|
+
exists has a shelf life, and the cost of being wrong is a duplicate primitive nobody notices for
|
|
75
|
+
months.
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@rungs/cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Installs and maintains a repository's agentic development system, composed from modules.",
|
|
5
|
+
"author": "Antoine Dancre",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/ThroughTheWind/rungs.git"
|
|
9
|
+
},
|
|
10
|
+
"homepage": "https://github.com/ThroughTheWind/rungs#readme",
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/ThroughTheWind/rungs/issues"
|
|
13
|
+
},
|
|
14
|
+
"type": "module",
|
|
15
|
+
"bin": {
|
|
16
|
+
"rungs": "./src/cli.ts"
|
|
17
|
+
},
|
|
18
|
+
"publishConfig": {
|
|
19
|
+
"access": "public"
|
|
20
|
+
},
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=22.18"
|
|
23
|
+
},
|
|
24
|
+
"scripts": {
|
|
25
|
+
"rungs": "node src/cli.ts",
|
|
26
|
+
"test": "node --test test/"
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"src",
|
|
30
|
+
"modules"
|
|
31
|
+
],
|
|
32
|
+
"keywords": [
|
|
33
|
+
"agents",
|
|
34
|
+
"claude-code",
|
|
35
|
+
"agent-skills",
|
|
36
|
+
"scaffolding"
|
|
37
|
+
],
|
|
38
|
+
"license": "MIT",
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"smol-toml": "^1.8.0"
|
|
41
|
+
}
|
|
42
|
+
}
|