@bompus/house-rules 0.0.0-stage → 0.11.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.
Files changed (191) hide show
  1. package/CONTRIBUTING.md +54 -0
  2. package/LICENSE +21 -0
  3. package/README.md +347 -2
  4. package/THIRD_PARTY_NOTICES.md +188 -0
  5. package/compose.mjs +119 -0
  6. package/composition.mjs +353 -0
  7. package/config-view.mjs +194 -0
  8. package/config.mjs +320 -0
  9. package/docs/assets/banner-dark.png +0 -0
  10. package/docs/assets/banner-light.png +0 -0
  11. package/docs/assets/overview.png +0 -0
  12. package/docs/configuration.md +193 -0
  13. package/docs/npm-release.md +45 -0
  14. package/docs/skill-names.md +50 -0
  15. package/examples/person/house-rules.json +15 -0
  16. package/examples/person/rules/10-tooling.md +8 -0
  17. package/examples/person/rules/20-reporting.md +9 -0
  18. package/examples/project/AGENTS.md +18 -0
  19. package/package.json +40 -4
  20. package/rule-references.mjs +91 -0
  21. package/rules/core.md +431 -0
  22. package/rules/modifiers/coded-offers.md +40 -0
  23. package/rules/modifiers/effort-estimates.md +41 -0
  24. package/rules/modifiers/land-when-done.md +12 -0
  25. package/rules/modifiers/low-quota-handoff.md +19 -0
  26. package/rules/modifiers/multi-agent.md +34 -0
  27. package/rules/modifiers/no-attribution.md +10 -0
  28. package/rules/modifiers/plan-files.md +50 -0
  29. package/rules/modifiers/question-cards.md +48 -0
  30. package/rules/modifiers/release-batching.md +13 -0
  31. package/rules/modifiers/scratch-on-disk.md +15 -0
  32. package/rules/modifiers/shared-host-load.md +48 -0
  33. package/rules/modifiers/solo-operator.md +13 -0
  34. package/rules/modifiers/squash-landing.md +22 -0
  35. package/rules/modifiers/swarmail.md +12 -0
  36. package/rules/references/github-actions.md +45 -0
  37. package/rules/references/landing.md +39 -0
  38. package/rules/references/multi-agent.md +47 -0
  39. package/rules/references/release-batching.md +43 -0
  40. package/rules/references/squash-landing.md +44 -0
  41. package/setup.mjs +375 -0
  42. package/skills/hr-agent-guidance-audit/LICENSE +21 -0
  43. package/skills/hr-agent-guidance-audit/SKILL.md +86 -0
  44. package/skills/hr-agent-guidance-audit/agents/openai.yaml +2 -0
  45. package/skills/hr-agent-guidance-audit/references/records-and-lessons.md +60 -0
  46. package/skills/hr-agent-guidance-refresh/LICENSE +21 -0
  47. package/skills/hr-agent-guidance-refresh/SKILL.md +68 -0
  48. package/skills/hr-agent-guidance-refresh/agents/openai.yaml +2 -0
  49. package/skills/hr-agent-guidance-refresh/references/verification.md +47 -0
  50. package/skills/hr-api-exposure-check/SKILL.md +159 -0
  51. package/skills/hr-api-exposure-check/agents/openai.yaml +3 -0
  52. package/skills/hr-audit-choices/SKILL.md +67 -0
  53. package/skills/hr-audit-choices/agents/openai.yaml +2 -0
  54. package/skills/hr-benchmarking/NOTICE.md +36 -0
  55. package/skills/hr-benchmarking/SKILL.md +316 -0
  56. package/skills/hr-benchmarking/agents/openai.yaml +2 -0
  57. package/skills/hr-benchmarking/references/javascript.md +17 -0
  58. package/skills/hr-benchmarking/references/priority-findings-20261006.md +115 -0
  59. package/skills/hr-benchmarking/references/tools.md +160 -0
  60. package/skills/hr-benchmarking/scripts/process-attribution.ps1 +20 -0
  61. package/skills/hr-benchmarking/scripts/process-attribution.py +107 -0
  62. package/skills/hr-better-accessibility/LICENSE +22 -0
  63. package/skills/hr-better-accessibility/NOTICE.md +46 -0
  64. package/skills/hr-better-accessibility/SKILL.md +119 -0
  65. package/skills/hr-better-accessibility/agents/openai.yaml +3 -0
  66. package/skills/hr-better-accessibility/focus-and-keyboard.md +130 -0
  67. package/skills/hr-better-accessibility/forms.md +82 -0
  68. package/skills/hr-better-accessibility/hit-areas.md +98 -0
  69. package/skills/hr-better-accessibility/motion-and-zoom.md +81 -0
  70. package/skills/hr-better-accessibility/screen-readers.md +101 -0
  71. package/skills/hr-better-accessibility/semantics-and-aria.md +84 -0
  72. package/skills/hr-better-colors/LICENSE +21 -0
  73. package/skills/hr-better-colors/NOTICE.md +37 -0
  74. package/skills/hr-better-colors/SKILL.md +114 -0
  75. package/skills/hr-better-colors/agents/openai.yaml +3 -0
  76. package/skills/hr-better-colors/color-formats.md +90 -0
  77. package/skills/hr-better-colors/color-usage.md +140 -0
  78. package/skills/hr-better-colors/contrast.md +80 -0
  79. package/skills/hr-better-colors/palette-generation.md +104 -0
  80. package/skills/hr-better-colors/palette-structure.md +76 -0
  81. package/skills/hr-better-colors/token-naming.md +97 -0
  82. package/skills/hr-better-layout/LICENSE +22 -0
  83. package/skills/hr-better-layout/NOTICE.md +48 -0
  84. package/skills/hr-better-layout/SKILL.md +92 -0
  85. package/skills/hr-better-layout/agents/openai.yaml +3 -0
  86. package/skills/hr-better-layout/grouping-and-alignment.md +144 -0
  87. package/skills/hr-better-layout/spacing-and-adaptivity.md +215 -0
  88. package/skills/hr-better-typography/LICENSE +22 -0
  89. package/skills/hr-better-typography/NOTICE.md +46 -0
  90. package/skills/hr-better-typography/SKILL.md +172 -0
  91. package/skills/hr-better-typography/agents/openai.yaml +3 -0
  92. package/skills/hr-better-typography/choosing-fonts.md +69 -0
  93. package/skills/hr-better-typography/css-cheat-sheet.md +65 -0
  94. package/skills/hr-better-typography/details-and-accessibility.md +119 -0
  95. package/skills/hr-better-typography/spacing-and-sizing.md +131 -0
  96. package/skills/hr-better-typography/variable-fonts-and-opentype.md +105 -0
  97. package/skills/hr-better-typography/wrapping-and-punctuation.md +57 -0
  98. package/skills/hr-better-writing/LICENSE +22 -0
  99. package/skills/hr-better-writing/NOTICE.md +46 -0
  100. package/skills/hr-better-writing/SKILL.md +122 -0
  101. package/skills/hr-better-writing/agents/openai.yaml +3 -0
  102. package/skills/hr-change-impact/SKILL.md +113 -0
  103. package/skills/hr-change-impact/agents/openai.yaml +5 -0
  104. package/skills/hr-code-review/LICENSE +21 -0
  105. package/skills/hr-code-review/NOTICE.md +26 -0
  106. package/skills/hr-code-review/SKILL.md +147 -0
  107. package/skills/hr-code-review/agents/openai.yaml +3 -0
  108. package/skills/hr-code-review/references/evidence.md +57 -0
  109. package/skills/hr-design-exploration/SKILL.md +82 -0
  110. package/skills/hr-design-exploration/agents/openai.yaml +4 -0
  111. package/skills/hr-diagnosing-bugs/NOTICE.md +45 -0
  112. package/skills/hr-diagnosing-bugs/SKILL.md +108 -0
  113. package/skills/hr-diagnosing-bugs/agents/openai.yaml +3 -0
  114. package/skills/hr-diagnosing-bugs/references/reporting.md +72 -0
  115. package/skills/hr-explain-code/NOTICE.md +37 -0
  116. package/skills/hr-explain-code/SKILL.md +110 -0
  117. package/skills/hr-explain-code/agents/openai.yaml +5 -0
  118. package/skills/hr-explain-code/references/helper-brief.md +39 -0
  119. package/skills/hr-extract-shared-steps/SKILL.md +134 -0
  120. package/skills/hr-extract-shared-steps/agents/openai.yaml +3 -0
  121. package/skills/hr-handoff/SKILL.md +91 -0
  122. package/skills/hr-handoff/agents/openai.yaml +5 -0
  123. package/skills/hr-handoff/assets/ledger.example.json +10 -0
  124. package/skills/hr-handoff/assets/receipt.example.json +76 -0
  125. package/skills/hr-handoff/assets/source.example.json +20 -0
  126. package/skills/hr-handoff/references/reconciliation.md +105 -0
  127. package/skills/hr-handoff/scripts/check-reconciliation.mjs +282 -0
  128. package/skills/hr-house-rules-setup/SKILL.md +100 -0
  129. package/skills/hr-house-rules-setup/agents/openai.yaml +5 -0
  130. package/skills/hr-issue-tracker-setup/SKILL.md +49 -0
  131. package/skills/hr-issue-tracker-setup/agents/openai.yaml +5 -0
  132. package/skills/hr-lean-plan/SKILL.md +142 -0
  133. package/skills/hr-lean-plan/agents/openai.yaml +5 -0
  134. package/skills/hr-maintainability-review/SKILL.md +176 -0
  135. package/skills/hr-maintainability-review/agents/openai.yaml +5 -0
  136. package/skills/hr-manual-qa/NOTICE.md +36 -0
  137. package/skills/hr-manual-qa/SKILL.md +37 -0
  138. package/skills/hr-manual-qa/agents/openai.yaml +3 -0
  139. package/skills/hr-navigation-retro/LICENSE +21 -0
  140. package/skills/hr-navigation-retro/NOTICE.md +5 -0
  141. package/skills/hr-navigation-retro/SKILL.md +86 -0
  142. package/skills/hr-navigation-retro/agents/openai.yaml +4 -0
  143. package/skills/hr-navigation-retro/references/inputs.md +67 -0
  144. package/skills/hr-navigation-retro/scripts/retro.py +552 -0
  145. package/skills/hr-navigation-retro/scripts/transcripts.py +205 -0
  146. package/skills/hr-ordering-tests/SKILL.md +51 -0
  147. package/skills/hr-ordering-tests/agents/openai.yaml +2 -0
  148. package/skills/hr-plain-prose/SKILL.md +261 -0
  149. package/skills/hr-plain-prose/agents/openai.yaml +3 -0
  150. package/skills/hr-plain-prose/scripts/check.mjs +331 -0
  151. package/skills/hr-pr-followup/LICENSE +21 -0
  152. package/skills/hr-pr-followup/NOTICE.md +9 -0
  153. package/skills/hr-pr-followup/SKILL.md +71 -0
  154. package/skills/hr-pr-followup/agents/openai.yaml +2 -0
  155. package/skills/hr-pr-followup/references/review-helpers.md +90 -0
  156. package/skills/hr-pr-followup/scripts/check-pr-review.ts +510 -0
  157. package/skills/hr-pr-followup/scripts/resolve-pr-thread.ts +219 -0
  158. package/skills/hr-progress-report/SKILL.md +49 -0
  159. package/skills/hr-progress-report/agents/openai.yaml +5 -0
  160. package/skills/hr-read-reddit/SKILL.md +44 -0
  161. package/skills/hr-read-reddit/agents/openai.yaml +2 -0
  162. package/skills/hr-read-reddit/scripts/reddit.ts +252 -0
  163. package/skills/hr-read-x-links/SKILL.md +42 -0
  164. package/skills/hr-read-x-links/agents/openai.yaml +2 -0
  165. package/skills/hr-read-x-links/scripts/x-post.ts +264 -0
  166. package/skills/hr-split-to-prs/LICENSE +21 -0
  167. package/skills/hr-split-to-prs/NOTICE.md +9 -0
  168. package/skills/hr-split-to-prs/SKILL.md +61 -0
  169. package/skills/hr-split-to-prs/agents/openai.yaml +2 -0
  170. package/skills/hr-stock-ui-audit/SKILL.md +132 -0
  171. package/skills/hr-stock-ui-audit/agents/openai.yaml +3 -0
  172. package/skills/hr-stock-ui-audit/scripts/scan.mjs +619 -0
  173. package/skills/hr-tdd/LICENSE +21 -0
  174. package/skills/hr-tdd/NOTICE.md +10 -0
  175. package/skills/hr-tdd/SKILL.md +40 -0
  176. package/skills/hr-tdd/agents/openai.yaml +3 -0
  177. package/skills/hr-tdd/mocking.md +39 -0
  178. package/skills/hr-tdd/tests.md +77 -0
  179. package/skills/hr-test-audit/SKILL.md +146 -0
  180. package/skills/hr-test-audit/agents/openai.yaml +3 -0
  181. package/skills/hr-test-audit/scripts/floor-guard.ts +137 -0
  182. package/skills/hr-what-next/SKILL.md +61 -0
  183. package/skills/hr-what-next/agents/openai.yaml +4 -0
  184. package/skills/hr-what-next/references/reporting.md +68 -0
  185. package/skills/hr-writing-for-agents/NOTICE.md +46 -0
  186. package/skills/hr-writing-for-agents/SKILL-MECHANICS.md +83 -0
  187. package/skills/hr-writing-for-agents/SKILL.md +108 -0
  188. package/skills/hr-writing-for-agents/agents/openai.yaml +3 -0
  189. package/skills/hr-writing-pr/NOTICE.md +48 -0
  190. package/skills/hr-writing-pr/SKILL.md +104 -0
  191. package/skills/hr-writing-pr/agents/openai.yaml +2 -0
@@ -0,0 +1,282 @@
1
+ #!/usr/bin/env node
2
+ // Offline accounting check. See ../references/reconciliation.md for the input
3
+ // format and limits. No writes, host queries, or semantic-completeness claim.
4
+ // Exit 0: mechanically complete; 1: invalid/unreadable; 2: valid but partial.
5
+ import { readFileSync } from "node:fs";
6
+ import { dirname, resolve } from "node:path";
7
+
8
+ const errors = [];
9
+ const gaps = [];
10
+ const object = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
11
+ const text = (value) => typeof value === "string" && value.trim().length > 0;
12
+ const states = ["done", "open", "deferred"];
13
+ const key = (source, record) => JSON.stringify([source, record]);
14
+
15
+ function requireText(value, label) {
16
+ if (!text(value)) errors.push(`${label}: expected nonempty text`);
17
+ }
18
+
19
+ function list(value, label) {
20
+ if (Array.isArray(value)) return value;
21
+ errors.push(`${label}: expected an array`);
22
+ return [];
23
+ }
24
+
25
+ function entries(value, label) {
26
+ return list(value, label).filter((entry, index) => {
27
+ if (object(entry)) return true;
28
+ errors.push(`${label}[${index}]: expected an object`);
29
+ return false;
30
+ });
31
+ }
32
+
33
+ function timestamp(value, label) {
34
+ if (
35
+ !text(value) ||
36
+ !/^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d(?:\.\d{1,3})?Z$/.test(value) ||
37
+ !Number.isFinite(Date.parse(value)) ||
38
+ new Date(value).toISOString().slice(0, 19) !== value.slice(0, 19)
39
+ ) {
40
+ errors.push(`${label}: expected a UTC ISO timestamp`);
41
+ return 0;
42
+ }
43
+ return Date.parse(value);
44
+ }
45
+
46
+ function unique(entries, label) {
47
+ const map = new Map();
48
+ for (const entry of entries) {
49
+ requireText(entry.id, `${label}.id`);
50
+ if (map.has(entry.id)) errors.push(`${label}: duplicate id ${entry.id}`);
51
+ map.set(entry.id, entry);
52
+ }
53
+ return map;
54
+ }
55
+
56
+ function check(receipt, directory) {
57
+ if (!object(receipt)) throw new Error("receipt: expected an object");
58
+ if (receipt.version !== 1) errors.push("version: expected 1");
59
+ requireText(receipt.owner, "owner");
60
+ if (!["complete", "partial"].includes(receipt.coverage)) {
61
+ errors.push("coverage: expected complete or partial");
62
+ }
63
+ const sources = unique(entries(receipt.sources, "sources"), "source");
64
+ const items = unique(entries(receipt.items, "items"), "item");
65
+ const records = new Map();
66
+ const outcomes = new Map();
67
+ if (sources.size === 0) gaps.push("no source snapshots supplied");
68
+
69
+ for (const source of sources.values()) {
70
+ const label = `source ${source.id}`;
71
+ requireText(source.file, `${label}.file`);
72
+ requireText(source.snapshot, `${label}.snapshot`);
73
+ const declared = list(source.gaps, `${label}.gaps`);
74
+ for (const gap of declared) {
75
+ requireText(gap, `${label}.gaps[]`);
76
+ gaps.push(`${label}: ${gap}`);
77
+ }
78
+ if (source.expectedRecords === null) gaps.push(`${label}: total unknown`);
79
+ else if (!Number.isSafeInteger(source.expectedRecords) || source.expectedRecords < 0) {
80
+ errors.push(`${label}.expectedRecords: expected a nonnegative integer or null`);
81
+ }
82
+ if (!text(source.file)) continue;
83
+ let raw;
84
+ try {
85
+ raw = readFileSync(resolve(directory, source.file), "utf8");
86
+ } catch (error) {
87
+ if (error.code === "ENOENT" && declared.length > 0) {
88
+ gaps.push(`${label}: snapshot unavailable`);
89
+ } else errors.push(`${label}: cannot read snapshot (${error.code ?? "error"})`);
90
+ continue;
91
+ }
92
+ let data;
93
+ try {
94
+ data = JSON.parse(raw);
95
+ } catch {
96
+ errors.push(`${label}: invalid snapshot JSON`);
97
+ continue;
98
+ }
99
+ if (!object(data)) {
100
+ errors.push(`${label}: snapshot must be an object`);
101
+ continue;
102
+ }
103
+ const rows = unique(entries(data.records, `${label}.records`), `${label} record`);
104
+ if (Number.isSafeInteger(source.expectedRecords) && source.expectedRecords !== rows.size) {
105
+ gaps.push(`${label}: expected ${source.expectedRecords} records, read ${rows.size}`);
106
+ }
107
+ for (const row of rows.values()) {
108
+ requireText(row.text, `${label} record ${row.id}.text`);
109
+ records.set(key(source.id, row.id), row);
110
+ if (row.outcome !== undefined) {
111
+ if (!object(row.outcome) || !states.includes(row.outcome.state)) {
112
+ errors.push(`${label} record ${row.id}: invalid outcome`);
113
+ continue;
114
+ }
115
+ const item = row.outcome.item;
116
+ if (!items.has(item))
117
+ errors.push(`${label} record ${row.id}: unknown outcome item ${item}`);
118
+ const at = timestamp(row.at, `${label} record ${row.id}.at`);
119
+ const events = outcomes.get(item) ?? [];
120
+ events.push({ ...row.outcome, at, source: source.id, record: row.id });
121
+ outcomes.set(item, events);
122
+ }
123
+ }
124
+ }
125
+
126
+ const accounting = new Map();
127
+ for (const row of entries(receipt.accounting, "accounting")) {
128
+ const ref = key(row.source, row.record);
129
+ const label = `accounting ${row.source}/${row.record}`;
130
+ if (!records.has(ref)) errors.push(`${label}: unknown source record`);
131
+ if (accounting.has(ref)) errors.push(`${label}: duplicate accounting`);
132
+ const mapped = row.items === undefined ? [] : list(row.items, `${label}.items`);
133
+ if (mapped.length > 0 === text(row.context)) {
134
+ errors.push(`${label}: supply item IDs or a context-only reason`);
135
+ }
136
+ for (const item of mapped) {
137
+ if (!items.has(item)) errors.push(`${label}: unknown item ${item}`);
138
+ }
139
+ accounting.set(ref, mapped);
140
+ }
141
+ for (const ref of records.keys()) {
142
+ if (!accounting.has(ref)) errors.push(`unaccounted record ${ref}`);
143
+ }
144
+
145
+ function evidence(value, label, item) {
146
+ const refs = entries(value, `${label}.evidence`);
147
+ if (refs.length === 0) errors.push(`${label}: evidence required`);
148
+ for (const ref of refs) {
149
+ const id = key(ref.source, ref.record);
150
+ if (!records.has(id)) errors.push(`${label}: unknown evidence ${id}`);
151
+ if (item && !accounting.get(id)?.includes(item)) {
152
+ errors.push(`${label}: evidence ${id} is not mapped to this item`);
153
+ }
154
+ }
155
+ }
156
+
157
+ for (const item of items.values()) {
158
+ const label = `item ${item.id}`;
159
+ requireText(item.owner, `${label}.owner`);
160
+ requireText(item.next, `${label}.next`);
161
+ if (!states.includes(item.state)) errors.push(`${label}: invalid state`);
162
+ evidence(item.evidence, label, item.id);
163
+ const events = (outcomes.get(item.id) ?? []).sort((a, b) => a.at - b.at);
164
+ for (let i = 0; i < events.length; i++) {
165
+ const event = events[i];
166
+ if (!accounting.get(key(event.source, event.record))?.includes(item.id)) {
167
+ errors.push(`${label}: outcome ${event.source}/${event.record} is not mapped to this item`);
168
+ }
169
+ const previous = events[i - 1];
170
+ if (previous?.at === event.at && previous.state !== event.state) {
171
+ errors.push(`${label}: conflicting outcomes at the same time`);
172
+ }
173
+ if (previous?.state === "done" && event.state !== "done" && event.reopens !== true) {
174
+ errors.push(`${label}: reopening needs a declared later request or contradictory evidence`);
175
+ }
176
+ }
177
+ const latest = events.at(-1);
178
+ if (latest && latest.state !== item.state) {
179
+ errors.push(`${label}: state ${item.state} contradicts latest outcome ${latest.state}`);
180
+ }
181
+ if (
182
+ latest &&
183
+ !item.evidence?.some?.((ref) => ref.source === latest.source && ref.record === latest.record)
184
+ ) {
185
+ errors.push(`${label}: latest outcome must be included in its evidence`);
186
+ }
187
+ }
188
+
189
+ const git = receipt.git;
190
+ if (!object(git)) errors.push("git: explicit inventory required, including when empty");
191
+ else {
192
+ requireText(git.scope, "git.scope");
193
+ timestamp(git.checkedAt, "git.checkedAt");
194
+ evidence(git.evidence, "git");
195
+ const seen = new Set();
196
+ for (const group of ["ownedPrs", "dependencyPrs"]) {
197
+ for (const [index, pr] of list(git[group], `git.${group}`).entries()) {
198
+ const label = `git.${group}[${index}]`;
199
+ if (!object(pr)) {
200
+ errors.push(`${label}: expected an object`);
201
+ continue;
202
+ }
203
+ let url;
204
+ try {
205
+ url = new URL(pr.url);
206
+ } catch {
207
+ errors.push(`${label}: invalid URL`);
208
+ }
209
+ if (url && (url.protocol !== "https:" || url.username || url.password)) {
210
+ errors.push(`${label}: expected an HTTPS URL without credentials`);
211
+ }
212
+ if (seen.has(pr.url)) errors.push(`${label}: duplicate PR across inventories`);
213
+ seen.add(pr.url);
214
+ requireText(pr.owner, `${label}.owner`);
215
+ if (group === "ownedPrs" && pr.owner !== receipt.owner) {
216
+ errors.push(`${label}: owner differs from receipt owner; record dependencies separately`);
217
+ }
218
+ if (group === "dependencyPrs" && pr.owner === receipt.owner) {
219
+ errors.push(`${label}: self-owned PR belongs in ownedPrs`);
220
+ }
221
+ if (!["open", "closed", "merged"].includes(pr.state))
222
+ errors.push(`${label}: invalid state`);
223
+ timestamp(pr.checkedAt, `${label}.checkedAt`);
224
+ requireText(pr.next, `${label}.next`);
225
+ evidence(pr.evidence, label);
226
+ }
227
+ }
228
+ for (const [index, commit] of list(git.commitsWithoutPr, "git.commitsWithoutPr").entries()) {
229
+ const label = `git.commitsWithoutPr[${index}]`;
230
+ if (!object(commit)) {
231
+ errors.push(`${label}: expected an object`);
232
+ continue;
233
+ }
234
+ for (const field of ["repo", "branch", "sha", "next"])
235
+ requireText(commit[field], `${label}.${field}`);
236
+ if (typeof commit.sha !== "string" || !/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/i.test(commit.sha)) {
237
+ errors.push(`${label}: expected a full Git object ID`);
238
+ }
239
+ if (commit.owner !== receipt.owner) errors.push(`${label}: owner differs from receipt owner`);
240
+ evidence(commit.evidence, label);
241
+ }
242
+ }
243
+ if (receipt.coverage === "complete" && gaps.length > 0) {
244
+ errors.push("complete coverage claimed despite source gaps");
245
+ }
246
+ if (receipt.coverage === "partial" && gaps.length === 0)
247
+ gaps.push("receipt declares partial coverage");
248
+ return { sources: sources.size, records: records.size, items: items.size };
249
+ }
250
+
251
+ let counts = { sources: 0, records: 0, items: 0 };
252
+ try {
253
+ if (process.argv.length !== 3) throw new Error("usage: check-reconciliation.mjs <receipt.json>");
254
+ const file = resolve(process.argv[2]);
255
+ counts = check(JSON.parse(readFileSync(file, "utf8")), dirname(file));
256
+ } catch (error) {
257
+ // Avoid printing an input excerpt from JSON.parse or filesystem paths.
258
+ errors.push(
259
+ error instanceof SyntaxError
260
+ ? "receipt: invalid JSON"
261
+ : error.code
262
+ ? `receipt: ${error.code}`
263
+ : error.message,
264
+ );
265
+ }
266
+ const partial = gaps.length > 0;
267
+ const status = errors.length > 0 ? "invalid" : partial ? "partial" : "complete";
268
+ console.log(
269
+ JSON.stringify(
270
+ {
271
+ status,
272
+ ...counts,
273
+ errors,
274
+ gaps,
275
+ limits:
276
+ "Checks supplied accounting and declared outcomes only. Does not prove semantic completeness, export completeness, live PR state, or landing authorization.",
277
+ },
278
+ null,
279
+ 2,
280
+ ),
281
+ );
282
+ process.exitCode = errors.length > 0 ? 1 : partial ? 2 : 0;
@@ -0,0 +1,100 @@
1
+ ---
2
+ name: hr-house-rules-setup
3
+ description: "Set up or change house rules for this user: pick opt-in modifiers, create the personal layer, compose the rules file and connect it to each agent host. Use when the user asks to install, configure or change their house rules or modifiers."
4
+ disable-model-invocation: true
5
+ ---
6
+
7
+ # House rules setup
8
+
9
+ The user's choices live in a personal layer, by default
10
+ `~/.config/house-rules/`: `house-rules.json`, plus optional `rules/` and
11
+ `skills/` directories. Composition never edits host files; connecting a host
12
+ is a separate step the user approves.
13
+
14
+ The commands below use `node`. If only Bun is installed, replace `node` with
15
+ `bun` in every `compose.mjs` command.
16
+
17
+ For a user working directly in a terminal, use `node compose.mjs setup` for
18
+ numbered selection, review and explicit save. Read `docs/configuration.md`
19
+ for commands and safeguards. Saving completes only configuration selection;
20
+ composition and approved host connection remain separate steps below.
21
+ For an agent running without a terminal, use the preview/apply workflow below.
22
+
23
+ 1. Find the house-rules checkout (the directory holding `compose.mjs`;
24
+ `install.sh` puts it in `${XDG_DATA_HOME:-~/.local/share}/house-rules`,
25
+ `install.ps1` in `%LOCALAPPDATA%\house-rules`).
26
+ When there is none, ask the user where to clone it.
27
+ Done when the checkout and the user's config path are known.
28
+ 2. Inspect `node compose.mjs config status --config <path>`.
29
+ If status reports legacy skill names, stop inspection. Read the config and follow
30
+ `docs/skill-names.md` before continuing. If catalog or status reports
31
+ `unknown modifier` or `question-cards requires coded-offers`, read the config
32
+ for current choices. Follow `docs/configuration.md` and preview the user's
33
+ selection in step 3
34
+ with `--disable-modifier <name>` or `--questions`.
35
+ For any other status failure, show the error and preserve the existing file
36
+ or filesystem object. Show skill choices from the README's Skills table and
37
+ known personal skill directories. Stop before `config set`, composition or
38
+ host writes. Ask the user how to repair the configuration or select a usable
39
+ path. For `config must be a regular file, not a symlink or directory`, ask
40
+ them to select or create a regular config file. Resume this step after the
41
+ agreed repair or selected path passes `config validate`.
42
+ Preserve existing selections. Fresh installers use the sample config's small
43
+ skill set with no modifiers. When initializing a missing config manually,
44
+ show `examples/person/house-rules.json` and get the user's selection before
45
+ creating it; the CLI treats a missing config as the legacy full skill set.
46
+ Explain the current selection briefly. Ask about additions only when the
47
+ user's stated workflow needs them, or they ask to customize it. Use
48
+ `config catalog` to find those choices; show the full catalog only on request.
49
+ Personal skills override shipped ones. Keep independent names, layers,
50
+ unrelated settings and exclusions.
51
+
52
+ If the user wants Swarmail, offer its modifier. When its MCP tools are absent,
53
+ point to https://github.com/bompus/swarmail and ask before installing it.
54
+ If they want different question presentation, offer plain text, coded text,
55
+ or coded text with cards using `--questions plain|coded|cards`. Cards still
56
+ require a complete text offer. Explain that host, provider and model behavior
57
+ varies; card availability and presentation are unverified until checked.
58
+ Both text-only choices prohibit card tools. If personal rules still request
59
+ cards, resolve that conflict before changing those rules or composing.
60
+ Done when the user selects relevant changes or keeps the current setup.
61
+
62
+ 3. Use `node compose.mjs config set --config <path>` with the selected
63
+ modifier and skill toggles or question preset. It previews
64
+ without saving. Read every change and personal rule override; resolve any
65
+ conflict before writing. Repeat those flags with `--apply --expect <revision>`
66
+ using the preview's revision. Done when it exits 0 and status confirms the
67
+ selection. A stale revision requires a fresh preview, not a blind retry.
68
+ The CLI preserves layers, custom settings and unrelated exclusions; it
69
+ does not edit personal rule prose or connect hosts. Read
70
+ `docs/configuration.md` for flags, write safeguards and lock recovery.
71
+ Create `rules/` and `skills/` beside the config when the user needs them.
72
+ Explain the file format once. A rules file starts with frontmatter naming
73
+ `replaces:`, `after:`, `before:` or `removes:` and a core heading, then its
74
+ own `## ` heading; without frontmatter it is appended at the end. A skill in
75
+ the personal `skills/` replaces the house-rules skill of the same name.
76
+ 4. Run `node compose.mjs --config <path> --out <personal dir>/rules.md`. Done
77
+ when it exits 0; on an error, show it and fix the named file.
78
+ 5. Ask which hosts the user runs, then show what connecting each one changes
79
+ and ask before writing:
80
+ - Claude Code: add `@<personal dir>/rules.md` on its own line in
81
+ `~/.claude/CLAUDE.md`.
82
+ - Hosts without file includes (for example a global `AGENTS.md`): copy the
83
+ composed file there between `<!-- house-rules:start -->` and
84
+ `<!-- house-rules:end -->` lines, replacing only that block on later runs.
85
+ Place the `house-rules-references/` directory beside that file, because
86
+ the written rules link to it.
87
+ - Hosts with a settings screen for user rules: paste the stdout output of
88
+ `node compose.mjs --config <path>`, which keeps the procedures inline.
89
+ Keep everything else in those files. Done when every chosen host is
90
+ connected or the user declined it.
91
+ 6. For skills, compose them with `--skills-out <dir>` into a directory that
92
+ does not exist yet or is empty; when the previous run's directory is in the
93
+ way, ask before removing it. Then point or copy each host's skills
94
+ directory at it, again with approval.
95
+ 7. When the user asks to evaluate reply behavior, use `evals/end-of-reply/run.mjs`
96
+ against their selected models.
97
+
98
+ For a repository, suggest the pointer pattern in `examples/project/AGENTS.md`:
99
+ point at § End of every reply and § Offers instead of restating them, and add
100
+ only what the project changes.
@@ -0,0 +1,5 @@
1
+ interface:
2
+ display_name: "HR: House Rules Setup"
3
+ short_description: "Choose modifiers, create your layer, connect hosts"
4
+ policy:
5
+ allow_implicit_invocation: false
@@ -0,0 +1,49 @@
1
+ ---
2
+ name: hr-issue-tracker-setup
3
+ description: "Configure a project's issue tracker and conventions for domain terms and architecture decision records (ADRs). Use when explicitly asked to establish or change those conventions; reuse existing configuration. General agent, CI and development-environment setup are outside this workflow."
4
+ disable-model-invocation: true
5
+ ---
6
+
7
+ # Project tracker setup
8
+
9
+ Make the project's tracker and domain-document conventions usable from its
10
+ agent guidance. Configure only the conventions the user requested.
11
+
12
+ ## Establish what already exists
13
+
14
+ Read the project's instruction root and the tracker, glossary and ADR guidance
15
+ it names. Follow existing links before searching for another system. Identify
16
+ which tracker owns tasks, where its records live, and how domain terms and
17
+ decisions are recorded. A convention can be complete without a separate glossary
18
+ or ADR directory.
19
+
20
+ When the requested configuration already works, report its entry points and
21
+ leave it alone. Other workflows do not require this setup to run first.
22
+
23
+ ## Resolve the missing conventions
24
+
25
+ Reuse the project's tracker and document locations. Do not create a second
26
+ tracker, replace an established one or impose glossary/ADR files by default.
27
+ If a choice is missing, ask only for the decisions that change the requested
28
+ configuration, such as which tracker to use or whether to record domain terms.
29
+ Use the active host's decision format and existing user answers.
30
+
31
+ Document how the chosen tracker represents tasks, child decisions, supporting
32
+ evidence, blockers and ready work. Use its existing concepts; add fields or
33
+ structure only when a missing distinction prevents the requested workflow.
34
+
35
+ ## Update and verify the owning guidance
36
+
37
+ Edit the project's canonical guidance in place. Its instruction root should
38
+ point to the tracker and detailed domain-document conventions rather than
39
+ repeat them. Preserve project safeguards and generated loading adapters;
40
+ use their owning generator when an adapter needs updating.
41
+
42
+ Keep shared personal skills in their configured shared source and installation
43
+ scope. Project-only skills belong with the project. This workflow does not
44
+ install skills, configure agent accounts or set up CI and developer tooling.
45
+
46
+ Run the project's applicable guidance checks and verify the documented entry
47
+ points from the intended workspace. Report what changed, what was already
48
+ configured and any decision or check still unresolved. Do not claim runtime
49
+ loading from a file update alone.
@@ -0,0 +1,5 @@
1
+ interface:
2
+ display_name: "HR: Issue tracker setup"
3
+ short_description: "Configure project tracker and domain-doc conventions"
4
+ policy:
5
+ allow_implicit_invocation: false
@@ -0,0 +1,142 @@
1
+ ---
2
+ name: hr-lean-plan
3
+ description: "Draft or critique an implementation plan in the conversation, with the fewest moving parts, before implementation. Use when asked to plan, review or tighten an approach without changing files or writing code; saving a plan file or implementing the work falls outside this read-only mode."
4
+ disable-model-invocation: true
5
+ ---
6
+
7
+ # Lean plan
8
+
9
+ A plan is cheap to change and code is not. This skill spends effort on the plan
10
+ so the build has less to do. It has two modes with one bar: **write** a plan
11
+ from a request, or **critique** a plan someone else wrote. Either way the output
12
+ is a plan or a review of one; change no files and write no code here.
13
+
14
+ The bar: every step produces an outcome someone can check, and no step survives
15
+ that the goal does not need.
16
+
17
+ ## Pin the goal
18
+
19
+ Before drafting steps, write down four things:
20
+
21
+ - **Done means**: what is observably true when the work is finished.
22
+ - **Out of scope**: what a reader might expect but this work will not do.
23
+ - **Must not regress**: behavior, data, interfaces or performance that has to
24
+ stay as it is.
25
+ - **The real constraint**: the one limit that shapes the plan, such as a
26
+ deadline, a frozen interface, a migration window or a team boundary.
27
+
28
+ Read the code, docs and config that touch the goal before asking anything. Ask
29
+ only questions whose answer would change the plan's shape; a detail you can
30
+ choose sensibly, choose and record as an assumption.
31
+
32
+ Push back on an unclear request before planning around it. Name what is
33
+ unclear, why it blocks a good plan and what would settle it. Common causes:
34
+ a scope verb such as "improve" or "clean up" with no measurable result;
35
+ constraints that conflict; no named owner for the change; a solution stated
36
+ before the problem; a load-bearing decision (schema, API, flag strategy)
37
+ left for implementation; a term with no settled meaning in this codebase;
38
+ several goals with no priority between them.
39
+
40
+ When an answer that decides the shape is missing, stop there: return the
41
+ questions and an outline headed **Provisional, pending answers**, with each
42
+ open question marked where
43
+ it would change a step.
44
+
45
+ ## Shrink before you sequence
46
+
47
+ Look for a framing that deletes work before ordering it:
48
+
49
+ - Can an existing module, table, flag or endpoint carry the new behavior? Grow
50
+ the code that already owns that idea rather than adding a layer, service
51
+ or package for one feature.
52
+ - Does the plan need a migration, feature flag, compatibility shim or a second
53
+ code path running beside the first? Each one has to be built, tested and later
54
+ removed. Keep it only when the must-not-regress list or the real constraint
55
+ requires it, and say which.
56
+ - Would a narrower done-state, agreed with the user, remove a whole phase?
57
+ Offer it rather than assuming it.
58
+ - Does a step increase coupling or state in a cohesive module, or move work
59
+ across its boundary? Name the payoff and the behavior it could affect.
60
+ Route cross-cutting concerns such as auth, caching and error handling through
61
+ their existing owning layer. A new layer needs a reason the current one
62
+ cannot meet the goal.
63
+
64
+ Count the moving parts the plan adds: new files, modules, services,
65
+ dependencies, config keys, flags, migrations, background jobs and parallel
66
+ paths. A smaller count that still meets the done-state is the better plan.
67
+
68
+ ## Write each step
69
+
70
+ Each step carries these slots:
71
+
72
+ - **Outcome**: a concrete state of the system, not an activity. "Orders API
73
+ returns `shipped_at`" is an outcome; "work on the orders API" is not.
74
+ - **Owner**: the person, team, agent or module that holds it.
75
+ - **Check**: how anyone confirms the outcome, tied to that outcome.
76
+ - **Depends on**: earlier steps it needs, or "none".
77
+
78
+ Steps such as "handle edge cases", "refactor as needed", "clean up" or "add
79
+ tests" are defects: name the cases, the refactor or the behavior under test,
80
+ or delete the step. For each step ask what breaks if it is removed; if nothing
81
+ in the done-state does, remove it.
82
+
83
+ Watch for conditionals. Several steps that say "if X, also do Y" mean a
84
+ decision has not been made. Make that decision, or a short time-boxed spike
85
+ whose result is the decision, an explicit early step, then write the later
86
+ steps for the chosen branch only.
87
+
88
+ ## Order and slice
89
+
90
+ - Put the riskiest unknown first, so a bad answer changes the plan while it is
91
+ still cheap.
92
+ - Slice the work into pieces that can each merge on their own and leave the
93
+ system working. A slice that only makes sense once the next one lands is half
94
+ of a larger slice. Splitting the same coupling across pull requests does not
95
+ remove it.
96
+ - Check whether feature-specific behavior leaks into shared runtime paths.
97
+ Keep unrelated callers' behavior intact and name the boundary that does so;
98
+ isolation does not automatically require a new service or wrapper.
99
+ - Mark steps with no dependency between them as parallel.
100
+ - Make each cutover atomic. When it cannot be, name the intermediate state the
101
+ system will sit in, how long it may stay there, and how to roll back from it.
102
+
103
+ ## Verify against outcomes
104
+
105
+ Verification answers "is the done-state true and did nothing on the
106
+ must-not-regress list break?" Map each done-state item to the check that proves
107
+ it: a test of the behavior at its real boundary, a command, a query, a
108
+ measurement or a manual observation with its expected result. "Add tests" with
109
+ no named behavior is not verification. When a check needs a test, `hr-test-audit`
110
+ decides whether that test is worth writing.
111
+
112
+ ## Plan layout
113
+
114
+ Lay the plan out as:
115
+
116
+ 1. Goal: done means, out of scope, must not regress, real constraint.
117
+ 2. Assumptions you made and open questions, if any.
118
+ 3. The approach in two to four sentences, including what you chose not to
119
+ build and why.
120
+ 4. Steps, each with outcome, owner, check and dependencies.
121
+ 5. Cutover and rollback, when the system passes through an intermediate state.
122
+ 6. Moving-part count.
123
+
124
+ Use a table or diagram only when it changes how someone carries out the plan,
125
+ such as a dependency graph that shows which slices run in parallel. Prose and
126
+ lists cover the rest.
127
+
128
+ ## Critiquing a plan
129
+
130
+ Read the plan against the sections above, then report three things in order:
131
+
132
+ 1. **Blocks starting**: missing goal slots, undecided branches, steps with no
133
+ checkable outcome or owner, cutovers with no rollback, feature work routed
134
+ through shared paths with no isolation, and PR splits that leave the same
135
+ coupling in place and cannot merge independently.
136
+ 2. **Would make it smaller**: steps, layers, flags, migrations or parallel paths
137
+ that can go, each with the reason and the moving-part count before and after.
138
+ 3. **Revised outline**: the plan rewritten in the layout above, short enough to
139
+ compare with the original side by side.
140
+
141
+ Quote the plan's own wording when pointing at a defect. When the plan is
142
+ already lean, say so and list only what blocks starting.
@@ -0,0 +1,5 @@
1
+ interface:
2
+ display_name: "HR: Lean plan"
3
+ short_description: "Draft or critique a lean plan without file changes"
4
+ policy:
5
+ allow_implicit_invocation: false