cohorte 2.1.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/CHANGELOG.md +173 -0
  2. package/README.md +49 -41
  3. package/bin/cli.js +324 -28
  4. package/core/adapter/render.js +389 -0
  5. package/core/agents/implementer.template.md +3 -3
  6. package/core/agents/release.md +7 -6
  7. package/core/agents/review.md +17 -2
  8. package/core/commands/cohorte-audit.md +2 -0
  9. package/core/commands/cohorte-brainstorm.md +3 -11
  10. package/core/commands/cohorte-build.md +37 -23
  11. package/core/commands/cohorte-doctor.md +61 -36
  12. package/core/commands/cohorte-fix.md +3 -5
  13. package/core/commands/cohorte-init-pipeline.md +7 -8
  14. package/core/commands/cohorte-patch.md +113 -0
  15. package/core/commands/cohorte-refactor.md +5 -2
  16. package/core/commands/cohorte-review.md +20 -18
  17. package/core/commands/cohorte-ship.md +13 -14
  18. package/core/commands/cohorte-spec.md +4 -13
  19. package/core/commands/cohorte-update-pipeline.md +13 -12
  20. package/core/hooks/gate.py +203 -16
  21. package/core/runtimes/claude.json +73 -0
  22. package/core/runtimes/codex.json +82 -0
  23. package/core/runtimes/cursor.json +75 -0
  24. package/core/runtimes/gemini.json +75 -0
  25. package/core/runtimes/opencode.json +72 -0
  26. package/core/templates/patch.template.md +86 -0
  27. package/core/templates/spec.template.md +1 -3
  28. package/core/templates/steps/init-pipeline/01-detect-stack.md +1 -1
  29. package/core/templates/steps/init-pipeline/02-interview-gaps.md +1 -11
  30. package/core/templates/steps/init-pipeline/04-write-render.md +23 -17
  31. package/core/templates/steps/init-pipeline/05-report.md +1 -1
  32. package/core/workflows/review.js +1 -3
  33. package/dashboard/dist/assets/{index-P1I1JGtj.js → index-D1rsbLat.js} +1 -1
  34. package/dashboard/dist/index.html +1 -1
  35. package/dashboard/server/doctor.js +156 -69
  36. package/dashboard/server/index.js +12 -2
  37. package/dashboard/server/metrics.js +13 -6
  38. package/dashboard/server/runtime.js +115 -0
  39. package/dashboard/server/versions.js +12 -1
  40. package/install.ps1 +26 -3
  41. package/install.sh +28 -6
  42. package/package.json +6 -2
  43. package/profile/PIPELINE.template.md +8 -6
  44. package/profile/SCHEMA.md +70 -108
  45. package/profile/cohorte.config.template.yaml +0 -16
  46. package/scripts/kanban-move.sh +11 -1
  47. package/scripts/metrics/collect.mjs +5 -3
  48. package/scripts/preflight.sh +27 -8
  49. package/scripts/test-adapter.mjs +368 -0
  50. package/scripts/test-dashboard.mjs +70 -0
  51. package/scripts/test-gate.mjs +62 -0
  52. package/scripts/validate-core.mjs +26 -24
  53. package/core/commands/cohorte-loop.md +0 -110
  54. package/scripts/loop-detach.sh +0 -153
  55. package/scripts/loop.sh +0 -399
  56. package/scripts/telemetry-send.sh +0 -77
  57. package/scripts/test-loop.mjs +0 -330
@@ -0,0 +1,75 @@
1
+ {
2
+ "id": "cursor",
3
+ "label": "Cursor",
4
+ "docs": "https://cursor.com/docs/cli/overview",
5
+ "detect": [
6
+ "~/.cursor"
7
+ ],
8
+ "notes": [
9
+ "Cursor commands are plain markdown with NO placeholder substitution: whatever the human",
10
+ "types after the command name is appended to the prompt. The adapter therefore explains the",
11
+ "argument placeholder in the preamble rather than rewriting it to a token.",
12
+ "Subagents are markdown + frontmatter, and `readonly: true` is a real tool restriction \u2014",
13
+ "the review agent gets it. Cursor also reads .claude/agents and .codex/agents.",
14
+ "Hooks use Cursor's own envelope ({\"permission\": \u2026}), not Claude's.",
15
+ "Model pins are NOT carried across: the profile names Anthropic aliases (sonnet/haiku), which are meaningless here and would either error or be silently ignored. Agents and commands inherit this runtime's own model selection instead."
16
+ ],
17
+ "scopes": {
18
+ "global": {
19
+ "root": "~/.cursor",
20
+ "core": "~/.cohorte/cursor",
21
+ "commands": "~/.cursor/commands",
22
+ "agents": "~/.cursor/agents",
23
+ "hooks": "~/.cohorte/cursor/hooks",
24
+ "hooks_config": "~/.cursor/hooks.json",
25
+ "workflows": null,
26
+ "settings": null
27
+ },
28
+ "project": {
29
+ "root": ".cursor",
30
+ "core": ".cohorte/cursor",
31
+ "commands": ".cursor/commands",
32
+ "agents": ".cursor/agents",
33
+ "hooks": ".cohorte/cursor/hooks",
34
+ "hooks_config": ".cursor/hooks.json",
35
+ "workflows": null,
36
+ "settings": null
37
+ }
38
+ },
39
+ "exclude_commands": [],
40
+ "command": {
41
+ "format": "md",
42
+ "ext": ".md",
43
+ "frontmatter": [],
44
+ "args": null,
45
+ "args_note": "the text you typed after the command name",
46
+ "invoke": "/<name>"
47
+ },
48
+ "agent": {
49
+ "format": "md",
50
+ "ext": ".md",
51
+ "frontmatter": [
52
+ "name",
53
+ "description"
54
+ ],
55
+ "readonly_key": "readonly",
56
+ "readonly_value": "true",
57
+ "dispatch": "invoke it as /<name> <brief>, or name it in the prompt; several run in parallel",
58
+ "address": "/<name>"
59
+ },
60
+ "hook": {
61
+ "format": "cursor",
62
+ "event": "beforeShellExecution",
63
+ "supports_ask": true,
64
+ "config_shape": "json"
65
+ },
66
+ "capabilities": {
67
+ "subagents": true,
68
+ "hooks": true,
69
+ "workflows": false,
70
+ "mcp": true,
71
+ "tool_restriction": true
72
+ },
73
+ "memory": "AGENTS.md",
74
+ "headless": "cursor-agent -p"
75
+ }
@@ -0,0 +1,75 @@
1
+ {
2
+ "id": "gemini",
3
+ "label": "Gemini CLI",
4
+ "docs": "https://geminicli.com/docs/",
5
+ "detect": [
6
+ "~/.gemini"
7
+ ],
8
+ "notes": [
9
+ "Commands are TOML, not markdown: the whole prompt body becomes the `prompt` key, and the",
10
+ "argument placeholder is {{args}}. Sub-directories namespace with ':'.",
11
+ "Subagents ARE markdown + frontmatter (.gemini/agents), and are exposed to the main agent as",
12
+ "a tool of the same name; @agent-name forces one. They cannot call other subagents.",
13
+ "Hooks live in settings.json under `hooks`, event `BeforeTool`, matched on the tool name",
14
+ "(run_shell_command). The envelope is {\"decision\": \"deny\", \"reason\": \u2026} with no ask tier.",
15
+ "Model pins are NOT carried across: the profile names Anthropic aliases (sonnet/haiku), which are meaningless here and would either error or be silently ignored. Agents and commands inherit this runtime's own model selection instead."
16
+ ],
17
+ "scopes": {
18
+ "global": {
19
+ "root": "~/.gemini",
20
+ "core": "~/.cohorte/gemini",
21
+ "commands": "~/.gemini/commands",
22
+ "agents": "~/.gemini/agents",
23
+ "hooks": "~/.cohorte/gemini/hooks",
24
+ "hooks_config": "~/.gemini/settings.json",
25
+ "workflows": null,
26
+ "settings": "~/.gemini/settings.json"
27
+ },
28
+ "project": {
29
+ "root": ".gemini",
30
+ "core": ".cohorte/gemini",
31
+ "commands": ".gemini/commands",
32
+ "agents": ".gemini/agents",
33
+ "hooks": ".cohorte/gemini/hooks",
34
+ "hooks_config": ".gemini/settings.json",
35
+ "workflows": null,
36
+ "settings": ".gemini/settings.json"
37
+ }
38
+ },
39
+ "exclude_commands": [],
40
+ "command": {
41
+ "format": "toml",
42
+ "ext": ".toml",
43
+ "frontmatter": [
44
+ "description"
45
+ ],
46
+ "args": "{{args}}",
47
+ "invoke": "/<name>"
48
+ },
49
+ "agent": {
50
+ "format": "md",
51
+ "ext": ".md",
52
+ "frontmatter": [
53
+ "name",
54
+ "description"
55
+ ],
56
+ "dispatch": "the subagent is exposed as a tool of the same name; @<name> forces it",
57
+ "address": "@<name>"
58
+ },
59
+ "hook": {
60
+ "format": "gemini",
61
+ "event": "BeforeTool",
62
+ "matcher": "run_shell_command",
63
+ "supports_ask": false,
64
+ "config_shape": "settings"
65
+ },
66
+ "capabilities": {
67
+ "subagents": true,
68
+ "hooks": true,
69
+ "workflows": false,
70
+ "mcp": true,
71
+ "tool_restriction": false
72
+ },
73
+ "memory": "GEMINI.md",
74
+ "headless": "gemini -p"
75
+ }
@@ -0,0 +1,72 @@
1
+ {
2
+ "id": "opencode",
3
+ "label": "OpenCode",
4
+ "docs": "https://opencode.ai/docs/cli/",
5
+ "detect": [
6
+ "~/.config/opencode",
7
+ "~/.opencode"
8
+ ],
9
+ "notes": [
10
+ "Markdown agents in agents/ with `mode: subagent`, addressed with @<name>.",
11
+ "The one runtime here with NO hook mechanism \u2014 extension is via plugins (.opencode/plugin/*.js),",
12
+ "which is a different contract from a blocking PreToolUse hook. The gate therefore runs in",
13
+ "explicit --check mode, which is advisory by construction.",
14
+ "`opencode run` is the headless primitive; --agent selects an agent.",
15
+ "Model pins are NOT carried across: the profile names Anthropic aliases (sonnet/haiku), which are meaningless here and would either error or be silently ignored. Agents and commands inherit this runtime's own model selection instead."
16
+ ],
17
+ "scopes": {
18
+ "global": {
19
+ "root": "~/.config/opencode",
20
+ "core": "~/.cohorte/opencode",
21
+ "commands": "~/.config/opencode/commands",
22
+ "agents": "~/.config/opencode/agents",
23
+ "hooks": null,
24
+ "workflows": null,
25
+ "settings": null
26
+ },
27
+ "project": {
28
+ "root": ".opencode",
29
+ "core": ".cohorte/opencode",
30
+ "commands": ".opencode/commands",
31
+ "agents": ".opencode/agents",
32
+ "hooks": null,
33
+ "workflows": null,
34
+ "settings": null
35
+ }
36
+ },
37
+ "exclude_commands": [],
38
+ "command": {
39
+ "format": "md",
40
+ "ext": ".md",
41
+ "frontmatter": [
42
+ "description",
43
+ "agent",
44
+ "subtask"
45
+ ],
46
+ "args": "$ARGUMENTS",
47
+ "invoke": "/<name>"
48
+ },
49
+ "agent": {
50
+ "format": "md",
51
+ "frontmatter": [
52
+ "description",
53
+ "mode"
54
+ ],
55
+ "defaults": {
56
+ "mode": "subagent"
57
+ },
58
+ "dispatch": "address the subagent with @<name> and give it the full brief in one message",
59
+ "address": "@<name>",
60
+ "ext": ".md"
61
+ },
62
+ "capabilities": {
63
+ "subagents": true,
64
+ "hooks": false,
65
+ "workflows": false,
66
+ "mcp": true,
67
+ "tool_restriction": false
68
+ },
69
+ "memory": "AGENTS.md",
70
+ "headless": "opencode run",
71
+ "hook": null
72
+ }
@@ -0,0 +1,86 @@
1
+ ---
2
+ feature_id: patch-<slug>
3
+ kind: patch # a bug fix, not a feature — /cohorte-build skips contract authoring, /cohorte-ship commits `fix(…)`
4
+ title: <one line: the bug, from the user's side>
5
+ status: draft # draft → frozen → in-progress → in-review → shipped · blocked (see SCHEMA.md §Spec status)
6
+ severity: <critical | major | minor> # drives nothing mechanical; it is what the human triages on
7
+ branch: <patch_branch_prefix><feature_id>
8
+ created: <YYYY-MM-DD>
9
+ reviewed_base: # merge-base sha at the last SHIP verdict — freshness-gate anchor (written by /cohorte-review)
10
+ reviewed_digest: # sha256 (16 hex) of the reviewed source diff vs reviewed_base, specs excluded — /cohorte-ship re-checks
11
+ design_files: [] # omit unless the fix is itself visual
12
+ ---
13
+
14
+ # <one line: the bug, from the user's side>
15
+
16
+ ## 1. Symptom & repro
17
+
18
+ > What the user sees, and the shortest deterministic path to see it. A repro nobody can run is a
19
+ > hypothesis, not a bug — say so here rather than pretending otherwise.
20
+
21
+ - **Observed:** …
22
+ - **Expected:** …
23
+ - **Repro:** 1. … 2. … 3. …
24
+ - **Since / trigger:** <version, commit, or "unknown">
25
+
26
+ ## 2. Impact
27
+
28
+ > Who is affected, how often, and whether there is a workaround. This is what justifies the patch
29
+ > jumping the feature queue — one or two lines.
30
+
31
+ ## 3. Cause
32
+
33
+ > Confirmed or hypothesis — label which. Name the `file:line` if you have it. If the cause is
34
+ > unknown at freeze, say so: the implementer's first job is then to find it, and the review will
35
+ > check that the stated cause matches the diff.
36
+
37
+ ## 4. Regression test (red first)
38
+
39
+ > The heart of a patch spec — it replaces §5 CONTRACT as the thing the diff is checked against.
40
+ > Name the test file and what it asserts. It must fail on the current code and pass after the fix.
41
+
42
+ - **Test:** `<path/to/test>` · asserts: …
43
+ - **Fails today because:** …
44
+
45
+ ## 5. Contract delta
46
+
47
+ > Usually `none`. A fix that *changes* an existing contract entry describes the change here and
48
+ > `/cohorte-build` propagates it to every surface that names it. A fix that needs **new** contract
49
+ > surface area is not a patch — stop and run `/cohorte-spec` instead.
50
+
51
+ `none`
52
+
53
+ ## 6. Surface tasks
54
+
55
+ > One `###` subsection per surface the fix touches — a patch may legitimately span several (an
56
+ > unvalidated field on the API *and* the form that sends it). Each TDD: the §4 test first.
57
+
58
+ ### <surface.key>
59
+
60
+ - …
61
+
62
+ ## 7. Out of scope
63
+
64
+ > The adjacent things this patch deliberately does NOT fix. This is the section that keeps a bug fix
65
+ > from turning into a refactor — the reviewer reads it before flagging what you left alone. Real but
66
+ > out-of-scope findings belong in `specs/refactor-backlog.md`, not in this diff.
67
+
68
+ - …
69
+
70
+ ## 9. Acceptance criteria / DoD
71
+
72
+ > §9, not §8 — the number is load-bearing, so don't renumber it. `review.md` and
73
+ > `implementer.template.md` both name "contract §5, acceptance §9" verbatim, so a patch spec pins
74
+ > those two sections to the feature spec's numbers and just has no §8 (the feature spec has no §7 —
75
+ > a gap in the sequence is normal here).
76
+
77
+ - [ ] The §4 regression test exists, and failed before the fix
78
+ - [ ] Each touched surface's tests (TDD) green
79
+ - [ ] `PIPELINE.md` commands.lint · typecheck · test green
80
+ - [ ] The repro in §1 no longer reproduces
81
+ - [ ] Nothing outside §6's surfaces changed
82
+
83
+ ## Remediation
84
+
85
+ > Filled by `/cohorte-fix` from a REVIEW REPORT; empty otherwise. Each item:
86
+ > `[ ] <SEVERITY> · <file:line> · <spec-violation|quality|security> · <concrete fix>`
@@ -4,8 +4,6 @@ title: <Feature title>
4
4
  status: draft # draft → frozen → in-progress → in-review → shipped · blocked (see SCHEMA.md §Spec status)
5
5
  branch: <feature_branch_prefix><slug>
6
6
  created: <YYYY-MM-DD>
7
- loop_pass: 0 # /cohorte-loop bookkeeping — the review pass it is on; 0 = no loop running. Written by the driver, not by hand
8
- loop_phase: # /cohorte-loop bookkeeping — build | review | fix | done. With loop_pass, this is what `--resume` reads back
9
7
  reviewed_base: # merge-base sha at the last SHIP verdict — freshness-gate anchor (written by /cohorte-review)
10
8
  reviewed_digest: # sha256 (16 hex) of the reviewed source diff vs reviewed_base, specs excluded — /cohorte-ship re-checks
11
9
  design_files: [] # design page links — full URLs of the form https://claude.ai/design/p/<projectId>?file=<file> (each carries its own project + page); blank until designed; omit if no UI
@@ -64,7 +62,7 @@ design_files: [] # design page links — full URLs of the form https://claude.ai
64
62
 
65
63
  ## 8. Design brief (the "spec return")
66
64
 
67
- > Only if the project has UI. Standalone; mirrors `.claude/templates/design-brief.md`.
65
+ > Only if the project has UI. Standalone; mirrors `<core>/templates/design-brief.md`.
68
66
 
69
67
  - **Screens / views:** …
70
68
  - **Flows:** …
@@ -37,7 +37,7 @@ Gather evidence, then summarize what you found. Look for:
37
37
  `graphify`? Is either already registered in this repo's `.mcp.json` or `claude mcp list`? (Feeds
38
38
  the retrieval question + Phase 4 wiring — default provider is `serena`.)
39
39
  - **VCS:** `git remote -v` → host + `owner/repo`; the default branch (`git symbolic-ref refs/remotes/origin/HEAD` or `git branch`).
40
- - **Existing `CLAUDE.md`** — read it; it may already state stack/conventions to fold in. **Existing
40
+ - **Existing `<memory>`** — read it; it may already state stack/conventions to fold in. **Existing
41
41
  `PIPELINE.md`** — if present, this is a re-run: load it as the starting draft and only reconcile deltas.
42
42
 
43
43
  Print a compact **Detection Report**: layout, surfaces (with framework + commands), contract, release
@@ -47,19 +47,9 @@ Prefer sensible defaults from Phase 1 as the first (Recommended) option in each
47
47
 
48
48
  - **Kanban** (optional) — mirror this project's pipeline (`/cohorte-brainstorm`…`/cohorte-ship`) onto an Obsidian
49
49
  Kanban board? If the human says yes: confirm the shared vault path (`obsidian.vault_path` in
50
- `~/.claude/cohorte.config.yaml`; ask if empty) and the board's location inside it (default
50
+ `<config>`; ask if empty) and the board's location inside it (default
51
51
  `<ProjectName>/Tasks.md`). Phase 4 creates the board and records the link. Default: no.
52
52
 
53
53
  > The **kanban** link is user-scoped (it points at a personal vault, so it never goes in the committed
54
54
  > `PIPELINE.md`), but IS wired here because it is per-project — see Phase 4.
55
55
 
56
- - **Telemetry** (optional, machine-scoped — SKIP entirely if `~/.claude/cohorte.config.yaml` already
57
- has a `telemetry:` block with a `consent_date`, i.e. the human already answered on this machine).
58
- Ask ONE opt-in question, stating exactly: _"Share anonymous usage stats with the cohorte project?
59
- One ping per pipeline phase, `/cohorte-brainstorm` through `/cohorte-ship`: core version, OS, phase name, duration,
60
- per-surface result counts, and a hash of the feature id — never repo names, paths, code, or IPs.
61
- Setup and maintenance commands never ping. Off by default; withdraw anytime
62
- (`telemetry.enabled: false`); erase your history anytime (SCHEMA.md §Telemetry). Default: No."_
63
- On **yes**: in the global config set `telemetry.enabled: true`, mint `install_id` (`uuidgen`,
64
- lowercase), set `consent_date` (ISO date). On **no**: set `enabled: false` + `consent_date` (so
65
- no future command re-asks). Never touch `endpoint` — it ships with the template.
@@ -3,11 +3,11 @@
3
3
  ### Phase 4 — Write & render (after go-ahead)
4
4
 
5
5
  1. **Write `PIPELINE.md`** at the repo root (source: the installer's `pipeline/PIPELINE.template.md`).
6
- 2. **Wire it into `CLAUDE.md`:** if `CLAUDE.md` exists, ensure it references the profile (add a line
6
+ 2. **Wire it into `<memory>`:** if `<memory>` exists, ensure it references the profile (add a line
7
7
  near the top: `> Project profile & pipeline facts: **@PIPELINE.md**`). If not, create a minimal
8
- `CLAUDE.md` with that reference + a one-paragraph project intro.
8
+ `<memory>` with that reference + a one-paragraph project intro.
9
9
  3. **Render one agent per surface** — for each surface, follow SCHEMA.md §"Rendering / reconciling a
10
- surface agent" (steps 2–3): render `.claude/agents/<agent>.md` from the installer's
10
+ surface agent" (steps 2–3): render `<agents>/<agent>.md` from the installer's
11
11
  `pipeline/implementer.template.md`, substituting `<SURFACE_AGENT>`, `<SURFACE_LABEL>`, `<SURFACE_PATH>`,
12
12
  `<SURFACE_TOOLS>`, `<SURFACE_MODEL>`, `<PROJECT_NAME>`, `<SURFACE_CONVENTIONS>` (the surface's
13
13
  baked convention slice — §Shared + its `### Surface:` stanza + its §Testing lines from the
@@ -16,26 +16,24 @@
16
16
  only when `uses_design`).
17
17
  Leave the fixed agents as-is (generic, shipped by the installer): `review.md`, `release.md`,
18
18
  `profile-reader.md`.
19
- 4. **Generate `.claude/gate-config.json`** from the `gate` block — copy all five keys verbatim:
19
+ 4. **Generate `<state>/gate-config.json`** from the `gate` block — copy all five keys verbatim:
20
20
  `{"deny": [...], "ask": [...], "ask_on_default_branch": [...], "default_branch": "<vcs.default_branch>",
21
21
  "preflight": {"enabled": <gate.preflight.enabled>, "agents": [...], "max_age_minutes": <n>}}`
22
22
  (profile has no `preflight` block ⇒ omit the key — the hook then skips the phase gate).
23
+ <!-- cohorte:if hooks -->
23
24
  5. **Write `.claude/settings.json`** permissions (`ask`/`deny` lists mirroring the gate, **plus an
24
25
  `allow` list of the project's read-only / verification commands** so agents don't stall on
25
26
  permission prompts — including mid-workflow, where nobody is watching a prompt: the detected
26
27
  per-surface `test_cmd`/`lint_cmd`/`typecheck_cmd`/`build_cmd` **and their `*_quiet_cmd`
27
28
  variants** and repo-wide `commands.*` equivalents as `Bash(<cmd>:*)` rules, plus read-only git —
28
29
  `Bash(git status:*)`, `Bash(git diff:*)`, `Bash(git log:*)`, `Bash(git rev-parse:*)` — plus the
29
- shipped pipeline scripts for BOTH cores (`Bash(.claude/pipeline/scripts/:*)` and
30
- `Bash(~/.claude/pipeline/scripts/:*)` preflight, kanban-move, telemetry-send) **plus the
31
- `bash`-prefixed form the `/cohorte-loop` driver uses** (`Bash(bash .claude/pipeline/scripts/loop.sh:*)`
32
- and `Bash(bash ~/.claude/pipeline/scripts/loop.sh:*)`) — those prefix rules match a command
33
- *starting* with the path, so `bash <path>` needs its own entry or `/cohorte-loop` stalls on a permission
34
- prompt at every launch, and the
30
+ shipped pipeline scripts (`Bash(<core>/pipeline/scripts/:*)` — preflight, kanban-move;
31
+ a prefix rule matches a command *starting* with that path, so a `bash <path>`
32
+ invocation would need its own entry), and the
35
33
  retrieval provider's MCP tools when wired (e.g. `mcp__serena`). Never allowlist anything matching
36
34
  a `gate.ask`/`gate.deny` pattern. Mention the human can widen it later with
37
35
  `/fewer-permission-prompts`) + the hooks, **conditioned on the install mode:**
38
- - **bundled:** register the PreToolUse hook `.claude/hooks/gate.py` with matcher `Bash|Task`
36
+ - **bundled:** register the PreToolUse hook `<core>/hooks/gate.py` with matcher `Bash|Task`
39
37
  (Task is required — the preflight phase gate keys off Task dispatches; a `Bash`-only matcher
40
38
  leaves it dead) and the PostToolUse formatter (detected formatter). Before adding, drop any
41
39
  existing PreToolUse entry whose command ends in `gate.py` (here AND in `~/.claude/settings.json`
@@ -47,6 +45,14 @@
47
45
  registration serves every repo; you only supply this repo's `gate-config.json`. Still write the
48
46
  PostToolUse formatter hook + the permissions.
49
47
  Preserve any existing custom keys.
48
+ <!-- cohorte:else -->
49
+ 5. **No permissions or hook file to write.** This runtime has neither a PreToolUse hook nor a
50
+ settings.json permission model the pipeline can generate, so the gate is enforced by the
51
+ commands calling `<core>/hooks/gate.py --check` at the points the profile marks gated. That
52
+ means `<state>/gate-config.json` (step 4) is not decoration here — it is the ONLY place the
53
+ deny/ask patterns live, so fill it from the profile exactly and do not skip it. If this repo is
54
+ also driven from Claude Code, that install's hook reads the same file; nothing to duplicate.
55
+ <!-- cohorte:endif -->
50
56
  6. **Wire the retrieval provider** (skip if `retrieval.provider: none`):
51
57
  - **serena:** if the `serena` CLI is missing, have the human install it (`uv tool install -p 3.13
52
58
  serena-agent`) — or set the provider to `none` if they decline, and say `/cohorte-update-pipeline` can wire
@@ -74,14 +80,14 @@
74
80
  slug, DB pattern, port bases, compose file, branch prefix, install/dev/migrate commands, per-surface
75
81
  env stanzas). `chmod +x` them. If isolation is disabled, skip and note features build in the main checkout.
76
82
  8. **Ensure `specs/_template.md`** exists (copy from the installer's `templates/spec.template.md` if missing).
77
- 9. **Write the pointer** `.claude/pipeline.json` (committed — this is how a teammate who clones the repo
83
+ 9. **Write the pointer** `<state>/pipeline.json` (committed — this is how a teammate who clones the repo
78
84
  knows which core to install):
79
85
  `{ "pipeline": "cohorte", "mode": "<bundled|global>", "core_version": "<contents of the
80
86
  installer's pipeline/VERSION>", "install": "<per mode: bundled ⇒ \"npx cohorte install\"
81
87
  note that the core is committed under .claude/; global ⇒ \"npx cohorte install --global\"
82
88
  (or, without npm: curl -fsSL https://raw.githubusercontent.com/TheBidouilleAgency/cohorte/main/install.sh | sh -s -- --global;
83
89
  Windows: install.ps1 -Global from the same repo)> " }`.
84
- In **global** mode also add, near the top of `CLAUDE.md`, a one-liner:
90
+ In **global** mode also add, near the top of `<memory>`, a one-liner:
85
91
  `> Pipeline: global core — run the installer above if /cohorte-brainstorm etc. are missing.`
86
92
  10. **CI workflow** (if `vcs.host: github` and no existing workflow already runs the profile's
87
93
  checks): with the human's go-ahead, generate `.github/workflows/pipeline-ci.yml` — on
@@ -89,18 +95,18 @@
89
95
  `commands.install`, then `commands.lint` · `commands.typecheck` · `commands.test` (+ per-surface
90
96
  `build_cmd`s that are non-empty). Derive the setup steps from the detected stack — mirror what a
91
97
  sibling workflow does if one exists. `/cohorte-ship` watches these checks before the merge.
92
- 11. **Metrics sink, report buffer & preflight stamp:** add `.claude/pipeline-metrics.jsonl` to
98
+ 11. **Metrics sink, report buffer & preflight stamp:** add `<state>/pipeline-metrics.jsonl` to
93
99
  `.gitignore` — `/cohorte-build`, `/cohorte-review` and `/cohorte-fix` append per-dispatch evidence
94
100
  there (SCHEMA §Specialization reads it).
95
101
  Also add `specs/reports/` — `/cohorte-review` stages its last report there so a `/cohorte-fix` (or
96
102
  `/cohorte-spec` Mode B) survives a `/clear`; it's a derived buffer, not a versioned artifact.
97
- And `.claude/preflight.ok` — a local, per-checkout freshness stamp. Versioning it breaks the phase
103
+ And `<state>/preflight.ok` — a local, per-checkout freshness stamp. Versioning it breaks the phase
98
104
  gate for good (a committed stamp describes the tree *before* its own commit, and it rides into every
99
- new worktree as a green nobody earned). If it is already tracked: `git rm --cached .claude/preflight.ok`.
105
+ new worktree as a green nobody earned). If it is already tracked: `git rm --cached <state>/preflight.ok`.
100
106
  12. **Design system:** if `design.enabled` with a snapshot dir, note that `/cohorte-align-ds` is active; else the
101
107
  `/cohorte-align-ds` command will no-op with a clear message.
102
108
  13. **Kanban** (only if the human opted in at Phase 2): wire it per SCHEMA.md §Kanban, writing into the
103
- **global** `~/.claude/cohorte.config.yaml` (never into this repo — the board points at a
109
+ **global** `<config>` (never into this repo — the board points at a
104
110
  personal vault). Create the file from `pipeline/cohorte.config.template.yaml` if absent; set
105
111
  `kanban.enabled: true` and `obsidian.vault_path` if it was empty. Add a `kanban.boards[<PIPELINE
106
112
  name>]` entry with `board: <folder>/Tasks.md`. Then **create the board file**
@@ -3,7 +3,7 @@
3
3
  ### Phase 5 — Report
4
4
 
5
5
  Print: the install mode (bundled core under `.claude/` vs global core in `~/.claude/` + the committed
6
- `.claude/pipeline.json` pointer), the files written/rendered, the surface→agent mapping, and the
6
+ `<state>/pipeline.json` pointer), the files written/rendered, the surface→agent mapping, and the
7
7
  tailored workflow line, e.g.
8
8
  `/cohorte-brainstorm → /cohorte-spec → (design) → /cohorte-build <id> → test → /cohorte-review → /cohorte-ship`. Tell the human to sanity-check
9
9
  `PIPELINE.md`, commit it, and run `/cohorte-brainstorm` to start a feature. Note that this was the one-time
@@ -323,12 +323,10 @@ const staging = await agent(
323
323
  `1. Write EXACTLY this content to specs/reports/${feature}.md (overwrite):\n<<<REPORT\n${reportBody}\nREPORT\n` +
324
324
  `2. Append one line to $(dirname "$(git rev-parse --git-common-dir)")/.claude/pipeline-metrics.jsonl: ` +
325
325
  `{"ts":"<ISO now>","feature":"${feature}","phase":"review","seconds":0,"surfaces":{${results.map(r => `"${r.key}":"${verdict}:${r.kept.length}"`).join(',')}}}\n` +
326
- `3. Chain the opt-in usage ping: <core>/pipeline/scripts/telemetry-send.sh review "${feature}" 0 "${verdict}:${kept.length}" || true ` +
327
- '(<core> = .claude if .claude/pipeline/scripts/telemetry-send.sh exists, else ~/.claude; script on neither ⇒ skip the ping).\n' +
328
326
  // Deferred findings must land in the backlog on EVERY verdict — parked only on a
329
327
  // SHIP is parked nowhere the rest of the time, which is the leak this closes.
330
328
  (deferredAll.length
331
- ? `3b. Route the deferred findings to specs/refactor-backlog.md (create it if absent): for each line below, ` +
329
+ ? `3. Route the deferred findings to specs/refactor-backlog.md (create it if absent): for each line below, ` +
332
330
  `append it under the \`## <domain>\` heading named in its prefix (create that heading if absent) — with \`>>\`, ` +
333
331
  `never by rewriting the file, and skip any whose file path + first words already appear there (grep -F first, ` +
334
332
  `they may be left from a prior round or an /cohorte-audit):\n` +