docs(omo-codex): batch 93 (13 files)

This commit is contained in:
YeonGyu-Kim
2026-05-30 19:12:17 +09:00
parent 0f88a130b4
commit b4655c18d7
13 changed files with 346 additions and 0 deletions
@@ -0,0 +1,13 @@
# Normalize line endings: store LF in git, check out LF on every platform.
# Required so biome's --check passes on Windows (default core.autocrlf=true).
* text=auto eol=lf
# Explicit binary types
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.ico binary
*.zip binary
*.tgz binary
*.gz binary
@@ -0,0 +1,3 @@
dist/
node_modules/
*.log
@@ -0,0 +1,43 @@
# Repository Conventions
Conventions for human contributors and AI agents working on this repository.
## Stack
- Node >=20 runtime.
- npm package manager.
- TypeScript 6 strict mode.
- Biome 2 linting and formatting.
- Vitest 4 test runner.
## Forbidden
- No `as any` or `as unknown`.
- No `@ts-ignore` or `@ts-expect-error`.
- No enums.
- No non-null assertions.
- No default exports. `vitest.config.ts` is exempt because the framework requires that shape.
## File Ceiling
- Keep each `src/` TypeScript file under 250 pure LOC.
- Split by responsibility before a file reaches the ceiling.
## Test Discipline
- Use Vitest with nested `describe` names in `#given`, `#when`, and `#then` form, or inline `// given`, `// when`, and `// then` comments.
- Never use Arrange-Act-Assert comments.
- Keep fixtures in `test/fixtures/`.
## Build and Hooks
- Build output goes to `dist/`.
- `hooks/hooks.json` registers Codex `Stop` and `SubagentStop` hooks.
- Hook commands run `node ${PLUGIN_ROOT}/components/start-work-continuation/dist/cli.js hook stop` and `node ${PLUGIN_ROOT}/components/start-work-continuation/dist/cli.js hook subagent-stop`.
## Constraints
- Never let the hook block a Codex turn because of malformed input.
- Never make a network call from the hook.
- Keep the directive in `directive.md`. Do not inline it into TypeScript files.
- The hook only continues sessions listed in `.omo/boulder.json` as `codex:<session_id>`.
@@ -0,0 +1,5 @@
# Changelog
## 0.1.0 - 2026-05-28
- Initial release: Stop and SubagentStop continuation injection.
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Yeongyu Kim
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@@ -0,0 +1,5 @@
codex-start-work-continuation
Copyright (c) 2026 Yeongyu Kim
This product includes software released under the MIT License.
See LICENSE for the full text.
@@ -0,0 +1,55 @@
# codex-start-work-continuation
Codex Stop-hook continuation injector for the omo-codex `start-work` skill.
It reads `.omo/boulder.json` in the hook payload `cwd`, resolves the active work, inspects the active plan for incomplete top-level checkboxes, and emits Codex Stop-hook JSON when the plan still has work:
```json
{"decision":"block","reason":"<directive>"}
```
The `reason` is loaded from `directive.md` on every invocation and filled with current plan state. The hook returns no output when `stop_hook_active` is `true`, when no active Boulder work exists, when the work is completed, when the active work is not tied to `codex:<session_id>`, or when all top-level plan checkboxes are complete.
This pairs with the `start-work` skill at `plugin/skills/start-work/SKILL.md`. That skill writes `.omo/boulder.json` with Codex session ids prefixed as `codex:` so the hook can continue only its own active Codex session.
## Counted plan checkboxes
Only column-0 checkboxes under these sections are counted:
- `## TODOs`
- `## Final Verification Wave`
Nested checkboxes under `### Acceptance Criteria`, `### Evidence`, and `### Definition of Done` are ignored.
## Smoke test
```bash
TMP=$(mktemp -d)
mkdir -p "$TMP/.omo/plans"
cat > "$TMP/.omo/plans/test.md" <<EOF
## TODOs
- [ ] Task one
- [ ] Task two
EOF
cat > "$TMP/.omo/boulder.json" <<EOF
{"schema_version":2,"active_work_id":"w1","works":{"w1":{"work_id":"w1","active_plan":".omo/plans/test.md","plan_name":"test","session_ids":["codex:smoke-session"],"status":"active"}}}
EOF
PAYLOAD='{"session_id":"smoke-session","turn_id":"t1","transcript_path":"","cwd":"'"$TMP"'","hook_event_name":"Stop","model":"gpt-5.5","permission_mode":"default","stop_hook_active":false}'
npm run build
echo "$PAYLOAD" | node dist/cli.js hook stop
PAYLOAD_LOOP='{"session_id":"smoke-session","turn_id":"t1","transcript_path":"","cwd":"'"$TMP"'","hook_event_name":"Stop","model":"gpt-5.5","permission_mode":"default","stop_hook_active":true}'
echo "$PAYLOAD_LOOP" | node dist/cli.js hook stop
rm -rf "$TMP"
```
Expect the first command to print JSON containing `"decision":"block"`; expect the anti-loop command to print nothing.
## License
MIT. See `LICENSE`.
## Privacy
This plugin only reads local hook payloads, `.omo/boulder.json`, the active plan, and the bundled directive. It makes no network calls and stores no telemetry.
@@ -0,0 +1,48 @@
{
"$schema": "https://biomejs.dev/schemas/2.4.15/schema.json",
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"style": {
"noDefaultExport": "error",
"noEnum": "error",
"noNonNullAssertion": "error",
"useImportType": "error",
"useConst": "error",
"useNodejsImportProtocol": "off"
},
"complexity": {
"useLiteralKeys": "off"
},
"suspicious": {
"noExplicitAny": "error",
"noTsIgnore": "error",
"noControlCharactersInRegex": "off",
"noEmptyInterface": "off"
}
}
},
"formatter": {
"enabled": true,
"formatWithErrors": false,
"indentStyle": "tab",
"indentWidth": 3,
"lineWidth": 120
},
"files": {
"includes": ["src/**/*.ts", "test/**/*.ts", "vitest.config.ts", "!**/node_modules/**/*", "!**/dist/**/*"]
},
"overrides": [
{
"includes": ["vitest.config.ts"],
"linter": {
"rules": {
"style": {
"noDefaultExport": "off"
}
}
}
}
]
}
@@ -0,0 +1,51 @@
<start-work-continuation>
You are mid-flight on a Prometheus work plan. The turn just ended without finishing the plan. This is an automatic continuation — keep going. Do NOT ask the user whether to continue; the contract is auto-continue until every top-level checkbox is `- [x]`.
# State
- Plan: `{{PLAN_NAME}}`
- Plan file: `{{PLAN_PATH}}`
- Boulder state: `{{BOULDER_PATH}}`
- Remaining top-level checkboxes: `{{REMAINING_COUNT}}` of `{{TOTAL_COUNT}}`
- Next incomplete task: `{{NEXT_TASK_LABEL}}`
{{WORKTREE_BLOCK}}
- Ledger: `{{LEDGER_PATH}}`
- Your session id in boulder.json: `codex:{{SESSION_ID}}`
# What to do this turn
1. Read `{{PLAN_PATH}}` AND `{{LEDGER_PATH}}` first — ground truth for what remains and what evidence has already been recorded. The plan checkbox and the ledger are the only sources of truth; do not trust your own memory of prior turns.
2. Pick the FIRST unchecked top-level checkbox in `## TODOs` or `## Final Verification Wave`. Ignore nested checkboxes under Acceptance Criteria / Evidence / Definition of Done.
3. Follow the `start-work` skill in full. The skill is already loaded from your earlier turn — re-read its file at `packages/omo-codex/plugin/skills/start-work/SKILL.md` if you have lost context.
4. Decompose the checkbox into atomic sub-tasks. Dispatch them in PARALLEL via `spawn_agent` calls in this same response unless a sub-task has a NAMED blocking dependency (input from another sub-task or shared file).
5. Every sub-task message MUST include all 7 sections and name one Manual-QA channel with its exact tool and exact invocation (the literal `curl` / `send-keys` / `page.click` with concrete inputs and the binary PASS/FAIL observable), plus the applicable ultraqa adversarial classes, a captured artifact, and a cleanup receipt. Channels: HTTP call (`curl -i`); tmux (`send-keys` + `capture-pane`); browser use — use Chrome to drive the page, else download and use agent-browser (https://github.com/vercel-labs/agent-browser); computer use — OS-level GUI automation for a desktop app. Tests are the floor; the channel artifact plus probed adversarial classes are the ceiling. All are required.
6. After verification of ALL sub-tasks under this checkbox: `apply_patch` the plan to change `- [ ]``- [x]`, re-read the plan to confirm the count decreased, append a `task-completed` line to the ledger, then continue.
7. Do not start fresh on a sub-agent failure. Re-dispatch the same `task_name` with a fix-message: `FAILED: <exact error>` + `Diagnosis: <observation>` + `Fix: <instruction>`.
# Hard constraints
- No production code before a failing test exists. When the change touches existing behavior, PIN it first: write a baseline characterization test that passes on the unchanged code, with exact inputs, exact observable, and exact assertion. PIN → RED → GREEN → SURFACE.
- No `--dry-run` as evidence. No "should work". No "tests pass" as completion proof.
- No `as any` / `@ts-ignore` / `@ts-expect-error`. No deleting failing tests.
- Probe every applicable ultraqa adversarial class (malformed input, prompt injection, cancel/resume, stale state, dirty worktree, hung or long commands, flaky tests, misleading success output, repeated interruptions) and capture the observable for each. A clean happy-path artifact alone is NOT a PASS when an applicable class went unprobed; record skipped classes with a one-line not-applicable reason.
- Cleanup receipt is mandatory. Register each QA resource teardown (scripts, tmux assets, browser / agent-browser sessions, PIDs, ports, containers, temp dirs) as its own todo the moment it spawns, then execute it. Leftover PIDs / `tmux` sessions / browser contexts / bound ports / containers / temp dirs = BLOCKED, not PASS.
- The worktree path (if set in boulder.json) governs every file edit and command. Do not stray into the main repo.
- session_ids you write to boulder.json MUST be prefixed `codex:`. Bare ids on read are legacy `opencode:`.
# Stop conditions for THIS turn
- A top-level checkbox flipped to `- [x]` after the 5-phase QA gate (Phase 1 read, Phase 2 automated, Phase 3 channel scenario, Phase 4 adversarial-class probing, Phase 5 gate decision). Then the Stop hook will re-evaluate; if more checkboxes remain you will be continued again.
- 3 same-failure cycles on one sub-task → escalate via `spawn_agent(agent_type="codex-ultrawork-reviewer", ...)` and stop dispatch.
- Safety boundary (destructive command, secret exfiltration, production write) → stop and surface a safe substitute.
- All top-level checkboxes `- [x]` AND (if gate triggered) `codex-ultrawork-reviewer` approved unconditionally → print the ORCHESTRATION COMPLETE block and end.
# Output discipline
- Surface only state changes: sub-agent dispatched, channel scenario PASS/FAIL with artifact path, checkbox marked, evidence appended to ledger.
- Do NOT print "Should I continue?" — the Stop hook handles continuation.
- Do NOT restate the full plan. Do NOT recap prior turns. The ledger and the plan file are the durable record.
Begin now. Pick the next checkbox, dispatch the parallel sub-agents, verify, mark, continue.
</start-work-continuation>
@@ -0,0 +1,53 @@
{
"name": "@code-yeongyu/codex-start-work-continuation",
"version": "0.1.0",
"description": "Codex Stop hook continuation injector for omo-codex start-work plans.",
"type": "module",
"packageManager": "npm@11.12.1",
"license": "MIT",
"homepage": "https://github.com/code-yeongyu/codex-start-work-continuation",
"repository": {
"type": "git",
"url": "git+https://github.com/code-yeongyu/codex-start-work-continuation.git"
},
"bugs": {
"url": "https://github.com/code-yeongyu/codex-start-work-continuation/issues"
},
"keywords": [
"codex",
"codex-plugin",
"start-work",
"continuation",
"hooks",
"boulder"
],
"bin": {
"omo-start-work-continuation": "./dist/cli.js"
},
"scripts": {
"build": "tsc -p tsconfig.build.json",
"test": "vitest --run",
"test:watch": "vitest",
"typecheck": "tsc --noEmit",
"lint": "biome check .",
"lint:fix": "biome check --write .",
"check": "tsc --noEmit && biome check . && npm run build"
},
"files": [
"dist",
"directive.md",
"hooks",
"README.md",
"LICENSE",
"NOTICE"
],
"devDependencies": {
"@biomejs/biome": "2.4.15",
"@types/node": "^25.7.0",
"typescript": "^6.0.3",
"vitest": "^4.1.5"
},
"engines": {
"node": ">=20.0.0"
}
}
@@ -0,0 +1,12 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"allowImportingTsExtensions": false,
"declaration": true,
"outDir": "dist",
"rootDir": "src",
"noEmit": false
},
"include": ["src/**/*"],
"exclude": ["test/**/*"]
}
@@ -0,0 +1,27 @@
{
"compilerOptions": {
"target": "ESNext",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"lib": ["ESNext"],
"strict": true,
"exactOptionalPropertyTypes": true,
"noUncheckedIndexedAccess": true,
"noPropertyAccessFromIndexSignature": true,
"verbatimModuleSyntax": true,
"noImplicitOverride": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"esModuleInterop": true,
"allowImportingTsExtensions": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"useDefineForClassFields": false,
"types": ["node"],
"noEmit": true
},
"include": ["src/**/*", "test/**/*", "vitest.config.ts"]
}
@@ -0,0 +1,10 @@
import { defineConfig } from "vitest/config";
export default defineConfig({
test: {
include: ["test/**/*.test.ts"],
environment: "node",
pool: "threads",
isolate: true,
},
});