okstra 0.102.0 → 0.102.1

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.
@@ -0,0 +1,525 @@
1
+ # okstra-container-build Rename Implementation Plan
2
+
3
+ > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
4
+
5
+ **Goal:** Rename the public okstra container skill from `okstra-container` to `okstra-container-build` without changing the `okstra container` CLI or container runtime trace names.
6
+
7
+ **Architecture:** The public skill surface remains driven by `src/lib/skill-catalog.mjs` and `.claude-plugin/plugin.json`. Install copy/link mode reads skill directories by exact `USER_SKILL_NAMES`, so the source directory and frontmatter must move together. Upgrade cleanup uses an exact obsolete-name allowlist so old `/okstra-container` directories are pruned without wildcard deletion.
8
+
9
+ **Tech Stack:** Node.js ES modules, Python pytest contract tests, Markdown skill/docs sources, `tools/build.mjs` runtime sync.
10
+
11
+ ## Global Constraints
12
+
13
+ - Public skill name becomes exactly `okstra-container-build`.
14
+ - Old public skill name `okstra-container` is removed from installed skill manifests and pruned from agent skill homes.
15
+ - CLI command remains exactly `okstra container <up|status|logs|stop-watcher|down>`.
16
+ - Runtime tmux/docker trace prefix remains exactly `okstra-container-<slug>`.
17
+ - Do not wildcard-delete `okstra-*`; cleanup must use exact names only.
18
+ - Do not hand-edit `runtime/`; run `npm run build` after source edits.
19
+ - Preserve historical `CHANGES.md` / `CHANGELOG.md` entries except adding a new current entry.
20
+
21
+ ---
22
+
23
+ ## File Structure
24
+
25
+ - `skills/okstra-container-build/SKILL.md`: renamed public skill source. Keeps `okstra container ...` command examples.
26
+ - `src/lib/skill-catalog.mjs`: public/obsolete skill name SSOT.
27
+ - `.claude-plugin/plugin.json`: secondary skills channel manifest.
28
+ - `src/commands/lifecycle/install.mjs`: install copy/link exact-name prune path.
29
+ - `src/commands/lifecycle/uninstall.mjs`: legacy fallback cleanup names.
30
+ - `tests-js/skill-catalog.test.mjs`: catalog/plugin/obsolete-name contract.
31
+ - `tests-js/install-runtime.test.mjs`: install target manifest plus old skill prune regression.
32
+ - `tests-js/uninstall-runtime.test.mjs`: manifest-missing fallback cleanup regression.
33
+ - `tests/contract/test_docs_runtime_contract.py`: plugin manifest contract mirror.
34
+ - `README.md`, `README.kr.md`, `docs/kr/architecture.md`, `docs/project-structure-overview.md`, `docs/kr/container.md`: live user/internal docs.
35
+ - `docs/for-ai/README.md`, `docs/for-ai/skills/okstra-container-build.md`, `docs/for-ai/skills/okstra-inspect.md`: AI routing manuals.
36
+ - `prompts/profiles/_implementation-deliverable.md`, `prompts/profiles/final-verification.md`: live phase prompt references.
37
+ - `docs/superpowers/specs/2026-06-24-manual-user-test-section-design.md`, `docs/superpowers/plans/2026-06-24-manual-user-test-section.md`: active manual-test design/plan references.
38
+ - `CHANGES.md`: current user-visible change entry.
39
+
40
+ ### Task 1: Public Skill Contract Rename
41
+
42
+ **Files:**
43
+ - Move: `skills/okstra-container` -> `skills/okstra-container-build`
44
+ - Modify: `skills/okstra-container-build/SKILL.md`
45
+ - Modify: `src/lib/skill-catalog.mjs`
46
+ - Modify: `.claude-plugin/plugin.json`
47
+ - Modify: `src/commands/lifecycle/install.mjs`
48
+ - Modify: `tests-js/skill-catalog.test.mjs`
49
+ - Modify: `tests/contract/test_docs_runtime_contract.py`
50
+
51
+ **Interfaces:**
52
+ - Produces: `USER_SKILL_NAMES` contains `okstra-container-build`, not `okstra-container`.
53
+ - Produces: `OBSOLETE_SKILL_NAMES` contains old `okstra-container`.
54
+ - Produces: plugin manifest entry `./skills/okstra-container-build`.
55
+
56
+ - [ ] **Step 1: Write failing catalog contract tests**
57
+
58
+ Update `tests-js/skill-catalog.test.mjs` import and tests to this shape:
59
+
60
+ ```js
61
+ import assert from "node:assert/strict";
62
+ import { readFileSync } from "node:fs";
63
+ import test from "node:test";
64
+
65
+ import {
66
+ OBSOLETE_SKILL_NAMES,
67
+ USER_SKILL_NAMES,
68
+ } from "../src/lib/skill-catalog.mjs";
69
+
70
+ function readJson(path) {
71
+ return JSON.parse(readFileSync(path, "utf8"));
72
+ }
73
+
74
+ test("public skill catalog matches Claude plugin manifest", () => {
75
+ const plugin = readJson(".claude-plugin/plugin.json");
76
+ const pluginNames = plugin.skills.map((entry) => entry.replace("./skills/", ""));
77
+ assert.deepEqual(pluginNames, [...USER_SKILL_NAMES]);
78
+ });
79
+
80
+ test("obsolete skill names do not overlap public skills", () => {
81
+ for (const name of OBSOLETE_SKILL_NAMES) {
82
+ assert.equal(USER_SKILL_NAMES.includes(name), false, name);
83
+ }
84
+ });
85
+
86
+ test("container skill rename keeps old public name prunable", () => {
87
+ assert.equal(USER_SKILL_NAMES.includes("okstra-container-build"), true);
88
+ assert.equal(USER_SKILL_NAMES.includes("okstra-container"), false);
89
+ assert.equal(OBSOLETE_SKILL_NAMES.includes("okstra-container"), true);
90
+ });
91
+ ```
92
+
93
+ Update `tests/contract/test_docs_runtime_contract.py` public skill list:
94
+
95
+ ```python
96
+ USER_FACING_PLUGIN_SKILLS = [
97
+ "./skills/okstra-setup",
98
+ "./skills/okstra-brief",
99
+ "./skills/okstra-run",
100
+ "./skills/okstra-memory",
101
+ "./skills/okstra-inspect",
102
+ "./skills/okstra-schedule",
103
+ "./skills/okstra-container-build",
104
+ ]
105
+ ```
106
+
107
+ - [ ] **Step 2: Run failing contract tests**
108
+
109
+ Run:
110
+
111
+ ```bash
112
+ node --test tests-js/skill-catalog.test.mjs
113
+ python3 -m pytest tests/contract/test_docs_runtime_contract.py::test_plugin_manifest_exposes_only_user_facing_skills -q
114
+ ```
115
+
116
+ Expected: FAIL because `OBSOLETE_SKILL_NAMES` is not exported yet and `.claude-plugin/plugin.json` still lists `./skills/okstra-container`.
117
+
118
+ - [ ] **Step 3: Rename skill source and public catalog**
119
+
120
+ Run:
121
+
122
+ ```bash
123
+ git mv skills/okstra-container skills/okstra-container-build
124
+ ```
125
+
126
+ Edit `skills/okstra-container-build/SKILL.md` frontmatter/title:
127
+
128
+ ```markdown
129
+ ---
130
+ name: okstra-container-build
131
+ description: |
132
+ Use to bring up, inspect, or tear down the okstra user-test container runtime for an implementation task — the docker-compose group okstra deploys from a task's worktree so a human can poke at the running build. Trigger words include "okstra container", "okstra container up", "okstra container down", "컨테이너 띄워", "컨테이너 올려", "컨테이너 내려", "컨테이너 상태", "컨테이너 로그", "유저 테스트 환경", "user test environment", "docker compose 띄워줘", "이 task 컨테이너로 띄워", "watcher 멈춰", "stop watcher", "감시 종료".
133
+ ---
134
+
135
+ # OKSTRA Container Build
136
+ ```
137
+
138
+ Edit `src/lib/skill-catalog.mjs` public and obsolete exports:
139
+
140
+ ```js
141
+ export const USER_SKILL_NAMES = Object.freeze([
142
+ "okstra-setup",
143
+ "okstra-brief",
144
+ "okstra-run",
145
+ "okstra-memory",
146
+ "okstra-inspect",
147
+ "okstra-schedule",
148
+ "okstra-container-build",
149
+ ]);
150
+
151
+ // Names okstra used to install as skills before they were renamed or moved
152
+ // into the runtime resource tree. Install prunes these exact directories from
153
+ // each target skill home so upgrades do not leave stale slash commands behind.
154
+ // Exact-name only — never wildcard.
155
+ export const OBSOLETE_SKILL_NAMES = Object.freeze([
156
+ "okstra",
157
+ "okstra-context-loader",
158
+ "okstra-team-contract",
159
+ "okstra-convergence",
160
+ "okstra-report-writer",
161
+ "okstra-coding-preflight",
162
+ "okstra-container",
163
+ ]);
164
+
165
+ export function userSkillNames() {
166
+ return [...USER_SKILL_NAMES];
167
+ }
168
+
169
+ export function obsoleteSkillNames() {
170
+ return [...OBSOLETE_SKILL_NAMES];
171
+ }
172
+ ```
173
+
174
+ Edit `.claude-plugin/plugin.json`:
175
+
176
+ ```json
177
+ {
178
+ "name": "okstra",
179
+ "description": "Multi-agent cross-verification orchestrator (Claude lead + Codex/Antigravity workers).",
180
+ "skills": [
181
+ "./skills/okstra-setup",
182
+ "./skills/okstra-brief",
183
+ "./skills/okstra-run",
184
+ "./skills/okstra-memory",
185
+ "./skills/okstra-inspect",
186
+ "./skills/okstra-schedule",
187
+ "./skills/okstra-container-build"
188
+ ]
189
+ }
190
+ ```
191
+
192
+ Edit `src/commands/lifecycle/install.mjs` import and prune helper names:
193
+
194
+ ```js
195
+ import { OBSOLETE_SKILL_NAMES, USER_SKILL_NAMES } from "../../lib/skill-catalog.mjs";
196
+ ```
197
+
198
+ ```js
199
+ const pruned = await pruneObsoleteSkills(target, opts);
200
+ ```
201
+
202
+ ```js
203
+ // Remove former skill directories left by older installs. Exact-name only
204
+ // (OBSOLETE_SKILL_NAMES) — never wildcard-delete okstra-*.
205
+ async function pruneObsoleteSkills(target, opts) {
206
+ let pruned = 0;
207
+ for (const name of OBSOLETE_SKILL_NAMES) {
208
+ const path = join(target.skillsDir, name);
209
+ if (!(await pathEntryExists(path))) continue;
210
+ pruned += 1;
211
+ if (opts.dryRun) {
212
+ process.stdout.write(`[dry-run] rm ${path}\n`);
213
+ continue;
214
+ }
215
+ await fs.rm(path, { recursive: true, force: true });
216
+ }
217
+ return pruned;
218
+ }
219
+ ```
220
+
221
+ Apply the helper call rename in both `installSkillsCopy()` and `installSkillsLink()`.
222
+
223
+ - [ ] **Step 4: Run catalog tests to verify pass**
224
+
225
+ Run:
226
+
227
+ ```bash
228
+ node --test tests-js/skill-catalog.test.mjs
229
+ python3 -m pytest tests/contract/test_docs_runtime_contract.py::test_plugin_manifest_exposes_only_user_facing_skills -q
230
+ ```
231
+
232
+ Expected: PASS.
233
+
234
+ - [ ] **Step 5: Commit Task 1**
235
+
236
+ ```bash
237
+ git add skills/okstra-container-build src/lib/skill-catalog.mjs src/commands/lifecycle/install.mjs .claude-plugin/plugin.json tests-js/skill-catalog.test.mjs tests/contract/test_docs_runtime_contract.py
238
+ git commit -m "fix(container): rename public container skill to build"
239
+ ```
240
+
241
+ ### Task 2: Install/Uninstall Upgrade Cleanup
242
+
243
+ **Files:**
244
+ - Modify: `tests-js/install-runtime.test.mjs`
245
+ - Modify: `tests-js/uninstall-runtime.test.mjs`
246
+ - Modify: `src/commands/lifecycle/uninstall.mjs`
247
+
248
+ **Interfaces:**
249
+ - Consumes: `OBSOLETE_SKILL_NAMES` includes `okstra-container`.
250
+ - Produces: install removes old `okstra-container` skill dirs during upgrade.
251
+ - Produces: uninstall fallback can remove both `okstra-container-build` and old `okstra-container`.
252
+
253
+ - [ ] **Step 1: Write failing install prune regression**
254
+
255
+ In `tests-js/install-runtime.test.mjs`, extend `install copies skills to existing Claude and Agent homes and records targets` after the `.claude` / `.agent` roots are created:
256
+
257
+ ```js
258
+ for (const providerRoot of [join(root, ".claude", "skills"), join(root, ".agent", "skills")]) {
259
+ mkdirSync(join(providerRoot, "okstra-container"), { recursive: true });
260
+ writeFileSync(join(providerRoot, "okstra-container", "SKILL.md"), "old container skill\n");
261
+ }
262
+ ```
263
+
264
+ Extend the provider-root assertions:
265
+
266
+ ```js
267
+ assert.equal(existsSync(join(providerRoot, "okstra-container-build", "SKILL.md")), true);
268
+ assert.equal(existsSync(join(providerRoot, "okstra-container", "SKILL.md")), false);
269
+ ```
270
+
271
+ - [ ] **Step 2: Write failing uninstall fallback regression**
272
+
273
+ Add this test to `tests-js/uninstall-runtime.test.mjs`:
274
+
275
+ ```js
276
+ test("uninstall fallback removes renamed and legacy container skills", () => {
277
+ const { root, env } = makeInstallEnv();
278
+ mkdirSync(join(root, ".okstra"), { recursive: true });
279
+ mkdirSync(join(root, ".claude", "skills", "okstra-container"), { recursive: true });
280
+ mkdirSync(join(root, ".claude", "skills", "okstra-container-build"), { recursive: true });
281
+
282
+ const uninstall = runCli(["uninstall", "-y"], env);
283
+ assert.equal(uninstall.code, 0, uninstall.stderr);
284
+
285
+ assert.equal(existsSync(join(root, ".claude", "skills", "okstra-container")), false);
286
+ assert.equal(existsSync(join(root, ".claude", "skills", "okstra-container-build")), false);
287
+ });
288
+ ```
289
+
290
+ - [ ] **Step 3: Run failing install/uninstall tests**
291
+
292
+ Run:
293
+
294
+ ```bash
295
+ node --test tests-js/install-runtime.test.mjs tests-js/uninstall-runtime.test.mjs
296
+ ```
297
+
298
+ Expected: FAIL until `src/commands/lifecycle/uninstall.mjs` fallback names include both container skill names and install prune is wired to `OBSOLETE_SKILL_NAMES`.
299
+
300
+ - [ ] **Step 4: Implement fallback cleanup names**
301
+
302
+ Edit `src/commands/lifecycle/uninstall.mjs` `FALLBACK_SKILL_NAMES`:
303
+
304
+ ```js
305
+ const FALLBACK_SKILL_NAMES = [
306
+ "okstra",
307
+ "okstra-setup",
308
+ "okstra-brief",
309
+ "okstra-run",
310
+ "okstra-memory",
311
+ "okstra-inspect",
312
+ "okstra-schedule",
313
+ "okstra-container-build",
314
+ "okstra-container",
315
+ "okstra-context-loader",
316
+ "okstra-team-contract",
317
+ "okstra-convergence",
318
+ "okstra-report-writer",
319
+ // Pre-merge names — kept so uninstall on an upgraded machine still cleans
320
+ // them out of ~/.claude/skills/ once the user runs `okstra uninstall`.
321
+ // Safe to drop after a few releases when nobody has them installed.
322
+ "okstra-status",
323
+ "okstra-history",
324
+ "okstra-logs",
325
+ "okstra-report-finder",
326
+ "okstra-time-summary",
327
+ ];
328
+ ```
329
+
330
+ - [ ] **Step 5: Run install/uninstall tests to verify pass**
331
+
332
+ Run:
333
+
334
+ ```bash
335
+ node --test tests-js/install-runtime.test.mjs tests-js/uninstall-runtime.test.mjs
336
+ ```
337
+
338
+ Expected: PASS.
339
+
340
+ - [ ] **Step 6: Commit Task 2**
341
+
342
+ ```bash
343
+ git add src/commands/lifecycle/uninstall.mjs tests-js/install-runtime.test.mjs tests-js/uninstall-runtime.test.mjs
344
+ git commit -m "fix(install): prune legacy container skill on rename"
345
+ ```
346
+
347
+ ### Task 3: Docs, Prompts, and User-Visible Changelog
348
+
349
+ **Files:**
350
+ - Move: `docs/for-ai/skills/okstra-container.md` -> `docs/for-ai/skills/okstra-container-build.md`
351
+ - Modify: `README.md`
352
+ - Modify: `README.kr.md`
353
+ - Modify: `docs/kr/architecture.md`
354
+ - Modify: `docs/project-structure-overview.md`
355
+ - Modify: `docs/kr/container.md`
356
+ - Modify: `docs/for-ai/README.md`
357
+ - Modify: `docs/for-ai/skills/okstra-container-build.md`
358
+ - Modify: `docs/for-ai/skills/okstra-inspect.md`
359
+ - Modify: `prompts/profiles/_implementation-deliverable.md`
360
+ - Modify: `prompts/profiles/final-verification.md`
361
+ - Modify: `docs/superpowers/specs/2026-06-24-manual-user-test-section-design.md`
362
+ - Modify: `docs/superpowers/plans/2026-06-24-manual-user-test-section.md`
363
+ - Modify: `CHANGES.md`
364
+ - Test: `tests/contract/test_docs_runtime_contract.py`
365
+
366
+ **Interfaces:**
367
+ - Consumes: public skill name `okstra-container-build`.
368
+ - Produces: user-facing slash command docs use `/okstra-container-build`.
369
+ - Produces: prompt guidance uses `/okstra-container-build` for skill invocation and `okstra container up ...` for CLI execution.
370
+
371
+ - [ ] **Step 1: Write failing docs scan command**
372
+
373
+ Run:
374
+
375
+ ```bash
376
+ rg -n -P '/okstra-container(?!-build)\\b|okstra-container up|okstra-container-build[[:alpha:]]' README.md README.kr.md docs/kr/architecture.md docs/project-structure-overview.md docs/kr/container.md docs/for-ai prompts/profiles/_implementation-deliverable.md prompts/profiles/final-verification.md docs/superpowers/specs/2026-06-24-manual-user-test-section-design.md docs/superpowers/plans/2026-06-24-manual-user-test-section.md
377
+ ```
378
+
379
+ Expected before edits: matches showing old slash-command/prompt references and zero `builder` references after the corrected spec.
380
+
381
+ - [ ] **Step 2: Rename AI manual file and update live references**
382
+
383
+ Run:
384
+
385
+ ```bash
386
+ git mv docs/for-ai/skills/okstra-container.md docs/for-ai/skills/okstra-container-build.md
387
+ ```
388
+
389
+ Apply these text rules:
390
+
391
+ ```text
392
+ /okstra-container -> /okstra-container-build
393
+ `okstra-container` as public skill name -> `okstra-container-build`
394
+ skills/okstra-container.md -> skills/okstra-container-build.md
395
+ skills/okstra-container/SKILL.md -> skills/okstra-container-build/SKILL.md
396
+ ```
397
+
398
+ Do not change these runtime strings:
399
+
400
+ ```text
401
+ okstra container
402
+ okstra-container-<slug>
403
+ @okstra_container_run
404
+ ```
405
+
406
+ In `prompts/profiles/_implementation-deliverable.md`, replace the environment line with:
407
+
408
+ ```markdown
409
+ - **Manual user test draft**: when this run produces a user-observable change (UI / API / CLI / artifact), write `target / environment / steps / expected result` per change into §5.7.9 (data field `implementation.manualUserTest`, `applicable=true` with `items`). Environment line: if the project has a `docker-compose.yml`, use `/okstra-container-build` (which runs `okstra container up <task-id>`) then connect to the published port; otherwise the project's run command (e.g. `npm start`). When there is no user-observable change, set `applicable=false` and give a one-line `exemptionReason` instead of items. Base the steps on the approved plan's `Acceptance:` and this run's actual diff — these are the steps a human (or `final-verification`) re-runs by hand, NOT the automated validation commands in `Validation evidence`.
410
+ ```
411
+
412
+ In `prompts/profiles/final-verification.md`, replace the manual test example with:
413
+
414
+ ```markdown
415
+ - **Manual user test results**: take each item from the source implementation report's §5.7.9 Manual User Test (Draft), execute the ones reproducible in this environment (e.g. `/okstra-container-build`, which runs `okstra container up`, then the documented steps), and record `result` (`pass` / `fail` / `blocked`) + observed value in §5.8.7 (data field `finalVerification.manualUserTest`). Steps that need human-only interaction this run cannot perform are recorded as `blocked` with the reason (handed to the user), never silently skipped. A failed manual test is an Acceptance Blocker. If the draft was an exemption (`applicable=false`), reaffirm the reason in one line (`applicable=false` + `exemptionReaffirm`).
416
+ ```
417
+
418
+ - [ ] **Step 3: Add current CHANGES entry**
419
+
420
+ Under `## 2026-06-24`, add this entry above the existing entries:
421
+
422
+ ```markdown
423
+ ### fix(container): 공개 컨테이너 스킬명을 okstra-container-build 로 변경
424
+
425
+ - **배경**: 컨테이너 유저 테스트 기능의 공개 slash command 이름을 `okstra-container` 에서 `okstra-container-build` 로 바꾸기로 결정했다. CLI 명령 `okstra container ...` 와 런타임 tmux/docker trace prefix `okstra-container-<slug>` 는 컨테이너 런타임 계약이므로 유지한다.
426
+ - **해결**: 공개 skill catalog, 플러그인 manifest, 설치/업그레이드 cleanup, README/architecture/AI manual/phase prompt 의 사용자-facing 표기를 `okstra-container-build` 로 동기화한다. 옛 `okstra-container` skill directory 는 exact-name obsolete cleanup 으로 재설치 시 제거한다.
427
+ - 사용자 영향: 재설치 후 slash command 는 `/okstra-container-build` 로 노출된다. 기존 `okstra container up/status/logs/stop-watcher/down` CLI 와 실행 중 container watcher trace 이름은 그대로 유지된다. (설치 환경은 `okstra install` 로 런타임 갱신 후 적용.)
428
+ ```
429
+
430
+ - [ ] **Step 4: Run docs scans and contract test**
431
+
432
+ Run:
433
+
434
+ ```bash
435
+ rg -n -P '/okstra-container(?!-build)\\b|okstra-container up|okstra-container-build[[:alpha:]]' README.md README.kr.md docs/kr/architecture.md docs/project-structure-overview.md docs/kr/container.md docs/for-ai prompts/profiles/_implementation-deliverable.md prompts/profiles/final-verification.md docs/superpowers/specs/2026-06-24-manual-user-test-section-design.md docs/superpowers/plans/2026-06-24-manual-user-test-section.md
436
+ python3 -m pytest tests/contract/test_docs_runtime_contract.py -q
437
+ ```
438
+
439
+ Expected: `rg` exits 1 with no matches; pytest PASS.
440
+
441
+ - [ ] **Step 5: Commit Task 3**
442
+
443
+ ```bash
444
+ git add README.md README.kr.md docs/kr/architecture.md docs/project-structure-overview.md docs/kr/container.md docs/for-ai/README.md docs/for-ai/skills/okstra-container-build.md docs/for-ai/skills/okstra-inspect.md prompts/profiles/_implementation-deliverable.md prompts/profiles/final-verification.md docs/superpowers/specs/2026-06-24-manual-user-test-section-design.md docs/superpowers/plans/2026-06-24-manual-user-test-section.md CHANGES.md
445
+ git commit -m "docs(container): rename container skill docs to build"
446
+ ```
447
+
448
+ ### Task 4: Runtime Build and Full Verification
449
+
450
+ **Files:**
451
+ - Generated: `runtime/` via `npm run build` (gitignored, do not hand-edit)
452
+ - Test: `tests-js/*.test.mjs`
453
+ - Test: `tests/`
454
+ - Test: `validators/validate-workflow.sh`
455
+
456
+ **Interfaces:**
457
+ - Consumes: source skill directory `skills/okstra-container-build`.
458
+ - Produces: generated `runtime/skills/okstra-container-build/SKILL.md`.
459
+ - Produces: no generated `runtime/skills/okstra-container/SKILL.md`.
460
+
461
+ - [ ] **Step 1: Run build**
462
+
463
+ Run:
464
+
465
+ ```bash
466
+ npm run build
467
+ ```
468
+
469
+ Expected: exit 0. Output includes `skills -> runtime/skills` sync. `runtime/` remains untracked/ignored.
470
+
471
+ - [ ] **Step 2: Verify runtime skill sync**
472
+
473
+ Run:
474
+
475
+ ```bash
476
+ test -f runtime/skills/okstra-container-build/SKILL.md
477
+ test ! -e runtime/skills/okstra-container/SKILL.md
478
+ rg -n "name: okstra-container-build" runtime/skills/okstra-container-build/SKILL.md
479
+ ```
480
+
481
+ Expected: all commands exit 0.
482
+
483
+ - [ ] **Step 3: Run targeted test suite**
484
+
485
+ Run:
486
+
487
+ ```bash
488
+ node --test tests-js/skill-catalog.test.mjs tests-js/install-runtime.test.mjs tests-js/uninstall-runtime.test.mjs tests-js/doctor-runtime.test.mjs
489
+ python3 -m pytest tests/contract/test_docs_runtime_contract.py -q
490
+ ```
491
+
492
+ Expected: PASS.
493
+
494
+ - [ ] **Step 4: Run project check**
495
+
496
+ Run:
497
+
498
+ ```bash
499
+ npm run check
500
+ ```
501
+
502
+ Expected: exit 0.
503
+
504
+ - [ ] **Step 5: Run final identifier audit**
505
+
506
+ Run:
507
+
508
+ ```bash
509
+ rg -n "okstra-container-build[[:alpha:]]|container-build[[:alpha:]]" .
510
+ rg -n "okstra-container" .
511
+ ```
512
+
513
+ Expected:
514
+ - First command exits 1 with no matches.
515
+ - Second command still reports historical changelog/old design-plan references and runtime prefix tests such as `okstra-container-<slug>`, but not live public skill catalog, plugin manifest, README slash-command rows, AI routing manuals, or live phase prompt instructions.
516
+
517
+ - [ ] **Step 6: Record verification-only status**
518
+
519
+ Run:
520
+
521
+ ```bash
522
+ git status --short
523
+ ```
524
+
525
+ Expected: no new tracked source changes from Task 4. `runtime/` may have been regenerated by `npm run build` but remains ignored. Do not create a commit for this task unless a tracked verification fixture changed.
@@ -11,19 +11,19 @@
11
11
  - implementation 딜리버러블의 테스트 섹션은 [final-report.template.md:458](../../../templates/reports/final-report.template.md) §5.7.6 Validation Evidence 하나뿐이며, 이는 `Phase | Command | Exit code | Output tail | TDD evidence` 구조의 **자동 검증 증거 전용**이다. 딜리버러블 명세 [_implementation-deliverable.md:19-20](../../../prompts/profiles/_implementation-deliverable.md) 의 Validation evidence / TDD evidence 도 전부 실행 명령 기반이다.
12
12
  - planning 의 stage 별 `Acceptance:` 도 "RED 스텝을 PASS 로 뒤집는 같은 테스트 명령"으로 정의된다([implementation-planning.md:73](../../../prompts/profiles/implementation-planning.md)). 사람 절차가 아니다.
13
13
  - final-verification 에는 사람 검증에 가까운 입력란이 있으나([final-verification-input.template.md:58](../../../templates/reports/final-verification-input.template.md) `Manual verification notes:`, `:70` `## Acceptance Criteria`), 이는 **검증 단계 입력**일 뿐 implementation 산출물로 흘러드는 "구현 직후의 테스트 절차"가 아니다.
14
- - 결정적으로 [okstra-container/SKILL.md](../../../skills/okstra-container/SKILL.md) 는 사람이 만져볼 환경을 docker 로 띄워주지만(`:9` "so a human can poke at the running build"), `prompts/`·`templates/` 어디에서도 참조되지 않는 고립 스킬이다. **환경은 띄워주는데, 띄운 다음 무엇을 어떻게 확인할지를 적어둘 자리가 리포트에 없는 단절** 상태다.
14
+ - 결정적으로 [okstra-container-build/SKILL.md](../../../skills/okstra-container-build/SKILL.md) 는 사람이 만져볼 환경을 docker 로 띄워주지만(`:9` "so a human can poke at the running build"), `prompts/`·`templates/` 어디에서도 참조되지 않는 고립 스킬이다. **환경은 띄워주는데, 띄운 다음 무엇을 어떻게 확인할지를 적어둘 자리가 리포트에 없는 단절** 상태다.
15
15
 
16
16
  ## 2. 목표 / 비목표
17
17
 
18
18
  ### 목표
19
19
  - implementation 단계가 "Manual User Test" 절차 **초안**을 산출하고, final-verification 단계가 그 초안을 **실제로 수행해 결과를 확정**한다.
20
20
  - 관찰 가능한 변화가 있는 task 에 한해 절차를 요구한다(내부 전용 변경은 면제).
21
- - `okstra-container` 와 리포트를 조건부로 연결해 단절을 해소한다.
21
+ - `okstra-container-build` 와 리포트를 조건부로 연결해 단절을 해소한다.
22
22
 
23
23
  ### 비목표 (YAGNI)
24
24
  - 자동 E2E/통합 테스트 프레임워크 도입 — 기존 자동 검증(§5.7.6 / verifier)은 그대로 둔다.
25
25
  - 모든 task 에 일괄 강제 — 관찰 불가 변경까지 N/A 스텁을 양산하지 않는다.
26
- - okstra-container 가 없는(=docker-compose 부재) 프로젝트에 컨테이너 사용 강요.
26
+ - docker-compose 가 없는 프로젝트에 컨테이너 사용 강요.
27
27
 
28
28
  ## 3. 설계 결정 (확정)
29
29
 
@@ -31,7 +31,7 @@
31
31
  |---|---|---|
32
32
  | 위치 | implementation(초안) ↔ final-verification(실행·확정) **양쪽 연계** | 절차 작성 시점(구현 직후)과 실행 시점(검증)이 다름. 단일 소유자 분리: 초안=implementation, 결과=final-verification |
33
33
  | 범위 | 사람이 **관찰 가능한 변화**가 있을 때만 절차, 내부 전용이면 `사용자 테스트 불필요 — <사유>` 한 줄 | allowlist 방식으로 빈 섹션 노이즈 최소화 |
34
- | container 연계 | **조건부** — `docker-compose.yml` 있으면 `okstra-container up`, 없으면 일반 실행 명령 | 단절 해소 + compose 없는 프로젝트 과임 방지 |
34
+ | container 연계 | **조건부** — `docker-compose.yml` 있으면 `/okstra-container-build` (`okstra container up <task-id>`), 없으면 일반 실행 명령 | 단절 해소 + compose 없는 프로젝트 과임 방지 |
35
35
  | 강제 | **validator 구조 강제** — 헤딩 존재 + (절차 또는 면제사유) 중 하나, 빈 스텁 거부 | "관찰 가능 여부"는 의미 판단이라 validator 자동 판정 불가 → 구조만 강제, 적절성은 교차검증 워커가 판단. CLAUDE.md "MUST 는 강제 지점 명시" 충족 |
36
36
 
37
37
  ## 4. 섹션 포맷 명세
@@ -42,7 +42,7 @@
42
42
  ```
43
43
  - 대상: <어떤 사용자-facing 동작/기능>
44
44
  - 환경 기동:
45
- - (docker-compose.yml 있음) `okstra-container up <task-id>` → published port 접속
45
+ - (docker-compose.yml 있음) `/okstra-container-build` (`okstra container up <task-id>`) → published port 접속
46
46
  - (없음) <프로젝트 실행 명령, 예: npm start / 해당 바이너리 실행>
47
47
  - 확인 스텝:
48
48
  1. <사람이 하는 동작>
@@ -93,7 +93,7 @@ final-verification ──→ §5.8.x Manual User Test Results [결과 단일
93
93
  | [templates/reports/final-report.template.md](../../../templates/reports/final-report.template.md) | §5.7 에 `Manual User Test (Draft)` 하위섹션 신설(Routing Recommendation 직전 배치, 이후 번호 +1), §5.8 에 `Manual User Test Results` 하위섹션 신설(Routing 직전) |
94
94
  | [templates/reports/final-verification-input.template.md](../../../templates/reports/final-verification-input.template.md) | `Manual verification notes:`(:58) 를 implementation 초안 인용 흐름과 통합 |
95
95
  | [validators/validate-run.py](../../../validators/validate-run.py) | 기존 §5.7/§5.8 deliverable 섹션 scan 리스트(:1145-1163)에 신설 섹션명 추가 + 빈 스텁/면제사유 거부 체크 |
96
- | [skills/okstra-container/SKILL.md](../../../skills/okstra-container/SKILL.md) | 리포트↔container 양방향 포인터 한 줄(이 절차 섹션에서 호출됨을 명시) |
96
+ | [skills/okstra-container-build/SKILL.md](../../../skills/okstra-container-build/SKILL.md) | 리포트↔container 양방향 포인터 한 줄(이 절차 섹션에서 호출됨을 명시) |
97
97
 
98
98
  > 구현 후 `npm run build` 로 `runtime/` 동기화 필요. 엔드유저 반영은 `okstra install` 재실행.
99
99