cowork-flow 0.0.22 → 0.0.24
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.
- package/README.md +3 -1
- package/package.json +1 -1
- package/template/.agents/skills/party-mode/SKILL.md +120 -0
- package/template/.claude/skills/party-mode/SKILL.md +120 -0
- package/template/.cowork-flow/.version +1 -1
- package/template/.cowork-flow/scripts/change.py +59 -19
- package/template/.cowork-flow/scripts/task.py +57 -6
- package/template/.cowork-flow/spec/subagent-dispatch.md +6 -0
- package/template/.cowork-flow/workflow.md +13 -5
package/README.md
CHANGED
|
@@ -55,7 +55,9 @@ cowork-flow 是一个用于新项目初始化协作流程的模板仓库。它
|
|
|
55
55
|
项目级协作入口,包含编码前思考、简单优先、外科手术式改动、验证优先等基础原则。接入项目后,应把项目名称、技术栈、运行命令、测试命令和提交策略补齐。
|
|
56
56
|
|
|
57
57
|
`template/.agents/skills/`
|
|
58
|
-
本地技能入口,覆盖 start、before-dev、brainstorming、writing-plans、check、finish-work、continue、meta、python-design、update-spec 和 break-loop 等协作动作。这里的 skill 应保持通用,不承载某个业务项目的一次性细节。
|
|
58
|
+
本地技能入口,覆盖 start、before-dev、brainstorming、party-mode、writing-plans、check、finish-work、continue、meta、python-design、update-spec 和 break-loop 等协作动作。这里的 skill 应保持通用,不承载某个业务项目的一次性细节。
|
|
59
|
+
|
|
60
|
+
`party-mode` 是用户手动调用的 advisory roundtable。它通过当前 Host Adapter 协调真实子代理讨论,而不是模拟人设;默认 `max_agents=3`、`max_rounds=3`,配置优先级为本次调用、task/change、本地配置、skill 默认值。Party Mode 只产出建议、证据、分歧和验收信号,不能推进任务状态,也不能替代 `cowork-implement` 或 `cowork-check`。
|
|
59
61
|
|
|
60
62
|
`template/.codex/agents/`
|
|
61
63
|
固定角色 agent 定义,包含 `cowork-research`、`cowork-implement` 和 `cowork-check`,以及对 Codex 默认 `worker`、`default`、`explorer` 的项目级防漂移约束。主会话负责计划与收口,并通过 host adapter 派发固定 agent;正式派发必须先创建 runtime context,再把 `cowork_runtime_context_id` 和 `cowork_host_context_key` 传给子代理。
|
package/package.json
CHANGED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: party-mode
|
|
3
|
+
description: Use when the user manually requests Party Mode or a real multi-agent roundtable for advisory discussion, option review, risk review, or decision convergence.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Party Mode
|
|
7
|
+
|
|
8
|
+
Use this skill as a manual advisory roundtable. Coordinate true child agents, not simulated personas, through the current Host Adapter.
|
|
9
|
+
|
|
10
|
+
## Boundaries
|
|
11
|
+
|
|
12
|
+
- Party Mode is advisory only.
|
|
13
|
+
- It cannot mutate task status, start/review/complete/archive tasks, add sessions, commit, push, or satisfy formal workflow gates.
|
|
14
|
+
- It cannot satisfy formal Implement or Check completion. Use `cowork-implement` and `cowork-check` for formal phases.
|
|
15
|
+
- Discussion children are leaf executors. They do not dispatch, wait for, list, or close other agents.
|
|
16
|
+
- Generic `worker`, `default`, or `explorer` agents may provide advisory views, but their output never proves implementation or checking done.
|
|
17
|
+
|
|
18
|
+
## Defaults And Config
|
|
19
|
+
|
|
20
|
+
Built-in defaults:
|
|
21
|
+
|
|
22
|
+
- `max_agents=3`
|
|
23
|
+
- `max_rounds=3`
|
|
24
|
+
|
|
25
|
+
These defaults are configurable values, not hard-coded constants. Effective config order:
|
|
26
|
+
|
|
27
|
+
```text
|
|
28
|
+
call arguments > task/change config > `.cowork-flow/config.yaml` > skill defaults
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Configurable fields:
|
|
32
|
+
|
|
33
|
+
- `max_agents`
|
|
34
|
+
- `max_rounds`
|
|
35
|
+
- agent roster or review lenses
|
|
36
|
+
- report enabled
|
|
37
|
+
- report path
|
|
38
|
+
- whether round 2 or round 3 is allowed
|
|
39
|
+
|
|
40
|
+
Safety gates:
|
|
41
|
+
|
|
42
|
+
- continue conditions can be tightened but not removed
|
|
43
|
+
- stop conditions can be tightened but not removed
|
|
44
|
+
- schema core fields can be extended but not removed
|
|
45
|
+
- exceeding effective limits requires explicit user approval
|
|
46
|
+
|
|
47
|
+
## Round Model
|
|
48
|
+
|
|
49
|
+
1. Frame the question, decision needed, scope, and evidence packet.
|
|
50
|
+
2. Round 1 uses fresh child contexts. Send each child the same packet and one lens. Child agents cannot see each other.
|
|
51
|
+
3. Synthesize only evidence-backed positions, disagreements, rejected options, and acceptance signals.
|
|
52
|
+
4. Continue only when a continue condition is met. Send narrow follow-up prompts to the smallest useful set of children.
|
|
53
|
+
5. Stop when any stop condition is met. Close all live children through the Host Adapter close primitive.
|
|
54
|
+
|
|
55
|
+
Round intent:
|
|
56
|
+
|
|
57
|
+
- Opening round: independent first judgments.
|
|
58
|
+
- Challenge rounds: rebuttal, risk drilldown, or evidence repair on specific disagreements.
|
|
59
|
+
- Convergence rounds: decision check only. Verify, narrow, or choose. Do not open new directions.
|
|
60
|
+
|
|
61
|
+
Default mapping:
|
|
62
|
+
|
|
63
|
+
- Round 1 = Opening.
|
|
64
|
+
- Round 2 = Challenge.
|
|
65
|
+
- Round 3+ = Convergence.
|
|
66
|
+
|
|
67
|
+
Only the user can restart exploration after convergence begins.
|
|
68
|
+
|
|
69
|
+
## Continue Conditions
|
|
70
|
+
|
|
71
|
+
Run another round only if at least one condition holds:
|
|
72
|
+
|
|
73
|
+
- A disagreement could change the recommended decision.
|
|
74
|
+
- A high risk lacks enough evidence.
|
|
75
|
+
- Acceptance criteria are still not testable.
|
|
76
|
+
- A child found new file, command, rule, or user-scenario evidence.
|
|
77
|
+
- The coordinator cannot write one recommended direction.
|
|
78
|
+
|
|
79
|
+
## Stop Conditions
|
|
80
|
+
|
|
81
|
+
Stop when any condition holds:
|
|
82
|
+
|
|
83
|
+
- Recommendation, rejected options, and measurable acceptance criteria are clear.
|
|
84
|
+
- Remaining issue is user value preference, not missing evidence.
|
|
85
|
+
- A full round adds no evidence and does not narrow scope.
|
|
86
|
+
- Effective `max_rounds` is reached.
|
|
87
|
+
- Output fails schema, and one repair prompt does not fix it.
|
|
88
|
+
|
|
89
|
+
## Child Output Schema
|
|
90
|
+
|
|
91
|
+
Each child must return these core fields. Extra fields are allowed only after them.
|
|
92
|
+
|
|
93
|
+
```text
|
|
94
|
+
position:
|
|
95
|
+
evidence:
|
|
96
|
+
risk:
|
|
97
|
+
tradeoff:
|
|
98
|
+
rejected_option:
|
|
99
|
+
acceptance_signal:
|
|
100
|
+
what_would_change_my_mind:
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Reject unsupported opinion. Evidence should name files, commands, rules, observed behavior, user scenarios, or concrete assumptions.
|
|
104
|
+
|
|
105
|
+
## Coordinator Output Schema
|
|
106
|
+
|
|
107
|
+
Final synthesis must return these core fields. Extra fields are allowed only after them.
|
|
108
|
+
|
|
109
|
+
```text
|
|
110
|
+
consensus:
|
|
111
|
+
disagreements:
|
|
112
|
+
evidence:
|
|
113
|
+
decision:
|
|
114
|
+
rejected_options:
|
|
115
|
+
acceptance_criteria:
|
|
116
|
+
open_questions:
|
|
117
|
+
stop_reason:
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Keep the final decision traceable to child evidence. Do not count votes as validation.
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: party-mode
|
|
3
|
+
description: Use when the user manually requests Party Mode or a real multi-agent roundtable for advisory discussion, option review, risk review, or decision convergence.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Party Mode
|
|
7
|
+
|
|
8
|
+
Use this skill as a manual advisory roundtable. Coordinate true child agents, not simulated personas, through the current Host Adapter.
|
|
9
|
+
|
|
10
|
+
## Boundaries
|
|
11
|
+
|
|
12
|
+
- Party Mode is advisory only.
|
|
13
|
+
- It cannot mutate task status, start/review/complete/archive tasks, add sessions, commit, push, or satisfy formal workflow gates.
|
|
14
|
+
- It cannot satisfy formal Implement or Check completion. Use `cowork-implement` and `cowork-check` for formal phases.
|
|
15
|
+
- Discussion children are leaf executors. They do not dispatch, wait for, list, or close other agents.
|
|
16
|
+
- Generic `worker`, `default`, or `explorer` agents may provide advisory views, but their output never proves implementation or checking done.
|
|
17
|
+
|
|
18
|
+
## Defaults And Config
|
|
19
|
+
|
|
20
|
+
Built-in defaults:
|
|
21
|
+
|
|
22
|
+
- `max_agents=3`
|
|
23
|
+
- `max_rounds=3`
|
|
24
|
+
|
|
25
|
+
These defaults are configurable values, not hard-coded constants. Effective config order:
|
|
26
|
+
|
|
27
|
+
```text
|
|
28
|
+
call arguments > task/change config > `.cowork-flow/config.yaml` > skill defaults
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Configurable fields:
|
|
32
|
+
|
|
33
|
+
- `max_agents`
|
|
34
|
+
- `max_rounds`
|
|
35
|
+
- agent roster or review lenses
|
|
36
|
+
- report enabled
|
|
37
|
+
- report path
|
|
38
|
+
- whether round 2 or round 3 is allowed
|
|
39
|
+
|
|
40
|
+
Safety gates:
|
|
41
|
+
|
|
42
|
+
- continue conditions can be tightened but not removed
|
|
43
|
+
- stop conditions can be tightened but not removed
|
|
44
|
+
- schema core fields can be extended but not removed
|
|
45
|
+
- exceeding effective limits requires explicit user approval
|
|
46
|
+
|
|
47
|
+
## Round Model
|
|
48
|
+
|
|
49
|
+
1. Frame the question, decision needed, scope, and evidence packet.
|
|
50
|
+
2. Round 1 uses fresh child contexts. Send each child the same packet and one lens. Child agents cannot see each other.
|
|
51
|
+
3. Synthesize only evidence-backed positions, disagreements, rejected options, and acceptance signals.
|
|
52
|
+
4. Continue only when a continue condition is met. Send narrow follow-up prompts to the smallest useful set of children.
|
|
53
|
+
5. Stop when any stop condition is met. Close all live children through the Host Adapter close primitive.
|
|
54
|
+
|
|
55
|
+
Round intent:
|
|
56
|
+
|
|
57
|
+
- Opening round: independent first judgments.
|
|
58
|
+
- Challenge rounds: rebuttal, risk drilldown, or evidence repair on specific disagreements.
|
|
59
|
+
- Convergence rounds: decision check only. Verify, narrow, or choose. Do not open new directions.
|
|
60
|
+
|
|
61
|
+
Default mapping:
|
|
62
|
+
|
|
63
|
+
- Round 1 = Opening.
|
|
64
|
+
- Round 2 = Challenge.
|
|
65
|
+
- Round 3+ = Convergence.
|
|
66
|
+
|
|
67
|
+
Only the user can restart exploration after convergence begins.
|
|
68
|
+
|
|
69
|
+
## Continue Conditions
|
|
70
|
+
|
|
71
|
+
Run another round only if at least one condition holds:
|
|
72
|
+
|
|
73
|
+
- A disagreement could change the recommended decision.
|
|
74
|
+
- A high risk lacks enough evidence.
|
|
75
|
+
- Acceptance criteria are still not testable.
|
|
76
|
+
- A child found new file, command, rule, or user-scenario evidence.
|
|
77
|
+
- The coordinator cannot write one recommended direction.
|
|
78
|
+
|
|
79
|
+
## Stop Conditions
|
|
80
|
+
|
|
81
|
+
Stop when any condition holds:
|
|
82
|
+
|
|
83
|
+
- Recommendation, rejected options, and measurable acceptance criteria are clear.
|
|
84
|
+
- Remaining issue is user value preference, not missing evidence.
|
|
85
|
+
- A full round adds no evidence and does not narrow scope.
|
|
86
|
+
- Effective `max_rounds` is reached.
|
|
87
|
+
- Output fails schema, and one repair prompt does not fix it.
|
|
88
|
+
|
|
89
|
+
## Child Output Schema
|
|
90
|
+
|
|
91
|
+
Each child must return these core fields. Extra fields are allowed only after them.
|
|
92
|
+
|
|
93
|
+
```text
|
|
94
|
+
position:
|
|
95
|
+
evidence:
|
|
96
|
+
risk:
|
|
97
|
+
tradeoff:
|
|
98
|
+
rejected_option:
|
|
99
|
+
acceptance_signal:
|
|
100
|
+
what_would_change_my_mind:
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Reject unsupported opinion. Evidence should name files, commands, rules, observed behavior, user scenarios, or concrete assumptions.
|
|
104
|
+
|
|
105
|
+
## Coordinator Output Schema
|
|
106
|
+
|
|
107
|
+
Final synthesis must return these core fields. Extra fields are allowed only after them.
|
|
108
|
+
|
|
109
|
+
```text
|
|
110
|
+
consensus:
|
|
111
|
+
disagreements:
|
|
112
|
+
evidence:
|
|
113
|
+
decision:
|
|
114
|
+
rejected_options:
|
|
115
|
+
acceptance_criteria:
|
|
116
|
+
open_questions:
|
|
117
|
+
stop_reason:
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Keep the final decision traceable to child evidence. Do not count votes as validation.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
0.0.
|
|
1
|
+
0.0.24
|
|
@@ -9,6 +9,7 @@ import re
|
|
|
9
9
|
import sys
|
|
10
10
|
from datetime import datetime
|
|
11
11
|
from pathlib import Path
|
|
12
|
+
from typing import Iterable
|
|
12
13
|
|
|
13
14
|
from common.archive_utils import archive_directory_resumable
|
|
14
15
|
from common.paths import (
|
|
@@ -161,6 +162,63 @@ def _normalize_archive_metadata(repo_root: Path, change_dir: Path) -> None:
|
|
|
161
162
|
_write_metadata(change_dir, metadata)
|
|
162
163
|
|
|
163
164
|
|
|
165
|
+
def _same_path(left: Path, right: Path) -> bool:
|
|
166
|
+
try:
|
|
167
|
+
return left.resolve() == right.resolve()
|
|
168
|
+
except OSError:
|
|
169
|
+
return left.absolute() == right.absolute()
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def _task_link_matches(repo_root: Path, value: object, task_paths: Iterable[Path]) -> bool:
|
|
173
|
+
resolved = _resolve_link(repo_root, "tasks", value)
|
|
174
|
+
if resolved is None:
|
|
175
|
+
return False
|
|
176
|
+
return any(_same_path(resolved, task_path) for task_path in task_paths)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def linked_active_changes_for_task(repo_root: Path, task_paths: Iterable[Path]) -> list[str]:
|
|
180
|
+
"""Return active change slugs whose metadata points at one of the task paths."""
|
|
181
|
+
changes_dir = _changes_dir(repo_root)
|
|
182
|
+
if not changes_dir.is_dir():
|
|
183
|
+
return []
|
|
184
|
+
|
|
185
|
+
paths = tuple(task_paths)
|
|
186
|
+
slugs: list[str] = []
|
|
187
|
+
for change_dir in sorted(changes_dir.iterdir()):
|
|
188
|
+
if not change_dir.is_dir() or change_dir.name == DIR_ARCHIVE:
|
|
189
|
+
continue
|
|
190
|
+
metadata = _read_metadata(change_dir)
|
|
191
|
+
if metadata.get("status") == "archived":
|
|
192
|
+
continue
|
|
193
|
+
if _task_link_matches(repo_root, metadata.get("task"), paths):
|
|
194
|
+
slugs.append(change_dir.name)
|
|
195
|
+
return slugs
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def archive_change_by_slug(repo_root: Path, slug: str) -> Path | None:
|
|
199
|
+
source = _change_dir(repo_root, slug)
|
|
200
|
+
if source.is_dir():
|
|
201
|
+
_normalize_archive_metadata(repo_root, source)
|
|
202
|
+
|
|
203
|
+
if not validate_change(repo_root, slug, quiet=True):
|
|
204
|
+
return None
|
|
205
|
+
|
|
206
|
+
month = datetime.now().astimezone().strftime("%Y-%m")
|
|
207
|
+
destination = _archive_dir(repo_root) / month / slug
|
|
208
|
+
result = archive_directory_resumable(source, destination)
|
|
209
|
+
if not result.ok:
|
|
210
|
+
level = "Warning" if result.partial else "Error"
|
|
211
|
+
print(f"{level}: {result.message}", file=sys.stderr)
|
|
212
|
+
return None
|
|
213
|
+
|
|
214
|
+
metadata = _read_metadata(destination)
|
|
215
|
+
metadata["status"] = "archived"
|
|
216
|
+
metadata["archived_at"] = _now_iso()
|
|
217
|
+
metadata["task"] = _normalize_task_link(repo_root, metadata.get("task"))
|
|
218
|
+
_write_metadata(destination, metadata)
|
|
219
|
+
return destination
|
|
220
|
+
|
|
221
|
+
|
|
164
222
|
|
|
165
223
|
def _display_path(repo_root: Path, path: Path) -> str:
|
|
166
224
|
try:
|
|
@@ -272,27 +330,9 @@ def validate_command(args: argparse.Namespace) -> int:
|
|
|
272
330
|
def archive_change(args: argparse.Namespace) -> int:
|
|
273
331
|
repo_root = get_repo_root()
|
|
274
332
|
slug = args.slug
|
|
275
|
-
|
|
276
|
-
if source.is_dir():
|
|
277
|
-
_normalize_archive_metadata(repo_root, source)
|
|
278
|
-
|
|
279
|
-
if not validate_change(repo_root, slug, quiet=True):
|
|
333
|
+
if archive_change_by_slug(repo_root, slug) is None:
|
|
280
334
|
return 1
|
|
281
335
|
|
|
282
|
-
month = datetime.now().astimezone().strftime("%Y-%m")
|
|
283
|
-
destination = _archive_dir(repo_root) / month / slug
|
|
284
|
-
result = archive_directory_resumable(source, destination)
|
|
285
|
-
if not result.ok:
|
|
286
|
-
level = "Warning" if result.partial else "Error"
|
|
287
|
-
print(f"{level}: {result.message}", file=sys.stderr)
|
|
288
|
-
return 1
|
|
289
|
-
|
|
290
|
-
metadata = _read_metadata(destination)
|
|
291
|
-
metadata["status"] = "archived"
|
|
292
|
-
metadata["archived_at"] = _now_iso()
|
|
293
|
-
metadata["task"] = _normalize_task_link(repo_root, metadata.get("task"))
|
|
294
|
-
_write_metadata(destination, metadata)
|
|
295
|
-
|
|
296
336
|
print(f"archived {slug}")
|
|
297
337
|
return 0
|
|
298
338
|
|
|
@@ -14,7 +14,7 @@ Usage:
|
|
|
14
14
|
./.cowork-flow/run task complete [dir] # Mark task completed
|
|
15
15
|
./.cowork-flow/run task current # Show active session task
|
|
16
16
|
./.cowork-flow/run task finish # Clear active session task
|
|
17
|
-
./.cowork-flow/run task archive <task-name> # Archive completed task
|
|
17
|
+
./.cowork-flow/run task archive <task-name> # Archive completed task and linked changes
|
|
18
18
|
./.cowork-flow/run task list # List active tasks
|
|
19
19
|
./.cowork-flow/run task list-archive [month] # List archived tasks
|
|
20
20
|
./.cowork-flow/run task add-subtask <parent-dir> <child-dir> # Link child to parent
|
|
@@ -47,6 +47,7 @@ from common.active_task import (
|
|
|
47
47
|
from common.paths import (
|
|
48
48
|
DIR_WORKFLOW,
|
|
49
49
|
DIR_AGENTS,
|
|
50
|
+
DIR_CHANGES,
|
|
50
51
|
DIR_TASKS,
|
|
51
52
|
DIR_SPEC,
|
|
52
53
|
DIR_ARCHIVE,
|
|
@@ -1120,6 +1121,40 @@ def _print_blockers(blockers: list[str]) -> None:
|
|
|
1120
1121
|
print(f" - {blocker}")
|
|
1121
1122
|
|
|
1122
1123
|
|
|
1124
|
+
def _linked_active_changes_for_task(repo_root: Path, task_dir: Path) -> list[str]:
|
|
1125
|
+
from change import linked_active_changes_for_task
|
|
1126
|
+
|
|
1127
|
+
return linked_active_changes_for_task(repo_root, (task_dir,))
|
|
1128
|
+
|
|
1129
|
+
|
|
1130
|
+
def _linked_changes_ready_for_archive(repo_root: Path, slugs: list[str]) -> bool:
|
|
1131
|
+
from change import validate_change
|
|
1132
|
+
|
|
1133
|
+
ready = True
|
|
1134
|
+
for slug in slugs:
|
|
1135
|
+
if not validate_change(repo_root, slug, quiet=True):
|
|
1136
|
+
print(
|
|
1137
|
+
colored(f"Error: Linked change is not ready to archive: {slug}", Colors.RED),
|
|
1138
|
+
file=sys.stderr,
|
|
1139
|
+
)
|
|
1140
|
+
ready = False
|
|
1141
|
+
return ready
|
|
1142
|
+
|
|
1143
|
+
|
|
1144
|
+
def _archive_linked_changes(repo_root: Path, slugs: list[str]) -> bool:
|
|
1145
|
+
from change import archive_change_by_slug
|
|
1146
|
+
|
|
1147
|
+
for slug in slugs:
|
|
1148
|
+
if archive_change_by_slug(repo_root, slug) is None:
|
|
1149
|
+
print(
|
|
1150
|
+
colored(f"Error: Failed to archive linked change: {slug}", Colors.RED),
|
|
1151
|
+
file=sys.stderr,
|
|
1152
|
+
)
|
|
1153
|
+
return False
|
|
1154
|
+
print(colored(f"Archived linked change: {slug}", Colors.GREEN), file=sys.stderr)
|
|
1155
|
+
return True
|
|
1156
|
+
|
|
1157
|
+
|
|
1123
1158
|
def cmd_next(args: argparse.Namespace) -> int:
|
|
1124
1159
|
"""Print the next safe workflow action without mutating state."""
|
|
1125
1160
|
repo_root = get_repo_root()
|
|
@@ -1216,7 +1251,10 @@ def cmd_next(args: argparse.Namespace) -> int:
|
|
|
1216
1251
|
if status in DONE_STATUSES:
|
|
1217
1252
|
print("Next action: finalize, commit, archive, and record session")
|
|
1218
1253
|
print("Command: git status --short")
|
|
1254
|
+
linked_changes = _linked_active_changes_for_task(repo_root, task_dir)
|
|
1219
1255
|
print(f"Then: ./.cowork-flow/run task archive {Path(task_path).name}")
|
|
1256
|
+
for slug in linked_changes:
|
|
1257
|
+
print(f"Then: ./.cowork-flow/run change archive {slug} (handled by task archive)")
|
|
1220
1258
|
_print_blockers(blockers)
|
|
1221
1259
|
return 0
|
|
1222
1260
|
|
|
@@ -1231,7 +1269,7 @@ def cmd_next(args: argparse.Namespace) -> int:
|
|
|
1231
1269
|
# =============================================================================
|
|
1232
1270
|
|
|
1233
1271
|
def cmd_archive(args: argparse.Namespace) -> int:
|
|
1234
|
-
"""Archive completed task."""
|
|
1272
|
+
"""Archive completed task and linked changes."""
|
|
1235
1273
|
repo_root = get_repo_root()
|
|
1236
1274
|
task_name = args.name
|
|
1237
1275
|
|
|
@@ -1258,6 +1296,10 @@ def cmd_archive(args: argparse.Namespace) -> int:
|
|
|
1258
1296
|
if task_json_path.is_file():
|
|
1259
1297
|
task_data = _read_json_file(task_json_path)
|
|
1260
1298
|
|
|
1299
|
+
linked_changes = _linked_active_changes_for_task(repo_root, task_dir)
|
|
1300
|
+
if linked_changes and not _linked_changes_ready_for_archive(repo_root, linked_changes):
|
|
1301
|
+
return 1
|
|
1302
|
+
|
|
1261
1303
|
# Archive
|
|
1262
1304
|
result = archive_task_complete(task_dir, repo_root)
|
|
1263
1305
|
if "archived_to" not in result:
|
|
@@ -1284,6 +1326,9 @@ def cmd_archive(args: argparse.Namespace) -> int:
|
|
|
1284
1326
|
year_month = archive_dest.parent.name
|
|
1285
1327
|
print(colored(f"Archived: {dir_name} -> archive/{year_month}/", Colors.GREEN), file=sys.stderr)
|
|
1286
1328
|
|
|
1329
|
+
if linked_changes and not _archive_linked_changes(repo_root, linked_changes):
|
|
1330
|
+
return 1
|
|
1331
|
+
|
|
1287
1332
|
# Auto-commit only when explicitly requested.
|
|
1288
1333
|
if getattr(args, "commit", False) and not getattr(args, "no_commit", False):
|
|
1289
1334
|
_auto_commit_archive(dir_name, repo_root)
|
|
@@ -1297,13 +1342,19 @@ def cmd_archive(args: argparse.Namespace) -> int:
|
|
|
1297
1342
|
|
|
1298
1343
|
|
|
1299
1344
|
def _auto_commit_archive(task_name: str, repo_root: Path) -> None:
|
|
1300
|
-
"""Stage
|
|
1345
|
+
"""Stage task/change archive changes and commit after archive."""
|
|
1301
1346
|
tasks_rel = f"{DIR_WORKFLOW}/{DIR_TASKS}"
|
|
1302
|
-
|
|
1347
|
+
changes_rel = f"{DIR_WORKFLOW}/{DIR_CHANGES}"
|
|
1348
|
+
archive_rels = [
|
|
1349
|
+
rel
|
|
1350
|
+
for rel in (tasks_rel, changes_rel)
|
|
1351
|
+
if (repo_root / rel).exists()
|
|
1352
|
+
]
|
|
1353
|
+
_run_git_command(["add", "-A", *archive_rels], cwd=repo_root)
|
|
1303
1354
|
|
|
1304
1355
|
# Check if there are staged changes
|
|
1305
1356
|
rc, _, _ = _run_git_command(
|
|
1306
|
-
["diff", "--cached", "--quiet", "--",
|
|
1357
|
+
["diff", "--cached", "--quiet", "--", *archive_rels], cwd=repo_root
|
|
1307
1358
|
)
|
|
1308
1359
|
if rc == 0:
|
|
1309
1360
|
print("[OK] No task changes to commit.", file=sys.stderr)
|
|
@@ -1564,7 +1615,7 @@ Usage:
|
|
|
1564
1615
|
./.cowork-flow/run task complete [dir] Mark task completed
|
|
1565
1616
|
./.cowork-flow/run task finish Clear active session task
|
|
1566
1617
|
./.cowork-flow/run task next [dir] Show next safe workflow action
|
|
1567
|
-
./.cowork-flow/run task archive <task-name> Archive completed task
|
|
1618
|
+
./.cowork-flow/run task archive <task-name> Archive completed task and linked changes
|
|
1568
1619
|
./.cowork-flow/run task add-subtask <parent> <child> Link child task to parent
|
|
1569
1620
|
./.cowork-flow/run task remove-subtask <parent> <child> Unlink child from parent
|
|
1570
1621
|
./.cowork-flow/run task list [--mine] [--status <status>] List tasks
|
|
@@ -12,6 +12,12 @@ classification or a prompt handshake.
|
|
|
12
12
|
- Generic `worker`, `default`, or `explorer` dispatch is advisory only and
|
|
13
13
|
cannot satisfy formal Implement or Check completion.
|
|
14
14
|
|
|
15
|
+
## Advisory Party Mode
|
|
16
|
+
|
|
17
|
+
Party Mode discussion children are advisory leaf executors. They use fresh child contexts for evidence gathering, disagreement surfacing, risk review, and acceptance-signal review. They cannot mutate task status, write code, archive, commit, or coordinate other agents. Their output cannot satisfy formal Implement or Check completion.
|
|
18
|
+
|
|
19
|
+
The `party-mode` skill owns round limits, continuation gates, stop gates, and output schemas. This document remains the formal `cowork-*` dispatch protocol source.
|
|
20
|
+
|
|
15
21
|
## Runtime Context
|
|
16
22
|
|
|
17
23
|
Before spawning a formal child, the main session creates:
|
|
@@ -92,27 +92,33 @@ changes -> brainstorming -> read spec -> plan -> tasks -> implement -> check ->
|
|
|
92
92
|
- 固定 `cowork-*` 代理是叶子执行者;不得再派发、等待、列出或取消其他代理。
|
|
93
93
|
- 通用 `worker`、`default` 或 `explorer` 只能作为 advisory work,不能满足正式实现或检查完成条件。
|
|
94
94
|
|
|
95
|
-
## 3.2
|
|
95
|
+
## 3.2 手动 Party Mode
|
|
96
|
+
|
|
97
|
+
Party Mode 是用户手动触发的 advisory roundtable。主会话可通过当前宿主适配器创建 fresh child contexts,收集真实讨论子代理的证据、分歧、风险和可测验收信号,再由主会话综合结论。
|
|
98
|
+
|
|
99
|
+
Party Mode 不能推进任务状态,不能满足正式实现或检查完成条件,也不能替代 `cowork-implement` 或 `cowork-check`。轮次上限、继续/停止条件、输出 schema 和可配置默认值由 party-mode skill 定义;正式子代理协议仍以 `.cowork-flow/spec/subagent-dispatch.md` 为准。
|
|
100
|
+
|
|
101
|
+
## 3.3 并行会话
|
|
96
102
|
|
|
97
103
|
并行执行采用干净隔离的并行会话模型。并行只是执行策略,不改变固定代理叶子边界。
|
|
98
104
|
|
|
99
|
-
### 3.
|
|
105
|
+
### 3.3.1 并行决策
|
|
100
106
|
|
|
101
107
|
- 用户无需在需求输入时声明是否并行;计划阶段由主会话评估并行可行性。
|
|
102
108
|
- 开发计划必须明确执行策略:串行执行,或列出可并行的低冲突切片。
|
|
103
109
|
- 同一文件、同一行为链、依赖未合并或验收标准不清的工作不得并行,改为串行。
|
|
104
110
|
|
|
105
|
-
### 3.
|
|
111
|
+
### 3.3.2 隔离方式
|
|
106
112
|
|
|
107
113
|
- 多个独立任务优先拆成多个 cowork-flow 会话。
|
|
108
114
|
- 只要存在写入冲突风险,就用独立 `git worktree` 隔离。
|
|
109
115
|
|
|
110
|
-
### 3.
|
|
116
|
+
### 3.3.3 单任务内切片
|
|
111
117
|
|
|
112
118
|
- 单个任务内只允许低冲突切片并行。
|
|
113
119
|
- 每个切片必须写清文件归属、依赖关系、预期产物和验证命令。
|
|
114
120
|
|
|
115
|
-
### 3.
|
|
121
|
+
### 3.3.4 协调与验收
|
|
116
122
|
|
|
117
123
|
- 主会话是唯一协调者:派发所有子代理后逐个等待,核对子代理汇报的文件、命令和产物,再用适配器列表/取消原语收口。
|
|
118
124
|
- 多个实现切片合并后必须再执行最终集成验证;不能把各子代理的局部通过当成整体通过。
|
|
@@ -214,6 +220,8 @@ L2 任务在 `task start` 前必须通过 readiness gate;同一 blocker 列表
|
|
|
214
220
|
git add <expected files>
|
|
215
221
|
git commit -m "<message>"
|
|
216
222
|
```
|
|
223
|
+
`task archive <task-name>` 会归档 task,并自动归档 `change.yaml.task`
|
|
224
|
+
指向该 task 的 active change;无法通过 `change validate` 的 change 不会被自动归档。
|
|
217
225
|
|
|
218
226
|
## 9. 恢复规则
|
|
219
227
|
|