okstra 0.38.1 → 0.40.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.kr.md +1 -1
- package/README.md +1 -1
- package/docs/kr/architecture.md +18 -2
- package/docs/kr/cli.md +1 -1
- package/docs/project-structure-overview.md +2 -3
- package/docs/superpowers/plans/2026-06-02-final-verification-protocol-hardening.md +326 -0
- package/docs/superpowers/plans/2026-06-02-okstra-run-branch-confirm-step.md +337 -0
- package/docs/superpowers/plans/2026-06-02-okstra-run-phase-pane-cleanup.md +410 -0
- package/docs/superpowers/plans/2026-06-02-requirements-discovery-fanout.md +728 -0
- package/docs/superpowers/specs/2026-06-02-okstra-run-branch-confirm-step-design.md +113 -0
- package/docs/superpowers/specs/2026-06-02-okstra-run-phase-pane-cleanup-design.md +173 -0
- package/docs/superpowers/specs/2026-06-02-requirements-discovery-fanout-design.md +154 -0
- package/docs/task-process/requirements-discovery.md +1 -1
- package/package.json +3 -2
- package/runtime/BUILD.json +2 -2
- package/runtime/{python → bin}/lib/okstra/usage.sh +3 -2
- package/runtime/bin/okstra-codex-exec.sh +3 -3
- package/runtime/bin/okstra-trace-cleanup.sh +64 -26
- package/runtime/prompts/profiles/_common-contract.md +9 -5
- package/runtime/prompts/profiles/final-verification.md +18 -16
- package/runtime/prompts/profiles/implementation-planning.md +1 -0
- package/runtime/prompts/profiles/requirements-discovery.md +18 -1
- package/runtime/prompts/wizard/prompts.ko.json +11 -0
- package/runtime/python/okstra_ctl/consumers.py +1 -1
- package/runtime/python/okstra_ctl/fanout.py +35 -0
- package/runtime/python/okstra_ctl/migrate.py +21 -42
- package/runtime/python/okstra_ctl/reconcile.py +2 -2
- package/runtime/python/okstra_ctl/render_final_report.py +0 -1
- package/runtime/python/okstra_ctl/run.py +0 -29
- package/runtime/python/okstra_ctl/run_context.py +9 -12
- package/runtime/python/okstra_ctl/seeding.py +0 -192
- package/runtime/python/okstra_ctl/wizard.py +70 -5
- package/runtime/python/okstra_ctl/work_categories.py +21 -0
- package/runtime/python/okstra_ctl/worktree.py +74 -77
- package/runtime/python/okstra_project/__init__.py +0 -6
- package/runtime/python/okstra_project/dirs.py +0 -8
- package/runtime/schemas/final-report-v1.0.schema.json +34 -27
- package/runtime/skills/okstra-context-loader/SKILL.md +1 -1
- package/runtime/skills/okstra-convergence/SKILL.md +1 -1
- package/runtime/skills/okstra-inspect/SKILL.md +1 -1
- package/runtime/skills/okstra-run/SKILL.md +2 -0
- package/runtime/templates/prd/brief.template.md +1 -1
- package/runtime/templates/reports/fan-out-unit.template.md +25 -0
- package/runtime/templates/reports/final-report.template.md +24 -13
- package/runtime/templates/reports/final-verification-input.template.md +16 -5
- package/runtime/templates/reports/i18n/en.json +6 -3
- package/runtime/templates/reports/i18n/ko.json +6 -3
- package/runtime/templates/worker-prompt-preamble.md +7 -0
- package/runtime/validators/lib/fixtures.sh +2 -2
- package/runtime/validators/lib/validate-assets.sh +9 -0
- package/runtime/validators/validate-implementation-plan-stages.py +19 -11
- package/runtime/validators/validate-run.py +114 -0
- package/runtime/validators/validate-schedule.py +4 -4
- package/runtime/validators/validate_fanout.py +99 -0
- package/src/_proc.mjs +31 -0
- package/src/check-project.mjs +1 -25
- package/src/config.mjs +7 -31
- package/src/doctor.mjs +10 -29
- package/src/install.mjs +8 -36
- package/src/migrate.mjs +1 -18
- package/src/okstra-dirs.mjs +0 -11
- package/src/setup.mjs +1 -154
- package/src/uninstall.mjs +6 -13
- package/runtime/templates/okstra.CLAUDE.md +0 -104
- /package/runtime/{python → bin}/lib/okstra/cli.sh +0 -0
- /package/runtime/{python → bin}/lib/okstra/globals.sh +0 -0
- /package/runtime/{python → bin}/lib/okstra/interactive.sh +0 -0
- /package/runtime/{python → bin}/lib/okstra/project-resolver.sh +0 -0
- /package/runtime/{python → bin}/lib/okstra-ctl/cmd-batch.sh +0 -0
- /package/runtime/{python → bin}/lib/okstra-ctl/cmd-list.sh +0 -0
- /package/runtime/{python → bin}/lib/okstra-ctl/cmd-open.sh +0 -0
- /package/runtime/{python → bin}/lib/okstra-ctl/cmd-projects.sh +0 -0
- /package/runtime/{python → bin}/lib/okstra-ctl/cmd-reconcile.sh +0 -0
- /package/runtime/{python → bin}/lib/okstra-ctl/cmd-reindex.sh +0 -0
- /package/runtime/{python → bin}/lib/okstra-ctl/cmd-rerun.sh +0 -0
- /package/runtime/{python → bin}/lib/okstra-ctl/cmd-show.sh +0 -0
- /package/runtime/{python → bin}/lib/okstra-ctl/cmd-tail.sh +0 -0
- /package/runtime/{python → bin}/lib/okstra-ctl/main.sh +0 -0
- /package/runtime/{python → bin}/lib/okstra-ctl/prepare.sh +0 -0
- /package/runtime/{python → bin}/lib/okstra-ctl/usage.sh +0 -0
package/src/setup.mjs
CHANGED
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
import { promises as fs } from "node:fs";
|
|
2
|
-
import { spawn } from "node:child_process";
|
|
3
2
|
import { createInterface } from "node:readline";
|
|
4
3
|
import { homedir } from "node:os";
|
|
5
4
|
import { join, resolve as resolvePath } from "node:path";
|
|
6
5
|
import { resolvePaths } from "./paths.mjs";
|
|
7
|
-
import {
|
|
8
|
-
CLAUDE_MD_IMPORT_LINE as DIRS_CLAUDE_MD_IMPORT_LINE,
|
|
9
|
-
claudeMdSymlinkRelative,
|
|
10
|
-
} from "./okstra-dirs.mjs";
|
|
6
|
+
import { fileExists, runProcess } from "./_proc.mjs";
|
|
11
7
|
|
|
12
8
|
const USAGE = `okstra setup — register the current project with okstra
|
|
13
9
|
|
|
@@ -38,21 +34,6 @@ Exit codes:
|
|
|
38
34
|
2 PROJECT_ROOT could not be resolved
|
|
39
35
|
`;
|
|
40
36
|
|
|
41
|
-
function runProcess(cmd, args, env) {
|
|
42
|
-
return new Promise((resolve) => {
|
|
43
|
-
const child = spawn(cmd, args, {
|
|
44
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
45
|
-
env: { ...process.env, ...env },
|
|
46
|
-
});
|
|
47
|
-
let stdout = "";
|
|
48
|
-
let stderr = "";
|
|
49
|
-
child.stdout.on("data", (b) => (stdout += b.toString()));
|
|
50
|
-
child.stderr.on("data", (b) => (stderr += b.toString()));
|
|
51
|
-
child.on("error", (err) => resolve({ code: -1, stdout, stderr: err.message }));
|
|
52
|
-
child.on("close", (code) => resolve({ code, stdout, stderr }));
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
|
|
56
37
|
function parseArgs(args) {
|
|
57
38
|
const opts = {
|
|
58
39
|
projectId: null,
|
|
@@ -79,14 +60,6 @@ function parseArgs(args) {
|
|
|
79
60
|
return opts;
|
|
80
61
|
}
|
|
81
62
|
|
|
82
|
-
async function fileExists(p) {
|
|
83
|
-
try {
|
|
84
|
-
await fs.access(p);
|
|
85
|
-
return true;
|
|
86
|
-
} catch {
|
|
87
|
-
return false;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
63
|
|
|
91
64
|
function prompt(question) {
|
|
92
65
|
return new Promise((resolve) => {
|
|
@@ -297,26 +270,6 @@ export async function run(args) {
|
|
|
297
270
|
);
|
|
298
271
|
}
|
|
299
272
|
|
|
300
|
-
let claudeMd = { symlink: null, importInjected: false };
|
|
301
|
-
try {
|
|
302
|
-
claudeMd = await ensureProjectClaudeMd(projectRoot);
|
|
303
|
-
} catch (err) {
|
|
304
|
-
process.stderr.write(
|
|
305
|
-
`warning: failed to wire <PROJECT>/CLAUDE.md @ .okstra/CLAUDE.md import — ` +
|
|
306
|
-
`Claude Code sessions in this project will not auto-load okstra guidance. (${err.message})\n`,
|
|
307
|
-
);
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
let agentsMdSymlink = null;
|
|
311
|
-
try {
|
|
312
|
-
agentsMdSymlink = await ensureProjectAgentsMd(projectRoot);
|
|
313
|
-
} catch (err) {
|
|
314
|
-
process.stderr.write(
|
|
315
|
-
`warning: failed to provision <PROJECT>/AGENTS.md symlink — ` +
|
|
316
|
-
`codex / aider sessions in this project will not auto-load okstra guidance. (${err.message})\n`,
|
|
317
|
-
);
|
|
318
|
-
}
|
|
319
|
-
|
|
320
273
|
process.stdout.write(
|
|
321
274
|
JSON.stringify(
|
|
322
275
|
{
|
|
@@ -324,9 +277,6 @@ export async function run(args) {
|
|
|
324
277
|
...result,
|
|
325
278
|
projectJsonPath,
|
|
326
279
|
settingsLocalJson: settingsSymlink,
|
|
327
|
-
claudeMdSymlink: claudeMd.symlink,
|
|
328
|
-
claudeMdImportInjected: claudeMd.importInjected,
|
|
329
|
-
agentsMdSymlink,
|
|
330
280
|
},
|
|
331
281
|
null,
|
|
332
282
|
2,
|
|
@@ -381,106 +331,3 @@ async function backupAndReplace(target, template) {
|
|
|
381
331
|
await fs.symlink(template, target);
|
|
382
332
|
}
|
|
383
333
|
|
|
384
|
-
const CLAUDE_MD_TEMPLATE_PATH = join(homedir(), ".okstra", "templates", "okstra.CLAUDE.md");
|
|
385
|
-
const CLAUDE_MD_SYMLINK_REL = claudeMdSymlinkRelative();
|
|
386
|
-
const CLAUDE_MD_IMPORT_LINE = DIRS_CLAUDE_MD_IMPORT_LINE;
|
|
387
|
-
const CLAUDE_MD_MARKER_BEGIN = "<!-- okstra:claude-md:begin (managed by okstra setup — do not edit) -->";
|
|
388
|
-
const CLAUDE_MD_MARKER_END = "<!-- okstra:claude-md:end -->";
|
|
389
|
-
|
|
390
|
-
async function ensureProjectClaudeMd(projectRoot) {
|
|
391
|
-
try {
|
|
392
|
-
await fs.access(CLAUDE_MD_TEMPLATE_PATH);
|
|
393
|
-
} catch {
|
|
394
|
-
return { symlink: null, importInjected: false };
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
const symlink = await ensureClaudeMdSymlink(projectRoot);
|
|
398
|
-
const importInjected = await ensureClaudeMdImport(projectRoot);
|
|
399
|
-
return { symlink, importInjected };
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
async function ensureClaudeMdSymlink(projectRoot) {
|
|
403
|
-
const target = join(projectRoot, CLAUDE_MD_SYMLINK_REL);
|
|
404
|
-
await fs.mkdir(join(target, ".."), { recursive: true });
|
|
405
|
-
|
|
406
|
-
let existingStat;
|
|
407
|
-
try {
|
|
408
|
-
existingStat = await fs.lstat(target);
|
|
409
|
-
} catch {
|
|
410
|
-
existingStat = null;
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
if (existingStat?.isSymbolicLink()) {
|
|
414
|
-
const current = await fs.readlink(target);
|
|
415
|
-
const resolved = current.startsWith("/") ? current : join(target, "..", current);
|
|
416
|
-
if (resolved === CLAUDE_MD_TEMPLATE_PATH) return target;
|
|
417
|
-
await backupAndReplace(target, CLAUDE_MD_TEMPLATE_PATH);
|
|
418
|
-
return target;
|
|
419
|
-
}
|
|
420
|
-
if (existingStat) {
|
|
421
|
-
await backupAndReplace(target, CLAUDE_MD_TEMPLATE_PATH);
|
|
422
|
-
return target;
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
await fs.symlink(CLAUDE_MD_TEMPLATE_PATH, target);
|
|
426
|
-
return target;
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
async function ensureClaudeMdImport(projectRoot) {
|
|
430
|
-
const claudeMdPath = join(projectRoot, "CLAUDE.md");
|
|
431
|
-
const block = `${CLAUDE_MD_MARKER_BEGIN}\n${CLAUDE_MD_IMPORT_LINE}\n${CLAUDE_MD_MARKER_END}\n`;
|
|
432
|
-
|
|
433
|
-
let existing;
|
|
434
|
-
try {
|
|
435
|
-
existing = await fs.readFile(claudeMdPath, "utf8");
|
|
436
|
-
} catch (err) {
|
|
437
|
-
if (err.code !== "ENOENT") throw err;
|
|
438
|
-
await fs.writeFile(claudeMdPath, block, { mode: 0o644 });
|
|
439
|
-
return true;
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
if (existing.includes(CLAUDE_MD_MARKER_BEGIN) && existing.includes(CLAUDE_MD_MARKER_END)) {
|
|
443
|
-
return false;
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
const separator = blockSeparatorFor(existing);
|
|
447
|
-
await fs.writeFile(claudeMdPath, existing + separator + block, { mode: 0o644 });
|
|
448
|
-
return true;
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
function blockSeparatorFor(existing) {
|
|
452
|
-
if (existing.length === 0) return "";
|
|
453
|
-
if (existing.endsWith("\n\n")) return "";
|
|
454
|
-
if (existing.endsWith("\n")) return "\n";
|
|
455
|
-
return "\n\n";
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
async function ensureProjectAgentsMd(projectRoot) {
|
|
459
|
-
try {
|
|
460
|
-
await fs.access(CLAUDE_MD_TEMPLATE_PATH);
|
|
461
|
-
} catch {
|
|
462
|
-
return null;
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
const target = join(projectRoot, "AGENTS.md");
|
|
466
|
-
|
|
467
|
-
let existingStat;
|
|
468
|
-
try {
|
|
469
|
-
existingStat = await fs.lstat(target);
|
|
470
|
-
} catch {
|
|
471
|
-
existingStat = null;
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
if (existingStat?.isSymbolicLink()) {
|
|
475
|
-
const current = await fs.readlink(target);
|
|
476
|
-
const resolved = current.startsWith("/") ? current : join(target, "..", current);
|
|
477
|
-
if (resolved === CLAUDE_MD_TEMPLATE_PATH) return target;
|
|
478
|
-
return null;
|
|
479
|
-
}
|
|
480
|
-
if (existingStat) {
|
|
481
|
-
return null;
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
await fs.symlink(CLAUDE_MD_TEMPLATE_PATH, target);
|
|
485
|
-
return target;
|
|
486
|
-
}
|
package/src/uninstall.mjs
CHANGED
|
@@ -54,8 +54,7 @@ Usage:
|
|
|
54
54
|
okstra uninstall Remove ~/.okstra/{lib, bin/<known>, version,
|
|
55
55
|
dev-link, installed-skills.json,
|
|
56
56
|
installed-agents.json,
|
|
57
|
-
templates/settings.local.json
|
|
58
|
-
templates/okstra.CLAUDE.md} AND
|
|
57
|
+
templates/settings.local.json} AND
|
|
59
58
|
~/.claude/skills/<name> AND
|
|
60
59
|
~/.claude/agents/<worker>.md for every
|
|
61
60
|
entry in the install manifests (fallback:
|
|
@@ -64,10 +63,7 @@ Usage:
|
|
|
64
63
|
active.jsonl, projects/, archive/,
|
|
65
64
|
state.json, .locks/
|
|
66
65
|
Per-project artifacts created by 'okstra setup'
|
|
67
|
-
(<PROJECT>/.
|
|
68
|
-
<PROJECT>/CLAUDE.md import block,
|
|
69
|
-
<PROJECT>/AGENTS.md symlink,
|
|
70
|
-
<PROJECT>/.claude/settings.local.json symlink)
|
|
66
|
+
(<PROJECT>/.claude/settings.local.json symlink)
|
|
71
67
|
are NOT touched — remove them manually if needed.
|
|
72
68
|
okstra uninstall --purge Remove the entire ~/.okstra directory AND
|
|
73
69
|
~/.claude/skills/<okstra-*> AND
|
|
@@ -199,13 +195,10 @@ export async function runUninstall(args) {
|
|
|
199
195
|
await removePath(join(paths.home, AGENTS_MANIFEST_REL), opts);
|
|
200
196
|
|
|
201
197
|
await removePath(join(paths.home, "templates", "settings.local.json"), opts);
|
|
202
|
-
|
|
203
|
-
//
|
|
204
|
-
//
|
|
205
|
-
//
|
|
206
|
-
// machine-level and does not know which projects opted in. Symlinks will
|
|
207
|
-
// dangle until the user removes them manually or re-runs okstra install +
|
|
208
|
-
// okstra setup.
|
|
198
|
+
// Per-project <PROJECT>/.claude/settings.local.json symlinks are NOT removed
|
|
199
|
+
// here — uninstall is machine-level and does not know which projects opted
|
|
200
|
+
// in. They will dangle until the user removes them manually or re-runs
|
|
201
|
+
// okstra install + okstra setup.
|
|
209
202
|
// Remove templates/ if now empty.
|
|
210
203
|
const templatesDir = join(paths.home, "templates");
|
|
211
204
|
if (await pathExists(templatesDir)) {
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
# okstra — agent guidance (managed)
|
|
2
|
-
|
|
3
|
-
This file is shipped by `okstra install` and surfaced into the active project
|
|
4
|
-
through two channels by `okstra setup`:
|
|
5
|
-
|
|
6
|
-
- **Claude Code**: `<PROJECT>/CLAUDE.md` gets an `@.okstra/CLAUDE.md`
|
|
7
|
-
import block (per-project symlink to this template). Existing user content
|
|
8
|
-
in `CLAUDE.md` is preserved — the block lives between
|
|
9
|
-
`<!-- okstra:claude-md:begin ... -->` markers.
|
|
10
|
-
- **Codex / Aider / other AGENTS.md-aware agents**: `<PROJECT>/AGENTS.md` is
|
|
11
|
-
created as a direct symlink to this template — but **only if AGENTS.md does
|
|
12
|
-
not already exist**. okstra never overwrites a hand-written AGENTS.md.
|
|
13
|
-
|
|
14
|
-
It is **owned by the okstra package**: every `okstra install` overwrites
|
|
15
|
-
`~/.okstra/templates/okstra.CLAUDE.md` from the version currently on disk in
|
|
16
|
-
the npm package. Edits made directly to this file (or to the per-project
|
|
17
|
-
`<PROJECT>/.okstra/CLAUDE.md` / `<PROJECT>/AGENTS.md` symlinks)
|
|
18
|
-
will be lost on the next install. Put project-specific overrides outside the
|
|
19
|
-
managed block in `<PROJECT>/CLAUDE.md` instead (or replace `AGENTS.md` with
|
|
20
|
-
your own file — okstra will respect it).
|
|
21
|
-
|
|
22
|
-
## What okstra is
|
|
23
|
-
|
|
24
|
-
okstra is a multi-agent cross-verification orchestrator. A Claude lead drives
|
|
25
|
-
the run; codex / gemini / claude workers produce independent analyses; a
|
|
26
|
-
report writer worker synthesises the final report. Day-to-day usage happens
|
|
27
|
-
through slash commands inside a Claude Code session.
|
|
28
|
-
|
|
29
|
-
## Slash commands
|
|
30
|
-
|
|
31
|
-
| Command | When to use |
|
|
32
|
-
| --- | --- |
|
|
33
|
-
| `/okstra-setup` | First-time bootstrap in a new project — writes `.okstra/project.json`, provisions `.claude/settings.local.json` and `.okstra/CLAUDE.md`. |
|
|
34
|
-
| `/okstra-brief` | Turn a requirements doc / ticket / link / conversation into the markdown task brief consumed by `okstra-run`. |
|
|
35
|
-
| `/okstra-run` | Start a cross-verification task in the current session. Drives the interactive wizard. |
|
|
36
|
-
| `/okstra-status` | Inspect overall okstra task status, current phase, blockers, next recommended phase. Also flips a task's workStatus (todo / in-progress / blocked / done). |
|
|
37
|
-
| `/okstra-history` | List past runs, review previous results, queue a re-run. |
|
|
38
|
-
| `/okstra-schedule` | Generate a consolidated work plan across multiple tasks in a task-group. |
|
|
39
|
-
| `/okstra-convergence` | Iterative cross-verification between workers in Phase 5.5; classify findings by consensus level. |
|
|
40
|
-
| `/okstra-report-finder` | Locate the final-report path for a given task key, or read a past report to continue work. |
|
|
41
|
-
| `/okstra-report-writer` | Author / persist the final synthesis report (Phase 6–7). |
|
|
42
|
-
| `/okstra-time-summary` | Per-task / per-worker elapsed time breakdown for a task-id. |
|
|
43
|
-
| `/okstra-logs` | List, size, and clean up `*.log` sidecars from past worker dispatches. |
|
|
44
|
-
|
|
45
|
-
Type the slash command — do not paraphrase the trigger words into prose.
|
|
46
|
-
|
|
47
|
-
## Workflow boundaries
|
|
48
|
-
|
|
49
|
-
- The Claude lead **does not** produce independent worker findings. It
|
|
50
|
-
dispatches workers, monitors them, and drives convergence. Analysis is the
|
|
51
|
-
workers' job.
|
|
52
|
-
- Worker wrappers (`okstra-codex-exec.sh`, `okstra-gemini-exec.sh`) run from
|
|
53
|
-
`~/.okstra/bin/` and are pre-authorised via `<PROJECT>/.claude/settings.local.json`
|
|
54
|
-
(a symlink to `~/.okstra/templates/settings.local.json`). Do **not**
|
|
55
|
-
duplicate those permissions in the user's global `~/.claude/settings.json`.
|
|
56
|
-
- Each okstra run provisions a task-scoped git worktree under
|
|
57
|
-
`~/.okstra/worktrees/`. Files synced from the main checkout are governed by
|
|
58
|
-
`worktreeSyncDirs` in `.okstra/project.json` (default:
|
|
59
|
-
`.project-docs`, `.scratch`, `graphify-out`, `.claude`).
|
|
60
|
-
- QA gating is configured via `qaCommands` in the same `project.json`.
|
|
61
|
-
Verifiers reject mutation-style commands (`--fix`, `--write`, `cargo update`,
|
|
62
|
-
`npm install`, etc.); declare check-only variants only.
|
|
63
|
-
|
|
64
|
-
## Coding Principles (BLOCKING — agent self-checks before declaring work complete)
|
|
65
|
-
|
|
66
|
-
These apply to every worker writing or editing project code (executor and verifiers alike). Enforcement is self-check: the agent runs each rule's check immediately before reporting "done"; skipping a check is itself a contract violation.
|
|
67
|
-
|
|
68
|
-
1. **DRY — single reference point** — One implementation per capability. A second caller signals "extract shared logic", not "duplicate path".
|
|
69
|
-
2. **KISS — simplest sufficient design** — Add abstraction layers (helper modules, strategy/factory, configuration flags, indirection) only when an existing concrete call site requires them. *Self-check: name the second caller now; if you cannot, inline.* *Example violation: extracting `formatUserName()` helper used by exactly one call site, "in case we need it elsewhere".*
|
|
70
|
-
3. **YAGNI — build only for current requirements** — No speculative parameters, optional configs, "future-proof" hooks, or pre-1.0 backwards-compat shims. *Self-check: every newly introduced identifier has ≥1 current internal caller, or was explicitly user-requested.* *Example violation: adding `options?: { retries?, timeout? }` parameter when the current call passes nothing.*
|
|
71
|
-
4. **Clean Code — names carry WHAT, comments explain WHY** — Identifiers must make intent obvious; if a comment would describe WHAT the code does, rename instead. Reserve comments for non-obvious WHY (hidden constraint, workaround, surprising invariant). Delete dead/commented-out code immediately — git history is the archive. *Example — WHAT (rename instead): `// increment counter` above `i++`. WHY (keep): `// retry up to 3x: upstream returns 502 during deploys`.*
|
|
72
|
-
5. **Function length cap — 50 lines** — A single function/method body must stay within 50 lines, counting only effective code (exclude blank lines, comments, and pure data declarations such as large enums, lookup tables, or constant maps). Crossing the cap is an extraction signal, not a style nit. *Self-check: for any function newly added or substantially edited, count effective body lines; if over 50, split before declaring complete, or surface the violation and confirm with the user.*
|
|
73
|
-
|
|
74
|
-
## Phase model (high level)
|
|
75
|
-
|
|
76
|
-
```
|
|
77
|
-
Phase 1 context-loader → load brief + project context
|
|
78
|
-
Phase 2 team-contract → confirm lead + worker roster
|
|
79
|
-
Phase 3 worker dispatch → workers run independently in parallel
|
|
80
|
-
Phase 4 result collection → gather worker outputs
|
|
81
|
-
Phase 5 cross-review → workers critique each other's findings
|
|
82
|
-
Phase 5.5 convergence → classify findings, iterate until stable
|
|
83
|
-
Phase 6 report-writer dispatch → synthesise final report
|
|
84
|
-
Phase 7 artifact persistence → manifests, central index update
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
The lead skill (`okstra-run`) advances phases automatically. Use
|
|
88
|
-
`/okstra-status` to inspect the current phase and `next-recommended-phase`
|
|
89
|
-
hint when resuming.
|
|
90
|
-
|
|
91
|
-
## Troubleshooting hints
|
|
92
|
-
|
|
93
|
-
- "okstra runtime missing" / `InstallationError` — run
|
|
94
|
-
`npx okstra@latest install` (or `okstra ensure-installed`).
|
|
95
|
-
- Worker dispatch refused by Claude Code permissions — verify
|
|
96
|
-
`<PROJECT>/.claude/settings.local.json` is a symlink to
|
|
97
|
-
`~/.okstra/templates/settings.local.json`; re-run `/okstra-setup` to
|
|
98
|
-
re-provision.
|
|
99
|
-
- `qa-command not configured: <category>` warnings — add the missing
|
|
100
|
-
`lint` / `format` / `typecheck` / `test` entries to `qaCommands` in
|
|
101
|
-
`project.json`.
|
|
102
|
-
|
|
103
|
-
For deeper documentation see the package README and `docs/` in the okstra
|
|
104
|
-
source tree.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|