okstra 0.173.0 → 0.174.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/docs/architecture/storage-model.md +13 -3
- package/docs/architecture.md +5 -21
- package/docs/cli.md +3 -2
- package/docs/container.md +1 -1
- package/docs/contributor-change-matrix.md +1 -1
- package/docs/project-structure-overview.md +13 -13
- package/docs/task-process/README.md +1 -1
- package/docs/task-process/implementation-planning.md +1 -1
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/workers/claude-worker.md +1 -1
- package/runtime/bin/lib/okstra/globals.sh +1 -1
- package/runtime/bin/okstra-provider-exec.py +29 -12
- package/runtime/bin/okstra-trace-cleanup.sh +58 -129
- package/runtime/prompts/lead/adapters/cmux.md +2 -0
- package/runtime/prompts/lead/okstra-lead-contract.md +1 -1
- package/runtime/prompts/lead/plan-body-verification.md +3 -3
- package/runtime/prompts/lead/report-writer.md +6 -6
- package/runtime/prompts/profiles/_common-contract.md +2 -2
- package/runtime/prompts/profiles/_implementation-executor.md +2 -0
- package/runtime/prompts/profiles/_implementation-verifier.md +2 -2
- package/runtime/prompts/profiles/error-analysis.md +1 -1
- package/runtime/prompts/profiles/implementation-planning.md +12 -9
- package/runtime/prompts/profiles/implementation.md +2 -1
- package/runtime/prompts/profiles/release-handoff.md +1 -1
- package/runtime/python/okstra_ctl/adapters/dispatch/__init__.py +1 -6
- package/runtime/python/okstra_ctl/adapters/hosts/external/relay.md +4 -4
- package/runtime/python/okstra_ctl/adapters/providers/claude/adapter.py +5 -0
- package/runtime/python/okstra_ctl/conformance.py +68 -0
- package/runtime/python/okstra_ctl/dispatch_core.py +89 -39
- package/runtime/python/okstra_ctl/dispatch_state.py +142 -14
- package/runtime/python/okstra_ctl/doctor.py +2 -2
- package/runtime/python/okstra_ctl/domain/worker_exec.py +5 -0
- package/runtime/python/okstra_ctl/final_report_schema.py +5 -4
- package/runtime/python/okstra_ctl/pane_reclaim.py +13 -22
- package/runtime/python/okstra_ctl/render_final_report.py +15 -19
- package/runtime/python/okstra_ctl/report_contract.py +0 -1
- package/runtime/python/okstra_ctl/report_finalize.py +68 -9
- package/runtime/python/okstra_ctl/run.py +43 -2
- package/runtime/python/okstra_ctl/schema_excerpt.py +1 -1
- package/runtime/python/okstra_ctl/scope_provenance.py +1 -1
- package/runtime/python/okstra_ctl/session.py +69 -12
- package/runtime/python/okstra_ctl/team.py +51 -25
- package/runtime/python/okstra_ctl/tmux.py +19 -149
- package/runtime/python/okstra_ctl/worker_request.py +2 -0
- package/runtime/python/okstra_ctl/worktree.py +69 -3
- package/runtime/python/okstra_token_usage/cli.py +1 -1
- package/runtime/python/okstra_token_usage/collect.py +66 -6
- package/runtime/skills/okstra-setup/references/project-config.md +11 -0
- package/runtime/templates/reports/settings.template.json +0 -24
- package/runtime/validators/lib/fixtures.sh +49 -17
- package/runtime/validators/validate-implementation-plan-stages.py +63 -3
- package/runtime/validators/validate-run.py +14 -473
- package/runtime/validators/validate_session_conformance.py +1 -1
- package/src/cli-registry.mjs +8 -1
- package/src/commands/execute/team.mjs +3 -3
- package/src/commands/execute/worktree-status.mjs +109 -0
- package/src/commands/lifecycle/install.mjs +0 -2
- package/src/commands/report/finalize.mjs +13 -6
- package/runtime/bin/okstra-subagent-reclaim.sh +0 -26
- package/runtime/schemas/final-report-v1.0.schema.json +0 -6366
- package/runtime/templates/reports/final-report.template.md +0 -1258
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { runPythonSnippet, emitJsonError } from "../../lib/python-helper.mjs";
|
|
2
|
+
|
|
3
|
+
const USAGE = `okstra worktree-status — clean-worktree check that ignores okstra's own scaffolding
|
|
4
|
+
|
|
5
|
+
Usage:
|
|
6
|
+
okstra worktree-status [--path <dir>] [--check-clean]
|
|
7
|
+
|
|
8
|
+
--path <dir> directory to inspect (default: current directory)
|
|
9
|
+
--check-clean exit 1 when the tree is dirty (default: always exit 0)
|
|
10
|
+
|
|
11
|
+
okstra provisions \`.okstra\` plus the configured sync entries (\`.project-docs\`,
|
|
12
|
+
\`.claude\`, …) into every task worktree, and an \`implementation\` stage worktree
|
|
13
|
+
sits inside its task worktree. A bare \`git status --porcelain\` therefore never
|
|
14
|
+
comes back empty there, so a plan step asserting a clean tree with one fails on
|
|
15
|
+
okstra's scaffolding rather than on the stage's own work. This command asks the
|
|
16
|
+
same question against source paths only — the exact gate okstra's own handoff
|
|
17
|
+
and stage-integration steps use.
|
|
18
|
+
|
|
19
|
+
Use it as the assertion in a plan step:
|
|
20
|
+
okstra worktree-status --check-clean && git tag -f stage-<N>-exit
|
|
21
|
+
|
|
22
|
+
Output: JSON { ok, path, clean, entries, excluded }. \`entries\` holds the
|
|
23
|
+
\`git status --short\` rows that made it dirty; \`excluded\` lists the paths left
|
|
24
|
+
out of the question.
|
|
25
|
+
`;
|
|
26
|
+
|
|
27
|
+
function parseArgs(args) {
|
|
28
|
+
const opts = { path: ".", checkClean: false };
|
|
29
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
30
|
+
const arg = args[i];
|
|
31
|
+
if (arg === "--check-clean") {
|
|
32
|
+
opts.checkClean = true;
|
|
33
|
+
} else if (arg === "--path") {
|
|
34
|
+
i += 1;
|
|
35
|
+
if (i >= args.length) throw new Error("--path requires a directory");
|
|
36
|
+
opts.path = args[i];
|
|
37
|
+
} else if (arg.startsWith("--path=")) {
|
|
38
|
+
opts.path = arg.slice("--path=".length);
|
|
39
|
+
} else {
|
|
40
|
+
throw new Error(`unknown argument '${arg}'`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (!opts.path) throw new Error("--path requires a directory");
|
|
44
|
+
return opts;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const SCRIPT = `
|
|
48
|
+
import json, sys
|
|
49
|
+
from pathlib import Path
|
|
50
|
+
from okstra_ctl.worktree import (
|
|
51
|
+
dirty_entries_excluding_okstra,
|
|
52
|
+
is_git_work_tree,
|
|
53
|
+
nested_worktree_excludes,
|
|
54
|
+
okstra_clean_gate_excludes,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
path = Path(sys.argv[1]).resolve()
|
|
58
|
+
check_clean = sys.argv[2] == "1"
|
|
59
|
+
|
|
60
|
+
if not path.is_dir():
|
|
61
|
+
print(json.dumps({"ok": False, "stage": "input",
|
|
62
|
+
"reason": f"not a directory: {path}"}))
|
|
63
|
+
sys.exit(2)
|
|
64
|
+
if not is_git_work_tree(path):
|
|
65
|
+
print(json.dumps({"ok": False, "stage": "input",
|
|
66
|
+
"reason": f"not inside a git work tree: {path}"}))
|
|
67
|
+
sys.exit(2)
|
|
68
|
+
|
|
69
|
+
entries = dirty_entries_excluding_okstra(path)
|
|
70
|
+
excluded = [*okstra_clean_gate_excludes(path), *nested_worktree_excludes(path)]
|
|
71
|
+
print(json.dumps({
|
|
72
|
+
"ok": True,
|
|
73
|
+
"path": str(path),
|
|
74
|
+
"clean": not entries,
|
|
75
|
+
"entries": entries,
|
|
76
|
+
"excluded": excluded,
|
|
77
|
+
}, ensure_ascii=False, indent=2))
|
|
78
|
+
sys.exit(1 if (check_clean and entries) else 0)
|
|
79
|
+
`;
|
|
80
|
+
|
|
81
|
+
export async function run(args) {
|
|
82
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
83
|
+
process.stdout.write(USAGE);
|
|
84
|
+
return 0;
|
|
85
|
+
}
|
|
86
|
+
let opts;
|
|
87
|
+
try {
|
|
88
|
+
opts = parseArgs(args);
|
|
89
|
+
} catch (err) {
|
|
90
|
+
process.stderr.write(`error: ${err.message}\n\n${USAGE}`);
|
|
91
|
+
return 2;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const result = await runPythonSnippet({
|
|
95
|
+
script: SCRIPT,
|
|
96
|
+
args: [opts.path, opts.checkClean ? "1" : "0"],
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
if (result.code !== 0 && !result.stdout.trim()) {
|
|
100
|
+
emitJsonError({
|
|
101
|
+
stage: "python",
|
|
102
|
+
reason: `python invocation failed: ${result.stderr.trim() || "no output"}`,
|
|
103
|
+
});
|
|
104
|
+
return 1;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
process.stdout.write(result.stdout);
|
|
108
|
+
return result.code;
|
|
109
|
+
}
|
|
@@ -37,7 +37,6 @@ const BIN_ENTRYPOINTS = [
|
|
|
37
37
|
"okstra-kimi-exec.sh",
|
|
38
38
|
"okstra-provider-exec.py",
|
|
39
39
|
"okstra-trace-cleanup.sh",
|
|
40
|
-
"okstra-subagent-reclaim.sh",
|
|
41
40
|
"okstra-team-reconcile.sh",
|
|
42
41
|
"okstra-central.sh",
|
|
43
42
|
"okstra-token-usage.py",
|
|
@@ -1136,7 +1135,6 @@ export async function runEnsureInstalled(args) {
|
|
|
1136
1135
|
if (!(await dirExists(paths.pythonpath))) reasons.push(`missing ${paths.pythonpath}`);
|
|
1137
1136
|
if (!(await dirExists(paths.agents))) reasons.push(`missing agents dir ${paths.agents}`);
|
|
1138
1137
|
for (const relative of [
|
|
1139
|
-
["schemas", "final-report-v1.0.schema.json"],
|
|
1140
1138
|
["schemas", "final-report-v2.0.schema.json"],
|
|
1141
1139
|
["templates", "reports", "final-report-v2.template.md"],
|
|
1142
1140
|
["templates", "reports", "html", "base.template.html"],
|
|
@@ -7,16 +7,23 @@ Usage:
|
|
|
7
7
|
okstra report-finalize --project-root <dir> --run-manifest <path> \\
|
|
8
8
|
--report <final-report-<task-type>-<seq>.md> [--team-state <path>]
|
|
9
9
|
|
|
10
|
-
Runs the
|
|
10
|
+
Runs the six Phase 7 steps in their contractual order against one final-report:
|
|
11
11
|
|
|
12
|
-
1.
|
|
13
|
-
2.
|
|
12
|
+
1. project-activity project the run's activity events into the data.json
|
|
13
|
+
2. check-source verify the data.json is the English SSOT
|
|
14
|
+
3. token-usage substitute real token/cost numbers into the data.json
|
|
15
|
+
4. render-views write the schema-v2 task-specific *.html sibling; v1
|
|
14
16
|
keeps the legacy conditional interactive view
|
|
15
|
-
|
|
16
|
-
|
|
17
|
+
5. spawn-followups turn section 4 rows into task stubs
|
|
18
|
+
6. validate-run validate the finished run artifacts
|
|
17
19
|
|
|
18
20
|
Every step is idempotent, so re-running after a fixed failure is safe. The
|
|
19
|
-
sequence stops at the first non-zero exit and reports which step failed
|
|
21
|
+
sequence stops at the first non-zero exit and reports which step failed, except
|
|
22
|
+
token-usage: its input is the lead session log, so a failure there does not
|
|
23
|
+
take the html, follow-ups and validation after it down with it. Whenever a step
|
|
24
|
+
exits non-zero the command prints the --only flags that resume the sequence
|
|
25
|
+
from the earliest failing step — follow those rather than rerunning the failing
|
|
26
|
+
step alone, which would leave the html rendered before the tokens landed.
|
|
20
27
|
|
|
21
28
|
This is the same code path the Codex lead adapter runs automatically after its
|
|
22
29
|
report-writer completes, so a Claude-led and a Codex-led run finalize
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
#
|
|
3
|
-
# okstra-subagent-reclaim.sh — SubagentStop / TaskCompleted 훅 엔트리.
|
|
4
|
-
#
|
|
5
|
-
# stdin(JSON)은 읽고 버린다: 어느 worker 가 끝났든 활성 run 전체의 "완료" pane 만
|
|
6
|
-
# 회수하므로 session_id 매핑이 필요 없다. active.jsonl 의 각 활성 run 에 대해
|
|
7
|
-
# okstra-trace-cleanup.sh --reclaim-completed 를 호출한다 — 완료 판정(pane_reclaim)이
|
|
8
|
-
# cross-run 안전을 보장하므로 다른 run 의 진행 중 pane 은 건드리지 않는다.
|
|
9
|
-
#
|
|
10
|
-
# 훅은 세션 진행을 절대 막지 않는다: 모든 실패는 조용히 삼키고 항상 exit 0.
|
|
11
|
-
set -u
|
|
12
|
-
|
|
13
|
-
cat >/dev/null 2>&1 || true # drain stdin
|
|
14
|
-
|
|
15
|
-
_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
|
|
16
|
-
home="${OKSTRA_HOME:-$HOME/.okstra}"
|
|
17
|
-
# pane_reclaim 패키지는 repo 레이아웃(scripts/okstra_ctl)과 설치 레이아웃
|
|
18
|
-
# ($OKSTRA_HOME/lib/python) 양쪽에 있을 수 있으므로 둘 다 PYTHONPATH 에 둔다.
|
|
19
|
-
export PYTHONPATH="${_dir}:${home}/lib/python${PYTHONPATH:+:$PYTHONPATH}"
|
|
20
|
-
|
|
21
|
-
while IFS= read -r run_dir; do
|
|
22
|
-
[[ -n "$run_dir" ]] || continue
|
|
23
|
-
"$_dir/okstra-trace-cleanup.sh" --reclaim-completed --run-dir "$run_dir" >/dev/null 2>&1 || true
|
|
24
|
-
done < <(python3 -m okstra_ctl.pane_reclaim --active-dirs "$home" 2>/dev/null || true)
|
|
25
|
-
|
|
26
|
-
exit 0
|