@plainconceptsplatform/agent-harness 2.4.1 → 2.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/README.md +435 -437
  2. package/cli/fragments/archive/az.md +97 -95
  3. package/cli/fragments/archive/gh.md +96 -94
  4. package/cli/fragments/archive/gl.md +96 -94
  5. package/cli/fragments/archive/none.md +75 -73
  6. package/cli/fragments/guardrails/codegraph.md +5 -7
  7. package/cli/fragments/guardrails/humanizer.md +4 -4
  8. package/cli/fragments/guardrails/memory.md +4 -4
  9. package/cli/fragments/guardrails/rtk.md +3 -3
  10. package/cli/fragments/guardrails/simple-english.md +4 -4
  11. package/cli/fragments/ops-backlog/az.md +1 -1
  12. package/cli/fragments/ops-backlog/gh.md +1 -1
  13. package/cli/fragments/ops-backlog/jira.md +1 -1
  14. package/cli/fragments/ops-evidence/az.md +44 -41
  15. package/cli/fragments/ops-evidence/gh.md +54 -53
  16. package/cli/fragments/ops-evidence/jira.md +42 -38
  17. package/cli/fragments/ops-review/az.md +1 -1
  18. package/cli/fragments/ops-review/gh.md +1 -1
  19. package/cli/fragments/ops-review/gl.md +1 -1
  20. package/cli/fragments/ops-ship/az.md +81 -80
  21. package/cli/fragments/ops-ship/gh.md +68 -68
  22. package/cli/fragments/ops-ship/gl.md +85 -85
  23. package/cli/presets/agents-content.json +34 -53
  24. package/cli/steps/copy/agents.js +18 -17
  25. package/cli/steps/copy/opencode-json.js +5 -1
  26. package/cli/steps/copy/skills.js +98 -5
  27. package/cli/steps/optimization/patch-guardrails.js +5 -3
  28. package/cli/utils/copy.js +27 -3
  29. package/cli/utils/update-manifest.js +28 -2
  30. package/harness/.agents/skills/pc-guardrails-generic/SKILL.md +47 -68
  31. package/harness/.agents/skills/pc-make-architecture/SKILL.md +31 -51
  32. package/harness/.agents/skills/pc-make-design/SKILL.md +45 -68
  33. package/harness/.agents/skills/pc-make-engineer/SKILL.md +59 -219
  34. package/harness/.agents/skills/pc-make-engineer/signal-mapping.md +53 -68
  35. package/harness/.agents/skills/pc-make-engineer/template.md +42 -80
  36. package/harness/.agents/skills/pc-make-evidence-scaffold/SKILL.md +18 -18
  37. package/harness/.agents/skills/pc-make-evidence-scaffold/evidence-contract.md +29 -29
  38. package/harness/.agents/skills/pc-make-guardrails/SKILL.md +43 -74
  39. package/harness/.agents/skills/pc-make-guardrails/category-reference.md +10 -5
  40. package/harness/.agents/skills/pc-make-merge-risk-assess/category-reference.md +26 -7
  41. package/harness/.agents/skills/pc-make-user-model/SKILL.md +56 -66
  42. package/harness/.agents/skills/pc-ops-evidence/SKILL.md +133 -127
  43. package/harness/.agents/skills/pc-plan-apply/SKILL.md +14 -5
  44. package/harness/.agents/skills/pc-plan-apply/simple-mode.md +21 -21
  45. package/harness/.agents/skills/pc-plan-archive/SKILL.md +66 -66
  46. package/harness/.agents/skills/pc-plan-explore/SKILL.md +19 -2
  47. package/harness/.agents/skills/pc-plan-goal/SKILL.md +7 -5
  48. package/harness/.agents/skills/pc-plan-goal/output-mode.md +1 -0
  49. package/harness/.agents/skills/pc-plan-goal/output.md +71 -65
  50. package/harness/.agents/skills/pc-plan-propose/SKILL.md +1 -1
  51. package/harness/.agents/skills/pc-plan-quick/SKILL.md +46 -62
  52. package/harness/.agents/skills/pc-plan-story/SKILL.md +48 -149
  53. package/harness/.agents/skills/pc-repo-help/SKILL.md +89 -91
  54. package/harness/.agents/skills/pc-repo-initialize/SKILL.md +112 -130
  55. package/harness/.agents/skills/pc-repo-onboard/SKILL.md +32 -87
  56. package/harness/.agents/skills/pc-repo-verify/SKILL.md +2 -0
  57. package/harness/.agents/skills/pc-userstory-az/SKILL.md +71 -157
  58. package/harness/.agents/skills/pc-userstory-browser/SKILL.md +50 -122
  59. package/harness/.agents/skills/pc-userstory-gh/SKILL.md +63 -120
  60. package/harness/.agents/skills/pc-userstory-jira/SKILL.md +74 -131
  61. package/harness/.opencode/commands/init.md +5 -5
  62. package/harness/.opencode/commands/make-architecture.md +5 -5
  63. package/harness/.opencode/commands/make-design.md +5 -5
  64. package/harness/.opencode/commands/make-engineer.md +5 -5
  65. package/harness/.opencode/commands/make-evidence-scaffold.md +5 -5
  66. package/harness/.opencode/commands/make-guardrails.md +5 -5
  67. package/harness/.opencode/commands/make-user-model.md +5 -5
  68. package/harness/.opencode/commands/plan-apply.md +9 -9
  69. package/harness/.opencode/commands/plan-goal.md +5 -5
  70. package/harness/.opencode/commands/plan-quick.md +5 -5
  71. package/harness/.opencode/commands/plan-story.md +9 -9
  72. package/harness/.opencode/commands/repo-audit.md +5 -5
  73. package/harness/.opencode/commands/repo-initialize.md +5 -5
  74. package/harness/.opencode/commands/repo-onboard.md +5 -5
  75. package/harness/.opencode/commands/repo-verify.md +5 -5
  76. package/harness/.opencode/plugins/pc-subagent-monitor.js +82 -2
  77. package/harness/.opencode/plugins/pc-subagent-tiers.js +9 -6
  78. package/harness/.opencode/plugins/pc-system-reminders.js +329 -3
  79. package/harness/AGENTS.md +49 -71
  80. package/harness/opencode.jsonc +1 -1
  81. package/package.json +1 -1
@@ -1,131 +1,74 @@
1
- ---
2
- name: pc-userstory
3
- description: Parse a Jira work item and create an OpenSpec change. Use when the user provides a Jira URL or a bare issue key (e.g. PROJ-123).
4
- license: MIT
5
- compatibility: Requires openspec CLI and Atlassian CLI (acli).
6
- metadata:
7
- author: copilots
8
- version: "1.0"
9
- ---
10
-
11
- Use `acli` CLI for all Jira operations.
12
-
13
- ## Atlassian CLI Setup (One-Time)
14
-
15
- Install from: https://developer.atlassian.com/cloud/acli/guides/install-acli/
16
-
17
- Authenticate:
18
- ```bash
19
- # Option 1: API token (recommended for CI)
20
- echo <token> | acli jira auth login --site "<yoursite>.atlassian.net" --email "<email>" --token
21
-
22
- # Option 2: OAuth (interactive)
23
- acli jira auth login --web
24
- ```
25
-
26
- Generate API token at: https://id.atlassian.com/manage-profile/security/api-tokens
27
-
28
- ## Steps
29
-
30
- 1. **Extract Issue Key** from URL
31
- - `https://yoursite.atlassian.net/browse/PROJ-123` -> Key: PROJ-123
32
- - `https://yoursite.atlassian.net/jira/core/projects/PROJ/issues/PROJ-123` -> Key: PROJ-123
33
- - `https://yoursite.atlassian.net/browse/PROJ-123?filter=123` -> Key: PROJ-123
34
- - If the user provides just `PROJ-123` without a URL, use it directly as the key.
35
-
36
- 2. **Fetch Work Item**
37
- ```bash
38
- acli jira workitem view --key "PROJ-123"
39
- ```
40
-
41
- Parse the output for:
42
- - Summary -> title
43
- - Description -> proposal context
44
- - Acceptance Criteria (if present in description or custom fields) -> spec requirements
45
- - Labels -> tags for the OpenSpec change
46
- - Status -> current state (e.g. To Do, In Progress, Done)
47
- - Assignee -> who requested it
48
- - Priority -> complexity hint
49
-
50
- 3. **Offer to transition the Work Item to In Progress**
51
-
52
- This writes to Jira, so ask first using the `question` tool:
53
-
54
- ```json
55
- {
56
- "questions": [
57
- {
58
- "header": "Transition work item",
59
- "question": "Move {KEY} to In Progress?",
60
- "options": [
61
- { "label": "yes", "description": "Transition the work item to In Progress." },
62
- { "label": "no", "description": "Skip the transition." }
63
- ]
64
- }
65
- ]
66
- }
67
- ```
68
-
69
- Only if the user answers `yes` AND the status is currently "To Do" or "Backlog":
70
- ```bash
71
- acli jira workitem transition --key "PROJ-123" --status "In Progress"
72
- ```
73
- In unattended runs (`/plan-goal`), skip the question and the transition entirely.
74
-
75
- 4. **Create OpenSpec Change**
76
- ```bash
77
- openspec new change "{slug-from-summary}"
78
- ```
79
-
80
- Write `proposal.md` with:
81
- - Title: the Jira issue summary
82
- - Context: mention this is Jira issue `{KEY}`, link back to the URL
83
- - Requirements: extracted from description and acceptance criteria
84
- - Scope: what's in/out based on the issue
85
-
86
- 5. **Hand off to proposal.** Load the `pc-plan-propose` skill (interactive mode) to generate the proposal, specs, and tasks. After it completes, call the `question` tool:
87
-
88
- ```json
89
- {
90
- "questions": [
91
- {
92
- "header": "Ready to implement",
93
- "question": "Ready to implement?",
94
- "options": [
95
- { "label": "yes", "description": "Load the pc-plan-apply skill to start implementation." },
96
- { "label": "no", "description": "Stop here. You can run /plan-apply later." }
97
- ]
98
- }
99
- ]
100
- }
101
- ```
102
-
103
- Wait for confirmation before loading `pc-plan-apply`.
104
-
105
- ## Jira URL Patterns
106
-
107
- | URL format | Example |
108
- |------------|---------|
109
- | Browse URL | `https://yoursite.atlassian.net/browse/PROJ-123` |
110
- | Issue URL | `https://yoursite.atlassian.net/jira/core/projects/PROJ/issues/PROJ-123` |
111
- | Board URL | `https://yoursite.atlassian.net/jira/software/c/projects/PROJ/boards/1?selectedIssue=PROJ-123` |
112
- | Direct key | `PROJ-123` |
113
-
114
- ## Useful Commands
115
-
116
- ```bash
117
- # View work item
118
- acli jira workitem view --key "PROJ-123"
119
-
120
- # Transition work item
121
- acli jira workitem transition --key "PROJ-123" --status "In Progress"
122
- acli jira workitem transition --key "PROJ-123" --status "Done"
123
-
124
- # Search with JQL
125
- acli jira workitem search --jql "project = PROJ AND status = 'To Do' ORDER BY priority DESC"
126
-
127
- # Add a comment
128
- acli jira workitem comment create --key "PROJ-123" --body "Implementation started"
129
- ```
130
-
131
- Jira is a backlog-only platform: it has no code repos or PRs. PR creation and code review use the repo platform (GitHub or Azure DevOps) configured separately.
1
+ ---
2
+ name: pc-userstory
3
+ description: Parse a Jira work item and create an OpenSpec change. Use when the user provides a Jira URL or a bare issue key (e.g. PROJ-123).
4
+ license: MIT
5
+ compatibility: Requires openspec CLI and Atlassian CLI (acli).
6
+ metadata:
7
+ author: copilots
8
+ version: "1.0"
9
+ ---
10
+
11
+ Turn a Jira issue into an OpenSpec change, then hand the change to `pc-plan-propose`. Jira is backlog-only: it has no repositories and no pull requests, so shipping runs on whichever repo platform the project configured.
12
+
13
+ ## Rules
14
+
15
+ - Issue data comes from `acli`, never from a page fetch or a browser (denied by `pc-system-reminders`). An `acli` that cannot authenticate is a blocker to report; install and login are documented at https://developer.atlassian.com/cloud/acli/guides/install-acli/.
16
+ - Never transition the work item without asking, and never transition one that is not in `To Do` or `Backlog`. Moving somebody's ticket is visible to their whole team.
17
+ - In an unattended run, skip the question and the transition rather than resolving it yes.
18
+ - Never load `pc-plan-apply` until the user has said yes.
19
+
20
+ ## Contracts
21
+
22
+ The key is `PROJ-123`, taken from `/browse/PROJ-123`, `/jira/core/projects/PROJ/issues/PROJ-123`, `?selectedIssue=PROJ-123`, or given bare.
23
+
24
+ ```bash
25
+ acli jira workitem view --key "PROJ-123"
26
+ ```
27
+
28
+ From the output take the summary (title), the description (context), acceptance criteria wherever they live (spec requirements), labels, status, assignee and priority.
29
+
30
+ ```bash
31
+ acli jira workitem transition --key "PROJ-123" --status "In Progress"
32
+ ```
33
+
34
+ Ask before that one:
35
+
36
+ ```json
37
+ {
38
+ "questions": [
39
+ {
40
+ "header": "Transition work item",
41
+ "question": "Move {KEY} to In Progress?",
42
+ "options": [
43
+ { "label": "yes", "description": "Transition the work item to In Progress." },
44
+ { "label": "no", "description": "Skip the transition." }
45
+ ]
46
+ }
47
+ ]
48
+ }
49
+ ```
50
+
51
+ Then create the change, whose `proposal.md` names the Jira key and links back to it:
52
+
53
+ ```bash
54
+ openspec new change "{slug-from-summary}"
55
+ ```
56
+
57
+ Load `pc-plan-propose` (interactive) and, once it returns, ask:
58
+
59
+ ```json
60
+ {
61
+ "questions": [
62
+ {
63
+ "header": "Ready to implement",
64
+ "question": "Ready to implement?",
65
+ "options": [
66
+ { "label": "yes", "description": "Load the pc-plan-apply skill to start implementation." },
67
+ { "label": "no", "description": "Stop here. You can run /plan-apply later." }
68
+ ]
69
+ }
70
+ ]
71
+ }
72
+ ```
73
+
74
+ Also available: `acli jira workitem search --jql "..."` and `acli jira workitem comment create --key "PROJ-123" --body "..."`.
@@ -1,5 +1,5 @@
1
- ---
2
- description: Initialize the project. Presents a single form with all setup questions, then executes selected steps.
3
- ---
4
-
5
- Load the `pc-repo-initialize` skill and follow every step defined in it.
1
+ ---
2
+ description: Initialize the project. Presents a single form with all setup questions, then executes selected steps.
3
+ ---
4
+
5
+ Load the `pc-repo-initialize` skill.
@@ -1,5 +1,5 @@
1
- ---
2
- description: Generate or update ARCHITECTURE.md by analyzing the codebase structure. Safe to run at any time.
3
- ---
4
-
5
- Load the `pc-make-architecture` skill and follow every step defined in it.
1
+ ---
2
+ description: Generate or update ARCHITECTURE.md by analyzing the codebase structure. Safe to run at any time.
3
+ ---
4
+
5
+ Load the `pc-make-architecture` skill.
@@ -1,5 +1,5 @@
1
- ---
2
- description: Generate or update DESIGN.md by analyzing the codebase design system. Safe to run at any time.
3
- ---
4
-
5
- Load the `pc-make-design` skill and follow every step defined in it.
1
+ ---
2
+ description: Generate or update DESIGN.md by analyzing the codebase design system. Safe to run at any time.
3
+ ---
4
+
5
+ Load the `pc-make-design` skill.
@@ -1,5 +1,5 @@
1
- ---
2
- description: Create a custom engineer agent via persona-driven interactive design
3
- ---
4
-
5
- Load the `pc-make-engineer` skill and follow every step defined in it.
1
+ ---
2
+ description: Create a custom engineer agent via persona-driven interactive design
3
+ ---
4
+
5
+ Load the `pc-make-engineer` skill.
@@ -1,5 +1,5 @@
1
- ---
2
- description: One-time scaffold of a project-specific visual-evidence harness (deterministic capture + assertions + manifest + publisher) that /ops-evidence and /plan-goal delegate to.
3
- ---
4
-
5
- Load the `pc-make-evidence-scaffold` skill and follow every step defined in it.
1
+ ---
2
+ description: DEPRECATED. Per-project evidence scaffolds are gone; run /ops-evidence instead.
3
+ ---
4
+
5
+ Load the `pc-make-evidence-scaffold` skill.
@@ -1,5 +1,5 @@
1
- ---
2
- description: Generate or update a pc-guardrails-project skill from ARCHITECTURE.md and relevant project files.
3
- ---
4
-
5
- Load the `pc-make-guardrails` skill and follow every step defined in it.
1
+ ---
2
+ description: Generate or update a pc-guardrails-project skill from ARCHITECTURE.md and relevant project files.
3
+ ---
4
+
5
+ Load the `pc-make-guardrails` skill.
@@ -1,5 +1,5 @@
1
- ---
2
- description: Set the model for a tier (plan, build, or fast). Team-wide or user-local override.
3
- ---
4
-
5
- Load the `pc-make-user-model` skill and follow every step defined in it.
1
+ ---
2
+ description: Set the model for a tier (plan, build, or fast). Team-wide or user-local override.
3
+ ---
4
+
5
+ Load the `pc-make-user-model` skill.
@@ -1,9 +1,9 @@
1
- ---
2
- description: Implement tasks from a plan: works with OpenSpec proposals and in-conversation plans.
3
- ---
4
-
5
- Load the `pc-plan-apply` skill and execute it in **interactive mode** with `start_from: branch` (the full protocol, including branch creation).
6
-
7
- Input:
8
-
9
- $ARGUMENTS
1
+ ---
2
+ description: "Implement tasks from a plan: works with OpenSpec proposals and in-conversation plans."
3
+ ---
4
+
5
+ Load the `pc-plan-apply` skill and execute it in **interactive mode** with `start_from: branch`.
6
+
7
+ Input:
8
+
9
+ $ARGUMENTS
@@ -1,5 +1,5 @@
1
- ---
2
- description: Autonomous pipeline: explore, propose, apply, archive, then merge/PR/push. For loop-engineering.
3
- ---
4
-
5
- Load the `pc-plan-goal` skill and follow every step defined in it.
1
+ ---
2
+ description: "Autonomous pipeline: explore, propose, apply, archive, then merge/PR/push. For loop-engineering."
3
+ ---
4
+
5
+ Load the `pc-plan-goal` skill.
@@ -1,5 +1,5 @@
1
- ---
2
- description: Quick plan: analyze the codebase and create a task checklist using the Todo pane. No files, no OpenSpec.
3
- ---
4
-
5
- Load the `pc-plan-quick` skill and follow every step defined in it.
1
+ ---
2
+ description: "Quick plan: analyze the codebase and create a task checklist using the Todo pane. No files, no OpenSpec."
3
+ ---
4
+
5
+ Load the `pc-plan-quick` skill.
@@ -1,9 +1,9 @@
1
- ---
2
- description: Write a detailed, repo-aware user story from a feature idea or need.
3
- ---
4
-
5
- Load the `pc-plan-story` skill and follow every step defined in it.
6
-
7
- Input:
8
-
9
- $ARGUMENTS
1
+ ---
2
+ description: Write a detailed, repo-aware user story from a feature idea or need.
3
+ ---
4
+
5
+ Load the `pc-plan-story` skill.
6
+
7
+ Input:
8
+
9
+ $ARGUMENTS
@@ -1,5 +1,5 @@
1
- ---
2
- description: Audit every configured source root against the fullstack engineer's guardrails and abilities. Read-only.
3
- ---
4
-
5
- Load the `pc-repo-audit` skill and follow every step defined in it.
1
+ ---
2
+ description: Audit every configured source root against the fullstack engineer's guardrails and abilities. Read-only.
3
+ ---
4
+
5
+ Load the `pc-repo-audit` skill.
@@ -1,5 +1,5 @@
1
- ---
2
- description: Initialize the project. Presents a single form with all setup questions, then executes selected steps.
3
- ---
4
-
5
- Load the `pc-repo-initialize` skill and follow every step defined in it.
1
+ ---
2
+ description: Initialize the project. Presents a single form with all setup questions, then executes selected steps.
3
+ ---
4
+
5
+ Load the `pc-repo-initialize` skill.
@@ -1,5 +1,5 @@
1
- ---
2
- description: Walk the user through the project and its agentic infrastructure. Explains what exists, how agents work, and how to use the system.
3
- ---
4
-
5
- Load the `pc-repo-onboard` skill and follow every step defined in it.
1
+ ---
2
+ description: Walk the user through the project and its agentic infrastructure. Explains what exists, how agents work, and how to use the system.
3
+ ---
4
+
5
+ Load the `pc-repo-onboard` skill.
@@ -1,5 +1,5 @@
1
- ---
2
- description: Verify current-branch changes, plus immutable dependency installs, builds, and tests for every discovered project.
3
- ---
4
-
5
- Load the `pc-repo-verify` skill and follow every step defined in it.
1
+ ---
2
+ description: Verify current-branch changes, plus immutable dependency installs, builds, and tests for every discovered project.
3
+ ---
4
+
5
+ Load the `pc-repo-verify` skill.
@@ -1,14 +1,28 @@
1
1
  // pc-subagent-monitor: tracks spawned subagents in .opencode/harness-run.json for
2
- // live TUI display and crash recovery. Never throws: monitor failures must
3
- // not break a session.
2
+ // live TUI display and crash recovery, and holds a wave to agents.maxConcurrent.
3
+ //
4
+ // It throws in exactly one case: to deny a spawn over the cap. Every other
5
+ // failure, including its own bugs, is swallowed — a monitor that breaks the run
6
+ // it is watching is worse than no monitor.
4
7
 
5
8
  import fs from "node:fs/promises"
6
9
  import path from "node:path"
7
10
 
11
+ // A spawn that has been allowed but whose session has not appeared yet. A wave
12
+ // is one assistant turn, and opencode calls tool.execute.before for every
13
+ // task() in that turn before the first child session exists, so counting live
14
+ // sessions alone would let an entire wave through whatever the cap says.
15
+ //
16
+ // The slot is released when the session lands, and expires otherwise:
17
+ // tool.execute.after never fires for a tool that threw, so a failed spawn must
18
+ // not hold a slot for the rest of the run.
19
+ const PENDING_TTL_MS = 15_000
20
+
8
21
  export const PcSubagentMonitor = async ({ directory, client }) => {
9
22
  const root = directory || process.cwd()
10
23
  const statePath = path.join(root, ".opencode", "harness-run.json")
11
24
  const state = { updatedAt: null, agents: {} }
25
+ const pending = []
12
26
 
13
27
  try {
14
28
  const prev = JSON.parse(await fs.readFile(statePath, "utf-8"))
@@ -94,7 +108,69 @@ export const PcSubagentMonitor = async ({ directory, client }) => {
94
108
  }
95
109
  }
96
110
 
111
+ let _capCache = null
112
+ async function maxConcurrent() {
113
+ if (_capCache) return _capCache
114
+ let configured = 3
115
+ try {
116
+ const raw = await fs.readFile(path.join(root, ".opencode", "harness.json"), "utf-8")
117
+ const value = JSON.parse(raw)?.agents?.maxConcurrent
118
+ if (Number.isFinite(value)) configured = value
119
+ } catch {
120
+ // no config yet; the CLI's own default is 3
121
+ }
122
+ _capCache = Math.min(5, Math.max(1, Math.trunc(configured)))
123
+ return _capCache
124
+ }
125
+
126
+ function runningFor(parentID) {
127
+ return Object.values(state.agents)
128
+ .filter(entry => entry?.status === "running" && !entry.stale && entry.parentID === parentID)
129
+ .length
130
+ }
131
+
132
+ function livePending(parentID) {
133
+ const now = Date.now()
134
+ for (let i = pending.length - 1; i >= 0; i--) {
135
+ if (now - pending[i].at > PENDING_TTL_MS) pending.splice(i, 1)
136
+ }
137
+ return pending.filter(entry => entry.parentID === parentID).length
138
+ }
139
+
140
+ function releasePending(parentID) {
141
+ const index = pending.findIndex(entry => entry.parentID === parentID)
142
+ if (index !== -1) pending.splice(index, 1)
143
+ }
144
+
97
145
  return {
146
+ // The cap was prose in pc-plan-apply ("you enforce the cap"), which asked
147
+ // the lead to count its own parallel calls. Six disjoint groups and a cap
148
+ // of three is exactly the moment a model stops counting.
149
+ "tool.execute.before": async (input) => {
150
+ if (input?.tool !== "task") return
151
+
152
+ let cap
153
+ let live
154
+ try {
155
+ pruneStale()
156
+ cap = await maxConcurrent()
157
+ live = runningFor(input.sessionID) + livePending(input.sessionID)
158
+ if (live < cap) {
159
+ pending.push({ parentID: input.sessionID, at: Date.now() })
160
+ return
161
+ }
162
+ } catch (error) {
163
+ // Compute inside the try, deny outside it, so this catch can never
164
+ // swallow the one exception the hook is meant to raise.
165
+ console.error(`[harness] concurrency check failed open: ${error?.message}`)
166
+ return
167
+ }
168
+
169
+ throw new Error(
170
+ `[harness] ${live} subagents are already in flight and agents.maxConcurrent is ${cap}.\n` +
171
+ "Collect a running worker before spawning another. A denied spawn is not a failed task: re-issue it in the next wave.",
172
+ )
173
+ },
98
174
  event: async ({ event }) => {
99
175
  try {
100
176
  if (!event?.type?.startsWith("session.")) return
@@ -103,9 +179,13 @@ export const PcSubagentMonitor = async ({ directory, client }) => {
103
179
 
104
180
  if (event.type === "session.created" && info.parentID) {
105
181
  pruneStale()
182
+ // The spawn this session came from no longer needs its pending slot;
183
+ // the session itself is now what the cap counts.
184
+ releasePending(info.parentID)
106
185
 
107
186
  state.agents[info.id] = {
108
187
  agent: info.agent ?? null,
188
+ parentID: info.parentID,
109
189
  model: await modelForAgent(info.agent),
110
190
  tasks: parseTasks(info.title),
111
191
  title: info.title ?? null,
@@ -6,7 +6,8 @@
6
6
  // Agent topology:
7
7
  // build.md / plan.md mode: primary the only agents a human selects.
8
8
  // Both are fullstack-engineer with a
9
- // tier model; plan cannot edit.
9
+ // tier model; plan can neither edit
10
+ // nor spawn.
10
11
  // fullstack-engineer.md mode: subagent the shared body of build and plan,
11
12
  // and the fallback worker.
12
13
  // *-engineer.md mode: subagent specialists, spawned by task().
@@ -22,8 +23,10 @@ import path from "node:path"
22
23
  const TIERS = ["build", "fast", "plan"]
23
24
 
24
25
  // The two primaries, and the tier each takes its model from. plan denies edit
25
- // so a planning session cannot mutate the tree; bash stays allowed because the
26
- // planning skills shell out to git and openspec to read state.
26
+ // and task: a planning session cannot mutate the tree itself, and it cannot
27
+ // spawn a worker that would do it on its behalf. bash stays allowed because the
28
+ // planning skills shell out to git and openspec to read state, and
29
+ // pc-system-reminders holds it to inspection commands.
27
30
  //
28
31
  // Their colours are theme keywords rather than derived hexes, and they are
29
32
  // fixed: these are the two agents a human picks, so they should look the same
@@ -39,7 +42,7 @@ const PRIMARIES = {
39
42
  tier: "plan",
40
43
  color: "warning",
41
44
  description: "Explore and plan without touching the tree. Read-only: proposes work for build to carry out.",
42
- permission: { edit: "deny" },
45
+ permission: { edit: "deny", task: "deny" },
43
46
  },
44
47
  }
45
48
 
@@ -228,8 +231,8 @@ export const PcSubagentTiers = async ({ directory }) => {
228
231
  if (model) lines.push(`model: ${model}`)
229
232
  lines.push(`color: ${yamlColor(spec.color)}`)
230
233
  lines.push('permission:')
231
- // plan denies edit; everything else stays allowed so the planning skills can
232
- // still read the tree, shell out to git and openspec, and spawn engineers.
234
+ // plan denies edit and task; the rest stays allowed so the planning skills
235
+ // can still read the tree and shell out to git and openspec.
233
236
  lines.push(` edit: ${spec.permission?.edit ?? 'allow'}`)
234
237
  for (const key of ['bash', 'read', 'glob', 'grep', 'question', 'todowrite', 'task', 'skill']) {
235
238
  lines.push(` ${key}: ${spec.permission?.[key] ?? 'allow'}`)