@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,54 @@
|
|
|
1
|
+
# Gate table for the `concurrency` module.
|
|
2
|
+
|
|
3
|
+
# ── concurrency-no-integration-checkout ───────────────────────────────────────
|
|
4
|
+
|
|
5
|
+
[git_state]
|
|
6
|
+
refuse_checked_out = ["{{integration_branch}}"]
|
|
7
|
+
scope = "all-worktrees"
|
|
8
|
+
message = """
|
|
9
|
+
`{{integration_branch}}` is checked out in {worktree}. Nothing should hold it: `rungs land` runs
|
|
10
|
+
from wherever you are, verifies on a scratch ref, and advances the branch with a compare-and-swap.
|
|
11
|
+
Holding it checked out blocks every other session and does not prevent concurrent landing anyway —
|
|
12
|
+
switching to the scratch ref releases it mid-run, and two land processes have been measured running
|
|
13
|
+
at once under exactly that design.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
# ── concurrency-generated-not-text-merged ─────────────────────────────────────
|
|
17
|
+
#
|
|
18
|
+
# Two failure modes, and the second is the quiet one: drivers declared in .gitattributes are inert
|
|
19
|
+
# until installed, so a fresh clone silently gets git's default merge on files that must never be
|
|
20
|
+
# text-merged.
|
|
21
|
+
|
|
22
|
+
[merge_driver_check]
|
|
23
|
+
attributes_file = ".gitattributes"
|
|
24
|
+
required_drivers = ["rungs-ledger", "rungs-generated"]
|
|
25
|
+
verify_installed = true
|
|
26
|
+
install_command = "rungs setup git"
|
|
27
|
+
message = """
|
|
28
|
+
The merge drivers named in .gitattributes are not installed in this clone, so they are inert and
|
|
29
|
+
merges are falling back to git's default. For a generated artifact that default is a clean text
|
|
30
|
+
merge of a file that is a fixed point of nothing — it surfaces later as a mismatch with an
|
|
31
|
+
uninformative diff. Run `rungs setup git` once per clone.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
# ── self-tests ────────────────────────────────────────────────────────────────
|
|
35
|
+
|
|
36
|
+
[[self_test]]
|
|
37
|
+
gate = "concurrency-no-integration-checkout"
|
|
38
|
+
expect = "fail"
|
|
39
|
+
fixture = { worktrees = [{ branch = "{{integration_branch}}" }] }
|
|
40
|
+
|
|
41
|
+
[[self_test]]
|
|
42
|
+
gate = "concurrency-no-integration-checkout"
|
|
43
|
+
expect = "pass"
|
|
44
|
+
fixture = { worktrees = [{ branch = "feature/wi-001" }] }
|
|
45
|
+
|
|
46
|
+
[[self_test]]
|
|
47
|
+
gate = "concurrency-generated-not-text-merged"
|
|
48
|
+
expect = "fail"
|
|
49
|
+
fixture = { declared = ["rungs-ledger", "rungs-generated"], installed = [] }
|
|
50
|
+
|
|
51
|
+
[[self_test]]
|
|
52
|
+
gate = "concurrency-generated-not-text-merged"
|
|
53
|
+
expect = "pass"
|
|
54
|
+
fixture = { declared = ["rungs-ledger", "rungs-generated"], installed = ["rungs-ledger", "rungs-generated"] }
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
[module]
|
|
2
|
+
name = "concurrency"
|
|
3
|
+
version = "1.0.0"
|
|
4
|
+
rung = 5
|
|
5
|
+
summary = "Many sessions on one integration branch: a verified base to cut from, attributed failures, a land protocol that cannot redden the branch, and merge drivers per conflict class."
|
|
6
|
+
|
|
7
|
+
[requires]
|
|
8
|
+
modules = ["backlog", "gates", "ci"]
|
|
9
|
+
|
|
10
|
+
[conflicts]
|
|
11
|
+
modules = []
|
|
12
|
+
|
|
13
|
+
# ── THRESHOLD ─────────────────────────────────────────────────────────────────
|
|
14
|
+
#
|
|
15
|
+
# `add` states this and requires explicit confirmation:
|
|
16
|
+
#
|
|
17
|
+
# Below roughly five concurrent sessions on one integration branch, every
|
|
18
|
+
# mechanism here is pure overhead. It is a real tooling surface with its own
|
|
19
|
+
# failure modes, and it earns its cost only when sessions genuinely cannot see
|
|
20
|
+
# each other's work.
|
|
21
|
+
#
|
|
22
|
+
# Selling this to a rung-1 repo is the most likely way this tool does harm.
|
|
23
|
+
|
|
24
|
+
[threshold]
|
|
25
|
+
metric = "concurrent sessions on one integration branch"
|
|
26
|
+
minimum = 5
|
|
27
|
+
confirm = true
|
|
28
|
+
|
|
29
|
+
[params.integration_branch]
|
|
30
|
+
description = "The shared branch work lands on"
|
|
31
|
+
default = "main"
|
|
32
|
+
|
|
33
|
+
[params.green_prefix]
|
|
34
|
+
description = """
|
|
35
|
+
Ref marking the last merge that was actually verified. Branches are cut from this rather than from
|
|
36
|
+
the tip, so a session does not start on top of an unverified merge.
|
|
37
|
+
"""
|
|
38
|
+
default = "green/"
|
|
39
|
+
|
|
40
|
+
[params.integ_prefix]
|
|
41
|
+
description = "Scratch ref a merge is verified on before the integration branch moves"
|
|
42
|
+
default = "integ/"
|
|
43
|
+
|
|
44
|
+
[[gates]]
|
|
45
|
+
id = "concurrency-no-integration-checkout"
|
|
46
|
+
kind = "declared"
|
|
47
|
+
engine = "git-state"
|
|
48
|
+
table = "gates/concurrency.toml"
|
|
49
|
+
tier = "fast"
|
|
50
|
+
why = """
|
|
51
|
+
Recorded as a correction, not a preference. The first design held the integration branch checked
|
|
52
|
+
out for the whole verification and called that single-writer. It was both worse than useless: it
|
|
53
|
+
blocked every other session — on one measured day every session in the repo sat in standby behind
|
|
54
|
+
one land — and it did not prevent concurrent landing anyway, because switching to the scratch ref
|
|
55
|
+
releases the branch mid-run. Two land processes were measured running at once.
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
[[gates]]
|
|
59
|
+
id = "concurrency-generated-not-text-merged"
|
|
60
|
+
kind = "declared"
|
|
61
|
+
engine = "merge-driver-check"
|
|
62
|
+
table = "gates/concurrency.toml"
|
|
63
|
+
tier = "fast"
|
|
64
|
+
why = """
|
|
65
|
+
Two branches that each regenerated an artifact produce hunks git interleaves cleanly into something
|
|
66
|
+
neither pipeline would emit. The failure surfaces later as a digest mismatch with an uninformative
|
|
67
|
+
diff. The driver refuses and prints the regenerate command instead.
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
[detect]
|
|
71
|
+
# `.gitattributes` was in this list and is a near-universal file — it reported
|
|
72
|
+
# axiom-mesh, a repo with 29 branches and no land protocol at all, as having
|
|
73
|
+
# concurrency tooling. A signature that matches nearly every repo is not a
|
|
74
|
+
# signature. What is actually distinctive is a declared merge driver.
|
|
75
|
+
paths = [".git/rungs-land.lock"]
|
|
76
|
+
# A custom merge driver declaration is what actually discriminates: 21 of them
|
|
77
|
+
# in the one repo of the four that runs a land protocol, and none in the other
|
|
78
|
+
# three. The file itself is near-universal, so it is scanned rather than counted.
|
|
79
|
+
marker_paths = [".gitattributes"]
|
|
80
|
+
markers = ["rungs:begin concurrency", "merge="]
|
|
81
|
+
|
|
82
|
+
[[detect.paradigm]]
|
|
83
|
+
id = "trunk-based"
|
|
84
|
+
compare = "docs/research/synthesis.md#35-concurrency-model"
|
|
85
|
+
note = """
|
|
86
|
+
Few branches and direct merges is not a lesser model — it is the right one below the threshold, and
|
|
87
|
+
two of the four source repos ran hundreds of commits that way. Report the branch and worktree
|
|
88
|
+
counts and let the operator decide; never install this because the mechanism is interesting.
|
|
89
|
+
"""
|
|
90
|
+
|
|
91
|
+
[provenance]
|
|
92
|
+
sources = ["rift-forge"]
|
|
93
|
+
patterns = [
|
|
94
|
+
"green-ref",
|
|
95
|
+
"failure-attribution",
|
|
96
|
+
"land-protocol",
|
|
97
|
+
"lock-not-checkout",
|
|
98
|
+
"no-pre-land-full-verify",
|
|
99
|
+
"preflight",
|
|
100
|
+
"conflict-classes",
|
|
101
|
+
"regenerate-never-merge",
|
|
102
|
+
"id-claiming",
|
|
103
|
+
"worktree-lifecycle",
|
|
104
|
+
"ci-at-land-time",
|
|
105
|
+
]
|
|
106
|
+
incident = """
|
|
107
|
+
rift-forge reached 401 branches and 51 live worktrees with many sessions branching off one
|
|
108
|
+
candidate and unable to see each other. This module ships its corrections included, because both
|
|
109
|
+
were expensive: the checkout-based lock that blocked every session and did not work, and the
|
|
110
|
+
pre-land full verify that widened the window the merge then conflicted in — three of five land
|
|
111
|
+
attempts refused in one measured session, every one after a 4–12 minute pre-verify, every one on
|
|
112
|
+
the same four generated artifacts.
|
|
113
|
+
"""
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<!-- rungs:begin design-sync@1.0.0 -->
|
|
2
|
+
## Design
|
|
3
|
+
|
|
4
|
+
The design authority is external. Pull it with **`/design-pull`** into `{{mirror}}/` — generated,
|
|
5
|
+
never hand-edited — and decide what the pull means with **`/design-align`**. It owns layout and
|
|
6
|
+
visual language; it does not own this repo's technical constraints, and a conflict is a request to
|
|
7
|
+
send upstream. **Every delta is routed; there is no fourth option.**
|
|
8
|
+
<!-- rungs:end design-sync -->
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Gate table for the `design-sync` module.
|
|
2
|
+
|
|
3
|
+
# ── design-deltas-routed ──────────────────────────────────────────────────────
|
|
4
|
+
#
|
|
5
|
+
# Reads the pull record and requires every delta to name a route. The gate cannot judge whether a
|
|
6
|
+
# route is the right one — only that a decision was made and written down.
|
|
7
|
+
|
|
8
|
+
[register_schema]
|
|
9
|
+
file = "{{mirror}}/PULL-RECORD.md"
|
|
10
|
+
table = "Deltas"
|
|
11
|
+
required_cols = ["Delta", "Route", "Where"]
|
|
12
|
+
enum = { Route = ["implement", "defer", "upstream", "accept-as-is"] }
|
|
13
|
+
non_empty = ["Where"]
|
|
14
|
+
message = """
|
|
15
|
+
Delta "{delta}" has no route. Every difference is work we have decided to do, work we have decided
|
|
16
|
+
to defer, a disagreement to send upstream, or a mirror that is stale. A fifth state — noticed and
|
|
17
|
+
unrouted — is how the design and the implementation drift while both sides believe they are aligned.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
# A deferred delta must carry a revisit trigger. "Later" is not a route.
|
|
21
|
+
[[register_schema.conditional]]
|
|
22
|
+
when = { Route = "defer" }
|
|
23
|
+
non_empty = ["Where"]
|
|
24
|
+
min_words = { Where = 4 }
|
|
25
|
+
message = "Delta \"{delta}\" is deferred with no revisit trigger. A deferral with no trigger is a decision to never do it, taken without saying so."
|
|
26
|
+
|
|
27
|
+
# ── design-mirror-not-edited ──────────────────────────────────────────────────
|
|
28
|
+
|
|
29
|
+
[[render_freshness]]
|
|
30
|
+
id = "design-mirror"
|
|
31
|
+
targets = ["{{mirror}}/**"]
|
|
32
|
+
generated_by = "rungs design pull"
|
|
33
|
+
detect = "local-modification"
|
|
34
|
+
message = """
|
|
35
|
+
{file} in the mirror has been edited locally. The mirror is generated and the next pull destroys
|
|
36
|
+
this — and until then it is a fact about the design system that the design system does not know.
|
|
37
|
+
Route it instead: a work item if we should change, an upstream request if they should.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
# ── self-tests ────────────────────────────────────────────────────────────────
|
|
41
|
+
|
|
42
|
+
[[self_test]]
|
|
43
|
+
gate = "design-deltas-routed"
|
|
44
|
+
expect = "fail"
|
|
45
|
+
fixture = { row = { Delta = "spacing scale changed", Route = "", Where = "" } }
|
|
46
|
+
|
|
47
|
+
[[self_test]]
|
|
48
|
+
gate = "design-deltas-routed"
|
|
49
|
+
expect = "pass"
|
|
50
|
+
fixture = { row = { Delta = "spacing scale changed", Route = "implement", Where = "WI-051" } }
|
|
51
|
+
|
|
52
|
+
[[self_test]]
|
|
53
|
+
gate = "design-deltas-routed"
|
|
54
|
+
expect = "fail"
|
|
55
|
+
fixture = { row = { Delta = "new chart component", Route = "defer", Where = "later" } }
|
|
56
|
+
|
|
57
|
+
[[self_test]]
|
|
58
|
+
gate = "design-deltas-routed"
|
|
59
|
+
expect = "pass"
|
|
60
|
+
fixture = { row = { Delta = "new chart component", Route = "defer", Where = "revisit when analytics ships" } }
|
|
61
|
+
|
|
62
|
+
[[self_test]]
|
|
63
|
+
gate = "design-mirror-not-edited"
|
|
64
|
+
expect = "fail"
|
|
65
|
+
fixture = { modified = ["{{mirror}}/tokens.json"] }
|
|
66
|
+
|
|
67
|
+
[[self_test]]
|
|
68
|
+
gate = "design-mirror-not-edited"
|
|
69
|
+
expect = "pass"
|
|
70
|
+
fixture = { modified = [] }
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
[module]
|
|
2
|
+
name = "design-sync"
|
|
3
|
+
version = "1.0.0"
|
|
4
|
+
rung = 3
|
|
5
|
+
summary = "An external design authority pulled into a local mirror, with every delta routed to a work item, a future item, or an upstream change request — never silent divergence."
|
|
6
|
+
|
|
7
|
+
[requires]
|
|
8
|
+
modules = ["backlog", "skills"]
|
|
9
|
+
|
|
10
|
+
[conflicts]
|
|
11
|
+
modules = []
|
|
12
|
+
|
|
13
|
+
[params.source]
|
|
14
|
+
description = "The upstream design authority — a URL, a project id, or a repository."
|
|
15
|
+
default = ""
|
|
16
|
+
required = true
|
|
17
|
+
consumed_by = "design-pull"
|
|
18
|
+
# Behavioural: it tells the pull where to fetch from and never appears in a
|
|
19
|
+
# template. Deliberately not written into the skill body — the skill would then
|
|
20
|
+
# carry a URL that changes independently of it.
|
|
21
|
+
|
|
22
|
+
[params.mirror]
|
|
23
|
+
description = "Local mirror of the pulled design system. Generated; never hand-edited."
|
|
24
|
+
default = "design-system"
|
|
25
|
+
|
|
26
|
+
[params.request_path]
|
|
27
|
+
description = "Where upstream change requests are drafted before being sent"
|
|
28
|
+
default = "docs/design-requests"
|
|
29
|
+
|
|
30
|
+
[[gates]]
|
|
31
|
+
id = "design-deltas-routed"
|
|
32
|
+
kind = "declared"
|
|
33
|
+
engine = "register-schema"
|
|
34
|
+
table = "gates/design.toml"
|
|
35
|
+
tier = "fast"
|
|
36
|
+
why = """
|
|
37
|
+
Every difference between the mirror and the implementation is either work we have decided to do,
|
|
38
|
+
work we have decided to defer, or a disagreement to send upstream. A fourth category — noticed and
|
|
39
|
+
not routed — is how a design system and its implementation drift apart while both sides believe
|
|
40
|
+
they are aligned.
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
[[gates]]
|
|
44
|
+
id = "design-mirror-not-edited"
|
|
45
|
+
kind = "declared"
|
|
46
|
+
engine = "render-freshness"
|
|
47
|
+
table = "gates/design.toml"
|
|
48
|
+
tier = "fast"
|
|
49
|
+
why = "A hand-edit in the mirror is a local change that the next pull silently destroys, and until then it is a fact about the design system that the design system does not know."
|
|
50
|
+
|
|
51
|
+
[skills.design-pull]
|
|
52
|
+
# Read-only against upstream. Safe for a model to run unprompted.
|
|
53
|
+
|
|
54
|
+
[skills.design-align]
|
|
55
|
+
# No extensions: it writes backlog items and drafts requests, both of which
|
|
56
|
+
# are reversible and reviewable. It does not send anything upstream.
|
|
57
|
+
|
|
58
|
+
[detect]
|
|
59
|
+
paths = ["design-system/**", "docs/**/design-tokens*", ".design/**"]
|
|
60
|
+
markers = ["rungs:begin design-sync"]
|
|
61
|
+
|
|
62
|
+
[[detect.paradigm]]
|
|
63
|
+
id = "design-in-repo"
|
|
64
|
+
paths = ["**/tokens.{json,css,scss}", "**/theme.{ts,css}"]
|
|
65
|
+
compare = "docs/research/synthesis.md#2-convergences--where-independent-repos-agreed"
|
|
66
|
+
note = """
|
|
67
|
+
A design system authored *in* this repo is not an external authority and does not need syncing —
|
|
68
|
+
it needs the ordinary rules for shared code. This module is for the case where somebody else owns
|
|
69
|
+
the design and disagreements have to travel back to them. Report and stop.
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
[provenance]
|
|
73
|
+
sources = ["rift-forge", "hexguard-templates"]
|
|
74
|
+
patterns = ["external-authority-precedence", "two-way-design-sync"]
|
|
75
|
+
incident = """
|
|
76
|
+
hexguard-templates states the precedence rule that makes an external authority workable: the design
|
|
77
|
+
project is "layout/visual guidance, not a functional spec, and its output must still satisfy this
|
|
78
|
+
repo's technical constraints". Without a stated precedence, an external source either gets ignored
|
|
79
|
+
or overrides constraints it knows nothing about. rift-forge industrialised the same integration into
|
|
80
|
+
a down-pull plus an alignment pass whose rule is that every delta is routed somewhere — never
|
|
81
|
+
silent divergence, and never forced convergence either.
|
|
82
|
+
"""
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: design-align
|
|
3
|
+
description: >-
|
|
4
|
+
Reconcile the pulled design mirror against what is actually implemented, routing every difference
|
|
5
|
+
to a work item, a deferred item, or an upstream change request. Use after a design pull, or when
|
|
6
|
+
asked to "align with the design", "does this match the mock", "triage the design deltas", or
|
|
7
|
+
"what does the new design mean for us". Pulling the mirror down is /design-pull; this decides
|
|
8
|
+
what the pull means.
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Align with the design
|
|
12
|
+
|
|
13
|
+
**Every delta is routed. There is no fourth option.**
|
|
14
|
+
|
|
15
|
+
A difference that is noticed and not routed is how a design system and its implementation drift
|
|
16
|
+
apart while both sides believe they are aligned — and the drift is discovered by a user, or by
|
|
17
|
+
someone rebuilding a screen and finding two sources of truth.
|
|
18
|
+
|
|
19
|
+
## Precedence, before anything
|
|
20
|
+
|
|
21
|
+
The design authority owns **layout, visual language, spacing, colour, motion intent**. It does not
|
|
22
|
+
own this repo's technical constraints, accessibility floor, or component boundaries. A design that
|
|
23
|
+
requires breaking one of those is a **disagreement to send upstream**, not an instruction.
|
|
24
|
+
|
|
25
|
+
State which side owns a contested decision *before* deciding the delta — most arguments here are
|
|
26
|
+
really arguments about ownership.
|
|
27
|
+
|
|
28
|
+
## For each delta, choose exactly one
|
|
29
|
+
|
|
30
|
+
| Route | When | What you write |
|
|
31
|
+
| --- | --- | --- |
|
|
32
|
+
| **Implement now** | It is in scope, and the current phase covers it | A work item, with the delta as its rationale |
|
|
33
|
+
| **Defer** | Real, agreed, but belongs to a later phase | A work item marked deferred, **with the revisit trigger** — not a vague "later" |
|
|
34
|
+
| **Send upstream** | The design conflicts with a constraint it does not know about, or is internally inconsistent | A draft in `{{request_path}}`, stating the constraint and what we need |
|
|
35
|
+
| **Accept as-is** | The implementation is right and the mirror is stale | Nothing local — but say so in the pull's record, or the same delta reappears next pull |
|
|
36
|
+
|
|
37
|
+
## What makes this fail
|
|
38
|
+
|
|
39
|
+
- **Batching deltas into one "design alignment" item.** They have different owners, different
|
|
40
|
+
phases, and different answers. One item hides all of that and gets deferred as a lump.
|
|
41
|
+
- **Implementing ahead of the roadmap** because the design shows it. A design is not a schedule.
|
|
42
|
+
- **Silently adjusting the mirror** to match what is built. The mirror is generated; the next pull
|
|
43
|
+
destroys the edit, and until then it is a fact about the design that the designer does not know.
|
|
44
|
+
- **Treating a stale mirror as authoritative.** Check when the pull happened before trusting a
|
|
45
|
+
delta.
|
|
46
|
+
|
|
47
|
+
## Finish
|
|
48
|
+
|
|
49
|
+
Report the counts per route, and **how many deltas were examined**. A pass that found nothing and a
|
|
50
|
+
pass that compared nothing produce the same output, and only one of them means the design is
|
|
51
|
+
implemented.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: design-pull
|
|
3
|
+
description: >-
|
|
4
|
+
Pull the design system down from the upstream authority into the local mirror, and record what
|
|
5
|
+
changed since the last pull. Use when asked to "pull the design", "sync the design system", "get
|
|
6
|
+
the latest tokens/components", or before starting UI work that should match a current design.
|
|
7
|
+
Down-sync only — it never pushes local changes upstream. Deciding what the pull means is
|
|
8
|
+
/design-align.
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Pull the design system
|
|
12
|
+
|
|
13
|
+
**Down-sync only.** This skill never sends anything upstream; that is `/design-align`'s change
|
|
14
|
+
request, drafted for a person to send.
|
|
15
|
+
|
|
16
|
+
## 1. Record where you are starting
|
|
17
|
+
|
|
18
|
+
Note the mirror's current state before pulling — what came down last time and when. Without it,
|
|
19
|
+
"what changed" degrades into "everything looks different", which is not a delta list.
|
|
20
|
+
|
|
21
|
+
## 2. Pull into `{{mirror}}/`
|
|
22
|
+
|
|
23
|
+
The mirror is **generated**. Anything hand-edited there is destroyed by this step, which is why
|
|
24
|
+
`design-mirror-not-edited` exists — if it has fired, resolve that before pulling or the edit
|
|
25
|
+
disappears along with whatever it was compensating for.
|
|
26
|
+
|
|
27
|
+
## 3. Report what changed, in design terms
|
|
28
|
+
|
|
29
|
+
Not a file diff. Group by what a person would recognise:
|
|
30
|
+
|
|
31
|
+
- tokens added, changed, removed
|
|
32
|
+
- components added, changed, removed
|
|
33
|
+
- layout or spacing rules changed
|
|
34
|
+
- anything **removed** — the most consequential category and the easiest to miss, because a removal
|
|
35
|
+
looks like nothing rather than like a change
|
|
36
|
+
|
|
37
|
+
## 4. Say what you could not tell
|
|
38
|
+
|
|
39
|
+
An upstream that reorganised its files produces a diff that looks like a rewrite. Say so rather
|
|
40
|
+
than reporting hundreds of false deltas — an alignment pass fed noise gets abandoned, and the real
|
|
41
|
+
deltas go with it.
|
|
42
|
+
|
|
43
|
+
## 5. Stop
|
|
44
|
+
|
|
45
|
+
**Do not implement anything.** Do not open work items. Do not adjust components to match. The pull
|
|
46
|
+
establishes what upstream says; `/design-align` decides what that means here, against precedence
|
|
47
|
+
rules and the current phase.
|
|
48
|
+
|
|
49
|
+
Hand off with the delta list and the pull date. That date is what makes the next reader able to
|
|
50
|
+
tell a live delta from a stale one.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
**Authoritative for:** which document owns which topic.
|
|
2
|
+
**Not authoritative for:** the content of any topic listed below.
|
|
3
|
+
|
|
4
|
+
# Document ownership
|
|
5
|
+
|
|
6
|
+
The arbiter. **When a question arises about where something belongs, it is resolved here first,
|
|
7
|
+
before any content is written.** If the answer is missing, add the row before writing.
|
|
8
|
+
|
|
9
|
+
## The registry
|
|
10
|
+
|
|
11
|
+
The third column is what makes this enforceable rather than aspirational: it converts "one source
|
|
12
|
+
of truth" from a principle into a lookup, and it is what `rungs check` reads.
|
|
13
|
+
|
|
14
|
+
| Topic | Owner | Must NOT appear in |
|
|
15
|
+
| --- | --- | --- |
|
|
16
|
+
| *(example)* Build and test commands | `AGENTS.md` | `README.md`, `docs/**` |
|
|
17
|
+
| | | |
|
|
18
|
+
|
|
19
|
+
<!-- Add a row before writing content whose home is unclear. A row costs a minute; a topic living
|
|
20
|
+
in two documents costs a divergence nobody notices until they disagree. -->
|
|
21
|
+
|
|
22
|
+
## The rules this enforces
|
|
23
|
+
|
|
24
|
+
**No fact is stated in two documents.** If a value, rule or contract already exists in its owning
|
|
25
|
+
document, the second one references it with a link — never restates it, **not even as a summary.**
|
|
26
|
+
|
|
27
|
+
**Paraphrase is not permitted.** This is the one people argue with, so it is worth being explicit:
|
|
28
|
+
a paraphrase looks maintained while it decays. A summary that "just gives the gist" is a second
|
|
29
|
+
statement of the fact, and when the original changes, the gist becomes wrong while still reading
|
|
30
|
+
as deliberate.
|
|
31
|
+
|
|
32
|
+
**Drift is a bug**, not untidiness. A document that paraphrases rather than references *will*
|
|
33
|
+
diverge — the only question is when it is noticed and what was built on it in the meantime.
|
|
34
|
+
|
|
35
|
+
**No redirect stubs.** A file that exists only to say "go elsewhere" gets cited, and the citation
|
|
36
|
+
looks authoritative. Delete it and repoint the citers.
|
|
37
|
+
|
|
38
|
+
**Review outbound links on every edit.** After editing, confirm the documents you link to still
|
|
39
|
+
say what you are citing them for. This is the step that catches drift while it is cheap.
|
|
40
|
+
|
|
41
|
+
## Scope headers
|
|
42
|
+
|
|
43
|
+
Every document opens with both blocks, so a reader who opened the wrong file learns it in one line:
|
|
44
|
+
|
|
45
|
+
```markdown
|
|
46
|
+
**Authoritative for:** …
|
|
47
|
+
**Not authoritative for:** …
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
The repository root `README.md` is exempt — it is a navigation page.
|
|
51
|
+
|
|
52
|
+
**A stub may contain only scope headers and cross-references** until it is explicitly promoted.
|
|
53
|
+
This is what stops a placeholder quietly accreting content it does not own.
|
|
54
|
+
|
|
55
|
+
## When two documents both claim a topic
|
|
56
|
+
|
|
57
|
+
Do not split the difference, and do not pick the better-written one. Decide which is the **owner**,
|
|
58
|
+
move the content there, and leave a link. A topic owned by two documents is owned by neither, and
|
|
59
|
+
the next reader will find whichever one search puts first.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
**Authoritative for:** which cross-cutting working rules exist, and which surfaces restate each one.
|
|
2
|
+
**Not authoritative for:** why any rule is what it is — that lives in the rule's own authority document.
|
|
3
|
+
|
|
4
|
+
# Working rules
|
|
5
|
+
|
|
6
|
+
A **working rule** is how we do something: how an id is claimed, how a dataset is republished,
|
|
7
|
+
which source outranks which. It is different from a domain fact in one specific way, and it is
|
|
8
|
+
worse: a stale domain fact produces a number someone can check, while **a stale working rule
|
|
9
|
+
produces someone confidently doing the retired thing, correctly, all day.**
|
|
10
|
+
|
|
11
|
+
A working rule lives in more places than its authority:
|
|
12
|
+
|
|
13
|
+
| Surface | What it holds | What keeps it true |
|
|
14
|
+
| --- | --- | --- |
|
|
15
|
+
| the **authority document** | the rule, and *why* — the only place the reasoning belongs | review |
|
|
16
|
+
| the **always-on document** | the one-paragraph version every session reads | this registry's gate |
|
|
17
|
+
| the **skills that execute it** | the step-by-step form, where it is actually met | this registry's gate |
|
|
18
|
+
| the **tool that automates it** | the rule made mechanical, and its self-test | its own gate |
|
|
19
|
+
|
|
20
|
+
## The registry
|
|
21
|
+
|
|
22
|
+
Each rule is declared with the surfaces that restate it. A surface that **engages the topic** must
|
|
23
|
+
carry the rule's current vocabulary and must not carry the retired instruction.
|
|
24
|
+
|
|
25
|
+
| Rule | Authority | Surfaces that restate it | Retired wording |
|
|
26
|
+
| --- | --- | --- | --- |
|
|
27
|
+
| *(example)* Ids are claimed on your own branch | `docs/backlog/README.md` | `AGENTS.md`, `skills/work-item` | "claim on the integration branch" |
|
|
28
|
+
| | | | |
|
|
29
|
+
|
|
30
|
+
## Four rules about changing a rule
|
|
31
|
+
|
|
32
|
+
1. **Fix the authority first, then the citers.** A citer corrected against a stale authority is a
|
|
33
|
+
second wrong statement, and the next reader cannot tell which one won.
|
|
34
|
+
2. **A citation is not propagation.** *"See §3"* ages into a **false claim** the moment §3 changes
|
|
35
|
+
— and it reads as verified precisely because it names a source.
|
|
36
|
+
3. **Declare the rule here** when it is cross-cutting, with its surfaces. That is what turns "we
|
|
37
|
+
should remember to update the skills" into a build failure. Adding a cross-cutting rule without
|
|
38
|
+
a row is how the next several get missed.
|
|
39
|
+
4. **A tool that invalidates a generated artifact must say so in its own output.** The standing
|
|
40
|
+
rule: **a green check means "not yet regenerated", never "current".**
|
|
41
|
+
|
|
42
|
+
## Exemptions
|
|
43
|
+
|
|
44
|
+
Prose that *quotes* a retired rule to explain why it was retired is exempted by name and reason:
|
|
45
|
+
|
|
46
|
+
```markdown
|
|
47
|
+
<!-- working-rule-ok: <rule-id> — quotes the retired wording to explain the change -->
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**The marker is ignored unless it states a reason.** An escape hatch nobody has to justify is not
|
|
51
|
+
an escape hatch, it is an off switch. Never exempt by rewriting the history.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<!-- rungs:begin doc-authority@1.0.0 -->
|
|
2
|
+
## Document ownership
|
|
3
|
+
|
|
4
|
+
Before writing content whose home is unclear, resolve it in
|
|
5
|
+
[`{{registry_path}}`]({{registry_path}}) — it is the arbiter, and its third column says where a
|
|
6
|
+
topic must **not** appear. **No fact is stated in two documents, and paraphrase is not permitted**:
|
|
7
|
+
a paraphrase looks maintained while it decays. Changing a cross-cutting working rule means updating
|
|
8
|
+
every surface in [`{{rules_path}}`]({{rules_path}}) — **a citation is not propagation.**
|
|
9
|
+
<!-- rungs:end doc-authority -->
|