okstra 0.50.0 → 0.51.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 (57) hide show
  1. package/README.kr.md +8 -7
  2. package/README.md +8 -7
  3. package/bin/okstra +2 -0
  4. package/docs/kr/architecture.md +15 -16
  5. package/docs/kr/cli.md +5 -5
  6. package/docs/project-structure-overview.md +10 -6
  7. package/package.json +1 -1
  8. package/runtime/BUILD.json +2 -2
  9. package/runtime/agents/SKILL.md +15 -11
  10. package/runtime/agents/workers/claude-worker.md +3 -3
  11. package/runtime/agents/workers/codex-worker.md +2 -2
  12. package/runtime/agents/workers/gemini-worker.md +2 -2
  13. package/runtime/bin/lib/okstra/cli.sh +8 -1
  14. package/runtime/bin/lib/okstra/globals.sh +3 -0
  15. package/runtime/bin/lib/okstra/interactive.sh +14 -12
  16. package/runtime/bin/lib/okstra/usage.sh +6 -0
  17. package/runtime/bin/okstra-team-reconcile.sh +28 -0
  18. package/runtime/bin/okstra.sh +2 -0
  19. package/runtime/prompts/launch.template.md +3 -1
  20. package/runtime/prompts/profiles/_common-contract.md +4 -4
  21. package/runtime/prompts/profiles/_implementation-executor.md +2 -2
  22. package/runtime/prompts/profiles/_implementation-verifier.md +1 -1
  23. package/runtime/prompts/profiles/implementation-planning.md +1 -0
  24. package/runtime/prompts/profiles/implementation.md +1 -1
  25. package/runtime/python/okstra_ctl/analysis_packet.py +259 -0
  26. package/runtime/python/okstra_ctl/context_cost.py +308 -0
  27. package/runtime/python/okstra_ctl/migrate.py +2 -12
  28. package/runtime/python/okstra_ctl/paths.py +22 -0
  29. package/runtime/python/okstra_ctl/render.py +284 -125
  30. package/runtime/python/okstra_ctl/render_final_report.py +31 -0
  31. package/runtime/python/okstra_ctl/run.py +507 -245
  32. package/runtime/python/okstra_ctl/sequence.py +2 -5
  33. package/runtime/python/okstra_ctl/team_reconcile.py +131 -0
  34. package/runtime/python/okstra_ctl/wizard.py +129 -133
  35. package/runtime/python/okstra_ctl/worktree.py +13 -5
  36. package/runtime/schemas/final-report-v1.0.schema.json +4 -0
  37. package/runtime/skills/okstra-coding-preflight/SKILL.md +69 -0
  38. package/runtime/skills/okstra-coding-preflight/architecture/hexagonal.md +116 -0
  39. package/runtime/skills/okstra-coding-preflight/clean-code.md +254 -0
  40. package/runtime/skills/okstra-coding-preflight/languages/java.md +64 -0
  41. package/runtime/skills/okstra-coding-preflight/languages/javascript-typescript.md +87 -0
  42. package/runtime/skills/okstra-coding-preflight/languages/kotlin.md +69 -0
  43. package/runtime/skills/okstra-coding-preflight/languages/nodejs.md +66 -0
  44. package/runtime/skills/okstra-coding-preflight/languages/python.md +179 -0
  45. package/runtime/skills/okstra-coding-preflight/languages/rust.md +105 -0
  46. package/runtime/skills/okstra-coding-preflight/languages/sql.md +68 -0
  47. package/runtime/skills/okstra-context-loader/SKILL.md +12 -6
  48. package/runtime/skills/okstra-inspect/SKILL.md +100 -1
  49. package/runtime/skills/okstra-report-writer/SKILL.md +5 -1
  50. package/runtime/skills/okstra-run/SKILL.md +1 -1
  51. package/runtime/skills/okstra-team-contract/SKILL.md +7 -4
  52. package/runtime/templates/reports/final-report.template.md +1 -0
  53. package/runtime/templates/worker-prompt-preamble.md +3 -3
  54. package/src/_python-helper.mjs +3 -3
  55. package/src/context-cost.mjs +27 -0
  56. package/src/install.mjs +1 -0
  57. package/src/uninstall.mjs +1 -0
@@ -0,0 +1,179 @@
1
+ # Python Conventions
2
+
3
+ If a project-level Python style skill or repo-local `CONTRIBUTING.md` / `pyproject.toml` config exists, **it wins**. This file is the fallback.
4
+
5
+ ## Core principles
6
+
7
+ - Write simple, explicit, readable Python. Prefer boring, maintainable code over clever abstractions.
8
+ - Optimize for correctness first, then clarity, then performance.
9
+ - Keep functions small and single-responsibility. Prefer composition over inheritance.
10
+ - Type-hint all public functions, class methods, and non-trivial internal functions.
11
+ - Use dataclasses or Pydantic models for structured data — never loose `dict`s for domain data.
12
+ - Make side effects explicit. Fail fast with clear errors. No hidden global state.
13
+ - Code must be testable without network, filesystem, database, or time dependencies.
14
+
15
+ ## Python version
16
+
17
+ Target **Python 3.11+** unless told otherwise. Use modern syntax:
18
+
19
+ - `str | None`, not `Optional[str]`
20
+ - `list[str]` / `dict[str, int]`, not `List` / `Dict`
21
+ - `match` only when it improves clarity
22
+ - `pathlib.Path`, not string paths
23
+
24
+ ## Style guide
25
+
26
+ PEP 8, enforced by `ruff format` (or `black`).
27
+
28
+ - Indent: **4 spaces**. Line length: **88** (ruff/black default) unless the project sets otherwise.
29
+ - `snake_case` functions/variables, `PascalCase` classes, `SCREAMING_SNAKE_CASE` constants, leading `_` for private.
30
+
31
+ ## Function design
32
+
33
+ - One thing per function. Avoid bodies over ~40 lines without a strong reason.
34
+ - Explicit parameters over reading globals. Return values instead of mutating arguments.
35
+ - Avoid behavior-changing boolean flags — split into separate functions.
36
+ - Do not hide I/O inside functions that look pure. Do not catch broad exceptions unless re-raising with useful context.
37
+
38
+ Bad:
39
+
40
+ ```python
41
+ def process(data, save=True, notify=False):
42
+ ...
43
+ ```
44
+
45
+ Better:
46
+
47
+ ```python
48
+ def build_invoice(data: InvoiceInput) -> Invoice: ...
49
+ def save_invoice(invoice: Invoice) -> None: ...
50
+ def notify_invoice_created(invoice: Invoice) -> None: ...
51
+ ```
52
+
53
+ ## Typing
54
+
55
+ - Type hints everywhere meaningful. Avoid `Any` unless unavoidable; never silence a type error without a one-line why.
56
+ - No untyped dicts for domain data — use `dataclass`, `Enum`, `TypedDict`, or Pydantic.
57
+ - `Protocol` for dependency inversion.
58
+
59
+ Bad:
60
+
61
+ ```python
62
+ def create_user(data: dict): ...
63
+ ```
64
+
65
+ Better:
66
+
67
+ ```python
68
+ @dataclass(frozen=True)
69
+ class CreateUserCommand:
70
+ email: str
71
+ name: str
72
+
73
+ def create_user(command: CreateUserCommand) -> User: ...
74
+ ```
75
+
76
+ ## Error handling
77
+
78
+ - Specific exception types with useful context. Never swallow exceptions silently.
79
+ - Do not use exceptions for normal control flow.
80
+ - Convert infrastructure errors into application/domain errors at boundaries.
81
+ - Never expose internal stack traces or secrets to users.
82
+
83
+ Bad:
84
+
85
+ ```python
86
+ try:
87
+ send_email(user)
88
+ except Exception:
89
+ pass
90
+ ```
91
+
92
+ Better:
93
+
94
+ ```python
95
+ try:
96
+ send_email(user)
97
+ except EmailProviderError as exc:
98
+ raise NotificationFailedError(f"Failed to notify user_id={user.id}") from exc
99
+ ```
100
+
101
+ ## Async
102
+
103
+ - Use async only for real I/O concurrency. Never call sync-blocking I/O inside an async function.
104
+ - Always set timeouts on external calls. No fire-and-forget tasks without explicit lifecycle + error handling.
105
+
106
+ Bad:
107
+
108
+ ```python
109
+ async def fetch():
110
+ requests.get(url) # blocking call inside async
111
+ ```
112
+
113
+ Better:
114
+
115
+ ```python
116
+ async def fetch(client: httpx.AsyncClient, url: str) -> Response:
117
+ return await client.get(url, timeout=10)
118
+ ```
119
+
120
+ ## Project / module layout
121
+
122
+ Separate domain logic from infrastructure; keep business rules independent of frameworks, DBs, queues, and external APIs.
123
+
124
+ - `domain/` — entities, value objects, pure business logic
125
+ - `application/` — use cases, orchestration, commands, queries
126
+ - `infrastructure/` — DB, external APIs, filesystem, queues
127
+ - `interfaces/` — HTTP, CLI, workers, event handlers
128
+ - `tests/` — unit, integration, e2e
129
+
130
+ When the project is ports-and-adapters, also read [../architecture/hexagonal.md](../architecture/hexagonal.md).
131
+
132
+ ## Security
133
+
134
+ - Never hardcode secrets — read from env vars or a secret manager.
135
+ - Parameterized SQL only; never build SQL via string interpolation / f-strings.
136
+ - Validate all external input. Treat file paths, URLs, headers, and serialized input as untrusted.
137
+ - No unsafe deserialization (arbitrary `pickle.load`).
138
+
139
+ ## Database
140
+
141
+ - Explicit, reviewable SQL. Avoid N+1 queries. Wrap multi-step writes in transactions.
142
+ - Use repository interfaces (`Protocol`) when DB access should be decoupled from domain logic.
143
+ - Keep migrations backward-compatible when possible.
144
+
145
+ ## Logging
146
+
147
+ - Structured logging — never `print()` for application logs.
148
+ - Include IDs/context (request, user, job, entity). Never log secrets, tokens, passwords, cookies, API keys, or sensitive PII.
149
+
150
+ ## Tests
151
+
152
+ - Unit-test domain logic; integration-test DB, external-API wrappers, and framework wiring.
153
+ - Deterministic tests — no `sleep`, no execution-order dependence, no real external services unless marked integration/e2e.
154
+ - Mock only at boundaries. Use factories/builders for test data. Test behavior, not implementation.
155
+ - Every bug fix ships a regression test when feasible.
156
+
157
+ ### Self-mock signals to refuse (rule from `clean-code.md` → Testing discipline)
158
+
159
+ - `unittest.mock.patch`-ing a method **on the class under test**, then asserting that method was called. Mock the collaborator it depends on, not the unit it *is*.
160
+ - Splitting out a helper *only so* the test can patch it, then asserting `helper.assert_called_once()` as the real check — that proves the SUT calls the helper, not that the behavior works.
161
+ - Reaching into privates (`obj._internal`) to assert on internal state instead of observable outcomes.
162
+ - A `MagicMock` standing in for the SUT itself with a `return_value` that mirrors the very thing under test.
163
+
164
+ What's fine: mocking injected dependencies (`Mock(spec=UserRepository)`, `httpx.MockTransport`), asserting on return values / raised exceptions / emitted events / boundary calls.
165
+
166
+ ## Required tooling
167
+
168
+ Run before declaring a Python change complete:
169
+
170
+ - `ruff check .` — lint
171
+ - `ruff format --check .` — formatting (or `black --check .`)
172
+ - `mypy .` or `pyright` — type check (if configured)
173
+ - `pytest` — tests
174
+
175
+ Dependency/project management: `uv`, Poetry, or `pip-tools`. Pin dependencies in application projects; reach for the standard library before adding third-party deps, and avoid importing heavy deps at module-import time when unnecessary.
176
+
177
+ ## Forbidden anti-patterns
178
+
179
+ God classes/functions, hidden global mutable state, circular imports, catch-all `except Exception` without re-raise, silent failure, untyped domain dicts, business logic in controllers/routes or coupled to ORM models, hardcoded secrets, string-formatted SQL, unbounded retries, missing timeouts on external calls, fire-and-forget async without error handling, order-dependent tests, abstractions before two concrete use cases, magic constants without names, copy-pasted logic, comments that restate obvious code, leftover `print()` debugging.
@@ -0,0 +1,105 @@
1
+ # Rust Conventions
2
+
3
+ If a project-level `rust-guidelines` skill or repo-local `CONTRIBUTING.md` exists, **it wins**. This file is the fallback.
4
+
5
+ ## Style guide
6
+
7
+ Official Rust Style Guide (rustwiki.org/en/style-guide/), enforced by `rustfmt`. Set the edition in `rustfmt.toml`:
8
+
9
+ ```toml
10
+ style_edition = "2024"
11
+ ```
12
+
13
+ ## Naming (RFC 430)
14
+
15
+ | Element | Convention | Example |
16
+ |---|---|---|
17
+ | Crate / module | `snake_case` | `my_crate`, `parser` |
18
+ | Type / trait / enum variant | `UpperCamelCase` | `HttpClient`, `Error::Timeout` |
19
+ | Function / method / variable | `snake_case` | `send_request`, `retry_count` |
20
+ | Const / static | `SCREAMING_SNAKE_CASE` | `MAX_RETRIES` |
21
+ | Lifetime | short lowercase | `'a`, `'src` |
22
+ | Type parameter | single uppercase | `T`, `E` |
23
+
24
+ ## Formatting
25
+
26
+ - Indent: **4 spaces**.
27
+ - Max line length: **100** (`rustfmt` default).
28
+ - Always run `cargo fmt` before commit.
29
+
30
+ ## Ownership & borrowing
31
+
32
+ - Prefer borrowing (`&T`) over cloning. `.clone()` is a code smell unless you can name the reason in one sentence.
33
+ - Return owned types from constructors and "convert" methods; accept borrowed slices (`&str`, `&[T]`) as parameters.
34
+ - `&mut` only when you must mutate. Two `&` borrows are usually better than one `&mut`.
35
+ - `Cow<'_, str>` when a function sometimes allocates and sometimes does not.
36
+
37
+ ## Error handling
38
+
39
+ - **Library** crates: define a typed error with `thiserror`. One `Error` enum per crate, one variant per failure mode.
40
+ - **Application** crates: `anyhow::Result<T>` at the top level is fine; convert to typed errors at module boundaries.
41
+ - **No `unwrap()` / `expect()` in production paths.** Acceptable in:
42
+ - Tests.
43
+ - `main` for setup that genuinely cannot fail.
44
+ - `expect("invariant: ...")` when the message documents the invariant.
45
+ - Use the `?` operator. Do not write `match`-on-`Result` ladders.
46
+
47
+ ## Idioms
48
+
49
+ - Iterators (`map`, `filter`, `collect`, `fold`, `find`) over indexed loops.
50
+ - `if let` / `let else` over `match` with a single arm.
51
+ - `#[derive(Debug, Clone, ...)]` aggressively. Add `PartialEq` / `Eq` / `Hash` when the type lands in a collection.
52
+ - Newtype small primitives that carry meaning: `struct UserId(u64);`, not raw `u64`.
53
+ - `mut` and `pub` only when needed. Start private, widen on demand.
54
+ - Use `From` / `TryFrom` for conversions, not `parse_x_from_y` helpers.
55
+ - Pattern-match deeply (`if let Some(User { id, .. }) = user`) rather than chained `.unwrap().unwrap()`.
56
+
57
+ ## Async / Tokio
58
+
59
+ - Pick **one** runtime per binary (`tokio` is standard). Never mix `tokio` and `async-std`.
60
+ - Never `block_on` inside an async function.
61
+ - Spawn tasks deliberately; remember `JoinHandle`s and `await` them.
62
+ - `tokio::select!` for races; never poll futures by hand.
63
+ - Long CPU work goes in `tokio::task::spawn_blocking`.
64
+ - Cancellation: use `CancellationToken` (tokio-util) for cooperative shutdown.
65
+
66
+ ## Unsafe
67
+
68
+ - Every `unsafe` block needs a comment documenting **every** invariant the caller must uphold.
69
+ - New `unsafe` requires a second pair of eyes on review.
70
+ - Default to safe abstractions (`bytes`, `parking_lot`, `crossbeam`) before reaching for `unsafe`.
71
+
72
+ ## Module layout
73
+
74
+ - Public API at the crate root via `pub use`; implementation in private modules.
75
+ - One concept per file. Split a module before it passes ~500 lines.
76
+ - Unit tests in `mod tests { use super::*; ... }` at the bottom of the file.
77
+ - Integration tests in the `tests/` directory.
78
+
79
+ ## Tests
80
+
81
+ - Unit: `#[test]` inside `mod tests`. Use `assert_eq!`, `assert!`, `assert_matches!`.
82
+ - Async: `#[tokio::test]` or `#[test]` with an explicit `tokio::runtime::Runtime` when you need control.
83
+ - Property: `proptest` or `quickcheck` for pure transformations.
84
+ - **Doc tests**: runnable examples in doc comments are tests — keep them green.
85
+ - Use `pretty_assertions::assert_eq!` for readable diffs on large structs.
86
+
87
+ ### Self-mock signals to refuse (rule from `clean-code.md` → Testing discipline)
88
+
89
+ Rust's trait-based DI makes self-mocking rarer than in JVM/JS, but it still happens:
90
+
91
+ - Using `mockall::mock!` (or `automock`) to generate a mock of the **same struct under test**, then asserting on its own methods. The unit under test must be the real impl; mock the trait it depends on, not the trait it *is*.
92
+ - Splitting a behavior into a helper trait *only so* the test can stub it, then expecting `expect_helper().returning(...)` to be the real assertion. The test now proves the SUT calls the helper, not that the behavior works.
93
+ - Reaching into privates via `pub(crate)` widening, `#[cfg(test)] pub` shortcuts, or test-only modules that expose internal state to assert on.
94
+ - A test that constructs the SUT, replaces one of its trait-object dependencies with a mock whose `returning(...)` mirrors the very thing being tested.
95
+
96
+ What's fine: `mockall` mocks of injected trait dependencies (`MockHttpClient`, `MockUserRepository`), `unwrap()` in tests for setup that genuinely cannot fail, `assert_eq!` on returned values, `assert_matches!` on returned `Result` / `Option`.
97
+
98
+ ## Required tooling
99
+
100
+ Run before any commit:
101
+
102
+ - `cargo fmt --all` — formatting.
103
+ - `cargo clippy --all-targets --all-features -- -D warnings` — lint (warnings are errors).
104
+ - `cargo check --all-targets` — fast type check.
105
+ - `cargo test --all` — tests.
@@ -0,0 +1,68 @@
1
+ # SQL Conventions
2
+
3
+ Defaults below target PostgreSQL. MySQL-specific notes are marked.
4
+
5
+ ## Formatting / style
6
+
7
+ - Keywords **lowercase**: `select`, `from`, `where`, `join`. (Some teams prefer uppercase — match the file you are editing; never mix in one statement.)
8
+ - All identifiers `snake_case`: tables, columns, indexes, constraints.
9
+ - Always use explicit `as` for column aliases: `select count(*) as user_count`.
10
+ - Always state the join type: `inner join`, `left join`, `cross join`. **Never** a bare `join`.
11
+ - One column per line in `select`; one table or join per line in `from`.
12
+ - Prefer **CTEs** (`with foo as (...)`) over deeply nested subqueries.
13
+ - Dates / timestamps in ISO 8601: `'2026-05-17'`, `'2026-05-17T12:00:00Z'`.
14
+
15
+ ## Schema design
16
+
17
+ - Every table has an `id` primary key.
18
+ - PostgreSQL: `id bigint generated always as identity primary key`.
19
+ - MySQL: `id bigint unsigned not null auto_increment primary key`.
20
+ - Foreign keys named `<referenced_table>_id`: `user_id`, `order_id`.
21
+ - Singular table names (`user`, `order`, `invoice_line`) unless the project already uses plural — match, do not mix.
22
+ - `comment on table ... is '...'` for every table; `comment on column ...` for non-obvious columns. (MySQL: `comment '...'` inline.)
23
+ - Timestamps: `created_at`, `updated_at`, both `timestamptz` in Postgres. Keep timezone discipline explicit — store UTC, render in the user's zone at the edge.
24
+ - Soft delete: `deleted_at timestamptz null`. Index it if you query on it. Add a partial index `where deleted_at is null` for hot reads.
25
+ - Use `not null` aggressively. `null` should mean "unknown", not "default".
26
+ - Use enum or `check` constraints over free-text status columns.
27
+ - Composite indexes: column order matches your `where` / `order by` patterns; left-most prefix wins.
28
+
29
+ ## Migrations
30
+
31
+ - One change per migration file. Name: `<timestamp>_<verb>_<noun>.sql` (e.g. `20260517_120000_add_user_deleted_at.sql`).
32
+ - Migrations are **forward-only** in production. Write a "down" migration only if your tool requires it and you genuinely intend to run it.
33
+ - Splitting a destructive change:
34
+ 1. Add nullable column.
35
+ 2. Backfill (separate deploy).
36
+ 3. Add `not null` / index `concurrently`.
37
+ 4. Drop the old column.
38
+ - `create index concurrently` in Postgres for hot tables — avoids the write lock.
39
+ - Application-deploy migrations **never** destroy data. Destructive operations are a separate, explicit step gated on backups.
40
+
41
+ ## Query practices
42
+
43
+ - Always filter on indexed columns for OLTP queries. Verify with `explain` / `explain analyze`.
44
+ - Never `select *` in application code (migrations and ad-hoc inspection are fine).
45
+ - Parameterised queries from the application layer. String concatenation is SQL injection.
46
+ - `limit` every query that could return an unbounded set.
47
+ - `returning *` (Postgres) on `insert` / `update` / `delete` instead of a follow-up `select`.
48
+ - For pagination, prefer keyset (`where id > $last_seen`) over offset on large tables.
49
+
50
+ ## MySQL-specific
51
+
52
+ - Engine: **InnoDB** (default). Charset: `utf8mb4`, collation `utf8mb4_0900_ai_ci` (8.0+).
53
+ - Quote identifiers with backticks when they collide with reserved words.
54
+ - `select ... for update` only inside an explicit transaction.
55
+ - `on duplicate key update` for upserts; in Postgres use `on conflict (...) do update`.
56
+
57
+ ## Tests
58
+
59
+ - Run query tests against a **real** database via `testcontainers` or a disposable schema. In-memory SQLite is not equivalent to Postgres / MySQL — different SQL dialect, different isolation, different `null` semantics.
60
+ - Migrations themselves must be tested: apply from an empty schema in CI; assert the resulting structure.
61
+ - Seed test data via factory functions (one per table), not raw `insert` statements copy-pasted into each test.
62
+ - Verify performance assumptions with `explain` in CI for queries flagged as hot.
63
+
64
+ ## Tools
65
+
66
+ - Formatting: `pg_format`, `sqlfluff`, `sql-formatter`.
67
+ - Linting / safety: `squawk` (Postgres migration linter — catches missing `concurrently`, locking foot-guns, etc.).
68
+ - Schema diff: `migra` (Postgres), `mysqldiff` (MySQL).
@@ -56,7 +56,7 @@ user-invocable: false
56
56
  | `workflow.awaitingApproval` | Approval wait marker |
57
57
  | `workflow.routingStatus` | Routing decision status |
58
58
  | `workflow.lastSafeCheckpoint` | Safe resume checkpoint metadata |
59
- | `instructionSetPath` | Path to the `instruction-set/` **directory** containing `analysis-profile.md`, `analysis-material.md`, `reference-expectations.md`, `task-brief.md`, `final-report-template.md` (see Step 4). Not a single-file path. |
59
+ | `instructionSetPath` | Path to the `instruction-set/` **directory** containing `analysis-packet.md`, `analysis-profile.md`, `analysis-material.md`, `reference-expectations.md`, `task-brief.md`, `final-report-template.md` (see Step 4). Not a single-file path. |
60
60
  | `referenceExpectationsPath` | config/deployment expectation artifact path |
61
61
  | `latestRunPath` | latest run path |
62
62
  | `latestRunStatus` | latest run status |
@@ -78,6 +78,7 @@ After identifying the task root in `task-manifest.json`, derive all paths accord
78
78
  ├── task-index.md (human-readable summary, non-canonical)
79
79
  ├── instruction-set/
80
80
  │ ├── analysis-profile.md (analysis guide by task type)
81
+ │ ├── analysis-packet.md (primary compact input for analysis workers)
81
82
  │ ├── analysis-material.md (analysis materials)
82
83
  │ ├── reference-expectations.md (config/deployment expected values)
83
84
  │ ├── task-brief.md (task brief)
@@ -116,13 +117,18 @@ After identifying the task root in `task-manifest.json`, derive all paths accord
116
117
 
117
118
  ## Step 4: Instruction Set Reading Order
118
119
 
119
- After verifying `task-manifest.json`, read the instruction set in the following order:
120
+ After verifying `task-manifest.json`, read only the compact intake files needed for the current action. Do not bulk-read the whole instruction-set directory.
120
121
 
121
122
  1. `instruction-set/analysis-profile.md` (analysis guide by task type)
122
- 2. `instruction-set/analysis-material.md` (analysis materials, if available)
123
- 3. `instruction-set/reference-expectations.md` (config/deployment expected values)
124
- 4. `instruction-set/task-brief.md` (task brief)
125
- 5. `instruction-set/final-report-template.md` (report template)
123
+ 2. `instruction-set/analysis-packet.md` (primary compact input for analysis workers)
124
+ 3. `runs/<task-type>/state/active-run-context-<task-type>-<seq>.json` if present (compact current-run path/worker snapshot)
125
+
126
+ Read source files lazily:
127
+
128
+ - `instruction-set/task-brief.md` only for reporter-confirmation checks, source verification, or report-writer synthesis.
129
+ - `instruction-set/analysis-material.md` only when packet content is insufficient or a source citation needs verification.
130
+ - `instruction-set/reference-expectations.md` for report-writer synthesis or when packet expectation extract is insufficient.
131
+ - `instruction-set/final-report-template.md` only for report-writer authoring.
126
132
 
127
133
  ### Brief Reporter-Confirmation Precondition (BLOCKING)
128
134
 
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: okstra-inspect
3
3
  description: |
4
- Use for any read-side okstra inspection or status mutation. Single skill dispatches by sub-command to five facets — status, history, report, time, logs. Trigger words include "okstra status", "task status", "current phase", "next phase", "okstra status set", "okstra mark", "<task-id> done|in-progress|진행중|완료", "okstra history", "past runs", "re-run", "resume", "list tasks", "find report", "show report for", "read the okstra report", "continue from report", "작업 시간", "소요 시간", "time summary", "duration", "elapsed", "얼마나 걸렸", "시간 분석", "okstra logs", "로그 현황", "로그 파일", "log files", "log size", "log status", "로그 정리", "log cleanup".
4
+ Use for any read-side okstra inspection or status mutation. Single skill dispatches by sub-command to six facets — status, history, report, time, logs, cost. Trigger words include "okstra status", "task status", "current phase", "next phase", "okstra status set", "okstra mark", "<task-id> done|in-progress|진행중|완료", "okstra history", "past runs", "re-run", "resume", "list tasks", "find report", "show report for", "read the okstra report", "continue from report", "작업 시간", "소요 시간", "time summary", "duration", "elapsed", "얼마나 걸렸", "시간 분석", "okstra logs", "로그 현황", "로그 파일", "log size", "log status", "로그 정리", "log cleanup", "okstra context-cost", "context cost", "context-cost", "컨텍스트 비용", "읽기 비용", "산출물 비용".
5
5
  ---
6
6
 
7
7
  # OKSTRA Inspect
@@ -15,6 +15,7 @@ Single read-side entry point for okstra runtime inspection plus the one write-si
15
15
  | `report` | Resolve final-report path for a task-key. Optionally read it. |
16
16
  | `time` | Per-task-type and per-worker duration breakdown for a task. |
17
17
  | `logs` | Inventory codex/gemini wrapper `.log` sidecars; emit cleanup commands. |
18
+ | `cost` | Estimate file/read context cost for a task bundle. |
18
19
 
19
20
  ## Step 0: Verify okstra runtime + project setup (shared)
20
21
 
@@ -467,6 +468,104 @@ Never write `claude (claude)` — the parenthesized agent is shown only when it
467
468
 
468
469
  ---
469
470
 
471
+ ## cost
472
+
473
+ Trigger phrases: "okstra context-cost", "context cost", "context-cost", "컨텍스트 비용", "읽기 비용", "산출물 비용", "task bundle cost", "agent read cost".
474
+
475
+ Read-only estimate of how much file/context surface a prepared task bundle asks the lead, analysis workers, and report-writer to absorb. This sub-command does **not** mutate task artifacts.
476
+
477
+ ### cost.1 — Resolve target
478
+
479
+ Accepted target forms:
480
+
481
+ 1. Full task-key: `<project-id>:<task-group>:<task-id>`.
482
+ 2. Task id only, e.g. `DEV-9184`.
483
+ 3. Task root path, e.g. `<projectRoot>/.okstra/tasks/<group>/<task-id>`.
484
+
485
+ If the user gives a task root path, run `okstra context-cost <absolute-or-user-provided-path>` directly.
486
+
487
+ If the user gives a full task-key, run:
488
+
489
+ ```bash
490
+ okstra context-cost <task-key> --project-root <projectRoot>
491
+ ```
492
+
493
+ If the user gives only a task id:
494
+
495
+ 1. Read `.okstra/discovery/task-catalog.json`.
496
+ 2. Match `taskId` case-insensitively.
497
+ 3. Single match → use its `taskKey`.
498
+ 4. Multiple matches → list candidates and ask the user to retry with a full task-key.
499
+ 5. No match → report that the task cannot be found.
500
+
501
+ If the user asks generally ("컨텍스트 비용 보여줘") and does not name a task:
502
+
503
+ 1. Read `.okstra/discovery/task-catalog.json`.
504
+ 2. If exactly one task exists, use it.
505
+ 3. If multiple tasks exist, show the latest 10 by `updatedAt` and ask which task to measure. Do not guess.
506
+
507
+ ### cost.2 — Run estimator
508
+
509
+ Use the CLI output as the source of truth:
510
+
511
+ ```bash
512
+ okstra context-cost <resolved-target> --project-root <projectRoot>
513
+ ```
514
+
515
+ Do not re-count files manually unless the CLI fails and the user explicitly asks for manual fallback.
516
+
517
+ ### cost.3 — Summarize output
518
+
519
+ Parse the JSON and report these fields:
520
+
521
+ | Field | Source |
522
+ |---|---|
523
+ | Task bundle | `totals.taskFileCount`, `totals.taskBytes` |
524
+ | Current run | `totals.currentRunFileCount`, `totals.currentRunBytes`, `currentRunPath` |
525
+ | Legacy timestamp artifacts | `totals.legacyTimestampFileCount` |
526
+ | Instruction set | `instructionSet.fileCount`, `instructionSet.bytes`, `instructionSet.analysisPacketBytes`, `instructionSet.legacyTaskPacketBytes` |
527
+ | Lead Phase 1 | `leadPhase1.mode`, `leadPhase1.fileCount`, `leadPhase1.bytes` |
528
+ | Analysis worker | `analysisWorker.mode`, `analysisWorker.fileCount`, `analysisWorker.bytesPerWorker`, `analysisWorker.legacyFullContractBytesPerWorker`, `analysisWorker.estimatedPacketModeBytesPerWorker`, `analysisWorker.estimatedReductionPercent` |
529
+ | Report writer | `reportWriter.fileCount`, `reportWriter.bytes` |
530
+
531
+ Format bytes as both raw bytes and rounded KB/MB where useful. Use `analysisWorker.estimatedReductionPercent` for the worker-input reduction. Do not recompute it from `bytesPerWorker` when `analysisWorker.mode == "analysis-packet-primary"` because `bytesPerWorker` is already the packet-primary cost.
532
+
533
+ ### cost.4 — Output template
534
+
535
+ ```markdown
536
+ ## okstra Context Cost — <task-key>
537
+
538
+ | Surface | Files | Size |
539
+ |---|---:|---:|
540
+ | Task bundle | <N> | <bytes> (<human>) |
541
+ | Current run | <N> | <bytes> (<human>) |
542
+ | Instruction set | <N> | <bytes> (<human>) |
543
+ | Lead Phase 1 (`<mode>`) | <N> | <bytes> (<human>) |
544
+ | Analysis worker / worker (`<mode>`) | <N> | <bytes> (<human>) |
545
+ | Report writer synthesis | <N> | <bytes> (<human>) |
546
+
547
+ - Current run: `<currentRunPath-or-->`
548
+ - Legacy timestamp artifacts: `<N>`
549
+ - Legacy full worker contract: `<legacyFullContractBytesPerWorker>` bytes (`<human>`) per analysis worker
550
+ - Packet estimate: `<estimatedPacketModeBytesPerWorker>` bytes (`<human>`) per analysis worker
551
+ - Estimated worker-input reduction: `<percent>%`
552
+
553
+ ### Reading
554
+
555
+ <One or two Korean sentences explaining the main bottleneck and the next likely optimization target.>
556
+ ```
557
+
558
+ Interpretation rules:
559
+
560
+ - `leadPhase1.mode == "active-run-context"` means the compact lead intake file is present and should be treated as the primary lead read surface.
561
+ - `leadPhase1.mode == "legacy-five-file"` means this task was prepared before active-run-context, or the manifest does not reference it.
562
+ - `analysisWorker.mode == "analysis-packet-primary"` means new workers should read `analysis-packet.md` first and open full source inputs only for evidence checks or missing detail.
563
+ - If `analysisWorker.mode == "full-input-contract"` and `estimatedReductionPercent` is low, the next target is worker prompt/input contract slimming.
564
+ - If `reportWriter.bytes` dominates, the next target is a compact `synthesis-input` artifact.
565
+ - If `legacyTimestampFileCount` is high, recommend current-view/cold-artifact separation or retention cleanup, not destructive deletion by default.
566
+
567
+ ---
568
+
470
569
  ## logs
471
570
 
472
571
  Trigger phrases: "okstra logs", "로그 현황", "로그 파일", "log files", "log size", "log status", "로그 정리", "log cleanup".
@@ -12,6 +12,8 @@ The final-report **data.json** (JSON SSOT) at `runs/<task-type>/reports/final-re
12
12
 
13
13
  The data.json schema is `schemas/final-report-v1.0.schema.json`. The renderer + the run-validator both consume that schema, so a data.json that validates is guaranteed to render into a markdown that passes the contract checks.
14
14
 
15
+ Two `frontmatter` approval fields are always emitted with their unset default — never pre-fill them: `frontmatter.approved` is emitted as `false`, and `frontmatter.implementationOption` is emitted as an empty string `""`. The user later flips `approved` to `true` (via `--approve` or manual edit) and fills `implementationOption` with the chosen Option Candidate name (via `--implementation-option <name>` or manual edit) to authorise and scope the next `implementation` run.
16
+
15
17
  If you are reading this skill **as the report-writer-worker subagent**, YOU are the one calling the `Write` tool against the data.json path AND invoking the renderer via `Bash`. Do not return either artifact inline — the files on disk are the canonical record.
16
18
 
17
19
  If you are reading this skill **as Claude lead**, your job in Phase 6 is to (a) prepare the report-writer prompt, (b) dispatch the Report writer worker per the Phase 6 dispatch template in SKILL.md, (c) review both files in Phase 7. Do not call `Write` against either path yourself when Report writer worker is in the roster.
@@ -33,11 +35,13 @@ Agent(
33
35
  name: "report-writer",
34
36
  subagent_type: "report-writer-worker",
35
37
  team_name: "okstra-<task-key>", # omit if team is not alive — see Resume-safe dispatch
36
- model: "opus",
38
+ model: "<family token of Report writer worker's modelExecutionValue>", # opus/sonnet/haiku — NOT hardcoded; see below
37
39
  mode: "auto"
38
40
  )
39
41
  ```
40
42
 
43
+ The `model:` parameter is **derived from the Report writer worker's `modelExecutionValue`** in `task-manifest.json`, mapped to an Agent family token (`opus` / `sonnet` / `haiku`) per [okstra-team-contract](../okstra-team-contract/SKILL.md) "Model Assignment Rules" #3–#4. Do NOT hardcode it — the report-writer-worker definition is `model: inherit`, so without this explicit parameter the worker silently runs on the lead's model instead of its assignment. The same `modelExecutionValue` feeds the prompt header in item 6 below, so the spawn model and the recorded `**Model:**` header always agree.
44
+
41
45
  The prompt MUST include, in this order at the top:
42
46
 
43
47
  1. `**Project Root:** <absolute-path>`
@@ -186,7 +186,7 @@ You can delete the literal state-file path after this point — its job is done.
186
186
 
187
187
  ## Step 6: Take over as Claude lead
188
188
 
189
- Read `<INSTRUCTION_SET_PATH>/claude-execution-prompt.md` verbatim and enter `Claude lead` mode. The lead prompt itself enumerates every other instruction-set file to load (`analysis-profile.md`, `analysis-material.md`, `reference-expectations.md`, `final-report-template.md`, the run manifest, the team-state artifact, etc.) — follow its order, do not preempt it.
189
+ Read `<INSTRUCTION_SET_PATH>/claude-execution-prompt.md` verbatim and enter `Claude lead` mode. The lead prompt now points to compact intake artifacts first (`active-run-context`, `analysis-profile.md`, and `analysis-packet.md`); full source files such as `analysis-material.md`, `reference-expectations.md`, and `final-report-template.md` are lazy/fallback inputs. Follow the rendered prompt order, do not preempt it.
190
190
 
191
191
  Then proceed through the phases exactly as the lead prompt directs (Phase 1 context → Phase 2+ worker dispatch → final synthesis → final report).
192
192
 
@@ -37,6 +37,9 @@ okstra tasks are always operated using the `Claude lead` + required worker team
37
37
 
38
38
  1. `resultContract.requiredWorkerRoles` in `task-manifest.json` (and the lead model metadata) is the canonical source. There is no role-level fallback — a missing assignment is a manifest defect, not a license to invent one.
39
39
  2. If `modelExecutionValue` differs from `model`, use `modelExecutionValue` during execution.
40
+ 3. **Spawn-time enforcement for in-process Claude subagents (BLOCKING).** `Claude worker` and `Report writer worker` are in-process Claude subagents whose agent definitions declare `model: inherit` (`agents/workers/claude-worker.md`, `agents/workers/report-writer-worker.md`). `inherit` follows the **lead's** runtime model, NOT the role's assignment — so an opus assignment silently runs on a sonnet lead. To make the assignment binding (not merely declared), lead MUST pass an explicit `model:` parameter on every `Agent(...)` dispatch for these two roles, derived from that role's `modelExecutionValue`. The dispatch `model:` parameter overrides the `inherit` frontmatter; the frontmatter remains only as the fallback when no parameter is supplied. Omitting `model:` on a Claude-side dispatch is a contract violation that reproduces the assigned-vs-actual model deviation.
41
+ 4. **`modelExecutionValue` → Agent `model:` family token.** The Agent tool's `model` parameter accepts family tokens only — `opus` / `sonnet` / `haiku` (an exact version such as `claude-opus-4-7` is NOT a valid value). Map by prefix: a `modelExecutionValue` of `opus*` / `claude-opus*` → `"opus"`, `sonnet*` / `claude-sonnet*` → `"sonnet"`, `haiku*` / `claude-haiku*` → `"haiku"`. This enforces the assignment at **family granularity** (opus vs sonnet vs haiku); the exact version within a family is still inherited from the lead session and cannot be pinned via this parameter.
42
+ 5. **Codex / Gemini wrappers are out of scope for the Agent `model:` rule.** `Codex worker` / `Gemini worker` subagents are Claude wrappers that shell out to an external CLI; the role's `modelExecutionValue` is already applied via the CLI's own `--model <modelExecutionValue>` argument (see `agents/workers/_cli-wrapper-template.md`). The Agent `model:` parameter for these wrappers would only set the wrapper's own orchestration model, not the external CLI's model — leave it at `inherit` and do NOT map it from `modelExecutionValue`.
40
43
 
41
44
  ### Dynamic Worker Role Determination
42
45
 
@@ -91,7 +94,7 @@ Send byte-identical dispatch prompts to every analysis worker (Claude / Codex /
91
94
  The lead does NOT inline `[Required reading]` or `[Error reporting]` blocks into worker prompts. Both contracts live in a single canonical file at `~/.okstra/templates/worker-prompt-preamble.md` (source: `templates/worker-prompt-preamble.md`). The lead injects the path via the `**Worker Preamble Path:**` anchor header (header #5 above) and each worker Reads that file end-to-end before producing output.
92
95
 
93
96
  What the lead MUST still do per dispatch:
94
- - Inject the input file enumeration into the dispatch prompt body via an `## Inputs` section (or any heading the recipient agent expects), listing the actual project-relative paths derived from the run's `instruction-set/` and the carry-in clarification response if any. The preamble describes the rules; the lead provides the specific paths for THIS run.
97
+ - Inject the input file enumeration into the dispatch prompt body via an `## Inputs` section (or any heading the recipient agent expects), listing the actual project-relative primary inputs derived from the run's `instruction-set/`. For analysis workers, list `analysis-packet.md` as the required primary input and list task-brief / analysis-profile / analysis-material / reference-expectations / clarification-response as source/fallback paths only when useful. The preamble describes the rules; the lead provides the specific paths for THIS run.
95
98
  - Inject the absolute `**Errors log path:**` and `**Errors sidecar path:**` headers (#6 and #7 above) — workers cannot synthesize these paths.
96
99
  - Omit the preamble pointer for reverify dispatches (Phase 5.5 lightweight mode) — see [okstra-convergence](../okstra-convergence/SKILL.md) "Reverify prompt: required-reading suppression".
97
100
 
@@ -99,8 +102,8 @@ Audience-scoped file enumeration (BLOCKING — performance optimization):
99
102
 
100
103
  | Recipient | Files the lead lists under `## Inputs` |
101
104
  |---|---|
102
- | Claude / Codex / Gemini analysis workers | task-brief, analysis-profile, analysis-material (if present), reference-expectations, clarification-response (if carry-in) |
103
- | Report writer worker (Phase 6) | all of the above **plus** the instruction-set-local `final-report-template.md` (phase-stripped) and `final-report-schema.json` (per-task-type excerpt) — NOT the full `templates/reports/...` / `schemas/...` sources |
105
+ | Claude / Codex / Gemini analysis workers | `analysis-packet.md` as primary input; source/fallback paths may be listed below it but are not automatic first-read files |
106
+ | Report writer worker (Phase 6) | task-brief, analysis-profile, analysis-material, reference-expectations, clarification-response (if carry-in), **plus** the instruction-set-local `final-report-template.md` (phase-stripped) and `final-report-schema.json` (per-task-type excerpt) — NOT the full `templates/reports/...` / `schemas/...` sources |
104
107
  | Reverify dispatches | none — the lead provides only the items to reverify |
105
108
 
106
109
  Asymmetry note: `claude-worker` runs in-process and the Agent SDK auto-loads its agent definition; lead's dispatch prompt body for claude-worker can therefore be shorter than for codex/gemini. The Worker Preamble pointer is still emitted for all three so the contract source is identical regardless of dispatch path.
@@ -147,7 +150,7 @@ After each worker subagent returns (regardless of role), Lead MUST verify the ca
147
150
 
148
151
  ### Result Frontmatter (mandatory, precedes Section 1)
149
152
 
150
- Every worker result file MUST begin with a YAML frontmatter block. The values are sourced from the corresponding fields of the input files' frontmatter (e.g. `analysis-material.md`, `task-brief.md`) copy them verbatim; do NOT regenerate them. Only `workerId` and `title` are worker-specific.
153
+ Every worker result file MUST begin with a YAML frontmatter block. For analysis workers, values are sourced from `analysis-packet.md` frontmatter; fall back to `analysis-material.md` or `task-brief.md` only if the packet is missing a field. Report-writer can use `analysis-material.md` / `task-brief.md` as before. Copy values verbatim; do NOT regenerate them. Only `workerId` and `title` are worker-specific.
151
154
 
152
155
  ```yaml
153
156
  ---
@@ -18,6 +18,7 @@ project-id: {{ frontmatter.projectId | yaml_scalar }}
18
18
  taskType: {{ frontmatter.taskType | yaml_scalar }}
19
19
  workerId: {{ frontmatter.workerId | yaml_scalar }}
20
20
  approved: {{ frontmatter.approved | yaml_scalar }}
21
+ implementation-option: {{ frontmatter.implementationOption | yaml_scalar }}
21
22
  ---
22
23
 
23
24
  # {{ header.taskKey }} - Multi-Agent Cross Verification Final Report
@@ -8,7 +8,7 @@ It replaces the previous practice of inlining ~80 lines of identical boilerplate
8
8
 
9
9
  ## Required reading (analysis workers + report-writer worker)
10
10
 
11
- You are required to read every input file enumerated by the dispatcher (the lead's prompt lists them under `[Required reading]`) from the very first character to the very last character before you produce any analysis output. Skimming, partial reads, jumping to a single section, or relying on prior knowledge of a similar file's structure is not acceptable. Each file may contain decisive context that is not surfaced in its summary or first page.
11
+ You are required to read every primary input file enumerated by the dispatcher (the lead's prompt lists them under `[Required reading]`) from the very first character to the very last character before you produce any analysis output. Skimming, partial reads, jumping to a single section, or relying on prior knowledge of a similar file's structure is not acceptable. Source files listed as fallback/evidence paths are read on demand when you need to verify a citation, resolve ambiguity, or inspect material the packet says it omitted.
12
12
 
13
13
  ### Audience-scoped enumeration (BLOCKING — performance optimization)
14
14
 
@@ -16,7 +16,7 @@ Different recipients need different files. Do NOT include `final-report-template
16
16
 
17
17
  | Recipient | Files included in `[Required reading]` |
18
18
  |---|---|
19
- | Claude / Codex / Gemini analysis workers | task-brief, analysis-profile, analysis-material (if present), reference-expectations, clarification-response (if carry-in) |
19
+ | Claude / Codex / Gemini analysis workers | analysis-packet.md as the primary compact input; task-brief, analysis-profile, analysis-material, reference-expectations, and clarification-response remain source/fallback paths, not automatic first-read files |
20
20
  | Report writer worker (Phase 6) | all of the above **plus** the instruction-set-local `final-report-template.md` (phase-stripped) and `final-report-schema.json` (per-task-type excerpt) — NOT the full `templates/reports/...` / `schemas/...` sources |
21
21
  | Reverify dispatches (Phase 5.5, lightweight mode) | **do NOT inject `[Required reading]` at all** — see [okstra-convergence](../skills/okstra-convergence/SKILL.md) "Reverify prompt: required-reading suppression". |
22
22
 
@@ -25,7 +25,7 @@ Different recipients need different files. Do NOT include `final-report-template
25
25
  - Use a single `Read` tool call per file with no `offset` and no `limit`. If a file is genuinely too large for one read, page through with explicit `offset` / `limit` covering the entire file, and state the page boundaries in your Findings.
26
26
  - For the carry-in clarification response, walk every row of `## 1. Clarification Items` (`C-001`, `C-002`, ...) in full, including rows whose `User input` cell is blank — a blank `User input` with `Status=open` is itself a signal you must surface. The structural similarity between the prior final report and the upcoming output is NOT a license to skim.
27
27
  - Write the Reading Confirmation block to your **audit sidecar** at `runs/<task-type>/worker-results/<worker>-audit-<task-type>-<seq>.md` (sibling to the main worker-results file). One short line per input file confirming end-to-end reading. Do NOT include a `## 0. Reading Confirmation` heading in the main worker-results file — the validator fails worker-results that contain one. If you cannot truthfully confirm a file end-to-end, record a `tool-failure` in the errors sidecar instead of fabricating Findings.
28
- - Do not collapse multiple input files into a single mental summary before reading them all individually. Each file has its own canonical role: brief = the user's request, profile = the lead's rules for this phase, reference-expectations = ground-truth config/deployment values, clarification-response = prior run's open questions and the user's answers, final-report-template = the structure your eventual writeup must conform to. Conflating them loses signal.
28
+ - Treat `analysis-packet.md` as the canonical primary analysis input. It preserves the source files' frontmatter and extracts the task-specific brief, phase focus, reference expectations, and carry-in clarification rows. If the packet appears incomplete or a finding depends on a source citation, open the corresponding source file and cite it directly.
29
29
 
30
30
  ---
31
31