@shahboura/harness 3.0.4

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 (62) hide show
  1. package/.opencode/agents/developer.md +60 -0
  2. package/.opencode/agents/planner.md +51 -0
  3. package/.opencode/agents/reviewer.md +53 -0
  4. package/.opencode/commands/add-repo.md +26 -0
  5. package/.opencode/commands/dev-workflow.md +40 -0
  6. package/.opencode/commands/init-workspace.md +22 -0
  7. package/.opencode/commands/migrate-workspace.md +28 -0
  8. package/.opencode/commands/repo-map-refresh.md +23 -0
  9. package/.opencode/commands/story-workflow.md +24 -0
  10. package/.opencode/commands/workflow-status.md +23 -0
  11. package/.opencode/commands/workspace-config.md +27 -0
  12. package/.opencode/package.json +9 -0
  13. package/.opencode/plugins/harness-guards.ts +290 -0
  14. package/.opencode/skills/add-repo/SKILL.md +96 -0
  15. package/.opencode/skills/dev-workflow/SKILL.md +105 -0
  16. package/.opencode/skills/dev-workflow/shared/diagram-styling.md +65 -0
  17. package/.opencode/skills/dev-workflow/shared/engineering.md +14 -0
  18. package/.opencode/skills/dev-workflow/shared/status-block.md +29 -0
  19. package/.opencode/skills/dev-workflow/steps/analyze-comments.md +32 -0
  20. package/.opencode/skills/dev-workflow/steps/apply-fixes.md +24 -0
  21. package/.opencode/skills/dev-workflow/steps/comment-analysis.md +11 -0
  22. package/.opencode/skills/dev-workflow/steps/create-pr.md +39 -0
  23. package/.opencode/skills/dev-workflow/steps/develop-task.md +54 -0
  24. package/.opencode/skills/dev-workflow/steps/develop.md +60 -0
  25. package/.opencode/skills/dev-workflow/steps/fetch.md +34 -0
  26. package/.opencode/skills/dev-workflow/steps/fixup-task.md +12 -0
  27. package/.opencode/skills/dev-workflow/steps/gate.md +53 -0
  28. package/.opencode/skills/dev-workflow/steps/harden-task.md +12 -0
  29. package/.opencode/skills/dev-workflow/steps/harden.md +30 -0
  30. package/.opencode/skills/dev-workflow/steps/intake.md +52 -0
  31. package/.opencode/skills/dev-workflow/steps/metrics.md +21 -0
  32. package/.opencode/skills/dev-workflow/steps/plan-task.md +108 -0
  33. package/.opencode/skills/dev-workflow/steps/plan.md +80 -0
  34. package/.opencode/skills/dev-workflow/steps/pre-pr-fixes.md +16 -0
  35. package/.opencode/skills/dev-workflow/steps/pre-pr-review.md +14 -0
  36. package/.opencode/skills/dev-workflow/steps/pre-pr.md +14 -0
  37. package/.opencode/skills/dev-workflow/steps/preflight.md +32 -0
  38. package/.opencode/skills/dev-workflow/steps/quick-recheck.md +14 -0
  39. package/.opencode/skills/dev-workflow/steps/reconcile.md +27 -0
  40. package/.opencode/skills/dev-workflow/steps/review-task.md +32 -0
  41. package/.opencode/skills/dev-workflow/steps/security.md +18 -0
  42. package/.opencode/skills/dev-workflow/steps/triage-request.md +15 -0
  43. package/.opencode/skills/init-workspace/SKILL.md +155 -0
  44. package/.opencode/skills/migrate-workspace/SKILL.md +122 -0
  45. package/.opencode/skills/repo-map-refresh/SKILL.md +43 -0
  46. package/.opencode/skills/story-workflow/SKILL.md +78 -0
  47. package/.opencode/skills/story-workflow/commands/analyze.md +40 -0
  48. package/.opencode/skills/story-workflow/commands/groom.md +58 -0
  49. package/.opencode/skills/story-workflow/commands/improve.md +47 -0
  50. package/.opencode/skills/story-workflow/commands/refine.md +44 -0
  51. package/.opencode/skills/story-workflow/shared/context.md +22 -0
  52. package/.opencode/skills/story-workflow/shared/provider-io.md +73 -0
  53. package/.opencode/skills/story-workflow/templates/readiness-report.md +57 -0
  54. package/.opencode/skills/story-workflow/templates/story-template.md +51 -0
  55. package/.opencode/skills/story-workflow/templates/technical-notes.md +48 -0
  56. package/.opencode/skills/workflow-status/SKILL.md +30 -0
  57. package/.opencode/skills/workspace-config/SKILL.md +97 -0
  58. package/.opencode/tsconfig.json +11 -0
  59. package/CHANGELOG.md +396 -0
  60. package/LICENSE +21 -0
  61. package/README.md +351 -0
  62. package/package.json +32 -0
package/README.md ADDED
@@ -0,0 +1,351 @@
1
+ # ai-sdlc-harness · v3.0
2
+
3
+ **A governed multi-agent SDLC pipeline for Claude Code** — a ground-up rewrite of [ai-sdlc-harness](https://github.com/MostAshraf/ai-sdlc-harness). Drives a real engineering workflow — fetch → plan → proven-red TDD → review → security → PR → comment rounds → reconcile → metrics — across one or many repos. No application code lives here: only the pipeline manifest, the Python core that enforces it, and the agents, skills, and hooks that run it.
4
+
5
+ | Command | Purpose |
6
+ |---|---|
7
+ | `/init-workspace` | One-time setup interview: provider, repos, discovered toolchain, verification gate |
8
+ | `/dev-workflow <work-item-id>` | Take a work item from requirements to merged PR end-to-end |
9
+ | `/story-workflow <command> <work-item-id>` | Shape a story's quality before it's built: `analyze` · `refine` · `improve` · `groom` |
10
+ | `/workflow-status` | Read-only dashboard: cursor, tasks, gates, flagged events per run |
11
+ | `/workspace-config` | Change one config section without re-running the interview |
12
+ | `/add-repo` | Register one new repo into an already-bootstrapped workspace |
13
+ | `/migrate-workspace` | Adopt a v2.x workspace: config carries over, run history stays archived in place |
14
+ | `/repo-map-refresh` | Regenerate the auto-generated codebase map the planner grounds its plans in |
15
+
16
+ ## The rewrite, in one table
17
+
18
+ The original harness works, but almost all of its accumulated complexity compensates for one root cause: *orchestration logic lived in markdown prose an LLM had to faithfully execute, with hooks bolted on to catch the cases where it didn't.* The rewrite moves every mechanical rule into code that just runs, and reserves the model for judgment.
19
+
20
+ | Concern | ai-sdlc-harness (v2.x) | ai-sdlc-harness (v3.0) |
21
+ |---|---|---|
22
+ | Pipeline definition | Prose phase files the orchestrator re-derives every run; a separate hardcoded copy in guard scripts | One declared manifest ([pipeline/manifest.yaml](pipeline/manifest.yaml)) read by **both** the orchestrator and the enforcement layer — no second copy to drift |
23
+ | Git operations | Raw `git` calls reverse-engineered after the fact by `shlex`-parsing hooks | Owned entry points (`harness commit` / `merge-task` / `sync-branch` / `push` …); the raw verbs are blocked outright |
24
+ | Workflow state | `tracker.md`, honor-system updates | HMAC-chain-sealed `state.yaml` + append-only ndjson evidence ledgers — tamper-evident, `exit 3` on out-of-band edits |
25
+ | Human gates | The model reads your reply and acts on it | A hook captures your reply verbatim; deterministic code parses the decision — the model cannot approve on your behalf |
26
+ | TDD enforcement | Separate Tester and Developer subagents per task (token/latency tax) | One developer writes test + implementation; `harness verify-red` *proves* the failure and blob-SHA-locks the test set until completion |
27
+ | Providers | Markdown capability docs an agent must correctly read | Code modules behind one interface, each held to a shared contract test |
28
+ | Agents | 7 role files fusing permissions with procedure | 3 fixed tool-grant *shapes* (planner / developer / reviewer); procedure lives in shared step files |
29
+ | File-size budgets | Retrofitted after files passed 400 lines | ~100/200-line budget enforced from day one (`tools/budget_check.py`) |
30
+
31
+ ## Install
32
+
33
+ Not yet published to a plugin marketplace — run it from a local clone:
34
+
35
+ ```sh
36
+ git clone <this-repo> ai-sdlc-harness
37
+ claude --plugin-dir /path/to/ai-sdlc-harness
38
+ ```
39
+
40
+ Then, inside Claude Code:
41
+
42
+ ```
43
+ /init-workspace
44
+ ```
45
+
46
+ The interview asks only what it must (provider, repos), *discovers* your toolchain (proposing the test command it found), and offers **default** for everything else. It bootstraps a plugin-owned Python venv (PEP 668-safe — no system-python changes), ends with a verification gate — every check passes or you don't proceed — and writes per-section config under `.claude/context/`, a permission allowlist into `.claude/settings.json` (no manual `settings.json` editing needed), and the bootstrap marker.
47
+
48
+ ## Prerequisites
49
+
50
+ | Dependency | Why |
51
+ |---|---|
52
+ | **Claude Code** | The CLI that runs this harness. Install from [claude.ai/code](https://claude.ai/code). |
53
+ | **Git** | Branch management, per-task worktree isolation, owned commits. |
54
+ | **Python 3.10+** | The entire core is Python. `/init-workspace` creates the plugin's own venv with PyYAML as its first step; until then the guards print a one-line notice and stand down. |
55
+ | **Provider CLI, authed** *(optional)* | `gh auth login` / `glab auth login` / `az login`, if using that provider. MCP providers need their server connected. The `local-markdown` provider needs nothing at all. |
56
+
57
+ Target repos must be **cloned locally**, clean, and on their default branch when registered — the harness does not clone them. No language prerequisites; toolchains are discovered.
58
+
59
+ ---
60
+
61
+ ## Workflow at a Glance
62
+
63
+ ```mermaid
64
+ flowchart LR
65
+ classDef orch fill:#dde6fa,stroke:#446,stroke-width:1.5px,color:#013
66
+ classDef agent fill:#fff7d8,stroke:#b80,stroke-width:1px,color:#530
67
+ classDef human fill:#fff1de,stroke:#a60,stroke-width:1px,color:#420
68
+ classDef output fill:#eaf3ff,stroke:#36b,stroke-width:1px,color:#024
69
+
70
+ F([fetch + classify]):::orch
71
+ I[intake]:::agent
72
+ P[plan]:::agent
73
+ G1{approve-plan}:::human
74
+ PF[preflight]:::orch
75
+ D[develop — proven-red TDD per task]:::agent
76
+ G2{approve-impl}:::human
77
+ H[harden]:::agent
78
+ S[security scan]:::orch
79
+ G25{approve-security, when findings meet the threshold}:::human
80
+ QR[quick-recheck]:::orch
81
+ PP[pre-pr review]:::agent
82
+ G3{approve-pre-pr}:::human
83
+ FX[pre-pr-fixes]:::agent
84
+ CP[create-pr]:::output
85
+ AC[analyze-comments]:::agent
86
+ G4{select-comments}:::human
87
+ AF[apply-fixes]:::agent
88
+ RC[reconcile]:::output
89
+ M[metrics]:::output
90
+
91
+ F -->|full| I --> P --> G1
92
+ G1 -->|approved| PF
93
+ G1 -->|rejected| P
94
+ F -->|quick| PF
95
+ PF --> D
96
+ D -->|full| G2
97
+ G2 -->|approved| H --> S
98
+ G2 -->|rejected| D
99
+ S -->|below threshold| PP
100
+ S -->|findings meet the threshold| G25
101
+ G25 -->|waive / defer| PP
102
+ G25 -->|fix-now| D
103
+ D -->|quick| QR
104
+ QR -->|clean| PP
105
+ QR -->|dirty — escalate to full| S
106
+ PP --> G3
107
+ G3 -->|approved| CP
108
+ G3 -->|rejected| FX --> PP
109
+ CP --> RC --> M
110
+ CP -.->|on demand, repeatable| AC --> G4 --> AF -.-> RC
111
+ AF -.->|new comments| AC
112
+ ```
113
+
114
+ **Full mode** has three unconditional human gates (plan, implementation, pre-PR), one conditional gate (security — fires only when the aggregate finding severity meets the configured threshold, default `medium`), and one multi-pick gate (comment selection, inside the on-demand PR-comments group). **Quick mode** — trivial changes classified at fetch — keeps only the pre-PR gate. Everything between gates runs hands-off.
115
+
116
+ At **any** cursor position, an ad-hoc human request is legal: it spawns the reviewer in `request-triage` mode (a declared always-legal spawn), which classifies the request against the approved plan. Out-of-scope requests surface back to you with explicit options — never silently merged.
117
+
118
+ ## The Per-Task TDD Loop
119
+
120
+ ```mermaid
121
+ sequenceDiagram
122
+ autonumber
123
+ participant O as Orchestrator
124
+ participant C as harness CLI
125
+ participant D as Developer
126
+ participant R as Reviewer
127
+
128
+ Note over O: develop step — one task at a time, in a dedicated worktree
129
+ O->>C: worktree-add --task T1
130
+ O->>D: spawn with harness-mode develop headers
131
+ D->>D: write the failing tests from the plan's declared test-intents
132
+ Note over D: writes to non-test paths are hook-refused until the red-proof exists
133
+ D->>C: verify-red --task T1
134
+ Note over C: runs the test itself — must genuinely fail.<br/>Seals a chained red-proof + SHA-locks the test set
135
+ D->>D: implement until green, checkpoint via harness commit
136
+ D-->>O: status block SUCCESS
137
+ O->>C: task --id T1 --to in-review
138
+ Note over C: runs verify-green + red-proof check —<br/>a silently weakened test refuses the transition
139
+ O->>R: spawn with harness-mode review
140
+ R->>R: re-run build + tests independently, review the diff
141
+ R-->>O: verdict — hook-captured into reviews.ndjson
142
+ alt APPROVED
143
+ O->>C: task --to done, then merge-task (squash) + worktree-remove
144
+ else CHANGES_REQUESTED
145
+ O->>D: relay numbered findings — same worktree, bounded rounds
146
+ end
147
+ ```
148
+
149
+ `task --to done` mechanically **refuses** without a captured `APPROVED` verdict in the ledger — the orchestrator can't paraphrase a review into an approval. Rejection rounds are bounded (`review_rounds.max`, default 5); beyond that the rework transition is refused and you are escalated, on the theory that round N+ signals plan drift, not code drift.
150
+
151
+ ---
152
+
153
+ ## The Agent Shapes
154
+
155
+ Instead of one file per role fusing "who may do what" with "what procedure to follow", there are three fixed tool-grant **shapes**; the procedure lives in per-mode step files under [skills/dev-workflow/steps/](skills/dev-workflow/steps/) that the shape reads at spawn time.
156
+
157
+ | Shape | Modes | Tool grant | Guard-enforced restriction |
158
+ |---|---|---|---|
159
+ | **planner** | `intake` · `plan` · `repo-map` | Read/Grep/Glob/Write/Edit/Bash | Writes only under `ai/<run>/` and `.claude/context/` — never repo source |
160
+ | **developer** | `develop` · `harden` · `fixup` | Read/Grep/Glob/Write/Edit/Bash | Works only inside its task worktree; non-test writes refused until the task's red-proof is sealed |
161
+ | **reviewer** | `review` · `pre-pr` · `analyze-comments` · `request-triage` | Read/Grep/Glob/Bash | Strictly read-only — no Write/Edit granted, shell writes blocked (a literal `/tmp` scratch path is the one exception); builds and test runs allowed, so it verifies independently instead of trusting another agent's claim |
162
+
163
+ The **orchestrator** (the main Claude Code conversation running `/dev-workflow`) is deliberately thin: a coordinator that walks the manifest, spawns shapes with structured `harness-mode:` headers, and calls `harness` verbs. It never writes code, never touches run-authority files directly, and never runs raw git — the guards block those paths and point it back to the owned verbs.
164
+
165
+ ## Key Concepts
166
+
167
+ ### The run directory
168
+
169
+ Everything for one work item lives under a single directory keyed by date + work-item ID (same-day re-runs after an abort get a `-2`, `-3` suffix slot):
170
+
171
+ ```
172
+ ai/2026-07-08-PROJ-123/
173
+ ├── state.yaml # THE authority: cursor, tasks, artifacts, gate decisions — HMAC-chain-sealed
174
+ ├── work-item.json # fetched + provider-normalized work item
175
+ ├── requirements.md # intake output
176
+ ├── plan.md # edge cases, risk tiers, test-intents, approach options, diagrams
177
+ ├── events.ndjson # every deviation: test revisions, rejections, blocks, stalls, skipped gates
178
+ ├── tokens.ndjson # real per-invocation token spend
179
+ ├── reviews.ndjson # hook-captured reviewer verdicts (what "done" transitions check)
180
+ ├── human-input.ndjson # your gate replies, captured verbatim — never leaves the workspace
181
+ ├── .redproof/ # sealed per-task red-proofs (read only via `harness show-redproof`)
182
+ └── reports/ # security.md, pre-pr.md, metrics.md, coverage
183
+ ```
184
+
185
+ ### Sealed state and evidence ledgers
186
+
187
+ `state.yaml` is HMAC-chained (key at `.claude/context/.harness-key`, pinned into git's exclude list and refused by the commit verbs if ever staged). Any out-of-band edit fails verification: every CLI verb exits `3` and refuses to proceed until you inspect and run `harness reseal`. The ndjson ledgers are append-only and chained the same way. At gate crossings, `publish-mirror` commits a **path-exclusive** snapshot of `ai/<run>/` onto your feature branch — minus `human-input.ndjson`, the red-proofs, and lockfiles — so the audit trail travels with the PR while your raw chat text stays local.
188
+
189
+ ### Human gates
190
+
191
+ Your reply at a gate is captured verbatim by a `UserPromptSubmit` hook into `human-input.ndjson`; `harness gate --decide` then derives the decision from that evidence with a deterministic parser. The grammar:
192
+
193
+ - **`APPROVED`** — exactly. A qualified approval ("approved, but also…") is *not* an approval; it routes to ad-hoc request triage.
194
+ - **A numbered option** or the option's exact word (e.g. `2`, `waive`).
195
+ - **Rejection-side options may lead the reply and carry notes** (`rejected — split T2 into…`); forward decisions stay bare by design.
196
+ - The comment-selection gate additionally takes a comma-separated pick (`1,3`) or the literal `NONE`.
197
+
198
+ Gate presentations, skips (a conditional gate whose predicate didn't fire), and rejections are all ledgered; `/workflow-status` surfaces the flagged events.
199
+
200
+ ### Proof-anchored TDD
201
+
202
+ The property that matters — *the test genuinely failed before the fix existed* — without the original's two-agent tax:
203
+
204
+ 1. The developer writes the failing tests first. This ordering is **hook-enforced**, not advisory: until the red-proof exists, writes to non-test paths in the worktree are refused (test paths, fixtures, and build manifests for test dependencies stay writable — patterns configurable per language in [config/defaults/workflow.yaml](config/defaults/workflow.yaml)).
205
+ 2. `harness verify-red` runs the test itself — it must fail — then seals a chained red-proof and blob-SHA-locks the test files plus their declared closure (shared fixtures, `conftest.py`, …).
206
+ 3. The completion transition runs `verify-green` **and** re-checks the locked SHAs: a quietly weakened assertion refuses the transition.
207
+ 4. A genuinely wrong test is revised via `verify-red --revise --reason "…"` — an explicit, reviewer-visible flagged event, never a silent edit.
208
+ 5. Tasks a plan explicitly marks with no test-intents (docs, chores) are the approved opt-out: verify-red refuses, the completion guard exempts, review still applies.
209
+
210
+ ### Owned git entry points
211
+
212
+ Once a workspace has completed `/init-workspace`, raw commit-creating / history-rewriting git verbs (`commit`, `merge`, `rebase`, `cherry-pick`, `revert`, `am`, `pull`, `push`) are blocked for Claude for the life of that workspace — the guard cannot know a "harness" commit from any other, so it blocks the whole verb rather than trying to tell one commit's intent from another's (your own terminal outside Claude Code is unaffected, and a session that has never run `/init-workspace` sees ordinary git — see [Guardrail Hooks](#guardrail-hooks)). Mutations go through owned verbs that validate, execute, and ledger in one place: `commit` (declared classes `working`/`wip`, `--fixup-of`), `merge-task` (squash / `--autosquash` fold), `worktree-add`/`worktree-remove`, `sync-branch` (owned rebase), `push` (`--force-with-lease`), and `publish-mirror`. Branch and commit naming come from [config/defaults/naming.yaml](config/defaults/naming.yaml).
213
+
214
+ ### Quick mode — with a mechanical escape hatch
215
+
216
+ Trivial changes (explicit `Mode: quick` hint in the work item, no risk keywords) run the short pipeline: no plan step, no red-proof machinery, one gate. Because eligibility was classified *before code existed*, `quick-recheck` re-examines the **real diff** after develop: touching disqualifying patterns (security/auth/migration/API paths) or exceeding size caps (80 changed lines / 5 files, configurable) triggers the declared escalation edge into full mode's security step — forcibly, not at the model's discretion.
217
+
218
+ ### The security step
219
+
220
+ `security-scan` runs each repo's configured `scan_cmd`, parses severities via the configured regex, and records the aggregate maximum. The gate fires only at or above `security.gate_threshold` (default `medium`), with three dispositions: **fix-now** (routes back to develop), **waive** (recorded, pipeline continues), **defer** (pipeline continues *and* a follow-up work item is created via the provider, with paired ledger events so an in-flight, completed, or dropped deferral are distinguishable states).
221
+
222
+ ### Multi-repo runs
223
+
224
+ A work item spanning repos gets per-repo task lanes; cross-repo API contracts are declared in the plan and mechanically re-checked at reconcile time (`reconcile-contracts` greps each declared fragment across the other repos' sources, excluding test paths and the committed `ai/**` mirrors). Sync points fail closed: the cursor cannot leave `develop` while any task in any lane is non-terminal.
225
+
226
+ ### The repo map
227
+
228
+ `/init-workspace` (optionally) and `/repo-map-refresh` generate a tiered codebase map under `.claude/context/repo-map/` — directories and modules by purpose, key abstractions, notable patterns — stamped with the SHA it was generated at and flagged stale after 50 commits (configurable). A map with no content cannot be stamped, so an empty generation can never be certified fresh. The planner's intake and plan instructions point it at the map directly (index first, then only the areas the story touches) instead of re-deriving the codebase from scratch every run. Auto-generated only, never hand-maintained: corrections go through regeneration.
229
+
230
+ ## Guardrail Hooks
231
+
232
+ One Python entry point ([hooks/guards.py](hooks/guards.py)) handles every event, registered in [hooks/hooks.json](hooks/hooks.json). Guards scope themselves to workspaces with a live harness run (resolved from `CLAUDE_PROJECT_DIR` first, so a drifted shell `cwd` can't dodge them) — with one exception: the raw-git block is standing rather than run-scoped, active for the life of any workspace that has completed `/init-workspace` (same `CLAUDE_PROJECT_DIR`-first resolution, checking for the bootstrap marker instead of a live run) regardless of whether a run currently exists. It is still not global — a session that has never run `/init-workspace` sees ordinary git. Two documented residuals: a session rooted directly in a repo registered to a *sibling* workspace, rather than the workspace itself, isn't recognized as belonging to it (nothing today points from a registered repo back to the workspace that owns it); and the bootstrap marker itself (`.claude/context/overrides.yaml`) is an ordinary, non-chain-sealed config file — a direct edit stripping it can silently turn the block back off, a capability the pre-change unconditional block never had. Both accepted deliberately rather than closed in this pass — see `_is_harness_workspace`'s docstring.
233
+
234
+ | Guard | Event · Matcher | What it enforces |
235
+ |---|---|---|
236
+ | bash | PreToolUse · Bash | Blocks raw history-mutating git inside any workspace that has completed `/init-workspace` and points to the owned verbs. Role-aware shell-write analysis (quote-masked shape matching on redirects, `tee`, `cp`/`mv`, in-place editors): reviewer writes only to literal `/tmp` paths; developer confined to its worktree; secret/evidence files unreadable. |
237
+ | write | PreToolUse · Write/Edit/MultiEdit/NotebookEdit | Path confinement per shape (planner → `ai/<run>/` + `.claude/context/`; developer → its worktree with the pre-red test-first lock; reviewer → nothing) plus sensitive-file patterns. |
238
+ | spawn | PreToolUse · Agent/Task | Only the spawn-set the manifest declares for the current cursor is legal — shape *and* `harness-mode:` header both checked; out-of-run spawns (e.g. repo-map generation) must be declared in [pipeline/surfaces.yaml](pipeline/surfaces.yaml). Fail-closed. |
239
+ | skill | PreToolUse · Skill | USER-ENTRY skills (`/dev-workflow`, `/init-workspace`, …) refuse invocation from subagents or autonomous triggering — they run only when you ran them. |
240
+ | read | PreToolUse · Read/Grep | Red-proofs are readable by harness shapes only via `harness show-redproof` (chain-verified) — a raw `.redproof/` read skips integrity verification and is blocked. |
241
+ | prompt capture | UserPromptSubmit | Verbatim capture of your replies into `human-input.ndjson` — the only evidence `gate --decide` accepts. |
242
+ | verdict capture | PostToolUse · Agent/Task | The authoritative writer of `reviews.ndjson` (reviewer verdicts) and missing-status-block events — anchored here because this payload deterministically carries both the spawn prompt and the agent's final reply. |
243
+ | stop capture | SubagentStop | Per-invocation token accounting into `tokens.ndjson`; secondary status-block capture. |
244
+
245
+ Every guard's fail-open/fail-closed policy is chosen deliberately and tested: recognised violations always block; the spawn guard is fail-closed even on ambiguity.
246
+
247
+ ## The `harness` CLI
248
+
249
+ All ~49 owned verbs run through the wrapper `bin/harness` (resolves the plugin venv in either OS layout, falls back to system `python3`/`python`). It runs on macOS, Linux, and Windows — on Windows it executes under Git Bash, with `bin/harness.cmd` as the cmd.exe sibling. Agents call it; you rarely need to — except `abort`.
250
+
251
+ | Group | Verbs |
252
+ |---|---|
253
+ | Workspace setup | `init` · `discover` · `ensure-default-branch` · `init-verify` · `init-section` · `init-finalize` · `add-repo` · `migrate-detect` · `migrate-extract` · `resolve-model` · `resolve-coverage-cmd` |
254
+ | Pipeline steps | `fetch` · `preflight` · `plan-register` · `quick-recheck` · `security-scan` · `reconcile-contracts` · `create-pr` · `fetch-pr-comments` · `reconcile` · `write-back` · `metrics` |
255
+ | State & evidence | `bootstrap` · `cursor` · `task` · `artifact` · `gate` · `stall` · `log-event` · `verify` · `show` · `status` · `abort` · `complete` · `reseal` |
256
+ | TDD proof | `verify-red` (and `--revise`) · `show-redproof` |
257
+ | Git (owned) | `worktree-add` · `worktree-remove` · `commit` · `merge-task` · `sync-branch` · `push` · `publish-mirror` |
258
+ | Providers & misc | `provider` · `provider-normalize` · `validate-mermaid` · `repo-map-check` · `repo-map-stamp` |
259
+
260
+ Exit codes: `0` ok · `1` refused (read the JSON `error`) · `2` usage error · `3` integrity violation (a sealed file changed out-of-band — recover with `harness reseal` after review).
261
+
262
+ ## Providers
263
+
264
+ Work-item integrations are code modules behind one interface — callers name an *operation* (`work_item.fetch`, `work_item.create`, `create-pr`, …), never a provider — and every module must pass the shared contract test in [tests/test_providers.py](tests/test_providers.py). Adding a provider = implementing the interface.
265
+
266
+ | Provider | Transport | Needs |
267
+ |---|---|---|
268
+ | `local-markdown` | files | Nothing — work items are `.md` files in a configured stories directory |
269
+ | `github` | CLI (`gh`) | `gh auth login` |
270
+ | `gitlab` | CLI (`glab`) | `glab auth login` |
271
+ | `ado` | CLI (`az boards`) | `az login` + DevOps extension |
272
+ | `ado-mcp` | MCP | Azure DevOps MCP server connected |
273
+ | `jira` | MCP | Jira MCP server connected |
274
+ | `zoho` | MCP | Zoho MCP server connected |
275
+
276
+ CLI/file-transport providers execute inside the harness process. MCP-transport providers can't be script-called: the module declares a tool **mapping**, the model invokes the MCP tool, and pipes the raw result to `harness fetch --from-raw` for the same shared normalize + bootstrap path.
277
+
278
+ ## Project Structure
279
+
280
+ ```
281
+ ai-sdlc-harness/
282
+ ├── .claude-plugin/plugin.json # plugin manifest
283
+ ├── pipeline/
284
+ │ ├── manifest.yaml # THE pipeline source of truth: steps, modes, gates, escalations
285
+ │ ├── task-fsm.yaml # legal task-status transitions
286
+ │ └── surfaces.yaml # subagent shapes, write surfaces, out-of-run spawns
287
+ ├── config/defaults/ # shipped knobs (workflow, naming, quick-mode, review-policy,
288
+ │ # status-mapping, subagent-models) — overrides live in the
289
+ │ # workspace's .claude/context/
290
+ ├── harness/ # the Python core behind every owned verb
291
+ │ ├── cli.py # verb surface
292
+ │ ├── workflow.py # step implementations
293
+ │ ├── state.py · transitions.py · gates.py · chain.py # sealed state + FSM + gate parser
294
+ │ ├── gitops.py # owned git machinery
295
+ │ ├── migrate.py # v2.x workspace adoption (the fork seam)
296
+ │ └── providers/ # code-modular provider adapters
297
+ ├── hooks/
298
+ │ ├── hooks.json # hook registrations
299
+ │ └── guards.py # all guard + capture logic (one entry point)
300
+ ├── agents/ # the 3 shapes: planner.md, developer.md, reviewer.md
301
+ ├── skills/
302
+ │ ├── dev-workflow/ # thin orchestrator walker + per-step instruction files
303
+ │ └── init-workspace/ · add-repo/ · migrate-workspace/ · workspace-config/ · workflow-status/ · repo-map-refresh/
304
+ ├── bin/harness # wrapper script resolving the plugin venv (+ harness.cmd for Windows)
305
+ ├── tools/ # meta-tooling: line-budget checker, sandbox workspace generators
306
+ └── tests/ # 606 stdlib-unittest tests
307
+ ```
308
+
309
+ Workspace artifacts — `ai/<date>-<id>/` and `.claude/context/` — are generated inside *your* working directory by `/init-workspace` and the pipeline. They never live inside this plugin repo.
310
+
311
+ ## Development
312
+
313
+ Requires Python 3.10+ and PyYAML. CI runs the suite on Linux, macOS, and Windows — all three lanes enforcing.
314
+
315
+ ```sh
316
+ python3 -m venv .venv && .venv/bin/pip install pyyaml
317
+ .venv/bin/python -m harness.schema # validate all declared data against the fixed vocabulary
318
+ .venv/bin/python tools/budget_check.py # line budget + duplication sweep
319
+ .venv/bin/python -m unittest discover -s tests
320
+ ```
321
+
322
+ On Windows the venv lands its interpreter under `Scripts\` instead of `bin/`:
323
+
324
+ ```powershell
325
+ python -m venv .venv; .venv\Scripts\pip install pyyaml
326
+ .venv\Scripts\python -m unittest discover -s tests
327
+ ```
328
+
329
+ The test suite (606 tests) covers the state engine, gate grammar, guard behavior (via subprocess against real payloads), provider contracts, git machinery against real temp repos, breadth walks of both pipeline modes, composability probes (a scratch mode and scratch step must validate and walk with zero Python changes), Windows-only guard path shapes, and meta-checks (invocation consistency, declared-data schema, line budgets). See [CHANGELOG.md](CHANGELOG.md) for release history.
330
+
331
+ ## FAQ
332
+
333
+ **Can I resume after closing the terminal?** Yes — `state.yaml` *is* the resume point. Start a new session in the same workspace, run `/workflow-status` to see where you are, then `/dev-workflow <id>`: it detects the live run and offers **Resume or Abort** — never clobbers.
334
+
335
+ **How do I abandon a run?** `harness abort --run <run> --reason "<why>"` (via the plugin's `bin/harness`). Terminal: sweeps worktrees, keeps the full audit trail, frees the work item. A same-day re-run gets a fresh `-2` suffix slot; nothing is deleted.
336
+
337
+ **What does exit code 3 mean?** A sealed file (`state.yaml`, a ledger, a red-proof) changed outside the owned verbs. Every verb refuses until you inspect the diff and run `harness reseal` — deliberately loud, because silent tolerance would make the audit trail worthless.
338
+
339
+ **Why can't Claude run `git commit` in my harness workspace?** The guard can't distinguish a harness commit from any other, so it blocks the whole verb for the life of any workspace that has completed `/init-workspace` — regardless of whether a run is currently active. Your own terminal outside Claude Code is unaffected, and a project that has never run `/init-workspace` is unaffected too. If you need raw git inside a bootstrapped workspace, disable the plugin for that session.
340
+
341
+ **What if the reviewer keeps rejecting?** Rounds are bounded (`review_rounds.max`, default 5). Beyond that the rework transition is refused and you're escalated — persistent rejection signals plan drift, not code drift.
342
+
343
+ **What if an agent stalls or returns garbage?** A missing/invalid status block is detected mechanically; the orchestrator re-invokes with a continuation prompt (bounded, default 2), then escalates to you (default 3). It never acts on the agent's behalf.
344
+
345
+ **What if a test is genuinely wrong after it was proven red?** `harness verify-red --revise --reason "<why>"` — the revision is sealed and flagged in the events ledger, reviewer-visible. There is no silent path.
346
+
347
+ **What happens on a security finding?** Below the threshold: recorded, pipeline continues. At/above: the gate fires with **fix-now** (back to develop), **waive** (recorded), or **defer** — defer also creates a follow-up work item through your provider, with paired events so a dropped deferral is detectable.
348
+
349
+ **How do I trust what a run did?** Read the ledgers. `events.ndjson` records every deviation (test revisions, gate rejections, blocked actions, stalls, skipped gates), `tokens.ndjson` the real spend, `reviews.ndjson` the verdicts, and the chained `state.yaml` the decisions — all mirrored onto the feature branch at gate crossings (minus your raw replies). `/workflow-status` and `harness metrics` are deterministic projections of the same files.
350
+
351
+ **I'm coming from ai-sdlc-harness v2.x — can I migrate my workspace?** Yes — run `/migrate-workspace` in it. Config carries over as per-section proposals you confirm (provider, repos, test commands, stories directory), applied through the same verification gate as a fresh setup; the old markdown configs are archived to `.claude/context/legacy-2.1/` with a migration report. Run history is never converted — v3.0 state is sealed evidence v2.x never produced — so old `ai/` run dirs stay in place as readable archives, and in-flight v2.x stories finish on v2.x (or are abandoned) before you switch. Existing `local-markdown` stories work in place, including their `> Status:` blockquotes.
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "@shahboura/harness",
3
+ "version": "3.0.4",
4
+ "description": "Governed multi-agent SDLC pipeline for opencode",
5
+ "author": "Mostafa Ashraf",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/shahboura/ai-sdlc-harness-opencode.git"
10
+ },
11
+ "files": [
12
+ ".opencode/agents/",
13
+ ".opencode/commands/",
14
+ ".opencode/plugins/",
15
+ ".opencode/skills/",
16
+ ".opencode/package.json",
17
+ ".opencode/tsconfig.json",
18
+ "README.md",
19
+ "LICENSE",
20
+ "CHANGELOG.md"
21
+ ],
22
+ "publishConfig": {
23
+ "access": "public"
24
+ },
25
+ "engines": {
26
+ "node": ">=18.0.0"
27
+ },
28
+ "devDependencies": {
29
+ "@opencode-ai/plugin": "^1.17.0",
30
+ "typescript": "^5.0.0"
31
+ }
32
+ }