karajan-code 2.10.2 → 2.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -1
- package/package.json +1 -1
- package/src/audit/plan-adherence.js +222 -0
- package/src/audit/sonar-findings.js +12 -0
- package/src/cli/register-meta.js +4 -4
- package/src/cli/register-pipeline.js +3 -3
- package/src/cli/register-plan.js +1 -1
- package/src/commands/board.js +28 -4
- package/src/commands/init.js +24 -5
- package/src/config/loader.js +32 -1
- package/src/git/automation.js +94 -4
- package/src/git/hu-automation.js +35 -3
- package/src/golden/asserter.js +90 -0
- package/src/golden/loader.js +52 -0
- package/src/golden/runner.js +92 -0
- package/src/golden/schema.js +41 -0
- package/src/orchestrator/config-init.js +17 -0
- package/src/orchestrator/drivers/post-loop.js +64 -3
- package/src/orchestrator/flow-runner.js +41 -0
- package/src/orchestrator/stages/sonar-stage.js +19 -0
- package/src/session/journal/summary-writer.js +39 -0
- package/src/skills/addyosmani-catalog.js +29 -7
- package/src/sonar/project-key.js +37 -0
- package/src/types/session.js +2 -1
- package/src/utils/display/session-summary.js +12 -2
- package/src/utils/git.js +132 -1
- package/templates/roles/architect/anthropic.md +9 -0
- package/templates/roles/architect/default.md +9 -0
- package/templates/roles/architect/google.md +9 -0
- package/templates/roles/architect/openai.md +9 -0
- package/templates/roles/architect/opencode.md +9 -0
- package/templates/roles/architect.md +9 -0
- package/templates/roles/coder/anthropic.md +9 -0
- package/templates/roles/coder/default.md +9 -0
- package/templates/roles/coder/google.md +9 -0
- package/templates/roles/coder/openai.md +9 -0
- package/templates/roles/coder/opencode.md +9 -0
- package/templates/roles/coder.md +9 -0
- package/templates/roles/planner/anthropic.md +9 -0
- package/templates/roles/planner/default.md +9 -0
- package/templates/roles/planner/google.md +9 -0
- package/templates/roles/planner/openai.md +9 -0
- package/templates/roles/planner/opencode.md +9 -0
- package/templates/roles/planner.md +9 -0
- package/templates/roles/refactorer.md +9 -0
- package/templates/roles/repairer.md +9 -0
- package/templates/roles/tester.md +9 -0
|
@@ -146,7 +146,7 @@ export async function ensureCatalog(opts = {}) {
|
|
|
146
146
|
* ensureCatalog().
|
|
147
147
|
*
|
|
148
148
|
* @param {{ refreshMs?: number, repoUrl?: string, force?: boolean, logger?: object }} [opts]
|
|
149
|
-
* @returns {Promise<{ok: boolean, action: "cloned"|"pulled"|"fresh"|"skipped", error?: string}>}
|
|
149
|
+
* @returns {Promise<{ok: boolean, action: "cloned"|"pulled"|"reset"|"fresh"|"skipped", error?: string}>}
|
|
150
150
|
*/
|
|
151
151
|
export async function refreshIfStale(opts = {}) {
|
|
152
152
|
const { refreshMs = DEFAULT_REFRESH_MS, repoUrl = REPO_URL, force = false, logger } = opts;
|
|
@@ -171,16 +171,38 @@ export async function refreshIfStale(opts = {}) {
|
|
|
171
171
|
return { ok: true, action: "skipped" };
|
|
172
172
|
}
|
|
173
173
|
|
|
174
|
-
const
|
|
174
|
+
const pullResult = await runCommand(
|
|
175
175
|
"git",
|
|
176
176
|
["-C", root, "pull", "--ff-only", "--depth", "1"],
|
|
177
177
|
{ timeout: 60_000 }
|
|
178
178
|
);
|
|
179
179
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
180
|
+
let action = "pulled";
|
|
181
|
+
if (pullResult.exitCode !== 0) {
|
|
182
|
+
// The cached catalog is read-only (we never commit to it), so a
|
|
183
|
+
// non-fast-forward result is virtually always upstream rewriting
|
|
184
|
+
// history with `git push --force` — see KJC-BUG-0033 / N3-2 (the
|
|
185
|
+
// user hit this on 2026-05-07 when addyosmani force-pushed his
|
|
186
|
+
// skills repo). Recover by fetching shallow and hard-resetting to
|
|
187
|
+
// FETCH_HEAD: there's nothing local to lose.
|
|
188
|
+
const fetchResult = await runCommand(
|
|
189
|
+
"git",
|
|
190
|
+
["-C", root, "fetch", "--depth", "1", "origin", "HEAD"],
|
|
191
|
+
{ timeout: 60_000 }
|
|
192
|
+
);
|
|
193
|
+
const resetResult = fetchResult.exitCode === 0
|
|
194
|
+
? await runCommand("git", ["-C", root, "reset", "--hard", "FETCH_HEAD"], { timeout: 30_000 })
|
|
195
|
+
: { exitCode: fetchResult.exitCode, stderr: fetchResult.stderr };
|
|
196
|
+
|
|
197
|
+
if (resetResult.exitCode !== 0) {
|
|
198
|
+
const pullErr = (pullResult.stderr || "").trim();
|
|
199
|
+
const recoveryErr = (resetResult.stderr || "").trim();
|
|
200
|
+
const combined = recoveryErr ? `${pullErr} | recovery: ${recoveryErr}` : pullErr;
|
|
201
|
+
logger?.warn?.(`addyosmani-catalog: git pull failed — ${combined} (keeping stale cache)`);
|
|
202
|
+
return { ok: false, action: "skipped", error: combined };
|
|
203
|
+
}
|
|
204
|
+
action = "reset";
|
|
205
|
+
logger?.info?.("addyosmani-catalog: ff-only failed, recovered via fetch+reset (upstream force-pushed)");
|
|
184
206
|
}
|
|
185
207
|
|
|
186
208
|
await writeMeta({
|
|
@@ -189,7 +211,7 @@ export async function refreshIfStale(opts = {}) {
|
|
|
189
211
|
lastPullAt: new Date().toISOString(),
|
|
190
212
|
});
|
|
191
213
|
|
|
192
|
-
return { ok: true, action
|
|
214
|
+
return { ok: true, action };
|
|
193
215
|
}
|
|
194
216
|
|
|
195
217
|
/**
|
package/src/sonar/project-key.js
CHANGED
|
@@ -81,3 +81,40 @@ export async function resolveSonarProjectKey(config, options = {}) {
|
|
|
81
81
|
const repo = slug(repoId.split("/").pop());
|
|
82
82
|
return normalizeProjectKey(`kj-${repo}-${digest(repoId)}`);
|
|
83
83
|
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Non-throwing predicate: can a Sonar project key be resolved for `config`?
|
|
87
|
+
*
|
|
88
|
+
* Used by callers (audit collector, run-loop SonarStage) that want to skip
|
|
89
|
+
* the Sonar work cleanly instead of letting `resolveSonarProjectKey` throw
|
|
90
|
+
* "Missing git remote.origin.url" deep inside the scanner. The scanner's
|
|
91
|
+
* thrown error is fine for an explicit `kj sonar` call (the user asked
|
|
92
|
+
* for it), but for implicit pipeline gates it produces a noisy "Sonar
|
|
93
|
+
* scan failed" loop in fresh /tmp/... repos.
|
|
94
|
+
*
|
|
95
|
+
* Returns true when EITHER:
|
|
96
|
+
* - an explicit `project_key` is set (env var, config, or per-call option),
|
|
97
|
+
* - OR `git config --get remote.origin.url` succeeds with a non-empty value.
|
|
98
|
+
*
|
|
99
|
+
* Side-effect-free apart from the git probe.
|
|
100
|
+
*
|
|
101
|
+
* @param {object} config
|
|
102
|
+
* @param {object} [options]
|
|
103
|
+
* @param {string} [options.projectKey]
|
|
104
|
+
* @returns {Promise<boolean>}
|
|
105
|
+
*/
|
|
106
|
+
export async function canResolveSonarProjectKey(config, options = {}) {
|
|
107
|
+
const explicit = String(
|
|
108
|
+
options.projectKey || process.env.KJ_SONAR_PROJECT_KEY || config?.sonarqube?.project_key || ""
|
|
109
|
+
).trim();
|
|
110
|
+
if (explicit) return true;
|
|
111
|
+
// try/catch (instead of `.catch(...)`) tolerates two legitimate test
|
|
112
|
+
// realities: a runCommand mock that returns undefined, and a real
|
|
113
|
+
// promise rejection from a missing git binary. Either way we treat
|
|
114
|
+
// it as "no remote available" and let the caller skip Sonar cleanly.
|
|
115
|
+
let remote;
|
|
116
|
+
try { remote = await runCommand("git", ["config", "--get", "remote.origin.url"]); }
|
|
117
|
+
catch { return false; }
|
|
118
|
+
if (!remote) return false;
|
|
119
|
+
return remote.exitCode === 0 && String(remote.stdout || "").trim().length > 0;
|
|
120
|
+
}
|
package/src/types/session.js
CHANGED
|
@@ -64,7 +64,8 @@
|
|
|
64
64
|
* @property {string[]} [skillsRecommended] - wouldHaveUsed when openskills missing
|
|
65
65
|
* @property {string[]} [autoInstalledSkills]
|
|
66
66
|
* @property {RtkSavings} [rtk_savings]
|
|
67
|
-
* @property {string} [session_start_sha]
|
|
67
|
+
* @property {string} [session_start_sha] - baseline for `git diff`; may be the empty-tree SHA when no prior commits exist
|
|
68
|
+
* @property {string} [head_at_start] - actual HEAD when the run started; used by the post-loop journal to enumerate commits produced BY the run. Falls back to session_start_sha in zero-commits repos. (2026-05-07 dogfooding fix.)
|
|
68
69
|
* @property {string} [_journalDir] - runtime journal path
|
|
69
70
|
* @property {string[]} [_journalIterations] - rendered iteration markdown
|
|
70
71
|
* @property {string[]} [_journalFiles]
|
|
@@ -26,9 +26,19 @@ function printSessionPlanner(planner) {
|
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
function printSessionSonar(sonar) {
|
|
29
|
+
export function printSessionSonar(sonar) {
|
|
30
30
|
if (!sonar) return;
|
|
31
|
-
|
|
31
|
+
// Three buckets so the banner reads correctly:
|
|
32
|
+
// - OK \u2192 green (real pass).
|
|
33
|
+
// - SKIPPED / PENDING \u2192 gray (informational; sonar
|
|
34
|
+
// wasn't gated against, this is NOT a fail). Pre-2026-05-07 N4
|
|
35
|
+
// dogfooding these were rendered red, which made a clean run
|
|
36
|
+
// look like a sonar failure during the live demo.
|
|
37
|
+
// - everything else (ERROR, WARN, ...) \u2192 red (real fail).
|
|
38
|
+
let gateLabel;
|
|
39
|
+
if (sonar.gateStatus === "OK") gateLabel = ANSI.green;
|
|
40
|
+
else if (sonar.gateStatus === "SKIPPED" || sonar.gateStatus === "PENDING") gateLabel = ANSI.gray;
|
|
41
|
+
else gateLabel = ANSI.red;
|
|
32
42
|
console.log(` ${ANSI.dim}\ud83d\udd0d Sonar: ${gateLabel}${sonar.gateStatus}${ANSI.reset}${ANSI.dim} (${sonar.openIssues ?? 0} issues)${ANSI.reset}`);
|
|
33
43
|
if (typeof sonar.issuesInitial === "number" || typeof sonar.issuesResolved === "number") {
|
|
34
44
|
const issuesInitial = sonar.issuesInitial ?? sonar.openIssues ?? 0;
|
package/src/utils/git.js
CHANGED
|
@@ -65,6 +65,69 @@ export async function revParse(ref) {
|
|
|
65
65
|
return runGit(["rev-parse", ref]);
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
/**
|
|
69
|
+
* List commits in the range `fromSha..toRef`. Used by the post-loop
|
|
70
|
+
* journal builder to enumerate every commit produced during a `kj run`
|
|
71
|
+
* — both the coder's own commits AND the post-loop scaffold commit —
|
|
72
|
+
* so the user-visible summary doesn't lose track of them. Pre-fix the
|
|
73
|
+
* summary only listed `gitResult.commits` (post-loop only), which made
|
|
74
|
+
* `kj run "..."` runs look like the coder hadn't committed.
|
|
75
|
+
*
|
|
76
|
+
* Returns oldest-first so the list reads in execution order.
|
|
77
|
+
*
|
|
78
|
+
* @param {string|null|undefined} fromSha The SHA right BEFORE the run started
|
|
79
|
+
* (typically `session.session_start_sha`). Empty/missing → returns [].
|
|
80
|
+
* @param {string} [toRef="HEAD"]
|
|
81
|
+
* @returns {Promise<Array<{hash: string, message: string}>>}
|
|
82
|
+
*/
|
|
83
|
+
/**
|
|
84
|
+
* List the file paths changed in the range `fromSha..toRef`. Used by the
|
|
85
|
+
* plan-adherence metric (KJC-TSK-0376) to score whether the coder's
|
|
86
|
+
* file changes fall within any HU's declared scope.
|
|
87
|
+
*
|
|
88
|
+
* Returns a deduplicated array of file paths, oldest commit's files
|
|
89
|
+
* first. Defensive: returns [] on missing `fromSha`, non-zero git exit,
|
|
90
|
+
* or thrown error — consistent with `listCommitsBetween`'s contract.
|
|
91
|
+
*
|
|
92
|
+
* @param {string|null|undefined} fromSha
|
|
93
|
+
* @param {string} [toRef="HEAD"]
|
|
94
|
+
* @returns {Promise<string[]>}
|
|
95
|
+
*/
|
|
96
|
+
export async function listFilesChangedSince(fromSha, toRef = "HEAD") {
|
|
97
|
+
if (!fromSha) return [];
|
|
98
|
+
const result = await run(
|
|
99
|
+
"git",
|
|
100
|
+
["diff", "--name-only", `${fromSha}..${toRef}`],
|
|
101
|
+
).catch(() => null);
|
|
102
|
+
if (!result || result.exitCode !== 0) return [];
|
|
103
|
+
const lines = String(result.stdout || "")
|
|
104
|
+
.split("\n")
|
|
105
|
+
.map((line) => line.trim())
|
|
106
|
+
.filter(Boolean);
|
|
107
|
+
return Array.from(new Set(lines));
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export async function listCommitsBetween(fromSha, toRef = "HEAD") {
|
|
111
|
+
if (!fromSha) return [];
|
|
112
|
+
const result = await run(
|
|
113
|
+
"git",
|
|
114
|
+
["log", "--reverse", "--format=%H%x09%s", `${fromSha}..${toRef}`],
|
|
115
|
+
).catch(() => null);
|
|
116
|
+
if (!result || result.exitCode !== 0) return [];
|
|
117
|
+
return String(result.stdout || "")
|
|
118
|
+
.split("\n")
|
|
119
|
+
.map((line) => line.trimEnd())
|
|
120
|
+
.filter(Boolean)
|
|
121
|
+
.map((line) => {
|
|
122
|
+
const tabIdx = line.indexOf("\t");
|
|
123
|
+
// Defensive: if git emitted no tab (shouldn't happen with our format)
|
|
124
|
+
// treat the whole line as the hash and leave the message empty rather
|
|
125
|
+
// than throw — caller can still render the row.
|
|
126
|
+
if (tabIdx === -1) return { hash: line, message: "" };
|
|
127
|
+
return { hash: line.slice(0, tabIdx), message: line.slice(tabIdx + 1) };
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
68
131
|
export async function syncBaseBranch({ baseBranch, autoRebase }) {
|
|
69
132
|
const local = await revParse(baseBranch);
|
|
70
133
|
const remote = await revParse(`origin/${baseBranch}`);
|
|
@@ -109,11 +172,79 @@ export async function hasChanges() {
|
|
|
109
172
|
return status.length > 0;
|
|
110
173
|
}
|
|
111
174
|
|
|
175
|
+
/**
|
|
176
|
+
* Return the relative paths of every uncommitted change (modified,
|
|
177
|
+
* untracked, staged, etc.) in the working tree. Each path is the
|
|
178
|
+
* filename as `git status --porcelain` reports it (after the 2-char
|
|
179
|
+
* status code + space). Returns an empty array when the tree is clean.
|
|
180
|
+
*
|
|
181
|
+
* Used by `finalizeGitAutomation` to decide whether the changes
|
|
182
|
+
* pending at the end of a pipeline are exclusively Karajan
|
|
183
|
+
* scaffolding (and thus deserve a `chore:` commit message instead
|
|
184
|
+
* of inheriting the user task's `feat:`).
|
|
185
|
+
*
|
|
186
|
+
* NOTE: bypasses `runGit()` because that helper trims stdout, which
|
|
187
|
+
* eats the leading space of porcelain lines whose first status char
|
|
188
|
+
* is " " (e.g. " M .gitignore" → "M .gitignore"). The leading space
|
|
189
|
+
* is structural here — without it the slice(3) below would crop one
|
|
190
|
+
* character off the first path.
|
|
191
|
+
*/
|
|
192
|
+
export async function listPendingPaths() {
|
|
193
|
+
const res = await run("git", ["status", "--porcelain"]);
|
|
194
|
+
if (res.exitCode !== 0) {
|
|
195
|
+
throw new Error(`git status --porcelain failed: ${res.stderr || res.stdout}`);
|
|
196
|
+
}
|
|
197
|
+
const stdout = res.stdout;
|
|
198
|
+
if (!stdout) return [];
|
|
199
|
+
const lines = stdout.split("\n").map((l) => l.replace(/\r$/, "")).filter(Boolean);
|
|
200
|
+
const out = [];
|
|
201
|
+
for (const line of lines) {
|
|
202
|
+
// Each line is "XY <path>" (XY = 2-char status, space, path).
|
|
203
|
+
// Renames look like "R old -> new"; we want the new path.
|
|
204
|
+
const after = line.slice(3);
|
|
205
|
+
const arrow = after.indexOf(" -> ");
|
|
206
|
+
out.push(arrow !== -1 ? after.slice(arrow + 4) : after);
|
|
207
|
+
}
|
|
208
|
+
return out;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Locale-tolerant detection of "git commit refused because the working
|
|
212
|
+
// tree was already clean by the time we ran it". Observed in N4
|
|
213
|
+
// dogfooding (2026-05-07) — `hasChanges()` reported true after `add -A`
|
|
214
|
+
// but `git commit` then failed with the Spanish message "nada para hacer
|
|
215
|
+
// commit, el árbol de trabajo está limpio". The race window is real:
|
|
216
|
+
// post-loop journal writes can finish between hasChanges and commit, and
|
|
217
|
+
// some files come back from `add -A` with no diff vs HEAD even though
|
|
218
|
+
// they show up in `git status --porcelain`. We treat any of those as
|
|
219
|
+
// "no commit needed" rather than escalating to Solomon.
|
|
220
|
+
const NOTHING_TO_COMMIT_PATTERNS = [
|
|
221
|
+
/nothing to commit/i,
|
|
222
|
+
/nada para hacer commit/i, // git in es_ES locale
|
|
223
|
+
/no hay nada para confirmar/i, // git in es_ES alt phrasing
|
|
224
|
+
/aucune modification ajout[ée]e au commit/i, // fr_FR
|
|
225
|
+
/nichts zu committen/i, // de_DE
|
|
226
|
+
];
|
|
227
|
+
function isNothingToCommit(message) {
|
|
228
|
+
const m = String(message || "");
|
|
229
|
+
return NOTHING_TO_COMMIT_PATTERNS.some((re) => re.test(m));
|
|
230
|
+
}
|
|
231
|
+
|
|
112
232
|
export async function commitAll(message) {
|
|
113
233
|
await runGit(["add", "-A"]);
|
|
114
234
|
const changed = await hasChanges();
|
|
115
235
|
if (!changed) return { committed: false };
|
|
116
|
-
|
|
236
|
+
try {
|
|
237
|
+
await runGit(["commit", "-m", message]);
|
|
238
|
+
} catch (err) {
|
|
239
|
+
// `git status --porcelain` and `git commit` disagreed about whether
|
|
240
|
+
// there was anything to commit. Don't escalate — the only outcome
|
|
241
|
+
// either way is "no new commit was created", which the caller
|
|
242
|
+
// already handles via committed=false.
|
|
243
|
+
if (isNothingToCommit(err?.message)) {
|
|
244
|
+
return { committed: false };
|
|
245
|
+
}
|
|
246
|
+
throw err;
|
|
247
|
+
}
|
|
117
248
|
const raw = await runGit(["log", "-1", "--pretty=format:%H%x1f%s"]);
|
|
118
249
|
const [hash, commitMessage] = raw.split("\x1f");
|
|
119
250
|
return { committed: true, commit: { hash, message: commitMessage } };
|
|
@@ -63,3 +63,12 @@ Return a single valid JSON object and nothing else.
|
|
|
63
63
|
|
|
64
64
|
If the architecture is fully defined, `verdict: "ready"` and `questions: []`.
|
|
65
65
|
</output_format>
|
|
66
|
+
|
|
67
|
+
## PR atomicity (hard project rule)
|
|
68
|
+
|
|
69
|
+
Karajan projects MAY enforce a CI gate that fails any PR whose net delta exceeds **200 lines added** (the karajan-code repo itself enforces this since 2026-05-08). Plan your work to stay atomic:
|
|
70
|
+
|
|
71
|
+
- Aim for **~150 LOC** of changes per unit you produce (safety margin against the 200 hard limit).
|
|
72
|
+
- The gate counts the SUM of every changed file, not per-file. Tests count too. 5 files × 40 LOC = 200 = on the limit.
|
|
73
|
+
- Excluded from the count: lockfiles, snapshots, `dist/`, `node_modules/`, generated `tests/_diet/`, `public/docs/`. Source + tests count.
|
|
74
|
+
- Token-economy: oversized PRs get rejected at CI and the work is redone — partitioning upfront saves the round-trip.
|
|
@@ -61,3 +61,12 @@ If the architecture is fully defined with no open questions, return `verdict: "r
|
|
|
61
61
|
- Document WHY each pattern was chosen, not just WHAT
|
|
62
62
|
- If research context is provided, use it to inform architectural decisions
|
|
63
63
|
- Never invent requirements — if something is unclear, add it to questions
|
|
64
|
+
|
|
65
|
+
## PR atomicity (hard project rule)
|
|
66
|
+
|
|
67
|
+
Karajan projects MAY enforce a CI gate that fails any PR whose net delta exceeds **200 lines added** (the karajan-code repo itself enforces this since 2026-05-08). Plan your work to stay atomic:
|
|
68
|
+
|
|
69
|
+
- Aim for **~150 LOC** of changes per unit you produce (safety margin against the 200 hard limit).
|
|
70
|
+
- The gate counts the SUM of every changed file, not per-file. Tests count too. 5 files × 40 LOC = 200 = on the limit.
|
|
71
|
+
- Excluded from the count: lockfiles, snapshots, `dist/`, `node_modules/`, generated `tests/_diet/`, `public/docs/`. Source + tests count.
|
|
72
|
+
- Token-economy: oversized PRs get rejected at CI and the work is redone — partitioning upfront saves the round-trip.
|
|
@@ -50,3 +50,12 @@ Questions MUST include which isolation level (shared DB + tenant_id column vs sc
|
|
|
50
50
|
## Output (strict JSON)
|
|
51
51
|
|
|
52
52
|
Same shape as Example 1. Exactly one top-level object. No prose around it.
|
|
53
|
+
|
|
54
|
+
## PR atomicity (hard project rule)
|
|
55
|
+
|
|
56
|
+
Karajan projects MAY enforce a CI gate that fails any PR whose net delta exceeds **200 lines added** (the karajan-code repo itself enforces this since 2026-05-08). Plan your work to stay atomic:
|
|
57
|
+
|
|
58
|
+
- Aim for **~150 LOC** of changes per unit you produce (safety margin against the 200 hard limit).
|
|
59
|
+
- The gate counts the SUM of every changed file, not per-file. Tests count too. 5 files × 40 LOC = 200 = on the limit.
|
|
60
|
+
- Excluded from the count: lockfiles, snapshots, `dist/`, `node_modules/`, generated `tests/_diet/`, `public/docs/`. Source + tests count.
|
|
61
|
+
- Token-economy: oversized PRs get rejected at CI and the work is redone — partitioning upfront saves the round-trip.
|
|
@@ -49,3 +49,12 @@ You are the Architect. Design the technical architecture BEFORE implementation.
|
|
|
49
49
|
```
|
|
50
50
|
|
|
51
51
|
If fully defined → `verdict: "ready"` and `questions: []`.
|
|
52
|
+
|
|
53
|
+
## PR atomicity (hard project rule)
|
|
54
|
+
|
|
55
|
+
Karajan projects MAY enforce a CI gate that fails any PR whose net delta exceeds **200 lines added** (the karajan-code repo itself enforces this since 2026-05-08). Plan your work to stay atomic:
|
|
56
|
+
|
|
57
|
+
- Aim for **~150 LOC** of changes per unit you produce (safety margin against the 200 hard limit).
|
|
58
|
+
- The gate counts the SUM of every changed file, not per-file. Tests count too. 5 files × 40 LOC = 200 = on the limit.
|
|
59
|
+
- Excluded from the count: lockfiles, snapshots, `dist/`, `node_modules/`, generated `tests/_diet/`, `public/docs/`. Source + tests count.
|
|
60
|
+
- Token-economy: oversized PRs get rejected at CI and the work is redone — partitioning upfront saves the round-trip.
|
|
@@ -48,3 +48,12 @@ Rules for the JSON:
|
|
|
48
48
|
- Exact keys only. No extras.
|
|
49
49
|
- All arrays may be empty but never null.
|
|
50
50
|
- If `verdict` is "needs_clarification", `questions` must be non-empty.
|
|
51
|
+
|
|
52
|
+
## PR atomicity (hard project rule)
|
|
53
|
+
|
|
54
|
+
Karajan projects MAY enforce a CI gate that fails any PR whose net delta exceeds **200 lines added** (the karajan-code repo itself enforces this since 2026-05-08). Plan your work to stay atomic:
|
|
55
|
+
|
|
56
|
+
- Aim for **~150 LOC** of changes per unit you produce (safety margin against the 200 hard limit).
|
|
57
|
+
- The gate counts the SUM of every changed file, not per-file. Tests count too. 5 files × 40 LOC = 200 = on the limit.
|
|
58
|
+
- Excluded from the count: lockfiles, snapshots, `dist/`, `node_modules/`, generated `tests/_diet/`, `public/docs/`. Source + tests count.
|
|
59
|
+
- Token-economy: oversized PRs get rejected at CI and the work is redone — partitioning upfront saves the round-trip.
|
|
@@ -61,3 +61,12 @@ If the architecture is fully defined with no open questions, return `verdict: "r
|
|
|
61
61
|
- Document WHY each pattern was chosen, not just WHAT
|
|
62
62
|
- If research context is provided, use it to inform architectural decisions
|
|
63
63
|
- Never invent requirements — if something is unclear, add it to questions
|
|
64
|
+
|
|
65
|
+
## PR atomicity (hard project rule)
|
|
66
|
+
|
|
67
|
+
Karajan projects MAY enforce a CI gate that fails any PR whose net delta exceeds **200 lines added** (the karajan-code repo itself enforces this since 2026-05-08). Plan your work to stay atomic:
|
|
68
|
+
|
|
69
|
+
- Aim for **~150 LOC** of changes per unit you produce (safety margin against the 200 hard limit).
|
|
70
|
+
- The gate counts the SUM of every changed file, not per-file. Tests count too. 5 files × 40 LOC = 200 = on the limit.
|
|
71
|
+
- Excluded from the count: lockfiles, snapshots, `dist/`, `node_modules/`, generated `tests/_diet/`, `public/docs/`. Source + tests count.
|
|
72
|
+
- Token-economy: oversized PRs get rejected at CI and the work is redone — partitioning upfront saves the round-trip.
|
|
@@ -70,3 +70,12 @@ Return a single JSON object matching this schema:
|
|
|
70
70
|
}
|
|
71
71
|
```
|
|
72
72
|
</output_format>
|
|
73
|
+
|
|
74
|
+
## PR atomicity (hard project rule)
|
|
75
|
+
|
|
76
|
+
Karajan projects MAY enforce a CI gate that fails any PR whose net delta exceeds **200 lines added** (the karajan-code repo itself enforces this since 2026-05-08). Plan your work to stay atomic:
|
|
77
|
+
|
|
78
|
+
- Aim for **~150 LOC** of changes per unit you produce (safety margin against the 200 hard limit).
|
|
79
|
+
- The gate counts the SUM of every changed file, not per-file. Tests count too. 5 files × 40 LOC = 200 = on the limit.
|
|
80
|
+
- Excluded from the count: lockfiles, snapshots, `dist/`, `node_modules/`, generated `tests/_diet/`, `public/docs/`. Source + tests count.
|
|
81
|
+
- Token-economy: oversized PRs get rejected at CI and the work is redone — partitioning upfront saves the round-trip.
|
|
@@ -67,3 +67,12 @@ Return a JSON object:
|
|
|
67
67
|
"summary": "Human-readable summary of changes"
|
|
68
68
|
}
|
|
69
69
|
```
|
|
70
|
+
|
|
71
|
+
## PR atomicity (hard project rule)
|
|
72
|
+
|
|
73
|
+
Karajan projects MAY enforce a CI gate that fails any PR whose net delta exceeds **200 lines added** (the karajan-code repo itself enforces this since 2026-05-08). Plan your work to stay atomic:
|
|
74
|
+
|
|
75
|
+
- Aim for **~150 LOC** of changes per unit you produce (safety margin against the 200 hard limit).
|
|
76
|
+
- The gate counts the SUM of every changed file, not per-file. Tests count too. 5 files × 40 LOC = 200 = on the limit.
|
|
77
|
+
- Excluded from the count: lockfiles, snapshots, `dist/`, `node_modules/`, generated `tests/_diet/`, `public/docs/`. Source + tests count.
|
|
78
|
+
- Token-economy: oversized PRs get rejected at CI and the work is redone — partitioning upfront saves the round-trip.
|
|
@@ -76,3 +76,12 @@ An incomplete implementation is worse than an error — don't claim success if p
|
|
|
76
76
|
"summary": "Human-readable summary of changes"
|
|
77
77
|
}
|
|
78
78
|
```
|
|
79
|
+
|
|
80
|
+
## PR atomicity (hard project rule)
|
|
81
|
+
|
|
82
|
+
Karajan projects MAY enforce a CI gate that fails any PR whose net delta exceeds **200 lines added** (the karajan-code repo itself enforces this since 2026-05-08). Plan your work to stay atomic:
|
|
83
|
+
|
|
84
|
+
- Aim for **~150 LOC** of changes per unit you produce (safety margin against the 200 hard limit).
|
|
85
|
+
- The gate counts the SUM of every changed file, not per-file. Tests count too. 5 files × 40 LOC = 200 = on the limit.
|
|
86
|
+
- Excluded from the count: lockfiles, snapshots, `dist/`, `node_modules/`, generated `tests/_diet/`, `public/docs/`. Source + tests count.
|
|
87
|
+
- Token-economy: oversized PRs get rejected at CI and the work is redone — partitioning upfront saves the round-trip.
|
|
@@ -57,3 +57,12 @@ Return JSON:
|
|
|
57
57
|
"summary": "Human-readable summary of changes"
|
|
58
58
|
}
|
|
59
59
|
```
|
|
60
|
+
|
|
61
|
+
## PR atomicity (hard project rule)
|
|
62
|
+
|
|
63
|
+
Karajan projects MAY enforce a CI gate that fails any PR whose net delta exceeds **200 lines added** (the karajan-code repo itself enforces this since 2026-05-08). Plan your work to stay atomic:
|
|
64
|
+
|
|
65
|
+
- Aim for **~150 LOC** of changes per unit you produce (safety margin against the 200 hard limit).
|
|
66
|
+
- The gate counts the SUM of every changed file, not per-file. Tests count too. 5 files × 40 LOC = 200 = on the limit.
|
|
67
|
+
- Excluded from the count: lockfiles, snapshots, `dist/`, `node_modules/`, generated `tests/_diet/`, `public/docs/`. Source + tests count.
|
|
68
|
+
- Token-economy: oversized PRs get rejected at CI and the work is redone — partitioning upfront saves the round-trip.
|
|
@@ -47,3 +47,12 @@ Rules for the JSON:
|
|
|
47
47
|
- `approach` is one short sentence.
|
|
48
48
|
- `summary` is one short sentence.
|
|
49
49
|
- No extra keys. No text before or after the JSON.
|
|
50
|
+
|
|
51
|
+
## PR atomicity (hard project rule)
|
|
52
|
+
|
|
53
|
+
Karajan projects MAY enforce a CI gate that fails any PR whose net delta exceeds **200 lines added** (the karajan-code repo itself enforces this since 2026-05-08). Plan your work to stay atomic:
|
|
54
|
+
|
|
55
|
+
- Aim for **~150 LOC** of changes per unit you produce (safety margin against the 200 hard limit).
|
|
56
|
+
- The gate counts the SUM of every changed file, not per-file. Tests count too. 5 files × 40 LOC = 200 = on the limit.
|
|
57
|
+
- Excluded from the count: lockfiles, snapshots, `dist/`, `node_modules/`, generated `tests/_diet/`, `public/docs/`. Source + tests count.
|
|
58
|
+
- Token-economy: oversized PRs get rejected at CI and the work is redone — partitioning upfront saves the round-trip.
|
package/templates/roles/coder.md
CHANGED
|
@@ -12,6 +12,15 @@ You are the **Coder** in a multi-role AI pipeline. Your job is to write code and
|
|
|
12
12
|
- Before creating a new utility or helper, check if a similar one already exists in the codebase. Reuse existing code over creating duplicates.
|
|
13
13
|
- Follow existing code conventions and patterns in the repository.
|
|
14
14
|
|
|
15
|
+
## PR atomicity (hard project rule)
|
|
16
|
+
|
|
17
|
+
Karajan projects MAY enforce a CI gate that fails any PR whose net delta exceeds **200 lines added** (the karajan-code repo itself enforces this since 2026-05-08). Plan before writing:
|
|
18
|
+
|
|
19
|
+
- Stay under **~150 LOC per iteration** (safety margin against the 200 hard limit).
|
|
20
|
+
- The gate counts the SUM of every changed file, not per-file. Tests count too. 5 files × 40 LOC = 200 = on the limit.
|
|
21
|
+
- If the task clearly requires >150 LOC, STOP and report the partition needed. Don't ship a 500-LOC PR — the gate rejects it and the work is redone, burning tokens twice.
|
|
22
|
+
- Excluded from the count: lockfiles, snapshots, `dist/`, `node_modules/`, generated `tests/_diet/`, `public/docs/`. Source + tests count.
|
|
23
|
+
|
|
15
24
|
## Task completeness
|
|
16
25
|
|
|
17
26
|
Before reporting done, verify that ALL parts of the task are addressed:
|
|
@@ -48,3 +48,12 @@ Any of:
|
|
|
48
48
|
}
|
|
49
49
|
```
|
|
50
50
|
</output_format>
|
|
51
|
+
|
|
52
|
+
## PR atomicity (hard project rule)
|
|
53
|
+
|
|
54
|
+
Karajan projects MAY enforce a CI gate that fails any PR whose net delta exceeds **200 lines added** (the karajan-code repo itself enforces this since 2026-05-08). Plan your work to stay atomic:
|
|
55
|
+
|
|
56
|
+
- Aim for **~150 LOC** of changes per unit you produce (safety margin against the 200 hard limit).
|
|
57
|
+
- The gate counts the SUM of every changed file, not per-file. Tests count too. 5 files × 40 LOC = 200 = on the limit.
|
|
58
|
+
- Excluded from the count: lockfiles, snapshots, `dist/`, `node_modules/`, generated `tests/_diet/`, `public/docs/`. Source + tests count.
|
|
59
|
+
- Token-economy: oversized PRs get rejected at CI and the work is redone — partitioning upfront saves the round-trip.
|
|
@@ -46,3 +46,12 @@ You are the **Planner** in a multi-role AI pipeline. Your job is to create an im
|
|
|
46
46
|
"summary": "Plan: 4 steps, estimated 2 files modified, 1 new file"
|
|
47
47
|
}
|
|
48
48
|
```
|
|
49
|
+
|
|
50
|
+
## PR atomicity (hard project rule)
|
|
51
|
+
|
|
52
|
+
Karajan projects MAY enforce a CI gate that fails any PR whose net delta exceeds **200 lines added** (the karajan-code repo itself enforces this since 2026-05-08). Plan your work to stay atomic:
|
|
53
|
+
|
|
54
|
+
- Aim for **~150 LOC** of changes per unit you produce (safety margin against the 200 hard limit).
|
|
55
|
+
- The gate counts the SUM of every changed file, not per-file. Tests count too. 5 files × 40 LOC = 200 = on the limit.
|
|
56
|
+
- Excluded from the count: lockfiles, snapshots, `dist/`, `node_modules/`, generated `tests/_diet/`, `public/docs/`. Source + tests count.
|
|
57
|
+
- Token-economy: oversized PRs get rejected at CI and the work is redone — partitioning upfront saves the round-trip.
|
|
@@ -64,3 +64,12 @@ Approach must cite concurrency model, queue semantics, shutdown. Steps include i
|
|
|
64
64
|
"summary": "Plan: N steps, M files modified, K new files"
|
|
65
65
|
}
|
|
66
66
|
```
|
|
67
|
+
|
|
68
|
+
## PR atomicity (hard project rule)
|
|
69
|
+
|
|
70
|
+
Karajan projects MAY enforce a CI gate that fails any PR whose net delta exceeds **200 lines added** (the karajan-code repo itself enforces this since 2026-05-08). Plan your work to stay atomic:
|
|
71
|
+
|
|
72
|
+
- Aim for **~150 LOC** of changes per unit you produce (safety margin against the 200 hard limit).
|
|
73
|
+
- The gate counts the SUM of every changed file, not per-file. Tests count too. 5 files × 40 LOC = 200 = on the limit.
|
|
74
|
+
- Excluded from the count: lockfiles, snapshots, `dist/`, `node_modules/`, generated `tests/_diet/`, `public/docs/`. Source + tests count.
|
|
75
|
+
- Token-economy: oversized PRs get rejected at CI and the work is redone — partitioning upfront saves the round-trip.
|
|
@@ -44,3 +44,12 @@ You are the Planner. Produce an implementation plan for the task.
|
|
|
44
44
|
"summary": "Plan: 4 steps, estimated 2 files modified, 1 new file"
|
|
45
45
|
}
|
|
46
46
|
```
|
|
47
|
+
|
|
48
|
+
## PR atomicity (hard project rule)
|
|
49
|
+
|
|
50
|
+
Karajan projects MAY enforce a CI gate that fails any PR whose net delta exceeds **200 lines added** (the karajan-code repo itself enforces this since 2026-05-08). Plan your work to stay atomic:
|
|
51
|
+
|
|
52
|
+
- Aim for **~150 LOC** of changes per unit you produce (safety margin against the 200 hard limit).
|
|
53
|
+
- The gate counts the SUM of every changed file, not per-file. Tests count too. 5 files × 40 LOC = 200 = on the limit.
|
|
54
|
+
- Excluded from the count: lockfiles, snapshots, `dist/`, `node_modules/`, generated `tests/_diet/`, `public/docs/`. Source + tests count.
|
|
55
|
+
- Token-economy: oversized PRs get rejected at CI and the work is redone — partitioning upfront saves the round-trip.
|
|
@@ -51,3 +51,12 @@ Rules for the JSON:
|
|
|
51
51
|
- Exact keys only. No extras.
|
|
52
52
|
- `order` is a positive integer.
|
|
53
53
|
- Arrays may be empty but never null.
|
|
54
|
+
|
|
55
|
+
## PR atomicity (hard project rule)
|
|
56
|
+
|
|
57
|
+
Karajan projects MAY enforce a CI gate that fails any PR whose net delta exceeds **200 lines added** (the karajan-code repo itself enforces this since 2026-05-08). Plan your work to stay atomic:
|
|
58
|
+
|
|
59
|
+
- Aim for **~150 LOC** of changes per unit you produce (safety margin against the 200 hard limit).
|
|
60
|
+
- The gate counts the SUM of every changed file, not per-file. Tests count too. 5 files × 40 LOC = 200 = on the limit.
|
|
61
|
+
- Excluded from the count: lockfiles, snapshots, `dist/`, `node_modules/`, generated `tests/_diet/`, `public/docs/`. Source + tests count.
|
|
62
|
+
- Token-economy: oversized PRs get rejected at CI and the work is redone — partitioning upfront saves the round-trip.
|
|
@@ -46,3 +46,12 @@ You are the **Planner** in a multi-role AI pipeline. Your job is to create an im
|
|
|
46
46
|
"summary": "Plan: 4 steps, estimated 2 files modified, 1 new file"
|
|
47
47
|
}
|
|
48
48
|
```
|
|
49
|
+
|
|
50
|
+
## PR atomicity (hard project rule)
|
|
51
|
+
|
|
52
|
+
Karajan projects MAY enforce a CI gate that fails any PR whose net delta exceeds **200 lines added** (the karajan-code repo itself enforces this since 2026-05-08). Plan your work to stay atomic:
|
|
53
|
+
|
|
54
|
+
- Aim for **~150 LOC** of changes per unit you produce (safety margin against the 200 hard limit).
|
|
55
|
+
- The gate counts the SUM of every changed file, not per-file. Tests count too. 5 files × 40 LOC = 200 = on the limit.
|
|
56
|
+
- Excluded from the count: lockfiles, snapshots, `dist/`, `node_modules/`, generated `tests/_diet/`, `public/docs/`. Source + tests count.
|
|
57
|
+
- Token-economy: oversized PRs get rejected at CI and the work is redone — partitioning upfront saves the round-trip.
|
|
@@ -37,3 +37,12 @@ You are the **Refactorer** in a multi-role AI pipeline. Your job is to improve c
|
|
|
37
37
|
"summary": "Refactored 2 files: extracted helper, improved naming"
|
|
38
38
|
}
|
|
39
39
|
```
|
|
40
|
+
|
|
41
|
+
## PR atomicity (hard project rule)
|
|
42
|
+
|
|
43
|
+
Karajan projects MAY enforce a CI gate that fails any PR whose net delta exceeds **200 lines added** (the karajan-code repo itself enforces this since 2026-05-08). Plan your work to stay atomic:
|
|
44
|
+
|
|
45
|
+
- Aim for **~150 LOC** of changes per unit you produce (safety margin against the 200 hard limit).
|
|
46
|
+
- The gate counts the SUM of every changed file, not per-file. Tests count too. 5 files × 40 LOC = 200 = on the limit.
|
|
47
|
+
- Excluded from the count: lockfiles, snapshots, `dist/`, `node_modules/`, generated `tests/_diet/`, `public/docs/`. Source + tests count.
|
|
48
|
+
- Token-economy: oversized PRs get rejected at CI and the work is redone — partitioning upfront saves the round-trip.
|
|
@@ -92,3 +92,12 @@ Return ONLY a single valid JSON object:
|
|
|
92
92
|
defines the contract; the code is what's measured against it.
|
|
93
93
|
- Keep changes minimal. One bug at a time. If the test has multiple
|
|
94
94
|
problems, fix the most blocking one first.
|
|
95
|
+
|
|
96
|
+
## PR atomicity (hard project rule)
|
|
97
|
+
|
|
98
|
+
Karajan projects MAY enforce a CI gate that fails any PR whose net delta exceeds **200 lines added** (the karajan-code repo itself enforces this since 2026-05-08). Plan your work to stay atomic:
|
|
99
|
+
|
|
100
|
+
- Aim for **~150 LOC** of changes per unit you produce (safety margin against the 200 hard limit).
|
|
101
|
+
- The gate counts the SUM of every changed file, not per-file. Tests count too. 5 files × 40 LOC = 200 = on the limit.
|
|
102
|
+
- Excluded from the count: lockfiles, snapshots, `dist/`, `node_modules/`, generated `tests/_diet/`, `public/docs/`. Source + tests count.
|
|
103
|
+
- Token-economy: oversized PRs get rejected at CI and the work is redone — partitioning upfront saves the round-trip.
|
|
@@ -39,3 +39,12 @@ You are the **Tester** in a multi-role AI pipeline. You are a quality gate for t
|
|
|
39
39
|
"summary": "Tests pass with 85% coverage. 1 missing scenario identified (non-blocking)."
|
|
40
40
|
}
|
|
41
41
|
```
|
|
42
|
+
|
|
43
|
+
## PR atomicity (hard project rule)
|
|
44
|
+
|
|
45
|
+
Karajan projects MAY enforce a CI gate that fails any PR whose net delta exceeds **200 lines added** (the karajan-code repo itself enforces this since 2026-05-08). Plan your work to stay atomic:
|
|
46
|
+
|
|
47
|
+
- Aim for **~150 LOC** of changes per unit you produce (safety margin against the 200 hard limit).
|
|
48
|
+
- The gate counts the SUM of every changed file, not per-file. Tests count too. 5 files × 40 LOC = 200 = on the limit.
|
|
49
|
+
- Excluded from the count: lockfiles, snapshots, `dist/`, `node_modules/`, generated `tests/_diet/`, `public/docs/`. Source + tests count.
|
|
50
|
+
- Token-economy: oversized PRs get rejected at CI and the work is redone — partitioning upfront saves the round-trip.
|