@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.
- package/dist/cli.js +4057 -2510
- package/dist/core.js +39 -1
- package/package.json +1 -1
- package/src/gdskills/bundled/rules/core/cli-interface-design.mdc +237 -0
- package/src/gdskills/bundled/rules/core/definition-of-done.mdc +116 -0
- package/src/gdskills/bundled/rules/core/skills-storage-workflow.mdc +101 -11
- package/src/gdskills/bundled/rules/core/subagent-status-protocol.md +9 -2
- package/src/gdskills/bundled/skills/core/reviewer-skill-creator/SKILL.md +42 -5
- package/src/gdskills/bundled/skills/orchestration/code-verifier/SKILL.md +19 -3
- package/src/gdskills/bundled/skills/orchestration/context-collector/SKILL.md +20 -4
- package/src/gdskills/bundled/skills/orchestration/feature-analyzer/SKILL.md +32 -9
- package/src/gdskills/bundled/skills/orchestration/feature-dev/SKILL.md +18 -4
- package/src/gdskills/bundled/skills/orchestration/flow-orchestrator/SKILL.md +21 -5
- package/src/gdskills/bundled/skills/orchestration/issue-analyzer/SKILL.md +4 -4
- package/src/gdskills/bundled/skills/orchestration/issue-analyzer/orchestrator-prompt.md +1 -1
- package/src/gdskills/bundled/skills/orchestration/job-documenter/SKILL.md +42 -2
- package/src/gdskills/bundled/skills/orchestration/job-orchestrator/SKILL.md +23 -9
- package/src/gdskills/bundled/skills/orchestration/task-implementer/SKILL.md +33 -31
- package/src/gdskills/bundled/skills/orchestration/task-implementer/output-contract.schema.json +32 -1
- package/src/gdskills/bundled/skills/planning/autodoc-analyst/SKILL.md +16 -0
- package/src/gdskills/bundled/skills/planning/autodoc-architect/SKILL.md +16 -0
- package/src/gdskills/bundled/skills/planning/autodoc-assembler/SKILL.md +16 -0
- package/src/gdskills/bundled/skills/planning/autodoc-orchestrator/SKILL.md +17 -0
- package/src/gdskills/bundled/skills/planning/autodoc-scanner/SKILL.md +16 -0
- package/src/gdskills/bundled/skills/planning/autodoc-writer/SKILL.md +16 -0
- package/src/gdskills/bundled/skills/planning/brainstorm/SKILL.md +28 -3
- package/src/gdskills/bundled/skills/planning/consistency-checker/SKILL.codex.md +17 -0
- package/src/gdskills/bundled/skills/planning/consistency-checker/SKILL.cursor.md +17 -0
- package/src/gdskills/bundled/skills/planning/consistency-checker/SKILL.md +17 -0
- package/src/gdskills/bundled/skills/planning/docpack-orchestrator/SKILL.md +32 -2
- package/src/gdskills/bundled/skills/planning/docpack-review/SKILL.md +14 -2
- package/src/gdskills/bundled/skills/planning/interview/SKILL.md +29 -7
- package/src/gdskills/bundled/skills/planning/interviewer/SKILL.md +32 -6
- package/src/gdskills/bundled/skills/planning/patterns-researcher/SKILL.codex.md +16 -0
- package/src/gdskills/bundled/skills/planning/patterns-researcher/SKILL.cursor.md +16 -0
- package/src/gdskills/bundled/skills/planning/patterns-researcher/SKILL.md +16 -0
- package/src/gdskills/bundled/skills/planning/planner/SKILL.codex.md +17 -0
- package/src/gdskills/bundled/skills/planning/planner/SKILL.cursor.md +17 -0
- package/src/gdskills/bundled/skills/planning/planner/SKILL.md +17 -0
- package/src/gdskills/bundled/skills/planning/prd-creator/SKILL.md +20 -3
- package/src/gdskills/bundled/skills/planning/problem-definer/SKILL.codex.md +16 -0
- package/src/gdskills/bundled/skills/planning/problem-definer/SKILL.cursor.md +16 -0
- package/src/gdskills/bundled/skills/planning/problem-definer/SKILL.md +16 -0
- package/src/gdskills/bundled/skills/planning/project-discovery/SKILL.codex.md +16 -0
- package/src/gdskills/bundled/skills/planning/project-discovery/SKILL.cursor.md +16 -0
- package/src/gdskills/bundled/skills/planning/project-discovery/SKILL.md +16 -0
- package/src/gdskills/bundled/skills/planning/spec-writer/SKILL.codex.md +4 -0
- package/src/gdskills/bundled/skills/planning/spec-writer/SKILL.cursor.md +4 -0
- package/src/gdskills/bundled/skills/planning/spec-writer/SKILL.md +4 -0
- package/src/gdskills/bundled/skills/planning/stack-advisor/SKILL.codex.md +4 -0
- package/src/gdskills/bundled/skills/planning/stack-advisor/SKILL.cursor.md +4 -0
- package/src/gdskills/bundled/skills/planning/stack-advisor/SKILL.md +4 -0
- package/src/gdskills/bundled/skills/platform/agent-entrypoint-distiller/SKILL.md +31 -4
- package/src/gdskills/bundled/skills/platform/claude-md-management/SKILL.md +26 -2
- package/src/gdskills/bundled/skills/platform/hookify/SKILL.md +28 -3
- package/src/gdskills/bundled/skills/quality/api-truth/SKILL.md +226 -0
- package/src/gdskills/bundled/skills/quality/changelog/SKILL.md +24 -4
- package/src/gdskills/bundled/skills/quality/commit/SKILL.md +24 -3
- package/src/gdskills/bundled/skills/quality/db-migrate/SKILL.md +24 -3
- package/src/gdskills/bundled/skills/quality/dependency-update/SKILL.md +25 -4
- package/src/gdskills/bundled/skills/quality/deploy/SKILL.md +26 -3
- package/src/gdskills/bundled/skills/quality/deprecation-path/SKILL.md +268 -0
- package/src/gdskills/bundled/skills/quality/fresh-eyes/SKILL.md +190 -0
- package/src/gdskills/bundled/skills/quality/metaproject-security/SKILL.md +24 -3
- package/src/gdskills/bundled/skills/quality/perf-check/SKILL.md +29 -8
- package/src/gdskills/bundled/skills/quality/pr/SKILL.md +24 -4
- package/src/gdskills/bundled/skills/quality/pr-issue-documenter/SKILL.md +25 -2
- package/src/gdskills/bundled/skills/quality/push/SKILL.md +24 -3
- package/src/gdskills/bundled/skills/quality/root-cause/SKILL.md +204 -0
- package/src/gdskills/bundled/skills/quality/security-audit/SKILL.md +25 -4
- package/src/gdskills/bundled/skills/quality/test-gen/SKILL.md +24 -3
- package/src/gdskills/bundled/skills/quality/tests-creator/SKILL.md +17 -2
- package/src/gdskills/bundled/skills/review/code-ai-review/SKILL.md +40 -5
- package/src/gdskills/bundled/skills/review/code-learned-review/SKILL.md +41 -1
- package/src/gdskills/bundled/skills/review/code-mobx-store-review/SKILL.md +44 -2
- package/src/gdskills/bundled/skills/review/code-style-review/SKILL.md +44 -4
- package/src/gdskills/bundled/skills/review/review-architecture/SKILL.md +3 -3
- package/src/gdskills/bundled/skills/review/review-backend/SKILL.md +2 -3
- package/src/gdskills/bundled/skills/review/review-clean-code/SKILL.md +4 -4
- package/src/gdskills/bundled/skills/review/review-core-boundaries/SKILL.md +36 -2
- package/src/gdskills/bundled/skills/review/review-flow-graph/SKILL.md +37 -3
- package/src/gdskills/bundled/skills/review/review-frontend/SKILL.md +2 -4
- package/src/gdskills/bundled/skills/review/review-frontend-conventions/SKILL.md +36 -2
- package/src/gdskills/bundled/skills/review/review-highload/SKILL.md +3 -5
- package/src/gdskills/bundled/skills/review/review-layout/SKILL.md +23 -2
- package/src/gdskills/bundled/skills/review/review-logic/SKILL.md +3 -3
- package/src/gdskills/bundled/skills/review/review-orchestrator/SKILL.md +9 -29
- package/src/gdskills/bundled/skills/review/review-performance/SKILL.md +9 -9
- package/src/gdskills/bundled/skills/review/review-pr-feedback/SKILL.md +3 -2
- package/src/gdskills/bundled/skills/review/review-regression/SKILL.md +33 -2
- package/src/gdskills/bundled/skills/review/review-security-code/SKILL.md +4 -2
- package/src/gdskills/bundled/skills/review/review-style/SKILL.md +2 -2
- package/src/gdskills/bundled/skills/review/review-testing-practices/SKILL.md +40 -2
- package/src/gdskills/bundled/skills/review/review-verifier/SKILL.md +1 -1
- package/src/gdskills/bundled/skills/orchestration/code-verifier/SKILL.codex.md +0 -330
- package/src/gdskills/bundled/skills/orchestration/code-verifier/SKILL.cursor.md +0 -330
- package/src/gdskills/bundled/skills/orchestration/code-verifier/SKILL.opencode.md +0 -330
- package/src/gdskills/bundled/skills/orchestration/code-verifier/SKILL.zed.md +0 -330
- package/src/gdskills/bundled/skills/orchestration/context-collector/SKILL.codex.md +0 -655
- package/src/gdskills/bundled/skills/orchestration/context-collector/SKILL.cursor.md +0 -655
- package/src/gdskills/bundled/skills/orchestration/context-collector/SKILL.opencode.md +0 -655
- package/src/gdskills/bundled/skills/orchestration/context-collector/SKILL.zed.md +0 -655
- package/src/gdskills/bundled/skills/orchestration/feature-analyzer/SKILL.codex.md +0 -424
- package/src/gdskills/bundled/skills/orchestration/feature-analyzer/SKILL.cursor.md +0 -424
- package/src/gdskills/bundled/skills/orchestration/feature-analyzer/SKILL.opencode.md +0 -424
- package/src/gdskills/bundled/skills/orchestration/feature-analyzer/SKILL.zed.md +0 -424
- package/src/gdskills/bundled/skills/orchestration/feature-dev/SKILL.codex.md +0 -163
- package/src/gdskills/bundled/skills/orchestration/feature-dev/SKILL.cursor.md +0 -163
- package/src/gdskills/bundled/skills/orchestration/issue-analyzer/SKILL.codex.md +0 -373
- package/src/gdskills/bundled/skills/orchestration/issue-analyzer/SKILL.cursor.md +0 -373
- package/src/gdskills/bundled/skills/orchestration/issue-analyzer/SKILL.opencode.md +0 -373
- package/src/gdskills/bundled/skills/orchestration/issue-analyzer/SKILL.zed.md +0 -373
- package/src/gdskills/bundled/skills/orchestration/job-documenter/SKILL.codex.md +0 -374
- package/src/gdskills/bundled/skills/orchestration/job-documenter/SKILL.cursor.md +0 -374
- package/src/gdskills/bundled/skills/orchestration/job-documenter/SKILL.opencode.md +0 -374
- package/src/gdskills/bundled/skills/orchestration/job-documenter/SKILL.zed.md +0 -374
- package/src/gdskills/bundled/skills/orchestration/job-orchestrator/SKILL.codex.md +0 -2232
- package/src/gdskills/bundled/skills/orchestration/job-orchestrator/SKILL.cursor.md +0 -2232
- package/src/gdskills/bundled/skills/orchestration/job-orchestrator/SKILL.opencode.md +0 -2232
- package/src/gdskills/bundled/skills/orchestration/job-orchestrator/SKILL.zed.md +0 -2232
- package/src/gdskills/bundled/skills/orchestration/task-implementer/SKILL.codex.md +0 -668
- package/src/gdskills/bundled/skills/orchestration/task-implementer/SKILL.cursor.md +0 -668
- package/src/gdskills/bundled/skills/orchestration/task-implementer/SKILL.opencode.md +0 -668
- package/src/gdskills/bundled/skills/orchestration/task-implementer/SKILL.zed.md +0 -668
- package/src/gdskills/bundled/skills/planning/brainstorm/SKILL.codex.md +0 -90
- package/src/gdskills/bundled/skills/planning/brainstorm/SKILL.cursor.md +0 -90
- package/src/gdskills/bundled/skills/planning/interview/SKILL.codex.md +0 -187
- package/src/gdskills/bundled/skills/planning/interview/SKILL.cursor.md +0 -187
- package/src/gdskills/bundled/skills/planning/interviewer/SKILL.codex.md +0 -105
- package/src/gdskills/bundled/skills/planning/interviewer/SKILL.cursor.md +0 -105
- package/src/gdskills/bundled/skills/planning/prd-creator/SKILL.codex.md +0 -193
- package/src/gdskills/bundled/skills/planning/prd-creator/SKILL.cursor.md +0 -193
- package/src/gdskills/bundled/skills/planning/prd-creator/SKILL.opencode.md +0 -193
- package/src/gdskills/bundled/skills/planning/prd-creator/SKILL.zed.md +0 -193
- package/src/gdskills/bundled/skills/platform/claude-md-management/SKILL.codex.md +0 -87
- package/src/gdskills/bundled/skills/platform/claude-md-management/SKILL.cursor.md +0 -87
- package/src/gdskills/bundled/skills/platform/hookify/SKILL.codex.md +0 -100
- package/src/gdskills/bundled/skills/platform/hookify/SKILL.cursor.md +0 -100
- package/src/gdskills/bundled/skills/quality/changelog/SKILL.codex.md +0 -84
- package/src/gdskills/bundled/skills/quality/changelog/SKILL.cursor.md +0 -84
- package/src/gdskills/bundled/skills/quality/commit/SKILL.codex.md +0 -66
- package/src/gdskills/bundled/skills/quality/commit/SKILL.cursor.md +0 -66
- package/src/gdskills/bundled/skills/quality/db-migrate/SKILL.codex.md +0 -66
- package/src/gdskills/bundled/skills/quality/db-migrate/SKILL.cursor.md +0 -66
- package/src/gdskills/bundled/skills/quality/dependency-update/SKILL.codex.md +0 -81
- package/src/gdskills/bundled/skills/quality/dependency-update/SKILL.cursor.md +0 -81
- package/src/gdskills/bundled/skills/quality/deploy/SKILL.codex.md +0 -70
- package/src/gdskills/bundled/skills/quality/deploy/SKILL.cursor.md +0 -70
- package/src/gdskills/bundled/skills/quality/perf-check/SKILL.codex.md +0 -83
- package/src/gdskills/bundled/skills/quality/perf-check/SKILL.cursor.md +0 -83
- package/src/gdskills/bundled/skills/quality/pr/SKILL.codex.md +0 -75
- package/src/gdskills/bundled/skills/quality/pr/SKILL.cursor.md +0 -75
- package/src/gdskills/bundled/skills/quality/pr-issue-documenter/SKILL.codex.md +0 -378
- package/src/gdskills/bundled/skills/quality/pr-issue-documenter/SKILL.cursor.md +0 -378
- package/src/gdskills/bundled/skills/quality/pr-issue-documenter/SKILL.opencode.md +0 -378
- package/src/gdskills/bundled/skills/quality/pr-issue-documenter/SKILL.zed.md +0 -378
- package/src/gdskills/bundled/skills/quality/push/SKILL.codex.md +0 -52
- package/src/gdskills/bundled/skills/quality/push/SKILL.cursor.md +0 -52
- package/src/gdskills/bundled/skills/quality/security-audit/SKILL.codex.md +0 -108
- package/src/gdskills/bundled/skills/quality/security-audit/SKILL.cursor.md +0 -108
- package/src/gdskills/bundled/skills/quality/test-gen/SKILL.codex.md +0 -80
- package/src/gdskills/bundled/skills/quality/test-gen/SKILL.cursor.md +0 -80
- package/src/gdskills/bundled/skills/quality/tests-creator/SKILL.codex.md +0 -345
- package/src/gdskills/bundled/skills/quality/tests-creator/SKILL.cursor.md +0 -345
- package/src/gdskills/bundled/skills/quality/tests-creator/SKILL.opencode.md +0 -345
- package/src/gdskills/bundled/skills/quality/tests-creator/SKILL.zed.md +0 -345
- package/src/gdskills/bundled/skills/review/code-ai-review/SKILL.codex.md +0 -203
- package/src/gdskills/bundled/skills/review/code-ai-review/SKILL.cursor.md +0 -203
- package/src/gdskills/bundled/skills/review/code-ai-review/SKILL.opencode.md +0 -203
- package/src/gdskills/bundled/skills/review/code-ai-review/SKILL.zed.md +0 -203
- package/src/gdskills/bundled/skills/review/code-learned-review/SKILL.codex.md +0 -243
- package/src/gdskills/bundled/skills/review/code-learned-review/SKILL.cursor.md +0 -243
- package/src/gdskills/bundled/skills/review/code-learned-review/SKILL.opencode.md +0 -243
- package/src/gdskills/bundled/skills/review/code-learned-review/SKILL.zed.md +0 -243
- package/src/gdskills/bundled/skills/review/code-mobx-store-review/SKILL.codex.md +0 -259
- package/src/gdskills/bundled/skills/review/code-mobx-store-review/SKILL.cursor.md +0 -259
- package/src/gdskills/bundled/skills/review/code-mobx-store-review/SKILL.opencode.md +0 -259
- package/src/gdskills/bundled/skills/review/code-mobx-store-review/SKILL.zed.md +0 -259
- package/src/gdskills/bundled/skills/review/code-style-review/SKILL.codex.md +0 -168
- package/src/gdskills/bundled/skills/review/code-style-review/SKILL.cursor.md +0 -168
- package/src/gdskills/bundled/skills/review/code-style-review/SKILL.opencode.md +0 -168
- 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.
|
|
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 (
|
|
30
|
-
- `SKILL.codex.md` — Codex-specific variant (
|
|
31
|
-
- `SKILL.zed.md` — Zed-specific variant (
|
|
32
|
-
- `SKILL.opencode.md` — OpenCode-specific variant (
|
|
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.
|
|
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
|
|
87
|
-
- `codex` → `${CODEX_HOME:-~/.codex}/skills/<module>-<skill-name>/SKILL.md` (built from `SKILL.codex.md
|
|
88
|
-
- `zed` → `~/.config/zed/skills/<module>-<skill-name>/SKILL.md` (built from `SKILL.zed.md
|
|
89
|
-
- `opencode` → `~/.config/opencode/skills/<module>-<skill-name>/SKILL.md` (built from `SKILL.opencode.md
|
|
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
|
|
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
|
|
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.
|