# Harness Non-Gated Build Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use /ship (recommended) or /executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Build every harness sub-project deliverable that does NOT depend on Plan A (`mega-plan-harness`) landing — the OKF knowledge-base system and the adapter-config CLI + per-machine state.

**Architecture:** Two file-disjoint tracks. (1) OKF: zero-dep Node store-format contract → tier resolver → projection + capture engines → `bin/okf` CLI → harness hook shells over the existing global dispatcher. (2) Adapter-config: `bin/harness-adapter` core/state writers → read/merge + write-only secrets + available-models cache, unit-tested against a FIXTURE catalog (the real `presets/adapters.json` is Plan-A-gated and lives in the `harness-gated` plan). Nothing here touches a file Plan A owns, so the whole plan is buildable concurrently with a running Plan A — subject only to repo-level integration-branch serialization (run AFTER Plan A's integration branch merges to avoid worktree/branch collision in the same repo).

**Tech Stack:** Node.js stdlib only (zero-dep, no npm), POSIX shell, markdown + YAML frontmatter (OKF store), JSON state files.

---

## Wave Plan

| Wave | Tasks | Files touched | Safe to parallelize? |
|------|-------|---------------|----------------------|
| 1 | okf-T1, okf-T2, okf-T3, adp-T8, agg-T1 | `spec/okf-format.md`, `spec/okf-nav-protocol.md`, `lib/okf-scope`, `bin/harness-adapter`, `run-tests.sh` | ✅ no overlap |
| 2 | okf-T4, okf-T5, adp-T9 | `lib/okf-project`, `lib/okf-capture`, `bin/harness-adapter` (modify) | ✅ no overlap |
| 3 | okf-T6 | `bin/okf` | single task |
| 4 | okf-T7 | hook shells (global dispatcher) | single task |

**Execution strategy:** `dag-parallel` — waves 1 and 2 each hold ≥2 independent file-disjoint tasks.

> **Cross-plan note:** This plan ships `bin/okf` (okf-T6) and `bin/harness-adapter` (adp-T8/T9). The `harness-gated` plan's Web UI gateway consumes both — it gates on this plan having landed (its gate `g2`). Land this plan before the Web UI waves of `harness-gated` run.

---

## Land Gate

`meta.land_testcmd = "bash run-tests.sh"`. Run by `ship.sh land` before merge/PR. The discovery loop lives in the committed runner `run-tests.sh`, not inlined in `land_testcmd` — DRY: versioned logic in one file, not a baked config string. (`$` in a testcmd is safe: ship-init bakes every frozen fact single-quoted via `shq`, so a loop var no longer re-expands at wrapper-load under `set -u`.)

### Task agg-T1: Land-gate test runner
**Wave:** 1
**Blocks:** — | **Blocked by:** —
**Files:**
- Create `run-tests.sh` (repo ROOT — deliberately NOT under `test/`, so `test/*.sh` never matches it → no self-recursion).
**Literal — apply inline (LOC≤LOP), no dispatch:**
```bash
#!/usr/bin/env bash
# run-tests.sh — land-gate runner: discover + run every tree test, dedup, fail-closed on zero.
set -uo pipefail
shopt -s globstar nullglob
declare -A seen
n=0; rc=0
for t in test/*.sh **/*.test.sh **/test-*.sh; do
  [[ -v "seen[$t]" ]] && continue
  seen[$t]=1; echo "== bash $t"; bash "$t" || rc=1; n=$((n+1))
done
for t in **/*.test.js **/test-*.js; do
  [[ -v "seen[$t]" ]] && continue
  seen[$t]=1; echo "== node $t"; node "$t" || rc=1; n=$((n+1))
done
[[ $n -eq 0 ]] && { echo "land-gate-no-tests"; exit 1; }
exit $rc
```
- [ ] Apply edit; run `bash run-tests.sh` (expect it discovers + runs `test/*.sh`, exit reflects their pass/fail), commit: `git add run-tests.sh && git commit -m "feat: land-gate test runner"`.

---

## OKF Knowledge-Base Track

Source of truth: `docs/specs/2026-06-30-okf-kb-design.md`. Seams copied verbatim. Bodies = implementer.

### Task okf-T1: OKF store format contract
**Wave:** 1
**Blocks:** okf-T6 | **Blocked by:** —
**Files:**
- Create `spec/okf-format.md` — on-disk KB data contract every tier + agent obeys.
**Contract:** Frontmatter block (required), copied VERBATIM:
```yaml
---
type: <concept|guide|reference|decision|...>   # open enum; REQUIRED
title: <one line>
description: <one sentence — used by index + metadata filtering>
tags: [<kebab>, ...]
scope: global|project|job                        # tier this file belongs to
okf_id: <stable uuid>                            # identity across project/capture
okf_source: authored|claude-memory|<cli>         # provenance (loop prevention)
---
```
`index.md` line shape VERBATIM: `- [Title](path) — <one sentence>`
**Behavior:**
- Single concept per file — `<concept-slug>.md`, one minimal topic.
- One `index.md` per directory = the navigation surface (progressive descent); a markdown link + one-sentence summary per child (file or subdir).
- Inter-concept links are RELATIVE markdown links; `okf doctor` validates none dangle.
- `type` is an OPEN enum; `okf_id`/frontmatter MISSING ⇒ doctor flags, projection skips (visible, never silent).
**Acceptance:** Run: `grep -F 'okf_source: authored|claude-memory|<cli>' spec/okf-format.md && grep -F '- [Title](path) — <one sentence>' spec/okf-format.md` → Expected: exit 0 (both literals present verbatim).
- [ ] Write tests covering behavior above
- [ ] Implement to contract
- [ ] Run acceptance → expected
- [ ] Commit: `git add spec/okf-format.md && git commit -m "feat(okf): store format contract"`

---

### Task okf-T2: Navigation protocol canonical text
**Wave:** 1
**Blocks:** okf-T4 | **Blocked by:** —
**Files:**
- Create `spec/okf-nav-protocol.md` — the fixed reminder block projected verbatim into CLAUDE.md/AGENTS.md managed regions.
**Contract:** File content = byte-verbatim copy of `docs/design.txt` lines 19–38 (the "Repository Navigation Protocol" block: progressive `index.md` descent; metadata filtering on frontmatter `type`/`tags`; upkeep — single concept, required frontmatter, immediate index update). One canonical literal, edited once, re-projected everywhere.
**Behavior:**
- No paraphrase, no reformat — the spec pins this as one canonical literal so every CLI gets identical guidance.
- Source bytes (incl. existing indentation) preserved exactly.
**Acceptance:** Run: `diff <(sed -n '19,38p' docs/design.txt) spec/okf-nav-protocol.md` → Expected: no diff (exit 0).
- [ ] Write tests covering behavior above
- [ ] Implement to contract
- [ ] Run acceptance → expected
- [ ] Commit: `git add spec/okf-nav-protocol.md && git commit -m "feat(okf): canonical nav protocol text"`

---

### Task okf-T3: Tier resolution + promotion
**Wave:** 1
**Blocks:** okf-T4, okf-T6 | **Blocked by:** —
**Files:**
- Create `lib/okf-scope` — tier resolve + precedence + promote helper (zero-dep Node stdlib).
**Contract:** (spec component 6)
- Resolve: `global` ALWAYS; `project` if cwd is in a git repo containing `.okf/`; `job` if a run slug is active (`runstate/<slug>/okf/`).
- Tier dirs: global `~/.local/share/okf/`, project `<repo>/.okf/`, job `<repo>/runstate/<slug>/okf/`.
- Precedence on read/projection: `job` overrides `project` overrides `global` for same-`okf_id` (most-specific wins).
- Promote: `okf promote` moves a job-tier concept to project tier (re-homes file, re-mints `scope`, updates BOTH indexes).
**Behavior:**
- Missing `.okf/` in repo: auto-`okf new` bootstraps project tier.
- Precedence resolution is DETERMINISTIC so projected MEMORY/AGENTS view is stable.
- Promotion is the ONLY job→project write (explicit, audited) — the self-improvement-on-failure durable path.
- No run slug active ⇒ `job` tier absent (not an error).
**Acceptance:** Run: `node -e "const s=require('./lib/okf-scope'); /* same okf_id in job+project+global */ console.log(s.resolve(...).winner.scope)"` → Expected: `job` (precedence: job > project > global).
- [ ] Write tests covering behavior above
- [ ] Implement to contract
- [ ] Run acceptance → expected
- [ ] Commit: `git add lib/okf-scope && git commit -m "feat(okf): tier resolve + precedence + promote"`

---

### Task okf-T4: Projection engine
**Wave:** 2
**Blocks:** okf-T6 | **Blocked by:** okf-T2, okf-T3
**Files:**
- Create `lib/okf-project` — outbound projection engine (the deepest seam; callers can't tell which target format is produced).
**Contract:** (spec component 3)
```
okf-project <scope> <repo?> <run?> <target> → writes native artifacts; returns {written:[...], skipped:[...]}
```
- Managed-region sentinels VERBATIM: `<!-- okf:begin id=<scope> -->` … `<!-- okf:end -->`
- claude memory line shape VERBATIM (identical to index.md): `- [Title](file.md) — <hook>`
**Behavior:**
- **SAFETY (load-bearing):** all projection tests run against a FIXTURE `$HOME`/XDG dir (tmp) — NEVER real `~/.claude`. Wholesale `MEMORY.md` regen on a real dir is destructive.
- claude project memory (SUBSUMED — OKF owns dir): render each in-scope concept to `~/.claude/projects/<enc>/memory/<okf_id-or-slug>.md` writing OKF frontmatter VERBATIM (NO type→metadata mapping). `<enc>` = repo abs-path `/`→`-` (verify-at-build encoder note).
- `MEMORY.md`: regenerate WHOLESALE from the in-scope set (NOT managed-region merge — native protocol is replaced, no co-owner). Keep harness line shape `- [Title](file.md) — <hook>`.
- Output filename = concept's recorded origin filename if it has one (captured facts), else `<okf_id-or-slug>.md` — so capture→project round-trips to same path (no duplicate divergence).
- claude global: refresh managed block in `~/.claude/CLAUDE.md` (global-scope facts + nav pointer).
- AGENTS.md: refresh managed block (`<repo>/AGENTS.md` for project scope, `~/.codex/AGENTS.md` for global) = nav protocol (okf-T2 text) + tier-pointer index. ONE artifact, all three CLIs.
- Idempotent: re-projecting unchanged OKF = no-op (hash-compared managed region).
- Sentinels missing/corrupted ⇒ re-emit FULL block, never append duplicate. Hand-written content outside sentinels untouched.
- Concept missing `okf_id`/frontmatter ⇒ SKIP (into `skipped`), never silent.
- Every target write = temp-file + atomic rename (fail-closed, no partial write).
**Acceptance:** Run: `<test-runner> lib/okf-project` against fixture tier set under tmp `$HOME` → Expected: exact `memory/*.md` + `MEMORY.md` + AGENTS.md managed block match golden; re-run is byte-identical (no-op); content outside sentinels survives.
- [ ] Write tests covering behavior above
- [ ] Implement to contract
- [ ] Run acceptance → expected
- [ ] Commit: `git add lib/okf-project && git commit -m "feat(okf): projection engine"`

---

### Task okf-T5: Capture engine
**Wave:** 2
**Blocks:** okf-T6 | **Blocked by:** okf-T3
**Files:**
- Create `lib/okf-capture` — inbound reverse map (claude-memory → OKF only).
**Contract:** (spec component 4)
```
okf-capture <from> <repo?> <run?> → new/updated OKF concept files; returns {ingested:[...], skipped:[...]}
```
Subsume mapping (native frontmatter → OKF), VERBATIM:
- `metadata.type` → OKF `type` (open enum legitimately includes `user|feedback|project|reference`)
- `name` → slug / `okf_id`
- `description` → `description`
- `title` synthesized from `name`/`description` (native has no `title`)
**Behavior:**
- **SAFETY (load-bearing):** all capture tests run against a FIXTURE `$HOME`/XDG dir (tmp) — NEVER real `~/.claude`.
- Source = claude memory ONLY: each `~/.claude/projects/<enc>/memory/*.md` whose `okf_source` is absent or ≠ a value OKF wrote ⇒ migrate frontmatter (mapping above), create/update matching OKF concept (assign `scope` from project context, mint `okf_id`, set `okf_source: claude-memory`).
- Filename reconciliation (no duplicate fact): record origin filename in concept; projection writes back to THAT same path — origin filename WINS over a fresh `okf_id`-slug when one exists.
- Dedup / loop guard: SKIP if an OKF concept with same `okf_id` already holds byte-identical body, OR if memory file content hash matches OKF's last projection (`okf_id` + hash).
- AGENTS.md / codex / cursor are NOT capture sources (freeform docs / raw chats) — capture is claude-memory→OKF only.
- Capture conflict (same `okf_id`, divergent bodies) ⇒ keep OKF master copy, record native variant as `needs-merge` sibling + warn (never auto-clobber agent's words).
**Acceptance:** Run: `<test-runner> lib/okf-capture` against fixture tmp `$HOME` → Expected: seeded agent-authored memory file ingested with `okf_source: claude-memory`; re-run skipped (loop guard); byte-identical OKF-origin file never re-captured.
- [ ] Write tests covering behavior above
- [ ] Implement to contract
- [ ] Run acceptance → expected
- [ ] Commit: `git add lib/okf-capture && git commit -m "feat(okf): capture engine"`

---

### Task okf-T6: `okf` CLI
**Wave:** 3
**Blocks:** okf-T7 | **Blocked by:** okf-T3, okf-T4, okf-T5
**Files:**
- Create `bin/okf` — zero-dep Node stdlib CLI; fail-closed; exit 0/2/3.
**Contract:** Verb signatures VERBATIM (spec component 2):
```
okf project [--scope global|project|job|all] [--repo <root>] [--run <slug>] [--target claude|agents|all]
okf capture [--from claude-memory] [--repo <root>] [--run <slug>]
okf sync    [--repo <root>] [--run <slug>]          # capture THEN project (the reconcile)
okf new <concept-slug> --scope <s> [--type <t>]     # scaffold a frontmatter'd file + update index
okf index   [--scope <s>]                            # regenerate index.md from dir contents
okf promote <concept-slug> --from job --to project   # job→project promotion (self-improvement path)
okf doctor  [--scope all]                            # validate frontmatter, index coverage, dangling links, orphans
```
Exit codes: `0` ok · `2` usage/validation error · `3` target-unwritable / integrity failure.
**Behavior:**
- `--repo` defaults to cwd's git root; `--run` defaults to active run slug.
- `sync` = capture (okf-T5) THEN project (okf-T4), ALWAYS in that order (ordering invariant — projection never clobbers un-captured native edit).
- All target writes = temp + atomic rename, never silent partial write.
- `doctor` validates: required frontmatter present, index coverage (no omitted children), no dangling relative links, no orphan files; clean KB passes, each defect flagged.
- `new` scaffolds frontmatter'd file AND updates parent `index.md`.
- `promote` delegates to okf-T3 promote (job→project re-home + both indexes).
- Usage error ⇒ exit 2; unwritable target / integrity failure ⇒ exit 3.
**Acceptance:** Run: `bin/okf doctor --scope all` against a fixture KB with a dangling link, a missing-frontmatter file, and an index omission → Expected: exit 2, each defect named; same on a clean KB → exit 0.
- [ ] Write tests covering behavior above
- [ ] Implement to contract
- [ ] Run acceptance → expected
- [ ] Commit: `git add bin/okf && git commit -m "feat(okf): CLI verbs project/capture/sync/new/index/promote/doctor"`

---

### Task okf-T7: Harness hook integration
**Wave:** 4
**Blocks:** — | **Blocked by:** okf-T6
**Files:**
- Create: `hooks/okf-session-start.sh` — thin shell calling `okf project --scope all`
- Create: `hooks/okf-sync.sh` — thin shell calling `okf sync`
- Wiring these two shells into the EXISTING global `core.hooksPath` dispatcher (convention per commits `74dee35`/`e178459`) is local git config, not a tracked repo file.
**Contract:** (spec component 7) Event → verb pinning:
- CLI session-start (claude/codex/cursor/opencode entry) → `okf project --scope all`
- harness wave-boundary + session-end → `okf sync`
**Behavior:**
- No new daemon; dispatcher routes the hooks.
- Hooks are THIN shells over `bin/okf`.
- Hook failure is LOGGED, non-fatal to the agent turn (projection best-effort-fresh, never blocks work) — EXCEPT `okf doctor` integrity failures surface as WARNINGS (no-ignored-signals).
- NOTE: wave-boundary auto-trigger only lights up once Plan A's runner emits that event; session-start + manual `okf sync` work standalone now.
**Acceptance:** Run: hook smoke test — session-start fires `okf project`; wave-boundary fires `okf sync`; injected hook failure is non-fatal (agent turn proceeds) → Expected: each event invokes the pinned verb; failure logged, exit non-fatal.
- [ ] Write tests covering behavior above
- [ ] Implement to contract
- [ ] Run acceptance → expected
- [ ] Commit: `git add hooks/okf-session-start.sh hooks/okf-sync.sh && git commit -m "feat(okf): harness hook integration"`

---

## Adapter-Config CLI Track

Adapter CLI + per-machine state. New files only, touch nothing Plan A owns. Unit-tested standalone against a FIXTURE catalog (real `presets/adapters.json` is Plan-A-gated → built in plan `harness-gated`). Exit-code floor: `0` ok / `2` usage / `3` unavailable.

### Task adp-T8: harness-adapter core + per-machine state writers
**Wave:** 1
**Blocks:** adp-T9 | **Blocked by:** —
**Files:**
- Create `bin/harness-adapter` — zero-dep Node CLI, the one read/write seam; this task = state-mutating verbs + state file lifecycle.
- Create `$HARNESS_HOME/adapters.json` (runtime artifact, NOT committed) — written by the verbs; document path + gitignore.
- Test `test/adapter-state.sh` — mutating verbs + atomicity (both branches).
**Contract:**
- State file `$HARNESS_HOME/adapters.json` shape: `{ "version":"adapters-state/v1", "state": { "<id>": { "enabled": bool, "config": {…}, "modelAllow"?: [..] } } }`.
- Verbs (exit `0`/`2`/`3`):
  - `harness-adapter enable <id>` / `harness-adapter disable <id>` → flip `state.<id>.enabled`; atomic write.
  - `harness-adapter config <id> <k=v...>` → set non-secret keys into `state.<id>.config`; secret-looking key (e.g. `api_key`, `*token*`, `*secret*`, `*password*`) ⇒ exit `2` with detail.
  - `harness-adapter set-models <id> <m...>` → set `state.<id>.modelAllow`; MUST be subset of catalog `models` for `<id>` else exit `2`.
**Behavior:**
- Absent state file ⇒ all adapters disabled (fail-closed); absent ≠ error for read of enabled-state.
- All writes atomic: temp-file + `rename` so a killed process never leaves a half-written state file.
- Unknown adapter id (not in catalog) ⇒ exit `2` with detail.
- `set-models` non-subset model ⇒ exit `2`; `config` secret-looking key ⇒ exit `2` (secrets never land in state — they go to `envFile` via `set-secret`, built in adp-T9).
- Malformed catalog/state ⇒ exit `2` with detail, never silent.
- Tests run against a FIXTURE catalog (the real `presets/adapters.json` is Plan-A-gated, built in plan `harness-gated`).
**Acceptance:** Run: `harness-adapter enable codex && harness-adapter set-models codex not-a-real-model` → Expected: enable exits `0`; set-models exits `2` (non-subset rejected); state file is valid `adapters-state/v1` JSON with `codex.enabled=true` and no half-write.
- [ ] Write tests covering behavior
- [ ] Implement to contract
- [ ] Run acceptance → expected
- [ ] Commit: `git add bin/harness-adapter test/adapter-state.sh && git commit -m "feat(adapter): harness-adapter core + per-machine state writers"`

---

### Task adp-T9: harness-adapter read/merge + secrets + available-models
**Wave:** 2
**Blocks:** — | **Blocked by:** adp-T8
**Files:**
- Modify `bin/harness-adapter` — add read/merge verbs, `set-secret`, `available-models` + cache.
- Create `$HARNESS_HOME/adapters.cache.json` (runtime artifact) — per-id available-models cache.
- Test `test/adapter-read.sh` — merge precedence, secret write-only, list-models cache (against FIXTURE catalog + stub wrapper).
**Contract:**
- `harness-adapter list [--json] [--probe]` / `harness-adapter show <id> [--json]` → merge catalog ⊕ state → effective descriptor: `{id, kind, enabled, models, availableModels?, config, health}` where `models = state.modelAllow ?? catalog.models`, `health ∈ healthy|down|unknown`. `--probe` runs each `<wrapper> --health`; without `--probe`, `health:"unknown"`.
- `harness-adapter set-secret <id> <KEY> [-]` → write credential to that adapter's catalog `envFile` (created `0600`, gitignored); value read from stdin (`-`) or prompt, NEVER argv; never echoed; no verb ever reads/logs/returns a secret value.
- `harness-adapter available-models <id> [--json]` → run `<wrapper> --list-models` IF catalog `listModels` true; cache to `$HARNESS_HOME/adapters.cache.json` per-id, TTL 24h; `--refresh` forces re-run. `listModels` false/absent ⇒ fall back to curated `models` only.
**Behavior:**
- FIXTURE-catalog note: tests run against a **fixture** catalog file (real `presets/adapters.json` is Plan-A-gated, built in plan `harness-gated`) + a **stub wrapper** for `--health` / `--list-models`. The `--health`/`--list-models` wrapper modes themselves are authored in plan `harness-gated` (ca-T1/T2/T3, same wrapper files, one editor) — this task only CONSUMES that contract.
- Merge precedence: `modelAllow` narrowing applied over catalog `models`; `enabled` from state; secrets absent from ALL output.
- Wrapper with no `--health` ⇒ `health:"unknown"` (never fabricate healthy); `--health` exit `0` ⇒ `healthy`, exit `3` ⇒ `down` + reason.
- Unknown adapter id ⇒ exit `2`; malformed catalog/state ⇒ exit `2` with detail, never a silent empty list.
- Cache miss / stale (>24h) / `--refresh` ⇒ re-run wrapper; cache hit within TTL ⇒ no wrapper run.
**Acceptance:** Run: `harness-adapter set-secret codex API_KEY - <<<'sek' && harness-adapter show codex --json` → Expected: secret written to `envFile` `0600`, absent from output; `show` JSON is an effective descriptor `{id,kind,enabled,models,availableModels?,config,health}` with `models` reflecting `modelAllow ?? catalog.models` and no secret value anywhere.
- [ ] Write tests covering behavior
- [ ] Implement to contract
- [ ] Run acceptance → expected
- [ ] Commit: `git add bin/harness-adapter test/adapter-read.sh && git commit -m "feat(adapter): harness-adapter read/merge + write-only secrets + available-models cache"`
