okstra 0.36.1 → 0.37.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 (66) hide show
  1. package/README.kr.md +6 -6
  2. package/README.md +6 -6
  3. package/bin/okstra +4 -2
  4. package/docs/kr/architecture.md +29 -29
  5. package/docs/kr/cli.md +7 -6
  6. package/docs/pr-template-usage.md +2 -2
  7. package/docs/project-structure-overview.md +4 -4
  8. package/docs/superpowers/plans/2026-05-25-okstra-project-root-rename.md +159 -0
  9. package/docs/superpowers/plans/2026-05-26-wizard-3-option-picker.md +860 -0
  10. package/docs/task-process/common-flow.md +2 -2
  11. package/package.json +1 -1
  12. package/runtime/BUILD.json +2 -2
  13. package/runtime/agents/SKILL.md +2 -2
  14. package/runtime/agents/workers/claude-worker.md +1 -1
  15. package/runtime/prompts/profiles/_common-contract.md +6 -6
  16. package/runtime/prompts/profiles/_implementation-executor.md +2 -2
  17. package/runtime/prompts/profiles/_implementation-verifier.md +1 -1
  18. package/runtime/prompts/profiles/final-verification.md +1 -1
  19. package/runtime/prompts/profiles/implementation-planning.md +5 -5
  20. package/runtime/prompts/profiles/release-handoff.md +1 -1
  21. package/runtime/prompts/profiles/requirements-discovery.md +3 -3
  22. package/runtime/prompts/wizard/prompts.ko.json +80 -6
  23. package/runtime/python/lib/okstra/interactive.sh +2 -2
  24. package/runtime/python/lib/okstra/project-resolver.sh +1 -1
  25. package/runtime/python/lib/okstra/usage.sh +5 -5
  26. package/runtime/python/lib/okstra-ctl/cmd-rerun.sh +1 -1
  27. package/runtime/python/okstra_ctl/backfill.py +5 -3
  28. package/runtime/python/okstra_ctl/migrate.py +408 -0
  29. package/runtime/python/okstra_ctl/paths.py +12 -3
  30. package/runtime/python/okstra_ctl/pr_template.py +4 -2
  31. package/runtime/python/okstra_ctl/render.py +8 -6
  32. package/runtime/python/okstra_ctl/render_final_report.py +4 -1
  33. package/runtime/python/okstra_ctl/run.py +5 -5
  34. package/runtime/python/okstra_ctl/seeding.py +12 -6
  35. package/runtime/python/okstra_ctl/sequence.py +3 -1
  36. package/runtime/python/okstra_ctl/wizard.py +412 -77
  37. package/runtime/python/okstra_ctl/worktree.py +8 -6
  38. package/runtime/python/okstra_project/__init__.py +35 -5
  39. package/runtime/python/okstra_project/dirs.py +67 -0
  40. package/runtime/python/okstra_project/resolver.py +8 -8
  41. package/runtime/python/okstra_project/state.py +11 -9
  42. package/runtime/python/okstra_token_usage/collect.py +3 -1
  43. package/runtime/python/okstra_token_usage/report.py +6 -2
  44. package/runtime/skills/okstra-brief/SKILL.md +30 -30
  45. package/runtime/skills/okstra-context-loader/SKILL.md +7 -7
  46. package/runtime/skills/okstra-inspect/SKILL.md +25 -25
  47. package/runtime/skills/okstra-run/templates/pr-body.template.md +1 -1
  48. package/runtime/skills/okstra-schedule/SKILL.md +7 -7
  49. package/runtime/skills/okstra-setup/SKILL.md +8 -8
  50. package/runtime/templates/okstra.CLAUDE.md +4 -4
  51. package/runtime/templates/reports/brief.template.md +5 -5
  52. package/runtime/templates/reports/task-brief.template.md +1 -1
  53. package/runtime/validators/lib/fixtures.sh +2 -2
  54. package/runtime/validators/lib/paths.sh +9 -3
  55. package/runtime/validators/validate-brief.py +2 -2
  56. package/runtime/validators/validate-brief.sh +1 -1
  57. package/runtime/validators/validate-run.py +3 -1
  58. package/runtime/validators/validate-workflow.sh +2 -2
  59. package/src/check-project.mjs +3 -3
  60. package/src/config.mjs +6 -5
  61. package/src/install.mjs +5 -5
  62. package/src/migrate.mjs +163 -0
  63. package/src/okstra-dirs.mjs +37 -0
  64. package/src/paths.mjs +17 -0
  65. package/src/setup.mjs +8 -4
  66. package/src/uninstall.mjs +3 -3
@@ -76,7 +76,7 @@ sequenceDiagram
76
76
  participant Skill as okstra-run skill
77
77
  participant Node as okstra CLI
78
78
  participant Py as okstra_ctl.run
79
- participant FS as .project-docs/okstra
79
+ participant FS as .okstra
80
80
  participant Home as ~/.okstra
81
81
 
82
82
  Skill->>Node: okstra wizard render-args
@@ -119,7 +119,7 @@ flowchart TD
119
119
 
120
120
  ```mermaid
121
121
  flowchart TD
122
- Root["<PROJECT_ROOT>/.project-docs/okstra/tasks/<group>/<task>/"] --> IS[instruction-set/]
122
+ Root["<PROJECT_ROOT>/.okstra/tasks/<group>/<task>/"] --> IS[instruction-set/]
123
123
  Root --> Runs[runs/<task-type>/]
124
124
  Root --> Hist[history/timeline.json]
125
125
  IS --> Profile[analysis-profile.md]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "okstra",
3
- "version": "0.36.1",
3
+ "version": "0.37.0",
4
4
  "description": "Multi-agent cross-verification orchestrator runtime + Claude Code skills.",
5
5
  "license": "MIT",
6
6
  "author": "devonshin",
@@ -1,5 +1,5 @@
1
1
  {
2
- "package": "0.36.1",
3
- "builtAt": "2026-05-24T11:52:05.811Z",
2
+ "package": "0.37.0",
3
+ "builtAt": "2026-05-27T07:29:11.725Z",
4
4
  "repoRoot": "/home/runner/work/okstra/okstra"
5
5
  }
@@ -133,7 +133,7 @@ Executor is chosen at run-prep time via `--executor <claude|codex|gemini>` (or `
133
133
  - Location: `~/.okstra/worktrees/<project-id>/<task-group-segment>/<task-id-segment>/` (override `OKSTRA_HOME` only for tests). All segments are sanitised — `/`, `:`, and other special chars collapse to `-`.
134
134
  - Branch: `<work-category-prefix>-<task-id-segment>` (e.g. `feat-dev-9436`, `fix-dev-7311`). Branched from `HEAD` of the repo's **main** worktree at the first phase's prep time; base SHA is recorded in `EXECUTOR_WORKTREE_BASE_REF`.
135
135
  - A global registry at `~/.okstra/worktrees/registry.json` (flock-guarded) maps each task-key to its path + branch and prevents concurrent runs from colliding. Branch names are globally unique across task-keys on this machine.
136
- - Worktree sync mirrors the configured project-relative directories from the **main worktree** so task checkouts see the same filesystem state. This is filesystem continuity only: okstra-owned context and writes still stay inside `<PROJECT_ROOT>/.project-docs/okstra/**` unless the brief explicitly authorizes a non-okstra path.
136
+ - Worktree sync mirrors the configured project-relative directories from the **main worktree** so task checkouts see the same filesystem state. This is filesystem continuity only: okstra-owned context and writes still stay inside `<PROJECT_ROOT>/.okstra/**` unless the brief explicitly authorizes a non-okstra path.
137
137
  - The path, branch, base ref, and provisioning status (`created` | `reused` | `skipped-in-worktree` | `skipped-not-git`) are exposed through the launch prompt's `## Executor Worktree` section and the implementation profile's worktree block.
138
138
  - **Skip conditions** (worktree provisioning is a no-op; task uses `project_root` directly):
139
139
  - `project_root` is already inside a non-main worktree (the run reuses the caller's worktree to avoid nesting).
@@ -145,7 +145,7 @@ Executor is chosen at run-prep time via `--executor <claude|codex|gemini>` (or `
145
145
 
146
146
  **REQUIRED SUB-SKILL:** Invoke [okstra-context-loader](./skills/okstra-context-loader/SKILL.md) first to discover task bundle paths.
147
147
 
148
- Treat cross verify input as a task bundle, not as a single file. If the user did not specify an explicit task key or task path, use `.project-docs/okstra/discovery/latest-task.json` as the current-task convenience pointer. If task browsing, task-id disambiguation, or project-level task inventory is needed, inspect `.project-docs/okstra/discovery/task-catalog.json` first.
148
+ Treat cross verify input as a task bundle, not as a single file. If the user did not specify an explicit task key or task path, use `.okstra/discovery/latest-task.json` as the current-task convenience pointer. If task browsing, task-id disambiguation, or project-level task inventory is needed, inspect `.okstra/discovery/task-catalog.json` first.
149
149
 
150
150
  After context-loader completes, read **only the five mandatory files below** in a single parallel-Read message at the start of Phase 1. The other instruction-set files are loaded lazily at the phase that actually needs them — see "Lazy reading discipline" below. This split came from observed lead-token bloat: in `fontsninja-classifier-v2:dev-9461:dev-9495` RD-001 the lead burned 71 M tokens (97 % cache_read) largely because every phase entry re-absorbed a 93 KB instruction-set baseline that included files only one downstream phase ever actually used.
151
151
 
@@ -48,7 +48,7 @@ Unlike the Codex / Gemini workers, you are an in-process Claude subagent — you
48
48
  - **Verifier QA-gate exception:** verifier roles MAY use the same `cd <WORKTREE> && <cmd>` shape when executing project-declared `qaCommands` (lint / format / typecheck / test) from `project.json`, since those commands are cwd-sensitive by nature. Outside the QA gate, verifiers still read with absolute paths only — do NOT use `cd` for file inspection.
49
49
  - **No extra chaining beyond `cd && cmd`:** the permission matcher only allows the exact two-segment shape `cd <PATH> && <single-command>`. Do NOT append additional pipes, semicolons, redirects, or `&&` chains — e.g. `cd ... && cargo test ... 2>&1 | tail -20; echo "exit:$?"` will trigger a permission prompt every dispatch because the trailing `| tail`, `; echo`, and `2>&1` tokens disqualify the prefix match against `Bash(cargo:*)`. Let Claude Code capture the full stdout/stderr and exit code natively — do not post-process with `tail`, `head`, or `echo "exit:$?"`. If output truncation is genuinely needed, run the command first and read the result in a separate tool call.
50
50
 
51
- 5. **MCP usage**: The canonical list of MCP servers and tools available for this run lives in the lead prompt's `## Available MCP Servers` section (sourced from `.project-docs/okstra/project.json`'s `mcpServers` array). When the task requires inspection of an external system covered by one of those servers, call the listed tool directly by name (e.g. `mcp__<server>__<tool>`). Do NOT shell out via `claude --mcp-cli call ...` or run the tool name as a Bash command — those are not valid invocation paths. If a server you need is not listed, record `MCP not available for this run` in your worker output rather than guessing a tool name.
51
+ 5. **MCP usage**: The canonical list of MCP servers and tools available for this run lives in the lead prompt's `## Available MCP Servers` section (sourced from `.okstra/project.json`'s `mcpServers` array). When the task requires inspection of an external system covered by one of those servers, call the listed tool directly by name (e.g. `mcp__<server>__<tool>`). Do NOT shell out via `claude --mcp-cli call ...` or run the tool name as a Bash command — those are not valid invocation paths. If a server you need is not listed, record `MCP not available for this run` in your worker output rather than guessing a tool name.
52
52
 
53
53
  6. If the task brief includes an `## Available MCP Servers` section in the lead prompt, treat that as the canonical list of MCP tools you may invoke for this run. If a needed server is not listed, record `MCP not available for this run` rather than calling it.
54
54
 
@@ -19,9 +19,9 @@ profile document.
19
19
  - Tooling — read-only MCP availability (shared):
20
20
  - MCP is not implicit okstra context. Query an MCP server only when the task brief explicitly lists it as source material for this run. Any MCP-derived finding MUST cite server, table, and the SELECT used. MCP MUST NEVER be used as a write path — schema/data mutations go through repository migration files reviewed by humans.
21
21
  - Resource boundary (shared — artifact-home rule):
22
- - Okstra-owned project artifacts live only under `<PROJECT_ROOT>/.project-docs/okstra/**`. Treat `project.json`, task bundles, run artifacts, `glossary.md`, and `decisions/` under that subtree as the canonical okstra memory.
23
- - Treat paths outside `<PROJECT_ROOT>/.project-docs/okstra/**` as source material only when the task brief's `Source Material` or `Reporter Confirmations` explicitly cites them; they never become okstra memory.
24
- - Create, modify, or delete only inside `<PROJECT_ROOT>/.project-docs/okstra/**` unless the brief verbatim requests a specific non-okstra edit. The phase performing that edit must quote the user instruction in its report. Implementation source edits also require the approved implementation plan.
22
+ - Okstra-owned project artifacts live only under `<PROJECT_ROOT>/.okstra/**`. Treat `project.json`, task bundles, run artifacts, `glossary.md`, and `decisions/` under that subtree as the canonical okstra memory.
23
+ - Treat paths outside `<PROJECT_ROOT>/.okstra/**` as source material only when the task brief's `Source Material` or `Reporter Confirmations` explicitly cites them; they never become okstra memory.
24
+ - Create, modify, or delete only inside `<PROJECT_ROOT>/.okstra/**` unless the brief verbatim requests a specific non-okstra edit. The phase performing that edit must quote the user instruction in its report. Implementation source edits also require the approved implementation plan.
25
25
  - Authority & permissions assumption (applies to every okstra task-type):
26
26
  - **Assume the user (and their team) holds full authority and every permission required for the anticipated, in-flight, or follow-up work.** Treat external approvals, third-party access grants, role/IAM permissions, organisational sign-off, legal/compliance review, vendor coordination, and "verify access exists" steps as already satisfied unless the task brief explicitly states otherwise.
27
27
  - Do NOT add such items to routing decisions, missing-materials lists, clarification questions, option trade-offs, dependency/migration risk, validation checklists, rollout plans, acceptance blockers, residual risks, release recommendations, the `## 5. Clarification Items` table, or any day/effort estimate. They are not legitimate sources of schedule extension.
@@ -52,13 +52,13 @@ profile document.
52
52
  - `Source Material` is reporter-verbatim. Do NOT paraphrase, summarize, reorder, or restructure it. Quote it directly when needed.
53
53
  - `Augmentation` entries carry one of four labels — `evidence-link`, `format-conversion`, `terminology-mapping`, `intent-inference`. Treat them as follows:
54
54
  - `evidence-link` / `format-conversion` → trust without re-verification.
55
- - `terminology-mapping` → verify against `<PROJECT_ROOT>/.project-docs/okstra/glossary.md` (authoritative); raise a `Clarification Items` row if the mapping is missing or contradicts the glossary.
55
+ - `terminology-mapping` → verify against `<PROJECT_ROOT>/.okstra/glossary.md` (authoritative); raise a `Clarification Items` row if the mapping is missing or contradicts the glossary.
56
56
  - `intent-inference` → treat as an **unverified hypothesis**. Every `intent-inference` augmentation MUST be paired in the brief with an `Open Questions` row prefixed `intent-check:`. Promote that row into the run's `## 5. Clarification Items` table as `Kind=decision, Blocks=next-phase` (or `Blocks=approval` for `implementation-planning`) with the recommended answer set to "보고자에게 직접 확인 후 응답" unless the codebase can be inspected to confirm or refute the inference.
57
57
  - `Open Questions` row prefixes are signals — do not strip them when promoting:
58
58
  - `intent-check:` → `Kind=decision`, recommended answer = reporter confirmation. NEVER silently resolve an `intent-check:` by inference at this layer.
59
- - `terminology:` → `Kind=decision`, recommended answer = canonical term from `<PROJECT_ROOT>/.project-docs/okstra/glossary.md` (or "extend okstra glossary via brief Step 4.5").
59
+ - `terminology:` → `Kind=decision`, recommended answer = canonical term from `<PROJECT_ROOT>/.okstra/glossary.md` (or "extend okstra glossary via brief Step 4.5").
60
60
  - `conversion-block:` → `Kind=decision`, recommended answer = "보고자에게 직접 확인". The brief is explicitly signalling that translation failed; further inference is forbidden until the reporter clarifies.
61
- - `adr-candidate:` → handled by `implementation-planning`; carry forward without modification. Approved decision files land only at `<PROJECT_ROOT>/.project-docs/okstra/decisions/<NNNN>-<slug>.md`.
61
+ - `adr-candidate:` → handled by `implementation-planning`; carry forward without modification. Approved decision files land only at `<PROJECT_ROOT>/.okstra/decisions/<NNNN>-<slug>.md`.
62
62
  - `general:` → free-form; classify per the standard `Clarification Items` rules.
63
63
  - Any decision in this run that contradicts the brief's `Source Material` must be raised back to the reporter via a `Clarification Items` row; it must NOT be silently overridden. Disagreement with the reporter is allowed only after the row is resolved.
64
64
  - This contract is the single authority on brief consumption. Phase-specific addenda may *tighten* these rules but may not relax them.
@@ -14,7 +14,7 @@ until Phase 5 ends, then drop from active context for Phase 6/7.
14
14
  - The `Executor` (bound in `implementation.md` thin core) is the **only worker permitted to use Edit / Write / state-mutating Bash commands** on project files. All other workers run read-only. When the executor provider is `codex` or `gemini`, the actual file mutation happens inside the executor CLI's own auto-edit mode (e.g. `codex exec --sandbox workspace-write`, gemini's equivalent) — not through Claude-side Edit/Write tools — but the safety rules in this sidecar still apply identically.
15
15
  - Worktree cwd handling — when the thin core's Task worktree block resolves status to `created` or `reused`, the Executor MUST run every Edit / Write / build / test / commit command with the worktree path as cwd. Treat it as `project_root` for the duration of this run. Do NOT mutate the caller's original checkout. Do NOT `cd` out of the worktree to reach files; if a file outside the worktree is needed, the dependency is a planning gap — record it in `Out-of-plan edits` and continue.
16
16
  - **How to set cwd per Bash call**: the Claude Bash tool inherits its cwd from the lead session, which is NOT the worktree. To put cwd-sensitive toolchains (`cargo`, `npm`, `pnpm`, `bun`, `pytest`, `make`, `go`) into the worktree, prefix the command with `cd {{EXECUTOR_WORKTREE_PATH}} && ` inside the same Bash invocation — e.g. `cd {{EXECUTOR_WORKTREE_PATH}} && cargo test -p foo`. **Never wrap in `bash -lc "..."` or `bash -c "..."`** — the wrapper hides the leading `cd` token from Claude Code's permission auto-allow layer (causing prompts on every call) without any safety benefit. For tools that accept an explicit working-directory flag (`git -C <path>`, `cargo --manifest-path`, `pytest --rootdir`), prefer that form over the `cd && ` chain. Edit / Write / Read tool calls already use absolute paths and need no cwd handling. The codex / gemini executor CLI wrappers (`okstra-codex-exec.sh -C`, `okstra-gemini-exec.sh --include-directories`) already inject worktree cwd at the CLI layer, so this rule applies primarily to the Claude executor.
17
- - **Synced okstra state directory.** At provision time `okstra-ctl` may symlink `.project-docs/` from the repo's **main worktree** into the task worktree. This is NOT an independent copy — writes through it land in the main worktree. Inside this run the executor MUST confine okstra artifact writes to its own task scope (i.e. `.project-docs/okstra/tasks/<this-task-id>/...`). Other synced directories, if present due to local configuration, are not implicit okstra context; read them only when the brief explicitly cites them as source material.
17
+ - **Synced okstra state directory.** At provision time `okstra-ctl` may symlink `.project-docs/` from the repo's **main worktree** into the task worktree. This is NOT an independent copy — writes through it land in the main worktree. Inside this run the executor MUST confine okstra artifact writes to its own task scope (i.e. `.okstra/tasks/<this-task-id>/...`). Other synced directories, if present due to local configuration, are not implicit okstra context; read them only when the brief explicitly cites them as source material.
18
18
 
19
19
  ## Pre-implementation context exploration (executor before first edit)
20
20
 
@@ -54,7 +54,7 @@ until Phase 5 ends, then drop from active context for Phase 6/7.
54
54
  - `<scope>` SHOULD be the plan step identifier or the primary module touched (e.g. `feat(report-writer): ...`). Omit the parentheses only when no meaningful scope applies.
55
55
  - `<subject>` MUST be ≤72 characters, imperative mood (`add`, `fix`, `remove` — not `added` / `adding`), no trailing period, no emoji, no AI attribution lines (no `Co-Authored-By: Claude ...`, no `Generated with Claude Code`).
56
56
  - Body (when present) explains *why*, not *what*; wrap at ~100 chars.
57
- - Do NOT append okstra artefact paths to the commit message — no `Plan: .project-docs/okstra/...`, no `Report: ...`, no `Run: ...`, no `Task: ...` footers, and no other reference to files under `.project-docs/okstra/`. Those paths belong in the final report's `Plan link & approval evidence` section, not in git history; they rot quickly and leak internal layout into the upstream changelog.
57
+ - Do NOT append okstra artefact paths to the commit message — no `Plan: .okstra/...`, no `Report: ...`, no `Run: ...`, no `Task: ...` footers, and no other reference to files under `.okstra/`. Those paths belong in the final report's `Plan link & approval evidence` section, not in git history; they rot quickly and leak internal layout into the upstream changelog.
58
58
  - Allowed footers are limited to standard Conventional Commits trailers (`BREAKING CHANGE: ...`, `Refs: <issue/ticket-id>`, `Closes #<n>`). When citing a ticket, use the ticket id only (e.g. `Refs: DEV-9423`) — never a filesystem path.
59
59
  - One commit MUST correspond to one plan step (or one cohesive sub-step). Do NOT bundle unrelated steps into a single commit, and do NOT split a single step across commits unless the plan explicitly sequenced it that way.
60
60
  - The exact message used for each commit MUST be reproduced verbatim in the final report's `Commit list` so reviewers can audit it without re-running `git log`.
@@ -23,7 +23,7 @@ Every verifier acts as a QA gate, not just a diff reviewer. Trusting the executo
23
23
  Verifier obtains the QA command set from exactly two declared sources, in order — there is **no fallback to guessing tools from manifest files**.
24
24
 
25
25
  1. **Tier 1 — plan validation set (task-specific):** every command listed under the approved plan's `validation` block (pre / mid / post).
26
- 2. **Tier 2 — project baseline (`project.json.qaCommands`):** the project's standing QA baseline declared in `<PROJECT_ROOT>/.project-docs/okstra/project.json` under the `qaCommands` key. Schema (each category is an array of `{ "label", "cmd", "language"? }` objects):
26
+ 2. **Tier 2 — project baseline (`project.json.qaCommands`):** the project's standing QA baseline declared in `<PROJECT_ROOT>/.okstra/project.json` under the `qaCommands` key. Schema (each category is an array of `{ "label", "cmd", "language"? }` objects):
27
27
  ```json
28
28
  {
29
29
  "qaCommands": {
@@ -31,7 +31,7 @@
31
31
  - **Residual Risk block** (under section 4): risks that are not blockers but should be tracked, each with mitigation owner and a trigger that would escalate them to a blocker.
32
32
  - **Validation Evidence**: for every requirement in the originating plan or task brief, cite the artifact (commit SHA, test output, log line, MCP SELECT result) that demonstrates coverage. Paraphrased "verified" claims without an artifact are rejected.
33
33
  - **Read-only command log**: any pre-existing test/validation command executed during this run MUST be listed with its exact command line and exit code. No mutating commands may appear here.
34
- - **Two-tier command lookup (shared with `implementation`):** when this phase performs its own independent re-validation, the command source is exactly the same two tiers `implementation` verifiers use — Tier 1 is the originating task brief / approved plan's `validation` set, Tier 2 is `<PROJECT_ROOT>/.project-docs/okstra/project.json` under `qaCommands`. Auto-detecting tools from manifest files is forbidden; missing tiers are recorded as `qa-command not configured: <category>` and do NOT trigger a guess. The `cmd` deny-list (`--fix`, `--write`, ` -w`, ` -u`, `--snapshot-update`, `INSTA_UPDATE=<not-no>`, `cargo update`, `npm install` without `ci`, etc.) is enforced identically. NOTE: runtime fail-fast validation (`okstra_ctl.qa_commands.validate_qa_commands`) only fires at `--task-type implementation` run-prep, so this phase MUST self-check each `qaCommands` entry against the deny-list before executing it — if a denied token is present, skip the command and record it as a `Read-only command log` line `qa-command rejected (denied token: <token>): <label>`.
34
+ - **Two-tier command lookup (shared with `implementation`):** when this phase performs its own independent re-validation, the command source is exactly the same two tiers `implementation` verifiers use — Tier 1 is the originating task brief / approved plan's `validation` set, Tier 2 is `<PROJECT_ROOT>/.okstra/project.json` under `qaCommands`. Auto-detecting tools from manifest files is forbidden; missing tiers are recorded as `qa-command not configured: <category>` and do NOT trigger a guess. The `cmd` deny-list (`--fix`, `--write`, ` -w`, ` -u`, `--snapshot-update`, `INSTA_UPDATE=<not-no>`, `cargo update`, `npm install` without `ci`, etc.) is enforced identically. NOTE: runtime fail-fast validation (`okstra_ctl.qa_commands.validate_qa_commands`) only fires at `--task-type implementation` run-prep, so this phase MUST self-check each `qaCommands` entry against the deny-list before executing it — if a denied token is present, skip the command and record it as a `Read-only command log` line `qa-command rejected (denied token: <token>): <label>`.
35
35
  - **Routing recommendation**: brief note on the next safe phase (`done`, `error-analysis`, `implementation-planning`) tied to the verdict and blocker list.
36
36
  - Clarification request policy (phase-specific addendum — shared policy is in `_common-contract.md`):
37
37
  - populate `## 5. Clarification Items` only when a blocker hinges on information only the user can supply (deployment intent, intended target environment, business-rule interpretation); use `Blocks=next-phase` for items that gate continuing to release-handoff
@@ -18,7 +18,7 @@
18
18
  - skim recent commits touching those files (`git log -- <path>`) to surface in-flight work or contested areas
19
19
  - **codebase-first ambiguity resolution**: any ambiguity that can be answered by `Read` / `Grep` MUST be resolved that way and recorded with file:line evidence. Only ambiguities that genuinely require a human decision are escalated as `Clarification Items` rows. Writing a clarification row for something the code already answers is a defect of this phase.
20
20
  - flag any requirement that is ambiguous, contradictory, or missing success criteria — register each one as a row in the report's `## 5. Clarification Items` table with `Blocks=approval` instead of guessing
21
- - read `<PROJECT_ROOT>/.project-docs/okstra/glossary.md` and `<PROJECT_ROOT>/.project-docs/okstra/decisions/` titles if present. Absent okstra memory files are the normal state — do not error. Treat the brief's `terminology:*` resolutions from `requirements-discovery` (if any) as authoritative; if missing, resolve any remaining fuzzy term as a `Blocks=approval` clarification row.
21
+ - read `<PROJECT_ROOT>/.okstra/glossary.md` and `<PROJECT_ROOT>/.okstra/decisions/` titles if present. Absent okstra memory files are the normal state — do not error. Treat the brief's `terminology:*` resolutions from `requirements-discovery` (if any) as authoritative; if missing, resolve any remaining fuzzy term as a `Blocks=approval` clarification row.
22
22
  - Primary focus areas:
23
23
  - requirement gaps
24
24
  - affected components and boundaries
@@ -43,7 +43,7 @@
43
43
  - executing builds, migrations, deployments, or any command that mutates project state outside the run's own artifact directories (`reports/`, `prompts/`, `state/`, `manifests/`, `worker-results/`, `status/`, `sessions/`)
44
44
  - this run stays in `implementation-planning` regardless of user phrasing — the shared anti-escalation rule applies
45
45
  - dispatching parallel sub-agents beyond the required worker roster — okstra owns worker fan-out
46
- - writing artifacts anywhere except `<PROJECT_ROOT>/.project-docs/okstra/` — the run's `reports/` directory is the canonical location for this phase
46
+ - writing artifacts anywhere except `<PROJECT_ROOT>/.okstra/` — the run's `reports/` directory is the canonical location for this phase
47
47
  - Clarification request policy (phase-specific addenda — shared policy is in `_common-contract.md`):
48
48
  - every clarification row carries a recommended answer + one-line rationale inside the `Expected form` cell; rows that lack a recommendation are rejected as half-formed.
49
49
  - **Evidence note required inside `Statement`**: every clarification row includes `Evidence checked: <path:line>` or `Evidence checked: none — <human-only reason>` in the `Statement` cell. `none` is allowed ONLY when the row's nature is "only a human can answer this" (reporter intent, business priority, organisational decision). A row with `none` that *could* have been answered by the codebase is a defect of this phase, restated from the pre-planning rule above.
@@ -77,9 +77,9 @@
77
77
  1. **Hard to reverse** — would changing the decision later cost meaningfully more than deciding now?
78
78
  2. **Surprising without context** — would a future reader, seeing only the code, wonder "why was it built this way?"?
79
79
  3. **Real trade-off** — were there named alternatives, and was one picked for specific reasons?
80
- If **all three** hold, attach a decision draft as a report appendix section titled `Decision Drafts` (one decision per subsection). Each draft uses the `## Context / ## Decision / ## Consequences / ## Alternatives Considered` shape, names the alternatives that were rejected and why, and starts with `## Status: Proposed`. The next decision number is `(max existing in <PROJECT_ROOT>/.project-docs/okstra/decisions/ + 1)` zero-padded to 4 digits. If any of the three criteria is missing, do NOT raise a decision draft — instead record `skipped adr-candidate: <topic> — reason: <criterion that failed>` on one line under `Decision Drafts` so the next reader knows the candidate was evaluated and intentionally dropped.
81
- The drafts are NOT written by this phase. The approved plan's stepwise execution order MUST include the step `Create <PROJECT_ROOT>/.project-docs/okstra/decisions/<NNNN>-<slug>.md from the decision draft in section X` so the `implementation` run commits the file inside okstra's subtree.
82
- - **Glossary proposals**: if a term or definition should become okstra institutional memory, add the step `Update <PROJECT_ROOT>/.project-docs/okstra/glossary.md: <term> = <definition>` to the stepwise execution order. Use no other project-memory path.
80
+ If **all three** hold, attach a decision draft as a report appendix section titled `Decision Drafts` (one decision per subsection). Each draft uses the `## Context / ## Decision / ## Consequences / ## Alternatives Considered` shape, names the alternatives that were rejected and why, and starts with `## Status: Proposed`. The next decision number is `(max existing in <PROJECT_ROOT>/.okstra/decisions/ + 1)` zero-padded to 4 digits. If any of the three criteria is missing, do NOT raise a decision draft — instead record `skipped adr-candidate: <topic> — reason: <criterion that failed>` on one line under `Decision Drafts` so the next reader knows the candidate was evaluated and intentionally dropped.
81
+ The drafts are NOT written by this phase. The approved plan's stepwise execution order MUST include the step `Create <PROJECT_ROOT>/.okstra/decisions/<NNNN>-<slug>.md from the decision draft in section X` so the `implementation` run commits the file inside okstra's subtree.
82
+ - **Glossary proposals**: if a term or definition should become okstra institutional memory, add the step `Update <PROJECT_ROOT>/.okstra/glossary.md: <term> = <definition>` to the stepwise execution order. Use no other project-memory path.
83
83
  - No-placeholder rule (plan failures — reject any option or step that contains these):
84
84
  - "TBD", "TODO", "implement later", "fill in details", "add appropriate error handling", "handle edge cases", "write tests for the above" without actual test code
85
85
  - "similar to Option/Task N" without repeating the concrete content (readers may consume sections out of order)
@@ -43,7 +43,7 @@
43
43
  - `cancel` — end the run without executing push or PR commands; record the cancellation in the final report.
44
44
  - Inline drafting rules (Claude lead):
45
45
  - read the run brief, the cited final-verification report, `git log --oneline <base>..HEAD`, and `git diff <base>..HEAD --stat` to ground the drafted text in actual committed changes.
46
- - **PR body template** — the run context exposes `PR_TEMPLATE_PATH` and `PR_TEMPLATE_SOURCE`. The path MUST be an okstra-owned project artifact under `<PROJECT_ROOT>/.project-docs/okstra/**` or a file already materialised into this run's artifact directory by the prepare step. The lead MUST `Read` this file verbatim, strip HTML comments, then fill in the placeholders. Do NOT hard-code a section list — the template is the source of truth for the structure. If the resolved file is missing or outside the okstra resource boundary at draft time, abort the run with a clear error rather than inventing a structure.
46
+ - **PR body template** — the run context exposes `PR_TEMPLATE_PATH` and `PR_TEMPLATE_SOURCE`. The path MUST be an okstra-owned project artifact under `<PROJECT_ROOT>/.okstra/**` or a file already materialised into this run's artifact directory by the prepare step. The lead MUST `Read` this file verbatim, strip HTML comments, then fill in the placeholders. Do NOT hard-code a section list — the template is the source of truth for the structure. If the resolved file is missing or outside the okstra resource boundary at draft time, abort the run with a clear error rather than inventing a structure.
47
47
  - produce **two artifacts** before showing them to the user:
48
48
  1. **PR title** — by default the subject of the most recent implementation commit, or a concise Conventional Commits-style summary of the committed range.
49
49
  2. **PR body** — markdown filled from `PR_TEMPLATE_PATH`. The user-confirmation step's diff (Q3 `edit then proceed`) is computed against the filled template, not against the raw template file.
@@ -19,7 +19,7 @@
19
19
  - identify missing materials that block reliable routing
20
20
  - define task continuity expectations for long-running work under the same task key
21
21
  - capture approval or confirmation points before the next phase starts
22
- - **domain alignment check**: read `<PROJECT_ROOT>/.project-docs/okstra/glossary.md` and `<PROJECT_ROOT>/.project-docs/okstra/decisions/` titles if present. Absent okstra memory files are normal — do not error. Validate that every `terminology:*` entry under the brief's `Open Questions` has a canonical resolution before routing. Fuzzy or overloaded terms in the brief MUST be resolved to a single canonical term in this phase.
22
+ - **domain alignment check**: read `<PROJECT_ROOT>/.okstra/glossary.md` and `<PROJECT_ROOT>/.okstra/decisions/` titles if present. Absent okstra memory files are normal — do not error. Validate that every `terminology:*` entry under the brief's `Open Questions` has a canonical resolution before routing. Fuzzy or overloaded terms in the brief MUST be resolved to a single canonical term in this phase.
23
23
  - Decision-tree walk (bounded):
24
24
  - When the brief's `Desired Outcome`, classification, or routing target depends on a chain of decisions, walk that chain one branch at a time. Each branch is one `Clarification Items` row, not a free-form interview.
25
25
  - For every clarification row, put the single best answer and one-line rationale in `Expected form` as `Recommended: ...`. Put other options and one-sentence consequences in the same cell as `Alternatives: ...`.
@@ -29,7 +29,7 @@
29
29
  - evidence-backed routing decision
30
30
  - uncertainty boundaries and missing inputs
31
31
  - next recommended phase and safe resume guidance
32
- - canonical-term resolution for every `terminology:*` brief item, written as a one-line `<term> = <definition>` line in a new `Domain Alignment` subsection of the final report; alongside each, propose whether `<PROJECT_ROOT>/.project-docs/okstra/glossary.md` should be updated (proposal only — actual writes happen via `okstra-brief` Step 4.5 on a subsequent run)
32
+ - canonical-term resolution for every `terminology:*` brief item, written as a one-line `<term> = <definition>` line in a new `Domain Alignment` subsection of the final report; alongside each, propose whether `<PROJECT_ROOT>/.okstra/glossary.md` should be updated (proposal only — actual writes happen via `okstra-brief` Step 4.5 on a subsequent run)
33
33
  - Clarification request policy (phase-specific addenda — shared policy is in `_common-contract.md`):
34
34
  - if any blocking input is missing at the time of writing the final report, populate `## 5. Clarification Items` in `final-report-template.md` (a single unified table; `Blocks=next-phase` for items the next run cannot start without)
35
35
  - prefer concrete questions whose answers map directly to a routing decision (`bugfix` vs `feature`, `error-analysis` vs `implementation-planning`, etc.). State each option in plain language with one sentence describing what choosing it would mean for the next phase.
@@ -39,4 +39,4 @@
39
39
  - Non-goals:
40
40
  - full implementation design unless it is required to decide the next phase
41
41
  - **source code edits, plan authoring, builds, or deployments** — this run only classifies the work and routes it; deeper analysis and planning belong to subsequent phases
42
- - **writes outside `<PROJECT_ROOT>/.project-docs/okstra/`** — this phase only uses okstra's artifact root. Glossary additions land in `<PROJECT_ROOT>/.project-docs/okstra/glossary.md` (via `okstra-brief` Step 4.5); decision drafts land in `<PROJECT_ROOT>/.project-docs/okstra/decisions/` (via `implementation-planning`).
42
+ - **writes outside `<PROJECT_ROOT>/.okstra/`** — this phase only uses okstra's artifact root. Glossary additions land in `<PROJECT_ROOT>/.okstra/glossary.md` (via `okstra-brief` Step 4.5); decision drafts land in `<PROJECT_ROOT>/.okstra/decisions/` (via `implementation-planning`).
@@ -23,6 +23,20 @@
23
23
  "options": {
24
24
  "__use_suggested__": "brief 값 사용: {suggestion}",
25
25
  "__free_input__": "다른 값 입력"
26
+ },
27
+ "echo_variants": {
28
+ "free_input": "task-group: (직접 입력)"
29
+ }
30
+ },
31
+ "task_group_no_suggestion": {
32
+ "label": "Task group? (예: backend-api, INV-1234)",
33
+ "echo_template": "task-group: {value}",
34
+ "options": {
35
+ "__free_input__": "직접 입력"
36
+ },
37
+ "recent_label_prefix": "최근 사용: ",
38
+ "echo_variants": {
39
+ "free_input": "task-group: (직접 입력)"
26
40
  }
27
41
  },
28
42
  "task_group_text": {
@@ -42,6 +56,20 @@
42
56
  "options": {
43
57
  "__use_suggested__": "brief 값 사용: {suggestion}",
44
58
  "__free_input__": "다른 값 입력"
59
+ },
60
+ "echo_variants": {
61
+ "free_input": "task-id: (직접 입력)"
62
+ }
63
+ },
64
+ "task_id_no_suggestion": {
65
+ "label": "Task id? (예: login-error-analysis, dev-9043)",
66
+ "echo_template": "task-id: {value}",
67
+ "options": {
68
+ "__free_input__": "직접 입력"
69
+ },
70
+ "recent_label_prefix": "같은 group 의 기존: ",
71
+ "echo_variants": {
72
+ "free_input": "task-id: (직접 입력)"
45
73
  }
46
74
  },
47
75
  "task_id_text": {
@@ -66,6 +94,18 @@
66
94
  "kept": "brief: {brief_path} (유지)"
67
95
  }
68
96
  },
97
+ "brief_path_pick": {
98
+ "label": "task brief markdown 의 경로를 선택해주세요",
99
+ "echo_template": "brief(pick): {value}",
100
+ "options": {
101
+ "__existing__": "기존 brief 사용: {existing}",
102
+ "__standard__": "표준 경로 사용: {standard}",
103
+ "__free_input__": "직접 입력"
104
+ },
105
+ "errors": {
106
+ "existing_missing": "기존 brief 가 없습니다. 다른 옵션을 선택하세요."
107
+ }
108
+ },
69
109
  "brief_path": {
70
110
  "label": "task brief markdown 의 경로를 알려주세요 (project root 기준 상대경로 또는 절대경로)",
71
111
  "echo_template": "brief: {value}"
@@ -87,10 +127,16 @@
87
127
  "echo_template": "approved-plan(pick): {value}",
88
128
  "options": {
89
129
  "__use_default__": "기본 경로 사용: {default}",
90
- "__other__": "다른 경로 입력"
130
+ "__other__": "직접 입력"
131
+ },
132
+ "labels": {
133
+ "other_report": "이전 보고서: {path}"
134
+ },
135
+ "echo_suffixes": {
136
+ "other_report": "(이전 보고서)"
91
137
  },
92
138
  "errors": {
93
- "default_not_found": "기본 approved-plan 경로를 찾을 수 없습니다. '다른 경로 입력'을 선택하세요."
139
+ "default_not_found": "기본 approved-plan 경로를 찾을 수 없습니다. '직접 입력'을 선택하세요."
94
140
  }
95
141
  },
96
142
  "approved_plan": {
@@ -109,7 +155,14 @@
109
155
  "echo_template": "directive(pick): {value}",
110
156
  "options": {
111
157
  "__skip__": "없음 (건너뛰기)",
112
- "__enter__": "있음 (입력)"
158
+ "__free_input__": "직접 입력"
159
+ },
160
+ "labels": {
161
+ "reuse_last": "이전 run 의 directive 재사용: {snippet}"
162
+ },
163
+ "echo_suffixes": {
164
+ "reuse": "directive: {value} (재사용)",
165
+ "skip": "directive: (none)"
113
166
  }
114
167
  },
115
168
  "related_tasks_pick": {
@@ -117,7 +170,14 @@
117
170
  "echo_template": "related-tasks(pick): {value}",
118
171
  "options": {
119
172
  "__skip__": "없음 (건너뛰기)",
120
- "__enter__": "있음 (입력)"
173
+ "__free_input__": "직접 입력"
174
+ },
175
+ "labels": {
176
+ "siblings": "같은 group 의 task-id 사용: {snippet}"
177
+ },
178
+ "echo_suffixes": {
179
+ "skip": "related-tasks: (none)",
180
+ "siblings": "related-tasks: {value} (siblings)"
121
181
  }
122
182
  },
123
183
  "clarification_pick": {
@@ -125,7 +185,14 @@
125
185
  "echo_template": "clarification(pick): {value}",
126
186
  "options": {
127
187
  "__skip__": "없음 (건너뛰기)",
128
- "__enter__": "있음 (입력)"
188
+ "__free_input__": "직접 입력"
189
+ },
190
+ "labels": {
191
+ "latest_report": "최근 final-report 사용: {snippet}"
192
+ },
193
+ "echo_suffixes": {
194
+ "skip": "clarification: (none)",
195
+ "latest_report": "clarification: {value} (재사용)"
129
196
  }
130
197
  },
131
198
  "pr_template_pick": {
@@ -133,7 +200,14 @@
133
200
  "echo_template": "pr-template(pick): {value}",
134
201
  "options": {
135
202
  "__skip__": "자동 해석 (project.json → config → 기본)",
136
- "__enter__": "직접 경로 입력 (1회성 override)"
203
+ "__free_input__": "직접 입력"
204
+ },
205
+ "labels": {
206
+ "project_default": "project.json 의 prTemplatePath 사용: {snippet}"
207
+ },
208
+ "echo_suffixes": {
209
+ "skip": "pr-template: (auto-resolve)",
210
+ "project_default": "pr-template: {value} (project default)"
137
211
  }
138
212
  },
139
213
  "executor": {
@@ -78,7 +78,7 @@ def slugify(value: str) -> str:
78
78
 
79
79
  candidates = []
80
80
 
81
- catalog_path = project_root / ".project-docs" / "okstra" / "discovery" / "task-catalog.json"
81
+ catalog_path = project_root / ".okstra" / "discovery" / "task-catalog.json"
82
82
  catalog_keys = []
83
83
  if catalog_path.is_file():
84
84
  try:
@@ -111,7 +111,7 @@ if catalog_path.is_file():
111
111
  sys.exit(0)
112
112
  candidates.append(str(abs_path))
113
113
 
114
- slug_path = project_root / ".project-docs" / "okstra" / "tasks" / slugify(task_group) / slugify(task_id)
114
+ slug_path = project_root / ".okstra" / "tasks" / slugify(task_group) / slugify(task_id)
115
115
  if slug_path.is_dir():
116
116
  print(f"OK\t{slug_path}")
117
117
  sys.exit(0)
@@ -1,7 +1,7 @@
1
1
  # shellcheck shell=bash
2
2
 
3
3
  # bash wrappers around scripts/okstra_project resolver. okstra.sh 가 시작 시
4
- # PROJECT_ROOT 를 확정하고 <PROJECT_ROOT>/.project-docs/okstra/project.json 을
4
+ # PROJECT_ROOT 를 확정하고 <PROJECT_ROOT>/.okstra/project.json 을
5
5
  # upsert 한다. 과거 conf 파일 모델은 폐기되었다.
6
6
 
7
7
  # 절대경로(또는 빈 문자열) 를 stdout 으로 출력한다. 실패 시 stderr 에 메시지를
@@ -16,7 +16,7 @@ summary:
16
16
 
17
17
  required arguments:
18
18
  --project-id Globally unique project ID. Example: sample-project-v2-api.
19
- Each project is registered at <project-root>/.project-docs/okstra/project.json
19
+ Each project is registered at <project-root>/.okstra/project.json
20
20
  on first run; subsequent runs verify the projectId there matches.
21
21
  --task-group Logical task group. Example: backend-api, bugfix, linear-8858
22
22
  --task-id Stable task identifier inside the task group. Example: login-error-analysis
@@ -25,7 +25,7 @@ required arguments:
25
25
 
26
26
  optional arguments:
27
27
  --project-root Absolute path to the target project root. Resolution order when omitted:
28
- (1) ancestor of cwd that contains .project-docs/okstra/project.json,
28
+ (1) ancestor of cwd that contains .okstra/project.json,
29
29
  (2) `git rev-parse --show-toplevel` from cwd. Errors out if neither resolves.
30
30
  --directive Free-form user-supplied directive carried into the run as a "## Directive" section
31
31
  inside instruction-set/analysis-material.md. Lead, workers, and skills (e.g. okstra-schedule)
@@ -107,15 +107,15 @@ model defaults:
107
107
 
108
108
  output:
109
109
  Stable task bundles are stored under:
110
- <target-project>/.project-docs/okstra/tasks/<task-group>/<task-id>/
110
+ <target-project>/.okstra/tasks/<task-group>/<task-id>/
111
111
  Per-run history is stored under:
112
- <target-project>/.project-docs/okstra/tasks/<task-group>/<task-id>/runs/
112
+ <target-project>/.okstra/tasks/<task-group>/<task-id>/runs/
113
113
  Inside each run date folder, artifacts are grouped by type under:
114
114
  manifests/, state/, prompts/, reports/, status/, sessions/, worker-results/
115
115
 
116
116
  project-level discovery:
117
117
  Latest $DISPLAY_TOOL_NAME task pointer:
118
- <target-project>/.project-docs/okstra/discovery/latest-task.json
118
+ <target-project>/.okstra/discovery/latest-task.json
119
119
 
120
120
  interactive behavior:
121
121
  If required arguments are missing and stdin is interactive, $DISPLAY_TOOL_NAME prompts for them.
@@ -216,7 +216,7 @@ for original in targets:
216
216
  slug_group = slugify_task_segment(row["taskGroup"])
217
217
  slug_task = slugify_task_segment(row["taskId"])
218
218
  slug_type = slugify_task_segment(row["taskType"])
219
- base_run = (".project-docs/okstra/tasks/"
219
+ base_run = (".okstra/tasks/"
220
220
  f"{slug_group}/{slug_task}/runs/{slug_type}")
221
221
  final_rel = (f"{base_run}/reports/"
222
222
  f"final-report-{slug_type}-{next_seq:03d}.md")
@@ -7,6 +7,8 @@ import re as _re
7
7
  from pathlib import Path
8
8
  from typing import List
9
9
 
10
+ from okstra_project.dirs import tasks_root
11
+
10
12
  from .ids import build_run_id
11
13
  from .invocation import invocation_path, save_invocation
12
14
  from .jsonl import append_jsonl, read_jsonl, rotate_recent_if_needed
@@ -19,7 +21,7 @@ def discover_project_roots(home: Path) -> List[tuple]:
19
21
  project_root) 목록을 반환한다.
20
22
 
21
23
  신규 모델에서는 okstra.sh 가 첫 실행 시 PROJECT_ROOT 를 해석해
22
- `<PROJECT_ROOT>/.project-docs/okstra/project.json` 에 자기 등록하고,
24
+ `<PROJECT_ROOT>/.okstra/project.json` 에 자기 등록하고,
23
25
  record_start 가 그 PROJECT_ROOT 를 meta.json 에 mirror 한다. ctl 입장
24
26
  에서는 한 번이라도 record_start 를 거친 프로젝트는 meta.json 에 등재
25
27
  되어 있으므로, examples/projects 같은 외부 등록 디렉토리가 필요하지
@@ -88,11 +90,11 @@ def _apply_backfill_meta(home: Path, project_id: str, project_root: Path, *,
88
90
 
89
91
 
90
92
  def backfill_project(home: Path, project_id: str, project_root: Path) -> int:
91
- """타깃 프로젝트의 .project-docs/okstra/tasks 스캔해 누락된 run 을 인덱스에 채움.
93
+ """타깃 프로젝트의 okstra tasks 디렉토리를 스캔해 누락된 run 을 인덱스에 채움.
92
94
  실제 okstra 레이아웃: runs/<task_type>/manifests/run-manifest-<task_type>-<seq:03d>.json
93
95
  이미 존재하는 runId 는 스킵. 새로 추가된 run 수를 반환.
94
96
  """
95
- base = project_root / ".project-docs" / "okstra" / "tasks"
97
+ base = tasks_root(project_root)
96
98
  if not base.is_dir():
97
99
  return 0
98
100
  existing_index = home / "projects" / project_id / "index.jsonl"