okstra 0.125.5 → 0.127.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/docs/architecture.md +2 -1
  2. package/docs/project-structure-overview.md +2 -0
  3. package/package.json +1 -1
  4. package/runtime/BUILD.json +2 -2
  5. package/runtime/agents/workers/report-writer-worker.md +2 -2
  6. package/runtime/bin/okstra-antigravity-exec.sh +8 -2
  7. package/runtime/bin/okstra-claude-exec.sh +8 -2
  8. package/runtime/bin/okstra-codex-exec.sh +65 -8
  9. package/runtime/prompts/lead/adapters/claude-code.md +1 -0
  10. package/runtime/prompts/lead/convergence.md +4 -1
  11. package/runtime/prompts/lead/okstra-lead-contract.md +24 -1
  12. package/runtime/prompts/lead/report-writer.md +1 -1
  13. package/runtime/prompts/lead/team-contract.md +3 -0
  14. package/runtime/prompts/profiles/_common-contract.md +1 -1
  15. package/runtime/python/okstra_ctl/design_surfaces.py +30 -14
  16. package/runtime/python/okstra_ctl/render_final_report.py +71 -5
  17. package/runtime/python/okstra_ctl/run.py +3 -1
  18. package/runtime/python/okstra_ctl/schema_excerpt.py +17 -1
  19. package/runtime/python/okstra_ctl/worker_heartbeat.py +50 -0
  20. package/runtime/python/okstra_ctl/worker_liveness.py +140 -0
  21. package/runtime/python/okstra_ctl/worker_prompt_headers.py +20 -0
  22. package/runtime/templates/reports/final-report.template.md +9 -9
  23. package/runtime/templates/worker-prompt-preamble.md +1 -0
  24. package/runtime/validators/forbidden_actions.py +43 -4
  25. package/runtime/validators/validate-implementation-plan-stages.py +6 -4
  26. package/runtime/validators/validate-report-views.py +4 -4
  27. package/runtime/validators/validate-run.py +117 -7
  28. package/runtime/validators/validate-schedule.py +6 -4
  29. package/runtime/validators/validate_fanout.py +4 -3
  30. package/runtime/validators/validate_improvement_report.py +6 -4
  31. package/runtime/validators/validate_session_conformance.py +16 -7
  32. package/src/cli-registry.mjs +7 -0
  33. package/src/commands/inspect/worker-liveness.mjs +31 -0
  34. package/src/commands/lifecycle/preflight.mjs +25 -2
  35. package/src/lib/helper-scripts.mjs +23 -0
  36. package/src/lib/python-helper.mjs +1 -1
@@ -0,0 +1,31 @@
1
+ import { runPythonModule } from "../../lib/python-helper.mjs";
2
+
3
+ const USAGE = `okstra worker-liveness — report whether pending workers are still alive
4
+
5
+ Usage:
6
+ okstra worker-liveness [--audit <path>]... [--prompt <path>]...
7
+ [--max-idle <seconds>] [--launch-grace <seconds>] [--json]
8
+
9
+ --audit a claude-worker audit sidecar; stale past the heartbeat cadence means
10
+ the in-process worker hung.
11
+ --prompt a CLI-wrapper prompt-history path; no sibling .log or .status.json
12
+ past the launch grace means the wrapper never started.
13
+
14
+ Output: JSON { ok, checkedAt, probes[], unhealthy[] }. Exit 1 when any worker is
15
+ stalled or did not launch, so a poll loop can branch on the exit code. Read-only:
16
+ it reports, it never kills or re-dispatches.
17
+ `;
18
+
19
+ export async function run(args) {
20
+ if (args.includes("--help") || args.includes("-h")) {
21
+ process.stdout.write(USAGE);
22
+ return 0;
23
+ }
24
+
25
+ const result = await runPythonModule({
26
+ module: "okstra_ctl.worker_liveness",
27
+ args,
28
+ stdio: "inherit-stdout",
29
+ });
30
+ return result.code;
31
+ }
@@ -1,5 +1,8 @@
1
1
  import { checkProject } from "./check-project.mjs";
2
2
  import { runEnsureInstalled } from "./install.mjs";
3
+ import { missingHelperScripts } from "../../lib/helper-scripts.mjs";
4
+ import { resolveInstalledScript } from "../../lib/python-helper.mjs";
5
+ import { resolvePaths } from "../../lib/paths.mjs";
3
6
 
4
7
  const USAGE = `okstra preflight — one-call skill preflight (ensure-installed + check-project)
5
8
 
@@ -11,8 +14,9 @@ verifies the target project has .okstra/project.json. Prints one JSON object:
11
14
 
12
15
  ok: true {ok, projectRoot, projectJsonPath, projectId}
13
16
  ok: false {ok, stage, reason, ...}
14
- stage: install | resolve | project_json_missing |
15
- project_json_invalid | python | parse
17
+ stage: install | helper_scripts_missing | resolve |
18
+ project_json_missing | project_json_invalid |
19
+ python | parse
16
20
 
17
21
  Options:
18
22
  --runtime <name> Runtime whose assets must be present (e.g. claude-code);
@@ -48,6 +52,12 @@ function emit(payload) {
48
52
  process.stdout.write(JSON.stringify(payload, null, 2) + "\n");
49
53
  }
50
54
 
55
+ // A BLOCKING lead step calls these mid-run; resolving them here turns a stale
56
+ // install into an up-front failure instead of a dead end hours into a run.
57
+ async function findMissingHelperScripts() {
58
+ return missingHelperScripts(await resolvePaths(), resolveInstalledScript);
59
+ }
60
+
51
61
  export async function run(args) {
52
62
  if (args.includes("--help") || args.includes("-h")) {
53
63
  process.stdout.write(USAGE);
@@ -76,6 +86,19 @@ export async function run(args) {
76
86
  return installCode === 2 ? 2 : 1;
77
87
  }
78
88
 
89
+ const missingHelpers = await findMissingHelperScripts();
90
+ if (missingHelpers.length > 0) {
91
+ emit({
92
+ ok: false,
93
+ stage: "helper_scripts_missing",
94
+ missingHelpers,
95
+ reason:
96
+ `${missingHelpers.length} helper script(s) a lead prompt can call are not ` +
97
+ "installed — run 'okstra install' to repair before starting a run.",
98
+ });
99
+ return 1;
100
+ }
101
+
79
102
  const { code, payload } = await checkProject({ cwd: opts.cwd });
80
103
  emit(payload);
81
104
  return code;
@@ -0,0 +1,23 @@
1
+ // Single source of truth for the python helper scripts that `okstra <cmd>`
2
+ // subcommands front through runInstalledScript(). Preflight asserts every one
3
+ // of them resolves under ~/.okstra/bin so a stale install fails at the cheap
4
+ // environment gate instead of at the blocking lead step that calls it mid-run.
5
+ // A contract test keeps this list in step with the `scriptName:` literals in
6
+ // src/commands/**.
7
+
8
+ export const HELPER_SCRIPT_NAMES = Object.freeze([
9
+ "okstra-error-log.py",
10
+ "okstra-incremental-carry.py",
11
+ "okstra-incremental-scope.py",
12
+ "okstra-inject-report-index.py",
13
+ "okstra-render-final-report.py",
14
+ "okstra-render-report-views.py",
15
+ "okstra-spawn-followups.py",
16
+ "okstra-token-usage.py",
17
+ ]);
18
+
19
+ // `resolve` is the same resolver the wrappers call, so preflight cannot pass a
20
+ // script the dispatch path would then fail to find.
21
+ export function missingHelperScripts(paths, resolve) {
22
+ return HELPER_SCRIPT_NAMES.filter((name) => !resolve(paths, name));
23
+ }
@@ -4,7 +4,7 @@ import { join, resolve as resolvePath } from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
5
  import { buildPythonpath, resolvePaths } from "./paths.mjs";
6
6
 
7
- function resolveInstalledScript(paths, scriptName) {
7
+ export function resolveInstalledScript(paths, scriptName) {
8
8
  // Prefer the installed copy under ~/.okstra/bin (what production users run);
9
9
  // fall back to the in-repo source when invoked from a checkout that has not
10
10
  // been installed (dev / CI).