@mrciphersmith/keryx 0.2.98 → 0.2.99

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 (182) hide show
  1. package/dist/cli.js +4057 -2510
  2. package/dist/core.js +39 -1
  3. package/package.json +1 -1
  4. package/src/gdskills/bundled/rules/core/cli-interface-design.mdc +237 -0
  5. package/src/gdskills/bundled/rules/core/definition-of-done.mdc +116 -0
  6. package/src/gdskills/bundled/rules/core/skills-storage-workflow.mdc +101 -11
  7. package/src/gdskills/bundled/rules/core/subagent-status-protocol.md +9 -2
  8. package/src/gdskills/bundled/skills/core/reviewer-skill-creator/SKILL.md +42 -5
  9. package/src/gdskills/bundled/skills/orchestration/code-verifier/SKILL.md +19 -3
  10. package/src/gdskills/bundled/skills/orchestration/context-collector/SKILL.md +20 -4
  11. package/src/gdskills/bundled/skills/orchestration/feature-analyzer/SKILL.md +32 -9
  12. package/src/gdskills/bundled/skills/orchestration/feature-dev/SKILL.md +18 -4
  13. package/src/gdskills/bundled/skills/orchestration/flow-orchestrator/SKILL.md +21 -5
  14. package/src/gdskills/bundled/skills/orchestration/issue-analyzer/SKILL.md +4 -4
  15. package/src/gdskills/bundled/skills/orchestration/issue-analyzer/orchestrator-prompt.md +1 -1
  16. package/src/gdskills/bundled/skills/orchestration/job-documenter/SKILL.md +42 -2
  17. package/src/gdskills/bundled/skills/orchestration/job-orchestrator/SKILL.md +23 -9
  18. package/src/gdskills/bundled/skills/orchestration/task-implementer/SKILL.md +33 -31
  19. package/src/gdskills/bundled/skills/orchestration/task-implementer/output-contract.schema.json +32 -1
  20. package/src/gdskills/bundled/skills/planning/autodoc-analyst/SKILL.md +16 -0
  21. package/src/gdskills/bundled/skills/planning/autodoc-architect/SKILL.md +16 -0
  22. package/src/gdskills/bundled/skills/planning/autodoc-assembler/SKILL.md +16 -0
  23. package/src/gdskills/bundled/skills/planning/autodoc-orchestrator/SKILL.md +17 -0
  24. package/src/gdskills/bundled/skills/planning/autodoc-scanner/SKILL.md +16 -0
  25. package/src/gdskills/bundled/skills/planning/autodoc-writer/SKILL.md +16 -0
  26. package/src/gdskills/bundled/skills/planning/brainstorm/SKILL.md +28 -3
  27. package/src/gdskills/bundled/skills/planning/consistency-checker/SKILL.codex.md +17 -0
  28. package/src/gdskills/bundled/skills/planning/consistency-checker/SKILL.cursor.md +17 -0
  29. package/src/gdskills/bundled/skills/planning/consistency-checker/SKILL.md +17 -0
  30. package/src/gdskills/bundled/skills/planning/docpack-orchestrator/SKILL.md +32 -2
  31. package/src/gdskills/bundled/skills/planning/docpack-review/SKILL.md +14 -2
  32. package/src/gdskills/bundled/skills/planning/interview/SKILL.md +29 -7
  33. package/src/gdskills/bundled/skills/planning/interviewer/SKILL.md +32 -6
  34. package/src/gdskills/bundled/skills/planning/patterns-researcher/SKILL.codex.md +16 -0
  35. package/src/gdskills/bundled/skills/planning/patterns-researcher/SKILL.cursor.md +16 -0
  36. package/src/gdskills/bundled/skills/planning/patterns-researcher/SKILL.md +16 -0
  37. package/src/gdskills/bundled/skills/planning/planner/SKILL.codex.md +17 -0
  38. package/src/gdskills/bundled/skills/planning/planner/SKILL.cursor.md +17 -0
  39. package/src/gdskills/bundled/skills/planning/planner/SKILL.md +17 -0
  40. package/src/gdskills/bundled/skills/planning/prd-creator/SKILL.md +20 -3
  41. package/src/gdskills/bundled/skills/planning/problem-definer/SKILL.codex.md +16 -0
  42. package/src/gdskills/bundled/skills/planning/problem-definer/SKILL.cursor.md +16 -0
  43. package/src/gdskills/bundled/skills/planning/problem-definer/SKILL.md +16 -0
  44. package/src/gdskills/bundled/skills/planning/project-discovery/SKILL.codex.md +16 -0
  45. package/src/gdskills/bundled/skills/planning/project-discovery/SKILL.cursor.md +16 -0
  46. package/src/gdskills/bundled/skills/planning/project-discovery/SKILL.md +16 -0
  47. package/src/gdskills/bundled/skills/planning/spec-writer/SKILL.codex.md +4 -0
  48. package/src/gdskills/bundled/skills/planning/spec-writer/SKILL.cursor.md +4 -0
  49. package/src/gdskills/bundled/skills/planning/spec-writer/SKILL.md +4 -0
  50. package/src/gdskills/bundled/skills/planning/stack-advisor/SKILL.codex.md +4 -0
  51. package/src/gdskills/bundled/skills/planning/stack-advisor/SKILL.cursor.md +4 -0
  52. package/src/gdskills/bundled/skills/planning/stack-advisor/SKILL.md +4 -0
  53. package/src/gdskills/bundled/skills/platform/agent-entrypoint-distiller/SKILL.md +31 -4
  54. package/src/gdskills/bundled/skills/platform/claude-md-management/SKILL.md +26 -2
  55. package/src/gdskills/bundled/skills/platform/hookify/SKILL.md +28 -3
  56. package/src/gdskills/bundled/skills/quality/api-truth/SKILL.md +226 -0
  57. package/src/gdskills/bundled/skills/quality/changelog/SKILL.md +24 -4
  58. package/src/gdskills/bundled/skills/quality/commit/SKILL.md +24 -3
  59. package/src/gdskills/bundled/skills/quality/db-migrate/SKILL.md +24 -3
  60. package/src/gdskills/bundled/skills/quality/dependency-update/SKILL.md +25 -4
  61. package/src/gdskills/bundled/skills/quality/deploy/SKILL.md +26 -3
  62. package/src/gdskills/bundled/skills/quality/deprecation-path/SKILL.md +268 -0
  63. package/src/gdskills/bundled/skills/quality/fresh-eyes/SKILL.md +190 -0
  64. package/src/gdskills/bundled/skills/quality/metaproject-security/SKILL.md +24 -3
  65. package/src/gdskills/bundled/skills/quality/perf-check/SKILL.md +29 -8
  66. package/src/gdskills/bundled/skills/quality/pr/SKILL.md +24 -4
  67. package/src/gdskills/bundled/skills/quality/pr-issue-documenter/SKILL.md +25 -2
  68. package/src/gdskills/bundled/skills/quality/push/SKILL.md +24 -3
  69. package/src/gdskills/bundled/skills/quality/root-cause/SKILL.md +204 -0
  70. package/src/gdskills/bundled/skills/quality/security-audit/SKILL.md +25 -4
  71. package/src/gdskills/bundled/skills/quality/test-gen/SKILL.md +24 -3
  72. package/src/gdskills/bundled/skills/quality/tests-creator/SKILL.md +17 -2
  73. package/src/gdskills/bundled/skills/review/code-ai-review/SKILL.md +40 -5
  74. package/src/gdskills/bundled/skills/review/code-learned-review/SKILL.md +41 -1
  75. package/src/gdskills/bundled/skills/review/code-mobx-store-review/SKILL.md +44 -2
  76. package/src/gdskills/bundled/skills/review/code-style-review/SKILL.md +44 -4
  77. package/src/gdskills/bundled/skills/review/review-architecture/SKILL.md +3 -3
  78. package/src/gdskills/bundled/skills/review/review-backend/SKILL.md +2 -3
  79. package/src/gdskills/bundled/skills/review/review-clean-code/SKILL.md +4 -4
  80. package/src/gdskills/bundled/skills/review/review-core-boundaries/SKILL.md +36 -2
  81. package/src/gdskills/bundled/skills/review/review-flow-graph/SKILL.md +37 -3
  82. package/src/gdskills/bundled/skills/review/review-frontend/SKILL.md +2 -4
  83. package/src/gdskills/bundled/skills/review/review-frontend-conventions/SKILL.md +36 -2
  84. package/src/gdskills/bundled/skills/review/review-highload/SKILL.md +3 -5
  85. package/src/gdskills/bundled/skills/review/review-layout/SKILL.md +23 -2
  86. package/src/gdskills/bundled/skills/review/review-logic/SKILL.md +3 -3
  87. package/src/gdskills/bundled/skills/review/review-orchestrator/SKILL.md +9 -29
  88. package/src/gdskills/bundled/skills/review/review-performance/SKILL.md +9 -9
  89. package/src/gdskills/bundled/skills/review/review-pr-feedback/SKILL.md +3 -2
  90. package/src/gdskills/bundled/skills/review/review-regression/SKILL.md +33 -2
  91. package/src/gdskills/bundled/skills/review/review-security-code/SKILL.md +4 -2
  92. package/src/gdskills/bundled/skills/review/review-style/SKILL.md +2 -2
  93. package/src/gdskills/bundled/skills/review/review-testing-practices/SKILL.md +40 -2
  94. package/src/gdskills/bundled/skills/review/review-verifier/SKILL.md +1 -1
  95. package/src/gdskills/bundled/skills/orchestration/code-verifier/SKILL.codex.md +0 -330
  96. package/src/gdskills/bundled/skills/orchestration/code-verifier/SKILL.cursor.md +0 -330
  97. package/src/gdskills/bundled/skills/orchestration/code-verifier/SKILL.opencode.md +0 -330
  98. package/src/gdskills/bundled/skills/orchestration/code-verifier/SKILL.zed.md +0 -330
  99. package/src/gdskills/bundled/skills/orchestration/context-collector/SKILL.codex.md +0 -655
  100. package/src/gdskills/bundled/skills/orchestration/context-collector/SKILL.cursor.md +0 -655
  101. package/src/gdskills/bundled/skills/orchestration/context-collector/SKILL.opencode.md +0 -655
  102. package/src/gdskills/bundled/skills/orchestration/context-collector/SKILL.zed.md +0 -655
  103. package/src/gdskills/bundled/skills/orchestration/feature-analyzer/SKILL.codex.md +0 -424
  104. package/src/gdskills/bundled/skills/orchestration/feature-analyzer/SKILL.cursor.md +0 -424
  105. package/src/gdskills/bundled/skills/orchestration/feature-analyzer/SKILL.opencode.md +0 -424
  106. package/src/gdskills/bundled/skills/orchestration/feature-analyzer/SKILL.zed.md +0 -424
  107. package/src/gdskills/bundled/skills/orchestration/feature-dev/SKILL.codex.md +0 -163
  108. package/src/gdskills/bundled/skills/orchestration/feature-dev/SKILL.cursor.md +0 -163
  109. package/src/gdskills/bundled/skills/orchestration/issue-analyzer/SKILL.codex.md +0 -373
  110. package/src/gdskills/bundled/skills/orchestration/issue-analyzer/SKILL.cursor.md +0 -373
  111. package/src/gdskills/bundled/skills/orchestration/issue-analyzer/SKILL.opencode.md +0 -373
  112. package/src/gdskills/bundled/skills/orchestration/issue-analyzer/SKILL.zed.md +0 -373
  113. package/src/gdskills/bundled/skills/orchestration/job-documenter/SKILL.codex.md +0 -374
  114. package/src/gdskills/bundled/skills/orchestration/job-documenter/SKILL.cursor.md +0 -374
  115. package/src/gdskills/bundled/skills/orchestration/job-documenter/SKILL.opencode.md +0 -374
  116. package/src/gdskills/bundled/skills/orchestration/job-documenter/SKILL.zed.md +0 -374
  117. package/src/gdskills/bundled/skills/orchestration/job-orchestrator/SKILL.codex.md +0 -2232
  118. package/src/gdskills/bundled/skills/orchestration/job-orchestrator/SKILL.cursor.md +0 -2232
  119. package/src/gdskills/bundled/skills/orchestration/job-orchestrator/SKILL.opencode.md +0 -2232
  120. package/src/gdskills/bundled/skills/orchestration/job-orchestrator/SKILL.zed.md +0 -2232
  121. package/src/gdskills/bundled/skills/orchestration/task-implementer/SKILL.codex.md +0 -668
  122. package/src/gdskills/bundled/skills/orchestration/task-implementer/SKILL.cursor.md +0 -668
  123. package/src/gdskills/bundled/skills/orchestration/task-implementer/SKILL.opencode.md +0 -668
  124. package/src/gdskills/bundled/skills/orchestration/task-implementer/SKILL.zed.md +0 -668
  125. package/src/gdskills/bundled/skills/planning/brainstorm/SKILL.codex.md +0 -90
  126. package/src/gdskills/bundled/skills/planning/brainstorm/SKILL.cursor.md +0 -90
  127. package/src/gdskills/bundled/skills/planning/interview/SKILL.codex.md +0 -187
  128. package/src/gdskills/bundled/skills/planning/interview/SKILL.cursor.md +0 -187
  129. package/src/gdskills/bundled/skills/planning/interviewer/SKILL.codex.md +0 -105
  130. package/src/gdskills/bundled/skills/planning/interviewer/SKILL.cursor.md +0 -105
  131. package/src/gdskills/bundled/skills/planning/prd-creator/SKILL.codex.md +0 -193
  132. package/src/gdskills/bundled/skills/planning/prd-creator/SKILL.cursor.md +0 -193
  133. package/src/gdskills/bundled/skills/planning/prd-creator/SKILL.opencode.md +0 -193
  134. package/src/gdskills/bundled/skills/planning/prd-creator/SKILL.zed.md +0 -193
  135. package/src/gdskills/bundled/skills/platform/claude-md-management/SKILL.codex.md +0 -87
  136. package/src/gdskills/bundled/skills/platform/claude-md-management/SKILL.cursor.md +0 -87
  137. package/src/gdskills/bundled/skills/platform/hookify/SKILL.codex.md +0 -100
  138. package/src/gdskills/bundled/skills/platform/hookify/SKILL.cursor.md +0 -100
  139. package/src/gdskills/bundled/skills/quality/changelog/SKILL.codex.md +0 -84
  140. package/src/gdskills/bundled/skills/quality/changelog/SKILL.cursor.md +0 -84
  141. package/src/gdskills/bundled/skills/quality/commit/SKILL.codex.md +0 -66
  142. package/src/gdskills/bundled/skills/quality/commit/SKILL.cursor.md +0 -66
  143. package/src/gdskills/bundled/skills/quality/db-migrate/SKILL.codex.md +0 -66
  144. package/src/gdskills/bundled/skills/quality/db-migrate/SKILL.cursor.md +0 -66
  145. package/src/gdskills/bundled/skills/quality/dependency-update/SKILL.codex.md +0 -81
  146. package/src/gdskills/bundled/skills/quality/dependency-update/SKILL.cursor.md +0 -81
  147. package/src/gdskills/bundled/skills/quality/deploy/SKILL.codex.md +0 -70
  148. package/src/gdskills/bundled/skills/quality/deploy/SKILL.cursor.md +0 -70
  149. package/src/gdskills/bundled/skills/quality/perf-check/SKILL.codex.md +0 -83
  150. package/src/gdskills/bundled/skills/quality/perf-check/SKILL.cursor.md +0 -83
  151. package/src/gdskills/bundled/skills/quality/pr/SKILL.codex.md +0 -75
  152. package/src/gdskills/bundled/skills/quality/pr/SKILL.cursor.md +0 -75
  153. package/src/gdskills/bundled/skills/quality/pr-issue-documenter/SKILL.codex.md +0 -378
  154. package/src/gdskills/bundled/skills/quality/pr-issue-documenter/SKILL.cursor.md +0 -378
  155. package/src/gdskills/bundled/skills/quality/pr-issue-documenter/SKILL.opencode.md +0 -378
  156. package/src/gdskills/bundled/skills/quality/pr-issue-documenter/SKILL.zed.md +0 -378
  157. package/src/gdskills/bundled/skills/quality/push/SKILL.codex.md +0 -52
  158. package/src/gdskills/bundled/skills/quality/push/SKILL.cursor.md +0 -52
  159. package/src/gdskills/bundled/skills/quality/security-audit/SKILL.codex.md +0 -108
  160. package/src/gdskills/bundled/skills/quality/security-audit/SKILL.cursor.md +0 -108
  161. package/src/gdskills/bundled/skills/quality/test-gen/SKILL.codex.md +0 -80
  162. package/src/gdskills/bundled/skills/quality/test-gen/SKILL.cursor.md +0 -80
  163. package/src/gdskills/bundled/skills/quality/tests-creator/SKILL.codex.md +0 -345
  164. package/src/gdskills/bundled/skills/quality/tests-creator/SKILL.cursor.md +0 -345
  165. package/src/gdskills/bundled/skills/quality/tests-creator/SKILL.opencode.md +0 -345
  166. package/src/gdskills/bundled/skills/quality/tests-creator/SKILL.zed.md +0 -345
  167. package/src/gdskills/bundled/skills/review/code-ai-review/SKILL.codex.md +0 -203
  168. package/src/gdskills/bundled/skills/review/code-ai-review/SKILL.cursor.md +0 -203
  169. package/src/gdskills/bundled/skills/review/code-ai-review/SKILL.opencode.md +0 -203
  170. package/src/gdskills/bundled/skills/review/code-ai-review/SKILL.zed.md +0 -203
  171. package/src/gdskills/bundled/skills/review/code-learned-review/SKILL.codex.md +0 -243
  172. package/src/gdskills/bundled/skills/review/code-learned-review/SKILL.cursor.md +0 -243
  173. package/src/gdskills/bundled/skills/review/code-learned-review/SKILL.opencode.md +0 -243
  174. package/src/gdskills/bundled/skills/review/code-learned-review/SKILL.zed.md +0 -243
  175. package/src/gdskills/bundled/skills/review/code-mobx-store-review/SKILL.codex.md +0 -259
  176. package/src/gdskills/bundled/skills/review/code-mobx-store-review/SKILL.cursor.md +0 -259
  177. package/src/gdskills/bundled/skills/review/code-mobx-store-review/SKILL.opencode.md +0 -259
  178. package/src/gdskills/bundled/skills/review/code-mobx-store-review/SKILL.zed.md +0 -259
  179. package/src/gdskills/bundled/skills/review/code-style-review/SKILL.codex.md +0 -168
  180. package/src/gdskills/bundled/skills/review/code-style-review/SKILL.cursor.md +0 -168
  181. package/src/gdskills/bundled/skills/review/code-style-review/SKILL.opencode.md +0 -168
  182. package/src/gdskills/bundled/skills/review/code-style-review/SKILL.zed.md +0 -168
package/dist/core.js CHANGED
@@ -3225,6 +3225,39 @@ function countDigits(value) {
3225
3225
  function containsCalendarDate(value) {
3226
3226
  return CALENDAR_DATE.test(value);
3227
3227
  }
3228
+ function enclosingToken(content, start, end) {
3229
+ let from = start;
3230
+ const floor = Math.max(0, start - TOKEN_SCAN_LIMIT);
3231
+ while (from > floor && IDENTIFIER_CHAR.test(content[from - 1])) {
3232
+ from -= 1;
3233
+ }
3234
+ let to = end;
3235
+ const ceiling = Math.min(content.length, end + TOKEN_SCAN_LIMIT);
3236
+ while (to < ceiling && IDENTIFIER_CHAR.test(content[to])) {
3237
+ to += 1;
3238
+ }
3239
+ const truncated = from === floor && from > 0 && IDENTIFIER_CHAR.test(content[from - 1]) || to === ceiling && to < content.length && IDENTIFIER_CHAR.test(content[to]);
3240
+ return { text: content.slice(from, to), truncated };
3241
+ }
3242
+ function isHexIdentifierRun(segment) {
3243
+ return segment.length >= 8 && /^[0-9A-Fa-f]+$/.test(segment) && /[A-Fa-f]/.test(segment);
3244
+ }
3245
+ function isIdentifierFragment(content, matchStart, matchEnd) {
3246
+ const { text: token, truncated } = enclosingToken(content, matchStart, matchEnd);
3247
+ if (truncated) {
3248
+ return false;
3249
+ }
3250
+ if (token.length === matchEnd - matchStart) {
3251
+ return false;
3252
+ }
3253
+ if (UUID_TOKEN.test(token)) {
3254
+ return true;
3255
+ }
3256
+ const match = content.slice(matchStart, matchEnd);
3257
+ const before = token.slice(0, token.indexOf(match));
3258
+ const after = token.slice(token.indexOf(match) + match.length);
3259
+ return [...before.split(/[-_]/), ...after.split(/[-_]/)].some(isHexIdentifierRun);
3260
+ }
3228
3261
  function hasPhoneSeparatorShape(value) {
3229
3262
  if (/\s{2,}/.test(value)) {
3230
3263
  return false;
@@ -3262,6 +3295,9 @@ function detectPii(content) {
3262
3295
  if (containsCalendarDate(value)) {
3263
3296
  continue;
3264
3297
  }
3298
+ if (isIdentifierFragment(content, m.index, m.index + m[0].length)) {
3299
+ continue;
3300
+ }
3265
3301
  }
3266
3302
  if (rule.validate && !rule.validate(value)) {
3267
3303
  continue;
@@ -3285,7 +3321,7 @@ function detectPii(content) {
3285
3321
  }
3286
3322
  return matches;
3287
3323
  }
3288
- var RULES, CALENDAR_DATE;
3324
+ var RULES, CALENDAR_DATE, IDENTIFIER_CHAR, TOKEN_SCAN_LIMIT = 64, UUID_TOKEN;
3289
3325
  var init_pii = __esm(() => {
3290
3326
  RULES = [
3291
3327
  {
@@ -3352,6 +3388,8 @@ var init_pii = __esm(() => {
3352
3388
  }
3353
3389
  ];
3354
3390
  CALENDAR_DATE = /(?:19|20)\d{2}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])/;
3391
+ IDENTIFIER_CHAR = /[0-9A-Za-z_-]/;
3392
+ UUID_TOKEN = /^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}$/;
3355
3393
  });
3356
3394
 
3357
3395
  // src/security/detect/secrets.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrciphersmith/keryx",
3
- "version": "0.2.98",
3
+ "version": "0.2.99",
4
4
  "description": "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
5
5
  "private": false,
6
6
  "publishConfig": {
@@ -0,0 +1,237 @@
1
+ ---
2
+ description: "CLI surface rules: what an exit code means (0 clean, 1 found something, 2 cannot tell), machine-readable output on stdout and diagnostics on stderr, versioned --json shapes, and renaming a flag by aliasing it with a notice rather than removing it. Use when adding, changing, or retiring a command, subcommand, flag, or --json payload that people already run."
3
+ alwaysApply: false
4
+ ---
5
+
6
+ # CLI Interface Design
7
+
8
+ ## Purpose
9
+ A command-line surface is a contract the moment somebody scripts it. Once a
10
+ script reads an exit code, pipes stdout, or parses a `--json` payload, every
11
+ later edit is either compatible or a silent breakage in someone else's CI. This
12
+ rule fixes what those three channels mean and how a flag leaves the surface.
13
+
14
+ ## When To Apply
15
+ Apply when adding a command or subcommand, adding or renaming a flag, changing
16
+ what a command exits with, or changing the shape of anything it prints for a
17
+ machine. It governs keryx's own CLI (`src/cli.ts` and `src/commands/**`);
18
+ `rules/core/api-contracts.mdc` governs HTTP surfaces, and the versioning
19
+ discipline there is the same discipline stated for a different transport.
20
+
21
+ ---
22
+
23
+ ## Rules
24
+
25
+ ### 1. Three exit codes, and they mean three different things
26
+
27
+ keryx already runs a three-value convention, stated where it was first needed —
28
+ `src/commands/wiki.ts:561-570`, `keryx wiki sections resolve`:
29
+
30
+ - **0** — the command ran and the answer is clean.
31
+ - **1** — the command ran and found something the caller should act on: a
32
+ broken link, a failing gate, a finding, a refusal.
33
+ - **2** — the command could not tell. The registry was unreadable; the store
34
+ could not be opened; the input was not parseable.
35
+
36
+ The reason 2 exists is written into that code: *"a script that treats 1 as
37
+ 'gone' would otherwise read an unreadable registry as a deletion."*
38
+ `keryx memory search` adopted it deliberately and says so
39
+ (`src/commands/memory.ts:182-196`), calling it *"reserved for 'cannot tell',
40
+ distinct from both a completed search and a validation error."*
41
+
42
+ **Two shipped commands do not honour it, and converging them is this rule's
43
+ reason to exist:**
44
+
45
+ - `keryx health run` folds `incomplete` — *"a required check that is missing,
46
+ skipped, unparsed or unfinished"* — into **1**, the same code as an
47
+ established threshold violation (`src/commands/health.ts:120-132`, with the
48
+ justification at `:103-112`). "Nobody ran eslint" and "eslint found 40
49
+ errors" are indistinguishable to a caller reading the code.
50
+ - `keryx review` goes the other way and exits **0** on the same class of
51
+ answer: `reportCrossFamilyReviewProblems` and `reportFilterStatsProblems`
52
+ (both in `src/commands/review.ts`; cited by name because that file moves
53
+ under every review change, and a line number here has already gone stale
54
+ once) filter `not-recorded` out before failing, on the stated grounds that an
55
+ absence is honest. It is honest — and it is still not a pass.
56
+
57
+ Three commands, one binary, three answers to "I could not tell". A new command
58
+ uses 0/1/2 as defined above. An existing one moves toward it only with its
59
+ consumers named, because narrowing 1 into 1-or-2 is itself a breaking change.
60
+
61
+ Two settled corollaries, both worth keeping:
62
+
63
+ - **A checker that reports defects and exits 0 is a checker nothing can gate
64
+ on** (`src/commands/skills.ts:927-951`). That code also exits non-zero on an
65
+ *empty* denominator — `findings: 0` over zero skills evaluated is not a pass.
66
+ - **A wrapper forwards its child's code unchanged.** `keryx ctx run` sets
67
+ `process.exitCode = result.exitCode` (`src/commands/ctx.ts:210`); a wrapper
68
+ that collapsed every child failure to 1 would make the wrapped command
69
+ unusable under `set -e`.
70
+
71
+ ### 2. stdout is the answer, stderr is everything else
72
+
73
+ The canonical statement is `src/commands/mcp.ts:78-92`: the deprecation notice
74
+ goes to stderr *"because a bare `keryx mcp` IS the stdio MCP server: stdout
75
+ carries JSON-RPC frames there, and a notice written to it corrupts every client
76
+ session rather than informing anyone."* The same file routes every consumer
77
+ diagnostic to stderr for that reason (`:41-44`).
78
+
79
+ The discipline, applied:
80
+
81
+ - Structured output — `--json` payloads, rendered reports, the thing a pipe is
82
+ for — goes to **stdout**, alone. `keryx skills verify --bundled --json`
83
+ writes parseable JSON to stdout and its diagnostic to stderr, and still exits
84
+ 1 (`src/commands/skills.ts:934-947`). Verified: stdout parses, stderr is
85
+ empty on the clean path.
86
+ - Progress, warnings, deprecation notices, usage errors go to **stderr**.
87
+ - Under `--json`, a failure is *data on stdout*, not prose on stderr:
88
+ `keryx memory search --json` emits `{ outcome: "store-unreadable", … }` on
89
+ stdout and exits 2, where the plain mode prints to stderr
90
+ (`src/commands/memory.ts:190-195`). A machine reader gets a discriminated
91
+ result either way.
92
+ - **Where this is broken today:** an unrecognised command prints its one-line
93
+ diagnostic to stderr and then dumps the full help to **stdout**
94
+ (`src/cli.ts:147-149`, `printHelp` at `:152`). Measured: `keryx bogusverb`
95
+ writes 9,503 bytes of help to stdout; `keryx wiki check --bogus` writes
96
+ 4,150. Both numbers are the help text's own size and move whenever a command
97
+ is added — a later reading that differs is growth, not a correction to this
98
+ paragraph. A caller piping stdout for JSON receives a help screen instead, and
99
+ only the exit code says otherwise. **Help printed because of an error belongs
100
+ on stderr. Help printed because `--help` was asked for belongs on stdout.**
101
+
102
+ ### 3. A `--json` shape is versioned, deterministic, and pinned
103
+
104
+ `keryx skills export` is the worked example. Removing one boolean from its
105
+ manifest moved the version, and the code says why
106
+ (`src/gdskills/export.ts:167-193`): *"Removing a field is a breaking change for
107
+ anything parsing this file, so the version moves with it: a reader that still
108
+ expects the boolean sees `schemaVersion: 2` and knows why it is absent instead
109
+ of reading `undefined` as `false`."* The plugin manifest is a different shape
110
+ and stays at its own `schemaVersion: 1` (`src/gdskills/export-plugin.ts:153`) —
111
+ versions are per-shape, not per-repo. A test pins the number
112
+ (`src/gdskills/export-runtime-builds.test.ts:108`).
113
+
114
+ `keryx modules status --json` carries the other half: a `schemaVersion`, a
115
+ deterministic key order sorted independently of authoring order *"which is what
116
+ makes it safe to diff and to consume from a harness"*
117
+ (`src/commands/modules.ts:197-210`), and a test that pins the version
118
+ (`src/commands/modules.test.ts:26`).
119
+
120
+ So the standard for any new machine-readable payload is:
121
+
122
+ 1. A top-level **object** with a `schemaVersion`. Not an array — an array
123
+ cannot grow a field. `rules/core/skills-storage-workflow.mdc` has said "One
124
+ top-level JSON object only" since it was written.
125
+ 2. Deterministic ordering, so two runs of an unchanged tree are byte-identical.
126
+ 3. A test that asserts the version and the required keys, in the same change.
127
+ 4. Adding an optional field is compatible. **Removing or renaming one bumps the
128
+ version**, in the same commit as the removal.
129
+
130
+ **Where this is inconsistent today:** `keryx skills` serialises internal
131
+ TypeScript objects straight to stdout at eleven call sites
132
+ (`src/commands/skills.ts:222`, `:288`, `:398`, `:693`, `:735`, `:785`, `:822`,
133
+ `:883`, `:935`, `:989`, `:1101`). Three of the eleven print a type that
134
+ declares one — `LearningProposal` (`src/gdskills/learn.ts:17`, set at `:100`)
135
+ at `:785`, and `ProjectSkillVerificationReport` (`src/gdskills/verify.ts:25`,
136
+ set at `:96`) at `:883` and `:989`. The other eight carry no version at all.
137
+
138
+ **The gap is the envelope, and it is the worse half.** `verify --bundled
139
+ --json` emits an unversioned object (`root`, `skills`, `documents`,
140
+ `skillNames`, `findings`); `verify --all --json` emits a bare array — `[]` when
141
+ the registry is empty (`:973`), a list of reports otherwise (`:989`). So
142
+ `keryx skills verify --all --json` ships `[{ "schemaVersion": 1, … }]`: each
143
+ element versioned, the payload a caller actually parses not. That is standard 1
144
+ failing in the direction it warns about — an array cannot grow a field, so the
145
+ envelope can never be versioned later without the breaking change the version
146
+ existed to avoid, however carefully each element inside it is versioned. One
147
+ test pins one field of one of them
148
+ (`src/commands/skills.bundled-verify.test.ts:58` asserts `findings` is `[]`). A
149
+ rename inside any of the eight unversioned types is a CLI breaking change that
150
+ nothing in the repository would catch.
151
+
152
+ ### 4. An unknown flag is refused, never ignored
153
+
154
+ `rejectUnknownFlags` (`src/commands/review.ts:307-318`) is the standard, and
155
+ its message is the argument: *"Refused rather than ignored — a flag that is
156
+ silently dropped writes nothing and reports success."* (`:315`) It names the
157
+ offending flag, lists what is accepted, and refuses. Every `keryx review`
158
+ subcommand calls it.
159
+
160
+ The same rule applies to a flag's **value**, not just its name. An unknown
161
+ `--verifier` is refused because *"an unrecognised method would silently leave
162
+ the tier at its default"* (`src/commands/review.ts:754`), and an unknown
163
+ `--deny-tools` name is refused with the deniable names listed, because
164
+ *"`--deny-tools web_serch` must not leave the session with web search and a
165
+ clear conscience"* (`src/commands/interactive-agent-tools.ts:140-156`,
166
+ `src/commands/shell.ts:1797-1798`).
167
+
168
+ **Where this is inconsistent today:** the guard is per-command, and two
169
+ implementations exist — `rejectUnknownFlags` and `rejectUnknownOptions`
170
+ (`src/commands/workspace.ts:296-300`). Most commands have neither. Measured on
171
+ this tree: `keryx review scope --bogus` exits 1 with a named refusal, while
172
+ `keryx modules status --bogus --json` and `keryx skills verify --bundled
173
+ --bogus` both exit **0** and ignore the flag. The same binary answers the same
174
+ typo two different ways.
175
+
176
+ A new command validates its flags. A flag that takes a fixed set of values
177
+ validates the value too.
178
+
179
+ ### 5. A flag is renamed by aliasing it, and removed by refusing it out loud
180
+
181
+ keryx has never deleted a spelling. Three patterns, all shipped, all correct:
182
+
183
+ - **Rename → alias + one notice.** `keryx mcp install|uninstall` became
184
+ `keryx integrate` and the old spelling still works: one module translates the
185
+ old argument shape and calls the new implementation, so there is *"one
186
+ implementation and nothing to drift"* (`src/commands/mcp.ts:1-14`). It prints
187
+ exactly one line, on stderr, per invocation — *"A line a reader sees three
188
+ times in one command is a line they learn to skip, which is how deprecation
189
+ notices stop working at all"* (`:78-92`). A contract test asserts the retired
190
+ spellings still work, still write the same files, and name the replacement
191
+ **once** (`src/commands/mcp-naming.test.ts:453`, `:470`, `:490`).
192
+ - **Removal → refuse by name, with the reason.** `keryx workspace handoff
193
+ --from` was removed because a caller could not be allowed to state it. It is
194
+ refused by name rather than as a generic unknown option, *"so anyone who
195
+ scripted it learns why it is gone instead of reading it as a typo"*
196
+ (`src/commands/workspace.ts:221-231`).
197
+ - **A wrapper's own flag is consumed, not forwarded.** `keryx ctx rg --json` is
198
+ keryx's flag, stripped before the child command is built, because `rg --json`
199
+ means something else (`src/commands/ctx.ts:213-219`). Check the namespace
200
+ before claiming a flag name a wrapped tool already owns.
201
+
202
+ **Where this is inconsistent today:** `keryx review` accepts both `--ref` and
203
+ `--target-ref` for the same value, `--ref` winning
204
+ (`src/commands/review.ts:147`, `:409`). There is no notice, and `--target-ref`
205
+ appears nowhere in `keryx review --help`. An unannounced, undocumented alias is
206
+ indistinguishable from an accidental duplicate: nothing tells a caller to
207
+ migrate, so nothing will ever justify retiring it.
208
+
209
+ A rename therefore ships four things in one change: the new spelling, the old
210
+ one still working through the same implementation, one stderr notice naming the
211
+ replacement, and a test that asserts both.
212
+
213
+ ---
214
+
215
+ ## The three that are not negotiable
216
+
217
+ A nonzero exit never means "I could not tell" in a command that also uses
218
+ nonzero to mean "I found something" — pick 2 for the first. A `--json` payload
219
+ never loses or renames a field without a `schemaVersion` bump in the same
220
+ commit. An old flag spelling is never deleted: it is aliased with a notice, or
221
+ refused by name with the reason.
222
+
223
+ ---
224
+
225
+ ## Red Flags — Stop and re-read this rule if you are thinking:
226
+
227
+ | Rationalization | Why it's wrong |
228
+ |---|---|
229
+ | "Exit 1 is fine, the message explains which kind of failure it was" | Messages are for people; scripts read the code. `keryx health run` cannot distinguish "eslint was skipped" from "eslint failed" today, and that is the gap, not a style preference |
230
+ | "Nobody parses this `--json` yet, so I can change the shape freely" | You cannot observe who parses a published CLI's output. The version field costs one line and is the only thing that lets a reader tell absence from a downgrade |
231
+ | "I'll print the warning to stdout, it's more visible there" | stdout is the pipe. A warning there corrupts every consumer that expected data — which is why `keryx mcp` routes its notice to stderr instead |
232
+ | "The old flag is unused, I'll just delete it" | "Unused" means "no usage you can see". Alias it with a notice, or refuse it by name with the reason — both cost less than a silent breakage in someone's CI |
233
+ | "An unrecognised flag is harmless, the command still does the right thing" | It does less than the operator asked and reports success. That is the failure `rejectUnknownFlags` was written for |
234
+ | "It's just a help screen on stdout, who cares" | Nine kilobytes of it, where the caller expected JSON. Error output is error output regardless of how friendly it reads |
235
+ | "I'll add the schemaVersion when the shape actually changes" | The first change is the one that needs it, and by then the unversioned readers already exist |
236
+
237
+ **IRON LAW: ONCE A COMMAND IS PUBLISHED, ITS EXIT CODE, ITS STDOUT, AND ITS `--json` SHAPE ARE A CONTRACT — CHANGE THEM THE WAY YOU WOULD CHANGE AN API, OR NOT AT ALL.**
@@ -0,0 +1,116 @@
1
+ ---
2
+ description: "The single standing bar for calling a change done: evidence per criterion, gates that actually ran after the last edit, nothing disabled to go green, the diff committed. Use when reporting a task complete, accepting a worker's result, or deciding whether remaining work may be deferred."
3
+ alwaysApply: false
4
+ ---
5
+
6
+ # Definition Of Done
7
+
8
+ ## Purpose
9
+ State, once, the bar a change must clear before any agent calls it done — so
10
+ that "done" means the same thing to the worker that reports it and the
11
+ orchestrator that accepts it. Six rules and two skills each state a piece of
12
+ this bar today; this rule is the join, and it names which document owns which
13
+ piece so the pieces cannot drift apart.
14
+
15
+ ## When To Apply
16
+ Apply at the moment of reporting: before a worker writes its `STATUS:` line,
17
+ before an orchestrator accepts a worker's result, and before a flow's
18
+ acceptance criteria are confirmed. It applies to every change that ships —
19
+ code, rule, skill, schema or document — not only to code with tests.
20
+
21
+ ---
22
+
23
+ ## The Bar
24
+
25
+ A change is done when all five hold. Each is a fact about this working tree
26
+ that can be checked by reading output, not a judgement about how the work felt.
27
+
28
+ 1. **Every acceptance criterion has named evidence.** For each criterion: the
29
+ command that demonstrates it, and that command's actual result. A criterion
30
+ whose evidence is "implemented" or "verified" has no evidence.
31
+ 2. **Every gate that applies to the changed files ran to completion, after the
32
+ last edit, in this tree, and passed** — lint, type-check, tests. Ran *after
33
+ the last edit*: a green result captured before the final change is not a
34
+ result about the change being reported.
35
+ 3. **A gate that did not run is not a pass.** A missing binary, a filtered
36
+ suite, a skipped file — each is `skipped`, never `pass`. Zero tests executed
37
+ is not zero failures.
38
+ 4. **Nothing was disabled to get there.** No test deleted, `.skip`ped or
39
+ `.only`d; no assertion removed; no threshold lowered; no suppression,
40
+ `eslint-disable` or `@ts-expect-error` added, unless that suppression *is*
41
+ the change and is stated as such in the report.
42
+ 5. **The diff is committed** at the task boundary, by whoever owns commits.
43
+
44
+ If any of the five fails, the status is not `DONE`. Not `DONE` with the gap in
45
+ a note; not `DONE_WITH_CONCERNS` with the gap as a concern. Report `BLOCKED`,
46
+ or finish the work.
47
+
48
+ ---
49
+
50
+ ## What Each Document Owns
51
+
52
+ This rule does not restate these; it points at them. When one of them changes,
53
+ this list is what says whether the bar moved.
54
+
55
+ | Document | Owns |
56
+ |---|---|
57
+ | `rules/core/tdd-workflow.mdc` | Clause 2 for tests specifically: the red-green-refactor sequence, and the invariant that `STATUS: DONE` requires a green suite (its Iron Law 1, and Iron Law 3 forbidding deletion of tests to get there — clause 4's test case). |
58
+ | `.metaproject/skills/gdskills/orchestration/code-verifier/SKILL.md` | Clause 3: the gate decision, and the ruling that a check which did not run reports `status: skipped`, never `pass`. It also owns the distinction between the gate's result and the skill's own `STATUS:`. |
59
+ | `rules/core/implementation-doc-mandate.mdc` | The shape of the evidence for clauses 1 and 2 — the Verification Gate block, the Acceptance Criteria Met list, and its Iron Law 3: the numbers must come from an independent verifier, not from self-assessment. |
60
+ | `rules/core/subagent-status-protocol.md` | How the result is reported: `STATUS:` on line one, the five statuses, and the verification block every `DONE` response carries. |
61
+ | `rules/core/git-concurrency.mdc` | Clause 5: a task is not done until its diff is committed, whether the worker or the orchestrator commits it. |
62
+ | `.metaproject/skills/gdskills/orchestration/task-implementer/SKILL.md` | The worker-side reading of clause 3: skipped verification is a failed verification, and partial is not done. |
63
+ | `rules/core/gproject-contracts.mdc` | The gproject return payload only. It is silent on clauses 2-5; that silence is not an exemption, and a gproject subagent reporting `DONE` still owes the verification block from `rules/core/subagent-status-protocol.md`. |
64
+ | `rules/core/requirements-package-standard.mdc` | Clause 2 for a documentation package, where the gates are structural (required files, versions, links, honest status) rather than lint and tests. |
65
+
66
+ Two readings of `DONE_WITH_CONCERNS` are in circulation, and this rule settles
67
+ them: it is for information the orchestrator needs about work that **cleared
68
+ all five clauses** — an interpretation made, a workaround for something
69
+ non-blocking, a warning worth seeing. It is not a lower bar. An unmet
70
+ acceptance criterion, a suite that was not run, or an uncommitted diff is not a
71
+ concern attached to a completed task; it is an incomplete task.
72
+
73
+ ---
74
+
75
+ ## The Standing Bar And A Flow's Frozen Acceptance Criteria
76
+
77
+ They are conjunctive. Neither overrides the other, and the question "which
78
+ wins" has a definite answer in each direction:
79
+
80
+ - **The frozen criteria fix the scope.** They say what must be true *of this
81
+ change* — and only the flow can say that. The bar cannot add a criterion, and
82
+ satisfying the bar does not satisfy a criterion the work never touched.
83
+ - **The bar fixes the evidence, and is not waivable by silence.** Criteria that
84
+ say nothing about tests, lint or committing do not thereby excuse them. A
85
+ flow cannot lower the standing bar by omitting it, because the criteria were
86
+ frozen to stop scope drifting — not to enumerate the baseline every change
87
+ already owes.
88
+
89
+ So: **a change that meets every frozen criterion but fails a clause of the bar
90
+ is not done**, and a change that clears the bar while a criterion is unmet is
91
+ not done either.
92
+
93
+ When a frozen criterion genuinely conflicts with the bar — it asks for
94
+ something that cannot be delivered without failing a clause — the criterion is
95
+ not quietly reworded to match what was built. It changes through
96
+ `keryx flow ac update <id> --reason "<why>"`, with the conflict as the reason,
97
+ per `.metaproject/skills/gdskills/orchestration/flow-orchestrator/SKILL.md`.
98
+ Rewriting a criterion to describe the work is how a flow loses the only record
99
+ of what it set out to do.
100
+
101
+ ---
102
+
103
+ ## Red Flags — Stop and re-read this rule if you are thinking:
104
+
105
+ | Rationalization | Why it's wrong |
106
+ |---|---|
107
+ | "Everything passed, I'll just make the last small edit and report" | Clause 2 is about *this* tree after *this* edit. A result captured before the final change is evidence about a tree that no longer exists |
108
+ | "The test command printed no failures, so it's green" | No failures and no tests are the same output. Read the count: zero executed is clause 3, not a pass |
109
+ | "It's done except for the one flaky test / the docs / the commit" | "Done except for" is the phrase this rule exists to delete. The exception is the part that is not done, and it decides the status |
110
+ | "I'll report DONE_WITH_CONCERNS and name the unmet criterion as a concern" | `DONE_WITH_CONCERNS` is for information about completed work. An unmet criterion is incomplete work wearing a success status |
111
+ | "The lint binary isn't installed here, so there is nothing to fail" | A check that cannot run has told you nothing. Say it was skipped and which one; do not convert absence of a result into a result |
112
+ | "The orchestrator will run the gates anyway, so I don't have to" | Two agents each assuming the other verified is how a change reaches review with nothing ever having run against it |
113
+ | "The acceptance criterion doesn't mention lint, so lint isn't in scope" | The criteria bound the scope of the work, not the evidence every change owes. Silence is not an exemption |
114
+
115
+ **IRON LAW: A STEP THAT WAS SKIPPED IS A STEP THAT FAILED. "DONE EXCEPT FOR X"
116
+ IS NOT DONE — IT IS BLOCKED ON X.**
@@ -25,13 +25,42 @@ Before creating or editing a skill, ask and confirm:
25
25
  - Project-local skill: `.metaproject/project-skills/<skill-name>/`.
26
26
 
27
27
  ## Required Source Layout
28
- - `SKILL.md` — canonical source of truth
29
- - `SKILL.cursor.md` — Cursor-specific variant (optional; falls back to SKILL.md)
30
- - `SKILL.codex.md` — Codex-specific variant (optional; falls back to SKILL.md)
31
- - `SKILL.zed.md` — Zed-specific variant (optional; falls back to SKILL.md)
32
- - `SKILL.opencode.md` — OpenCode-specific variant (optional; falls back to SKILL.md)
28
+ - `SKILL.md` — canonical source of truth, the shared build used by every runtime
29
+ - `SKILL.cursor.md` — Cursor-specific variant, present only where Cursor's build genuinely differs from `SKILL.md` (falls back to `SKILL.md` otherwise)
30
+ - `SKILL.codex.md` — Codex-specific variant, present only where Codex's build genuinely differs from `SKILL.md` (falls back to `SKILL.md` otherwise)
31
+ - `SKILL.zed.md` — Zed-specific variant, present only where Zed's build genuinely differs from `SKILL.md` (falls back to `SKILL.md` otherwise)
32
+ - `SKILL.opencode.md` — OpenCode-specific variant, present only where OpenCode's build genuinely differs from `SKILL.md` (falls back to `SKILL.md` otherwise)
33
33
 
34
- `SKILL.md` is always required. Platform variants are optional — if absent, `keryx update` installs `SKILL.md` as fallback.
34
+ `SKILL.md` is always required. A `SKILL.<runtime>.md` variant is the
35
+ exception, not the norm: create one only when that runtime's build genuinely
36
+ differs from `SKILL.md`, and never add one as an identical copy. Falling
37
+ back to `SKILL.md` is the normal case for every runtime without a variant —
38
+ for `keryx update`/`keryx init` (installed mirror) and for `keryx skills
39
+ export`/`keryx skills sync` (global destinations) alike — not a degraded
40
+ path.
41
+
42
+ "Genuinely differs" covers two cases, and only two:
43
+
44
+ 1. **Different instructions.** The runtime needs prose `SKILL.md` does not
45
+ carry, or must not carry.
46
+ 2. **A different `metadata.compatible_harnesses`.** That field is the
47
+ machine-readable CSV of supported harnesses, so its correct value is
48
+ per-build by construction — a build cannot share a harness list that
49
+ excludes the harness reading it. This is the only field in the tree of
50
+ which that is true.
51
+
52
+ Case 2 is why fourteen shipped builds differ from their `SKILL.md` by exactly
53
+ one line: the seven `gproject-*` subagent skills under `planning/`
54
+ (`project-discovery`, `problem-definer`, `patterns-researcher`, `planner`,
55
+ `consistency-checker`, `spec-writer`, `stack-advisor`) each ship a
56
+ `SKILL.codex.md` and a `SKILL.cursor.md` that add
57
+ `compatible_harnesses: "cursor,codex,zed,opencode"`. Their `SKILL.md` declares
58
+ no harness list, because it is the Claude build and cannot carry one that
59
+ excludes Claude. These builds are NOT identical copies and must not be deleted
60
+ as such; `src/gdskills/build-parity.test.ts` exempts this one hunk, by name,
61
+ for these seven skills only, and its allow-list is where any widening has to be
62
+ argued. Every other difference between a build and its `SKILL.md` is drift and
63
+ gets reconciled.
35
64
 
36
65
  ## Frontmatter Fields (Agent Skills spec alignment)
37
66
 
@@ -69,6 +98,50 @@ back toward the spec:
69
98
  typed payloads to subagent workers instead, which the spec does not attempt
70
99
  to address.
71
100
 
101
+ ## Length Ceilings
102
+
103
+ Every shipped skill has a line-count ceiling recorded in
104
+ `src/gdskills/skill-length-ceilings.ts`, set to the skill's length on the day
105
+ the file was introduced; a skill with no entry is bounded by the default (500
106
+ lines, the point at which a skill should be split). `keryx skills verify
107
+ --bundled` reports `anatomy:length` when a skill passes its ceiling.
108
+
109
+ A ceiling **moves down**, never up:
110
+
111
+ - trimming a skill — moving reference material into a sibling document, cutting
112
+ a section that no longer changes behaviour — lowers the ceiling to the new
113
+ count in the same change;
114
+ - a skill that needs more room than its ceiling allows is split, or its
115
+ reference material moves out. Raising the number to clear the finding is the
116
+ one edit this rule forbids: it converts a measured limit into a record of
117
+ whatever the file grew to.
118
+
119
+ `keryx skills verify --bundled` states the same remedy in the same words, and
120
+ cites this section by name, so the finding and the rule cannot drift apart. The
121
+ `anatomy:length` message reads, after the count:
122
+
123
+ > Split the skill, or move reference material into a sibling document, and lower
124
+ > the ceiling to the new count in the same change. A ceiling only ever moves
125
+ > DOWN: raising it is the one edit rules/core/skills-storage-workflow.mdc
126
+ > ("Length Ceilings") forbids, because it converts a measured limit into a record
127
+ > of whatever the file grew to.
128
+
129
+ Changing either text without the other is the drift this quotation exists to
130
+ prevent; `src/gdskills/bundled-eval.ts` is where the message lives.
131
+
132
+ A ceiling is not a statement that the skill's current size is right — several
133
+ shipped skills are far past the default and were recorded as they stood. It
134
+ bounds unnoticed growth, which is the part no other check can see.
135
+
136
+ Editing a skill is therefore a two-file change. Every recorded ceiling equals
137
+ that skill's current count today, so adding a sentence needs a compensating
138
+ deletion or a split: recount the file (`wc -l SKILL.md`) and write that number
139
+ to the skill's key in `src/gdskills/skill-length-ceilings.ts` in the SAME
140
+ commit. A new skill needs a new entry; a deleted skill needs its entry removed.
141
+ `keryx skills verify --bundled` only reports `lines > ceiling`, so a TRIM that
142
+ leaves the old, higher number behind passes locally and fails the
143
+ ceiling-equals-count check in CI.
144
+
72
145
  ## Global Sync Mapping
73
146
  Global sync only ever reaches a **project-local skill**
74
147
  (`.metaproject/project-skills/<skill-name>/`) that has first been exported for
@@ -83,10 +156,10 @@ written by `keryx init`/`keryx update`), which stays inside the project.
83
156
 
84
157
  Once a project skill is exported for a runtime, `keryx skills sync --runtime
85
158
  <runtime> --global` copies it from `.metaproject/runtime/skills/<runtime>/` to:
86
- - `cursor` → `~/.cursor/skills/<module>-<skill-name>/SKILL.md` (built from `SKILL.cursor.md`, or `SKILL.md` as fallback)
87
- - `codex` → `${CODEX_HOME:-~/.codex}/skills/<module>-<skill-name>/SKILL.md` (built from `SKILL.codex.md`, or `SKILL.md` as fallback)
88
- - `zed` → `~/.config/zed/skills/<module>-<skill-name>/SKILL.md` (built from `SKILL.zed.md`, or `SKILL.md` as fallback)
89
- - `opencode` → `~/.config/opencode/skills/<module>-<skill-name>/SKILL.md` (built from `SKILL.opencode.md`, or `SKILL.md` as fallback)
159
+ - `cursor` → `~/.cursor/skills/<module>-<skill-name>/SKILL.md` (built from `SKILL.cursor.md` when the skill ships one, otherwise `SKILL.md`)
160
+ - `codex` → `${CODEX_HOME:-~/.codex}/skills/<module>-<skill-name>/SKILL.md` (built from `SKILL.codex.md` when the skill ships one, otherwise `SKILL.md`)
161
+ - `zed` → `~/.config/zed/skills/<module>-<skill-name>/SKILL.md` (built from `SKILL.zed.md` when the skill ships one, otherwise `SKILL.md`)
162
+ - `opencode` → `~/.config/opencode/skills/<module>-<skill-name>/SKILL.md` (built from `SKILL.opencode.md` when the skill ships one, otherwise `SKILL.md`)
90
163
 
91
164
  These four global destinations are written only by the explicit, opt-in
92
165
  `keryx skills sync --runtime <runtime> --global` command — never as a side
@@ -96,7 +169,7 @@ those instead.
96
169
 
97
170
  ## Mandatory Behavior
98
171
  1. Always create/update `SKILL.md` as the canonical source.
99
- 2. Create platform variants only when platform-specific adaptations are needed; otherwise `SKILL.md` serves all platforms via fallback.
172
+ 2. Create a `SKILL.<runtime>.md` only when that runtime's build genuinely differs from `SKILL.md` — either in its instructions or in its `metadata.compatible_harnesses`, the two cases named under Required Source Layout — never as an identical copy. Otherwise `SKILL.md` serves that runtime via fallback; that fallback is the normal case, not an exception.
100
173
  3. Keep all profiles aligned in structure and intent.
101
174
  4. Validate skills before sync.
102
175
  5. For a **project-local** skill only: export it per runtime first (`keryx skills export <skill-name> --runtime <runtime>`), then sync every global destination that applies (`cursor`, `codex`, `zed`, `opencode` — four in total) with `keryx skills sync --runtime <runtime> --global`, run once per runtime that needs it. A shipped skill cannot be exported and has no global-sync route — it reaches a runtime only via the project-local installed mirror (`keryx update`/`keryx init`).
@@ -154,6 +227,23 @@ If validation fails, do not sync.
154
227
  ## Forbidden
155
228
  - Do not create skills in `~/.cursor/skills-cursor/`.
156
229
 
230
+ ## Rejected Changes
231
+ Scope: keryx's own bundled skills and rules
232
+ (`src/gdskills/bundled/**` in the keryx repository) only. This rule ships to
233
+ every project via `keryx init`/`keryx update`, but the ledger below exists
234
+ only in the keryx source repo — this section does not apply to
235
+ project-local skills (`.metaproject/project-skills/<skill-name>/`).
236
+
237
+ - Before proposing a change to a shipped skill or rule, search
238
+ `docs/skills/rejected-skill-changes.md` in the keryx repository for that
239
+ skill and that idea.
240
+ - When a change to a shipped skill or rule is rejected (review, a
241
+ routing/behaviour-eval regression, or a user/maintainer decision), append a
242
+ row to that ledger — in the same change, on the default branch (`main`) —
243
+ recording what was tried, why it was rejected, and the evidence (e.g. a
244
+ routing-eval rank or behaviour-eval number, before → after). Never edit or
245
+ delete an existing row.
246
+
157
247
  ## Apply Changes Workflow
158
248
  Edit skills under `src/gdskills/bundled/skills/<category>/<skill-name>/`
159
249
  (shipped) or `.metaproject/project-skills/<skill-name>/` (project-local).
@@ -44,6 +44,12 @@ Task fully complete. All acceptance criteria met. Orchestrator can continue the
44
44
  ### `DONE_WITH_CONCERNS`
45
45
  Task complete, but the orchestrator should know something before continuing. This is NOT a failure — it is a completed task that carries information the orchestrator needs to make a good decision.
46
46
 
47
+ "Complete" here means the bar in `rules/core/definition-of-done.mdc`, which owns
48
+ what `DONE` costs and is not restated here. `DONE_WITH_CONCERNS` reports
49
+ information *about work that cleared that bar*; it is never a lower bar. If a
50
+ clause of the bar fails — an unmet acceptance criterion, a gate that did not
51
+ run, an uncommitted diff — the status is `BLOCKED`, not this one.
52
+
47
53
  Use when:
48
54
  - Implementation required a meaningful interpretation of ambiguous criteria
49
55
  - A workaround was used for a non-blocking issue
@@ -58,6 +64,7 @@ Use when:
58
64
  - Two valid approaches exist with significantly different tradeoffs and the subagent cannot choose alone
59
65
  - A command failed in a way that requires orchestrator-level intervention (not a self-fixable error)
60
66
  - The task scope needs to be clarified before proceeding
67
+ - An acceptance criterion is unmet, or another clause of `rules/core/definition-of-done.mdc` fails, and you cannot close it yourself
61
68
 
62
69
  Do NOT use `BLOCKED` for errors you can fix yourself. Self-fix first (up to your retry limit), then report `BLOCKED` if still stuck.
63
70
 
@@ -186,10 +193,10 @@ The following thoughts indicate a subagent is about to violate the protocol. Rej
186
193
 
187
194
  - **"I'll add a note at the end instead of BLOCKED"** — Notes at the end are invisible to automated orchestrators. If you are blocked, the status must be `BLOCKED`. The note goes in the `## Reason` field.
188
195
 
189
- - **"It's mostly done, DONE_WITH_CONCERNS feels like admitting failure"** — `DONE_WITH_CONCERNS` is not a failure status. It is a success status with an attached signal. Use it freely when something is worth the orchestrator knowing.
196
+ - **"It's mostly done, DONE_WITH_CONCERNS feels like admitting failure"** — `DONE_WITH_CONCERNS` is not a failure status. It is a success status with an attached signal, for work that already cleared the bar in `rules/core/definition-of-done.mdc`. Use it freely when something is worth the orchestrator knowing — and not at all when the bar was not cleared.
190
197
 
191
198
  - **"NEEDS_CONTEXT would slow things down, I'll just guess"** — Guessing propagates errors downstream. One `NEEDS_CONTEXT` and a re-dispatch is cheaper than four tasks built on a wrong assumption.
192
199
 
193
- - **"I didn't meet one criterion but I'll say DONE and mention it in notes"** — If an acceptance criterion is not met, the status is not `DONE`. Use `DONE_WITH_CONCERNS` and name the unmet criterion in the concerns section.
200
+ - **"I didn't meet one criterion but I'll say DONE and mention it in notes"** — If an acceptance criterion is not met, the status is not `DONE` — and it is not `DONE_WITH_CONCERNS` either. An unmet criterion is incomplete work, not information about complete work: report `BLOCKED`, name the criterion in `## Reason`, and say in `## What I need from orchestrator` what would let you meet it. Per `rules/core/definition-of-done.mdc`, which owns that ruling.
194
201
 
195
202
  - **"The orchestrator will figure it out from my explanation"** — The orchestrator reads `STATUS:` on line 1. Everything else is secondary. Do not make the orchestrator parse free text to determine the outcome.