@pennixrv/trellis 0.7.0-beta.32 → 0.7.0-beta.34
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/dist/commands/channel/index.d.ts.map +1 -1
- package/dist/commands/channel/index.js +15 -11
- package/dist/commands/channel/index.js.map +1 -1
- package/dist/commands/channel/spawn.d.ts.map +1 -1
- package/dist/commands/channel/spawn.js +16 -11
- package/dist/commands/channel/spawn.js.map +1 -1
- package/dist/migrations/manifests/0.7.0-beta.33.json +9 -0
- package/dist/migrations/manifests/0.7.0-beta.34.json +9 -0
- package/dist/templates/common/bundled-skills/trellis-channel/SKILL.md +3 -1
- package/dist/templates/common/bundled-skills/trellis-channel/references/command-reference.md +11 -10
- package/dist/templates/common/bundled-skills/trellis-channel/references/forum.md +4 -12
- package/dist/templates/common/bundled-skills/trellis-channel/references/multi-target-dispatch.md +113 -0
- package/dist/templates/common/bundled-skills/trellis-channel/references/progress-debugging.md +4 -4
- package/dist/templates/common/bundled-skills/trellis-channel/references/subnode-work.md +32 -7
- package/dist/templates/common/bundled-skills/trellis-channel/references/workers.md +2 -3
- package/dist/templates/common/bundled-skills/trellis-channel/references/workflows.md +11 -12
- package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-task-lifecycle.md +6 -2
- package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/task-system.md +6 -1
- package/dist/templates/common/commands/continue.md +3 -2
- package/dist/templates/common/commands/start.md +1 -0
- package/dist/templates/common/skills/brainstorm.md +14 -1
- package/dist/templates/shared-hooks/inject-workflow-state.py +2 -1
- package/dist/templates/trellis/index.d.ts +1 -0
- package/dist/templates/trellis/index.d.ts.map +1 -1
- package/dist/templates/trellis/index.js +2 -0
- package/dist/templates/trellis/index.js.map +1 -1
- package/dist/templates/trellis/scripts/common/task_planning.py +133 -0
- package/dist/templates/trellis/scripts/common/task_store.py +20 -1
- package/dist/templates/trellis/scripts/subnode_artifact.py +32 -0
- package/dist/templates/trellis/scripts/task.py +96 -2
- package/dist/templates/trellis/workflow.md +39 -2
- package/package.json +2 -2
package/dist/templates/common/bundled-skills/trellis-channel/references/multi-target-dispatch.md
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# Multiple Evidence Units
|
|
2
|
+
|
|
3
|
+
Use after the user has authorized independent evidence and the task owns a
|
|
4
|
+
reviewed unit mapping. Read `subnode-work.md` for artifact and acceptance rules.
|
|
5
|
+
Codex inline mode governs native implement/check agents; it does not disable
|
|
6
|
+
explicit Channel evidence. Missing independent reports cannot be replaced by
|
|
7
|
+
main-session pass claims.
|
|
8
|
+
|
|
9
|
+
## Prepare And Dispatch In One Host Call
|
|
10
|
+
|
|
11
|
+
Choose units by work, complexity, evidence range, and relationship. Combine only
|
|
12
|
+
individually quick/simple and obviously related points with shared context and
|
|
13
|
+
a bounded total workload. Each scope item keeps its own evidence, conclusion,
|
|
14
|
+
unknowns, and stop condition. Neither eight slots nor eight owner domains imply
|
|
15
|
+
eight work packages. Store this mapping in the task's existing plan or matrix.
|
|
16
|
+
|
|
17
|
+
Read the actual native guard and current workers/reservations. Initially fill
|
|
18
|
+
all available capacity in FIFO order; a justified dependency or resource limit
|
|
19
|
+
belongs in the task record, not an arbitrary coordinator throttle. Initialize
|
|
20
|
+
all reviewed briefs before queue creation or spawning. A failed initialization
|
|
21
|
+
stops admission; preserve/reconcile partial artifacts without dispatching them.
|
|
22
|
+
|
|
23
|
+
For a configured subnode profile, `spawn --profile <id> --as <worker>` implies
|
|
24
|
+
the subnode role and inherits its Codex provider; do not repeat
|
|
25
|
+
`--agent subnode --provider codex`. Keep each worker ID and `send --to` explicit.
|
|
26
|
+
Author identity on `send` defaults to `TRELLIS_CHANNEL_AS` or `main`, so routine
|
|
27
|
+
dispatch need not repeat `--as main`.
|
|
28
|
+
|
|
29
|
+
Coalesce operations that can share one noninteractive host invocation. Use ONE
|
|
30
|
+
native `exec_command` containing a standard-library loop over existing CLI
|
|
31
|
+
commands. Multiple `exec_command` calls inside `functions.exec` still count as
|
|
32
|
+
multiple host calls. This is a procedure, not a new Channel batch API, scheduler,
|
|
33
|
+
or worker-state store. Keep init and dispatch as separate admission stages.
|
|
34
|
+
|
|
35
|
+
The following runnable pattern consumes a temporary, reviewed JSON array of
|
|
36
|
+
`{"target": "unit-id", "argv": ["trellis", "channel", ...]}` entries. Use
|
|
37
|
+
`[sys.executable, ".trellis/scripts/subnode_artifact.py", ...]` for artifact
|
|
38
|
+
commands. Put claim, spawn, and send in queue order; do not include waits. Review
|
|
39
|
+
each argument vector and the native capacity before executing. The input and
|
|
40
|
+
raw receipts are temporary command data, not task authority; retain only needed
|
|
41
|
+
non-sensitive evidence and clean the temporary input after use.
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# python on Windows; python3 elsewhere. Execute this whole block in ONE host call.
|
|
45
|
+
python3 - /absolute/path/to/reviewed-actions.json <<'PY'
|
|
46
|
+
import json
|
|
47
|
+
import subprocess
|
|
48
|
+
import sys
|
|
49
|
+
|
|
50
|
+
actions = json.load(open(sys.argv[1], encoding="utf-8"))
|
|
51
|
+
failed = False
|
|
52
|
+
for action in actions:
|
|
53
|
+
if failed:
|
|
54
|
+
print(json.dumps({"target": action["target"], "status": "not_attempted"}))
|
|
55
|
+
continue
|
|
56
|
+
argv = [sys.executable if part == "{python}" else part for part in action["argv"]]
|
|
57
|
+
try:
|
|
58
|
+
result = subprocess.run(argv, capture_output=True, text=True, check=False)
|
|
59
|
+
failed = result.returncode != 0
|
|
60
|
+
receipt = {"target": action["target"], "argv": argv,
|
|
61
|
+
"status": "error" if failed else "completed",
|
|
62
|
+
"exit_code": result.returncode,
|
|
63
|
+
"stdout": result.stdout, "stderr": result.stderr}
|
|
64
|
+
except OSError as error:
|
|
65
|
+
failed = True
|
|
66
|
+
receipt = {"target": action["target"], "argv": argv,
|
|
67
|
+
"status": "error", "error": str(error)}
|
|
68
|
+
print(json.dumps(receipt), flush=True)
|
|
69
|
+
sys.exit(1 if failed else 0)
|
|
70
|
+
PY
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
The aggregate exit code is not per-target evidence. Inspect every receipt,
|
|
74
|
+
including missing or conflicting receipts; the first error stops later actions
|
|
75
|
+
and pauses the whole queue. Never retry a claimed attempt. A new approved
|
|
76
|
+
attempt needs a new brief and ID.
|
|
77
|
+
|
|
78
|
+
## Rolling FIFO
|
|
79
|
+
|
|
80
|
+
1. Capture a durable barrier before dispatch. Claim the earliest unclaimed unit,
|
|
81
|
+
spawn/send through native Channel, and preserve its receipt. The helper
|
|
82
|
+
enforces claim order; earlier claims need not be accepted to fill initial slots.
|
|
83
|
+
2. Establish exactly one native waiter for the dispatched workers without
|
|
84
|
+
`--all`. Resume only its live host continuation until it returns, times out,
|
|
85
|
+
or errors; run no other host action meanwhile.
|
|
86
|
+
3. Recheck dispatched items for failures, conflicts, and unresolved terminal
|
|
87
|
+
states. Still-running healthy workers do not need to finish. Validate the
|
|
88
|
+
completed worker's per-scope report, recheck sources and protected targets,
|
|
89
|
+
then write its create-once `accepted` disposition.
|
|
90
|
+
4. That acceptance plus verified native capacity authorizes the next unclaimed
|
|
91
|
+
FIFO unit immediately, while other healthy workers continue. Preserve the
|
|
92
|
+
barrier/event cursor so already emitted terminal events cannot be lost.
|
|
93
|
+
5. Incomplete/blocked/rejected results, validator concerns, capacity refusal,
|
|
94
|
+
missing/conflicting receipts, or unresolvable reservations pause the entire
|
|
95
|
+
queue. A terminal event alone never authorizes replacement. On recovery,
|
|
96
|
+
reconstruct claims, events, workers/reservations, reports, and dispositions;
|
|
97
|
+
never redispatch an old claim or require all healthy live predecessors to
|
|
98
|
+
complete merely to refill an accepted slot.
|
|
99
|
+
|
|
100
|
+
## Temporary Task Switch
|
|
101
|
+
|
|
102
|
+
Save the old task's checkpoint. Pause refills and drain ALL already dispatched
|
|
103
|
+
units: wait, validate, and disposition each result, including failures; confirm
|
|
104
|
+
the live continuation, worker, and reservation boundaries are closed. A drain
|
|
105
|
+
does not dispatch pending queue items or turn a failure into acceptance. Do not
|
|
106
|
+
automatically kill, retry, or abandon the old queue. Preserve unclaimed work and
|
|
107
|
+
the old phase. Only after drain, run native `task.py select <new-task>`; it
|
|
108
|
+
changes context without status, branch, after_start hooks, or implementation
|
|
109
|
+
authority. With no in-flight work, select directly after saving the checkpoint.
|
|
110
|
+
|
|
111
|
+
When returning, reconstruct only the missing old-task facts and resume only
|
|
112
|
+
still-authorized work. Selecting a user-stopped audit never restarts it. Ordinary
|
|
113
|
+
continue and sufficient compaction recovery retain their direct checkpoint path.
|
package/dist/templates/common/bundled-skills/trellis-channel/references/progress-debugging.md
CHANGED
|
@@ -127,7 +127,7 @@ wait command starts, capture a barrier before that operation and pass it back:
|
|
|
127
127
|
```bash
|
|
128
128
|
BARRIER="$(trellis channel barrier T)"
|
|
129
129
|
# spawn or trigger work that may emit a terminal event
|
|
130
|
-
trellis channel wait T --
|
|
130
|
+
trellis channel wait T --workers check \
|
|
131
131
|
--after-seq "$BARRIER" --timeout 15m
|
|
132
132
|
```
|
|
133
133
|
|
|
@@ -146,10 +146,10 @@ The separate event-filter mode wakes on:
|
|
|
146
146
|
Useful filters:
|
|
147
147
|
|
|
148
148
|
```bash
|
|
149
|
-
trellis channel wait T --
|
|
150
|
-
trellis channel wait T --
|
|
149
|
+
trellis channel wait T --from check --kind done --timeout 15m
|
|
150
|
+
trellis channel wait T --from check,check-cx --kind done --all --timeout 15m
|
|
151
151
|
trellis channel wait T --as worker --kind message --timeout 1h
|
|
152
|
-
trellis channel wait T --
|
|
152
|
+
trellis channel wait T --thread release-note --action status --timeout 10m
|
|
153
153
|
```
|
|
154
154
|
|
|
155
155
|
Exit codes: `0` matched, `124` timeout, `1`/`2` errors. On `wait --all`
|
|
@@ -20,7 +20,26 @@ dependencies, stop conditions, deadline, and `channel_ref`. Set `retry_of`
|
|
|
20
20
|
only for an explicit manual retry and `counter_of` only for intentional
|
|
21
21
|
counterwork. A retry names an existing, different subnode in the same task and
|
|
22
22
|
`work_id`; counterwork may be initialized independently. The helper supplies
|
|
23
|
-
the immutable task identity and report path.
|
|
23
|
+
the immutable task identity and report path. New units also require `unit_plan`:
|
|
24
|
+
|
|
25
|
+
```json
|
|
26
|
+
{
|
|
27
|
+
"unit_plan": {
|
|
28
|
+
"plan_ref": ".trellis/tasks/09-07-example/design.md#evidence-units",
|
|
29
|
+
"sizing_rationale": "One bounded dependency inspection with a separately reviewable result."
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
The reference must identify an existing regular Markdown document inside this
|
|
35
|
+
task. For multiple scope items, add `grouping_rationale` explaining why every
|
|
36
|
+
item is quick/simple, obviously related, and uses shared context while retaining
|
|
37
|
+
separate conclusions. Record the actual scope mapping and evidence in the task
|
|
38
|
+
plan before initializing briefs; owner domains and slot counts do not determine
|
|
39
|
+
unit counts. Complex or lengthy points need separate units or further splitting.
|
|
40
|
+
The helper checks structure, not the truth of the sizing judgment. Historical
|
|
41
|
+
brief/report readers remain compatible; new init and queue admission require
|
|
42
|
+
the unit plan.
|
|
24
43
|
|
|
25
44
|
```bash
|
|
26
45
|
TASK=.trellis/tasks/09-07-example
|
|
@@ -117,19 +136,20 @@ the worker can emit a terminal event. The CLI waits once for a worker lifecycle
|
|
|
117
136
|
transition after that barrier, including supervisor-authored terminal events:
|
|
118
137
|
|
|
119
138
|
```bash
|
|
120
|
-
trellis channel create subnode-example
|
|
139
|
+
trellis channel create subnode-example
|
|
121
140
|
BARRIER="$(trellis channel barrier subnode-example)"
|
|
122
|
-
trellis channel spawn subnode-example --agent subnode --
|
|
123
|
-
--as "$SUBNODE_ID" --cwd "$PWD"
|
|
141
|
+
trellis channel spawn subnode-example --agent subnode --as "$SUBNODE_ID"
|
|
124
142
|
|
|
125
143
|
printf '%s\n' "Read $TASK/subnodes/$WORK_ID/$SUBNODE_ID/brief.json and perform only that bounded work." \
|
|
126
|
-
| trellis channel send subnode-example --
|
|
144
|
+
| trellis channel send subnode-example --to "$SUBNODE_ID" \
|
|
127
145
|
--stdin --delivery-mode requireRunningWorker
|
|
128
146
|
|
|
129
|
-
trellis channel wait subnode-example --
|
|
147
|
+
trellis channel wait subnode-example --workers "$SUBNODE_ID" \
|
|
130
148
|
--after-seq "$BARRIER" --timeout 30m
|
|
131
149
|
```
|
|
132
150
|
|
|
151
|
+
When selecting a configured model profile, use `--profile <id>` in place of `--agent subnode`; it implies the subnode role and reads its Codex provider from role frontmatter. Keep the unique worker `--as` for dispatch identity. Omit author `--as` on `send` and `wait` to use `TRELLIS_CHANNEL_AS` or `main`.
|
|
152
|
+
|
|
133
153
|
`channel.subnode` in `.trellis/config.yaml` supplies the role defaults for
|
|
134
154
|
`max_live_workers` (generated default `8`), `idle_timeout`, `timeout`, and
|
|
135
155
|
`warn_before`. Change that section before a dispatch group when its resource
|
|
@@ -137,7 +157,12 @@ or lifetime needs differ; pass the corresponding `spawn` flag only for a
|
|
|
137
157
|
one-off override. The generic `channel.worker_guard` remains the fallback for
|
|
138
158
|
ordinary workers and for a subnode key omitted from the project config.
|
|
139
159
|
|
|
140
|
-
For
|
|
160
|
+
For rolling FIFO dispatch, follow `references/multi-target-dispatch.md`: initially
|
|
161
|
+
fill available native capacity, wait for the first terminal worker, review and
|
|
162
|
+
accept it, then refill while other workers continue. Do not use `--all` as an
|
|
163
|
+
ordinary refill prerequisite. Use `--workers a,b --all` only when the intent
|
|
164
|
+
requires draining all already dispatched workers, with the barrier captured
|
|
165
|
+
before dispatch.
|
|
141
166
|
Worker mode emits one terminal worker projection per satisfied target. Ordinary
|
|
142
167
|
adapter errors and peer turn completion do not end the wait. `--from` remains
|
|
143
168
|
an exact event-author filter; it is not a worker lifecycle selector.
|
|
@@ -106,8 +106,7 @@ trellis channel spawn cr-example --agent check --provider codex --as check-cx \
|
|
|
106
106
|
--file "$TASK/prd.md" \
|
|
107
107
|
--file "$TASK/design.md" \
|
|
108
108
|
--file "$TASK/implement.md" \
|
|
109
|
-
--jsonl "$TASK/check.jsonl"
|
|
110
|
-
--cwd "$PWD" --timeout 30m
|
|
109
|
+
--jsonl "$TASK/check.jsonl" --timeout 30m
|
|
111
110
|
```
|
|
112
111
|
|
|
113
112
|
The `spawned` event records both the literal `files` array and any `manifests`
|
|
@@ -128,7 +127,7 @@ channel:
|
|
|
128
127
|
trellis channel spawn cr-feature --agent check --as check-claude
|
|
129
128
|
trellis channel spawn cr-feature --agent check --provider codex --as check-cx
|
|
130
129
|
|
|
131
|
-
trellis channel wait cr-feature
|
|
130
|
+
trellis channel wait cr-feature \
|
|
132
131
|
--from check-claude,check-cx --kind done --all --timeout 15m
|
|
133
132
|
```
|
|
134
133
|
|
|
@@ -9,7 +9,7 @@ Use when the user says "和 codex/claude 讨论一下", "brainstorm", or "拉一
|
|
|
9
9
|
进来一起看".
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
trellis channel create brainstorm-storage-layer
|
|
12
|
+
trellis channel create brainstorm-storage-layer \
|
|
13
13
|
--task .trellis/tasks/05-XX-storage-adapter
|
|
14
14
|
BARRIER="$(trellis channel barrier brainstorm-storage-layer)"
|
|
15
15
|
|
|
@@ -20,10 +20,10 @@ trellis channel spawn brainstorm-storage-layer \
|
|
|
20
20
|
--as cx-arch --timeout 30m
|
|
21
21
|
|
|
22
22
|
trellis channel send brainstorm-storage-layer \
|
|
23
|
-
--
|
|
23
|
+
--to cx-arch --text-file /tmp/brainstorm-r1.md
|
|
24
24
|
|
|
25
25
|
trellis channel wait brainstorm-storage-layer \
|
|
26
|
-
--
|
|
26
|
+
--kind done --from cx-arch --after-seq "$BARRIER" --timeout 10m
|
|
27
27
|
```
|
|
28
28
|
|
|
29
29
|
Do not stop after one answer. Read the answer, identify vague areas, send a
|
|
@@ -53,7 +53,7 @@ Use when the user asks to dispatch implementation or review work.
|
|
|
53
53
|
|
|
54
54
|
```bash
|
|
55
55
|
TASK=.trellis/tasks/05-12-foo
|
|
56
|
-
trellis channel create cr-foo --task "$TASK"
|
|
56
|
+
trellis channel create cr-foo --task "$TASK"
|
|
57
57
|
BARRIER="$(trellis channel barrier cr-foo)"
|
|
58
58
|
|
|
59
59
|
trellis channel spawn cr-foo \
|
|
@@ -61,11 +61,10 @@ trellis channel spawn cr-foo \
|
|
|
61
61
|
--jsonl "$TASK/check.jsonl" \
|
|
62
62
|
--file "$TASK/prd.md" \
|
|
63
63
|
--file "$TASK/design.md" \
|
|
64
|
-
--file "$TASK/implement.md"
|
|
65
|
-
--cwd "$PWD" --timeout 15m
|
|
64
|
+
--file "$TASK/implement.md" --timeout 15m
|
|
66
65
|
|
|
67
|
-
trellis channel send cr-foo --
|
|
68
|
-
trellis channel wait cr-foo --
|
|
66
|
+
trellis channel send cr-foo --to check --text-file /tmp/cr-brief.md
|
|
67
|
+
trellis channel wait cr-foo --kind done --from check --after-seq "$BARRIER" --timeout 15m
|
|
69
68
|
trellis channel messages cr-foo --kind message --from check --tag final_answer
|
|
70
69
|
```
|
|
71
70
|
|
|
@@ -78,7 +77,7 @@ already run.
|
|
|
78
77
|
Use one channel and distinct worker names.
|
|
79
78
|
|
|
80
79
|
```bash
|
|
81
|
-
trellis channel create cr-feature --
|
|
80
|
+
trellis channel create cr-feature --ephemeral
|
|
82
81
|
BARRIER="$(trellis channel barrier cr-feature)"
|
|
83
82
|
|
|
84
83
|
trellis channel spawn cr-feature --agent check \
|
|
@@ -89,9 +88,9 @@ trellis channel spawn cr-feature --agent check --provider codex --as check-cx \
|
|
|
89
88
|
--jsonl "$TASK/check.jsonl" --file "$TASK/prd.md" --file "$TASK/design.md" \
|
|
90
89
|
--timeout 15m
|
|
91
90
|
|
|
92
|
-
trellis channel send cr-feature --
|
|
93
|
-
trellis channel send cr-feature --
|
|
94
|
-
trellis channel wait cr-feature --
|
|
91
|
+
trellis channel send cr-feature --to check --text-file /tmp/cr-brief.md
|
|
92
|
+
trellis channel send cr-feature --to check-cx --text-file /tmp/cr-brief.md
|
|
93
|
+
trellis channel wait cr-feature --kind done --from check,check-cx --all --after-seq "$BARRIER" --timeout 15m
|
|
95
94
|
```
|
|
96
95
|
|
|
97
96
|
`--all` means every listed worker must emit a matching event.
|
|
@@ -10,6 +10,7 @@ Task lifecycle includes creation, start, context configuration, finish, archive,
|
|
|
10
10
|
4. `.trellis/scripts/common/task_store.py`
|
|
11
11
|
5. `.trellis/scripts/common/task_utils.py`
|
|
12
12
|
6. The current task's `.trellis/tasks/<task>/task.json`
|
|
13
|
+
7. `.trellis/scripts/common/task_planning.py` for material revisions and start admission
|
|
13
14
|
|
|
14
15
|
## Common Needs And Edit Points
|
|
15
16
|
|
|
@@ -23,6 +24,8 @@ Task lifecycle includes creation, start, context configuration, finish, archive,
|
|
|
23
24
|
| Change default task fields | `.trellis/scripts/common/task_store.py`. |
|
|
24
25
|
| Change task parsing/search | `.trellis/scripts/common/task_utils.py`. |
|
|
25
26
|
| Change active task behavior | `.trellis/scripts/common/active_task.py`. |
|
|
27
|
+
| Select context without starting | Native `task.py select`; preserve phase/branch and drain live Channel work first. |
|
|
28
|
+
| Seal and record later approval of a material plan | Native `task.py plan seal/approve`; reserved `meta.planning`, owned by `common/task_planning.py`. |
|
|
26
29
|
|
|
27
30
|
## lifecycle hooks
|
|
28
31
|
|
|
@@ -44,7 +47,7 @@ Hook commands receive the `TASK_JSON_PATH` environment variable, pointing to the
|
|
|
44
47
|
|
|
45
48
|
## Change Task Fields
|
|
46
49
|
|
|
47
|
-
If the user wants to add project-local fields, prefer putting them under `meta` in `task.json` to avoid breaking existing scripts' assumptions about standard fields.
|
|
50
|
+
If the user wants to add project-local fields, prefer putting them under `meta` in `task.json` to avoid breaking existing scripts' assumptions about standard fields. `meta.planning` is reserved for native seal/approve; do not prefill or overwrite it through generic metadata. Classification uses `execution_class=direct|planned` and `delivery_mode=change_bearing|analysis_only`.
|
|
48
51
|
|
|
49
52
|
Example:
|
|
50
53
|
|
|
@@ -70,7 +73,8 @@ Active task is session-level state stored in `.trellis/.runtime/sessions/`. Do n
|
|
|
70
73
|
`cmd_create` in `.trellis/scripts/common/task_store.py` calls `set_active_task` best-effort right after writing the new task directory. The behavior:
|
|
71
74
|
|
|
72
75
|
- When the calling shell carries session identity (`TRELLIS_CONTEXT_ID` env var, or any platform-specific session env that `resolve_context_key` recognizes — see `active_task.py:_ENV_SESSION_KEYS`), the per-session pointer at `.trellis/.runtime/sessions/<context_key>.json` is rewritten to point at the new task. The task's `status=planning` and `[workflow-state:planning]` fires on the very next `UserPromptSubmit`.
|
|
73
|
-
- When session identity is unavailable (raw CLI invocation outside an AI session, or a platform that doesn't propagate identity to shell), the task directory is still created and `status=planning` is still written, but the active pointer is left untouched.
|
|
76
|
+
- When session identity is unavailable (raw CLI invocation outside an AI session, or a platform that doesn't propagate identity to shell), the task directory is still created and `status=planning` is still written, but the active pointer is left untouched. Attach planning context later with `task.py select <dir>` once identity is available; start requires its own phase/approval gates.
|
|
77
|
+
- `create --no-start` deliberately preserves the prior pointer; use select to switch context after saving the checkpoint and draining already dispatched Channel work. Selecting does not change phase/branch, run after_start, or grant implementation authority.
|
|
74
78
|
|
|
75
79
|
This makes `[workflow-state:planning]` the live breadcrumb during the brainstorm and JSONL curation work that follows `task.py create`. The pre-R7 behavior left the breadcrumb stuck on `no_task` until `task.py start`, so the planning block was effectively dead text.
|
|
76
80
|
|
|
@@ -43,6 +43,8 @@ The Trellis task system is stored entirely under `.trellis/tasks/` in the user p
|
|
|
43
43
|
| `children` / `parent` | Parent/child task relationships. |
|
|
44
44
|
| `commit` / `pr_url` | Commit and PR information after completion. |
|
|
45
45
|
| `meta` | Extension fields. |
|
|
46
|
+
| `meta.execution_class` / `meta.delivery_mode` | Explicit task classification; planned/change-bearing start needs current material-plan approval. |
|
|
47
|
+
| `meta.planning` | Reserved native material revision, seal, and later approval; use plan seal/approve, never generic metadata. |
|
|
46
48
|
|
|
47
49
|
## Parent / Child Task Trees
|
|
48
50
|
|
|
@@ -81,7 +83,7 @@ The user sees a "current task," but Trellis stores active task state per session
|
|
|
81
83
|
.trellis/.runtime/sessions/<context-key>.json
|
|
82
84
|
```
|
|
83
85
|
|
|
84
|
-
`task.py
|
|
86
|
+
`task.py select` writes only the current session's task context; status/branch and implementation authority are unchanged. `task.py start` also enters execution after its admission gates. `task.py current --source` shows the current task and where it came from. Different AI windows can point to different tasks without overwriting each other. Before switching with live Channel work, pause refills and drain already dispatched units/reservations while preserving pending work and the old phase.
|
|
85
87
|
|
|
86
88
|
If the platform or shell environment has no stable session identity, `task.py start` may be unable to set the active task. The AI should read the error, inspect the platform hook/session environment, and not fall back to a shared global pointer.
|
|
87
89
|
|
|
@@ -107,6 +109,9 @@ Rules:
|
|
|
107
109
|
|
|
108
110
|
```bash
|
|
109
111
|
python3 ./.trellis/scripts/task.py create "<title>" --description "<one-line summary>" --slug <slug>
|
|
112
|
+
python3 ./.trellis/scripts/task.py select <task>
|
|
113
|
+
python3 ./.trellis/scripts/task.py plan seal <task>
|
|
114
|
+
python3 ./.trellis/scripts/task.py plan approve <task> --revision <n> --basis "<actual later approval>"
|
|
110
115
|
python3 ./.trellis/scripts/task.py start <task>
|
|
111
116
|
python3 ./.trellis/scripts/task.py current --source
|
|
112
117
|
python3 ./.trellis/scripts/task.py add-context <task> implement <file> <reason>
|
|
@@ -33,7 +33,8 @@ a direct task binding. For `unbound` / `unbound_ambiguous`, review existing task
|
|
|
33
33
|
artifacts and select by the user's explicit intent. Exit code 1 for ambiguity
|
|
34
34
|
does not mean identity is absent. Do not guess, duplicate tasks, invent identity,
|
|
35
35
|
or edit runtime pointers. Return a planning candidate to the planning gates below;
|
|
36
|
-
use native `task.py
|
|
36
|
+
use native `task.py select <task>` to bind planning context after any required
|
|
37
|
+
Channel drain; use `task.py start` only when its activation contract permits it.
|
|
37
38
|
An eligible `analysis_only` candidate remains in planning without start.
|
|
38
39
|
If `session_source` is null, report unavailable identity separately.
|
|
39
40
|
|
|
@@ -55,7 +56,7 @@ Run only when the phase or routing rule is missing; otherwise reuse it.
|
|
|
55
56
|
- `status=in_progress` + a material unresolved decision → record the reason and run `task.py replan <task> "<reason>"`; do not ask a native question during implementation.
|
|
56
57
|
- `status=planning` + `prd.md` only → decide whether the task is lightweight or complex. Lightweight can move to **1.4** review; complex returns to **1.1** to add `design.md` + `implement.md`.
|
|
57
58
|
- `status=planning` + complex artifacts complete + sub-agent jsonl not curated (empty, or only a legacy `_example` placeholder row) → **1.3**
|
|
58
|
-
- `status=planning` + required artifacts complete + required jsonl curated or inline mode → run the Planning Seal closure pass, then **1.4
|
|
59
|
+
- `status=planning` + required artifacts complete + required jsonl curated or inline mode → run the Planning Seal closure pass, then **1.4**. Existing authorization must be a later explicit implementation approval for this task's current sealed material revision. Initial requests, design answers, and parent-task approval do not qualify. Use native plan seal/approve/start; ask only when that approval is missing. A replan invalidates it; minor progress edits do not.
|
|
59
60
|
- `status=in_progress` + implementation not started → **2.1**
|
|
60
61
|
- `status=in_progress` + implementation done, not yet checked → **2.2**
|
|
61
62
|
- `status=in_progress` + check passed → **3.3** (spec update) → **3.4** (commit)
|
|
@@ -45,6 +45,7 @@ From Step 1 you know the current task and status. Check the task directory:
|
|
|
45
45
|
- **Active task with `task.json.meta.delivery_mode = "analysis_only"`** → keep `planning`, complete the PRD's bounded evidence work, then verify the no-change boundary, commit task artifacts, and archive directly. Do not run `task.py start`; any protected-target change needs a separate change-bearing task.
|
|
46
46
|
- **Active task status `planning` + no `prd.md`** → Phase 1.1. Load the `trellis-brainstorm` skill.
|
|
47
47
|
- **Active task status `planning` + `prd.md` exists** → stay in Phase 1. Lightweight tasks can be PRD-only; complex tasks need `design.md` + `implement.md`. Load the relevant Phase 1 step detail before `task.py start`.
|
|
48
|
+
- **Existing task selected by explicit intent but not bound** → use native `task.py select` for context only. Drain already dispatched Channel units before a real task switch. Planned/change-bearing start requires native plan seal and matching later approval for this task's current material revision; selecting or recovering context does not approve implementation.
|
|
48
49
|
- **Active task status `in_progress`** → Phase 2 step 2.1. Load the step detail:
|
|
49
50
|
```bash
|
|
50
51
|
{{PYTHON_CMD}} ./.trellis/scripts/get_context.py --mode phase --step 2.1 --platform {{CLI_FLAG}}
|
|
@@ -30,6 +30,15 @@ Repository evidence establishes current behavior and technical constraints. The
|
|
|
30
30
|
|
|
31
31
|
When research, audit, review, or investigation is too large to leave one independently useful conclusion in the current bounded session, split it into evidence units. Each unit must have one question or scope, a minimal evidence range, a destination artifact, and a stop condition; write its facts, conclusion or blocker, unknowns, and recovery point before starting another unit. Size units so one normal context window can finish and persist one useful result; do not promise an exact token or time limit. Routine navigation and transient tool output do not need an artifact. Create a child task only when the unit has an independent owner, lifecycle, and acceptance contract.
|
|
32
32
|
|
|
33
|
+
Persist the unit-to-point mapping in this task's existing plan/matrix before
|
|
34
|
+
dispatch. Include workload rationale, dependencies, minimal evidence, shared
|
|
35
|
+
context, and per-point delivery. Combine points only when each is quick/simple
|
|
36
|
+
and they are obviously related; explain the grouping and bounded total workload.
|
|
37
|
+
Complex or lengthy points need separate units or further splitting. Slots and
|
|
38
|
+
owner domains do not determine unit count. Explicit independent subnodes use
|
|
39
|
+
`trellis-channel`'s unit-plan and multi-target procedure; planning itself does
|
|
40
|
+
not authorize dispatch.
|
|
41
|
+
|
|
33
42
|
---
|
|
34
43
|
|
|
35
44
|
Use this skill during Phase 1 planning to turn the user's request into clear requirements and planning artifacts.
|
|
@@ -65,7 +74,11 @@ Use a concise title from the user's request. Both the title and `--description`
|
|
|
65
74
|
6. When no user-owned decision remains, create or update `design.md` and `implement.md` for complex tasks.
|
|
66
75
|
7. Run the requirement convergence gate, then the PRD convergence pass. Finish with one Planning Seal closure pass.
|
|
67
76
|
8. Present the final planning summary and stop. Do not run `task.py start` or edit product code in the same turn.
|
|
68
|
-
9. Only a subsequent user message
|
|
77
|
+
9. For planned/change-bearing tasks, classify `execution_class=planned` and `delivery_mode=change_bearing` in native task meta, then run `task.py plan seal <task>` after closure. Only a subsequent user message explicitly approving this task's current material plan authorizes `task.py plan approve <task> --revision <n> --basis "<short non-sensitive actual approval basis>"`, followed by `task.py start` and implementation. Initial delivery requests, parent-task approval, and design answers are insufficient. If scope, owner, risk, public behavior, or acceptance materially changes, run `task.py replan <task> "<reason>"`; it invalidates seal/approval. Return through planning, seal the new revision, present it, and obtain later approval. Minor wording, formatting, and progress records do not require resealing. The native record enforces structure, not authenticity of chat approval.
|
|
78
|
+
|
|
79
|
+
When changing the active task only for planning, use native `task.py select`,
|
|
80
|
+
not start. `create --no-start` intentionally preserves the old pointer. Follow
|
|
81
|
+
the Channel drain procedure before switching with live evidence workers.
|
|
69
82
|
|
|
70
83
|
Do not invent a project-specific product/spec hierarchy. If the repository already has product, domain, or spec docs, use them. If it does not, proceed with the evidence that exists.
|
|
71
84
|
|
|
@@ -363,7 +363,8 @@ def _codex_mode_banner(config: dict) -> str:
|
|
|
363
363
|
else:
|
|
364
364
|
meaning = (
|
|
365
365
|
"inline: the main session implements/checks directly; "
|
|
366
|
-
"do not dispatch implement/check sub-agents."
|
|
366
|
+
"do not dispatch implement/check sub-agents. Explicit Channel independent-evidence "
|
|
367
|
+
"subnodes follow the selected project workflow and remain available in inline mode."
|
|
367
368
|
)
|
|
368
369
|
return f"<codex-mode>{meaning}</codex-mode>"
|
|
369
370
|
|
|
@@ -36,6 +36,7 @@ export declare const commonTasks: string;
|
|
|
36
36
|
export declare const commonTaskContext: string;
|
|
37
37
|
export declare const commonContextProjection: string;
|
|
38
38
|
export declare const commonTaskStore: string;
|
|
39
|
+
export declare const commonTaskPlanning: string;
|
|
39
40
|
export declare const commonSessionContext: string;
|
|
40
41
|
export declare const commonContinuationRecord: string;
|
|
41
42
|
export declare const commonOwnershipRecord: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/templates/trellis/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAcH,eAAO,MAAM,WAAW,QAAsC,CAAC;AAG/D,eAAO,MAAM,UAAU,QAA6C,CAAC;AACrE,eAAO,MAAM,WAAW,QAA0C,CAAC;AACnE,eAAO,MAAM,eAAe,QAA8C,CAAC;AAC3E,eAAO,MAAM,gBAAgB,QAAgD,CAAC;AAC9E,eAAO,MAAM,eAAe,QAA+C,CAAC;AAC5E,eAAO,MAAM,eAAe,QAA+C,CAAC;AAC5E,eAAO,MAAM,gBAAgB,QAAgD,CAAC;AAC9E,eAAO,MAAM,gBAAgB,QAAgD,CAAC;AAC9E,eAAO,MAAM,YAAY,QAA2C,CAAC;AACrE,eAAO,MAAM,QAAQ,QAAuC,CAAC;AAC7D,eAAO,MAAM,SAAS,QAAwC,CAAC;AAC/D,eAAO,MAAM,SAAS,QAAwC,CAAC;AAC/D,eAAO,MAAM,WAAW,QAA0C,CAAC;AACnE,eAAO,MAAM,WAAW,QAA0C,CAAC;AACnE,eAAO,MAAM,iBAAiB,QAAiD,CAAC;AAChF,eAAO,MAAM,uBAAuB,QAEnC,CAAC;AACF,eAAO,MAAM,eAAe,QAA+C,CAAC;AAC5E,eAAO,MAAM,oBAAoB,QAEhC,CAAC;AACF,eAAO,MAAM,wBAAwB,QAEpC,CAAC;AACF,eAAO,MAAM,qBAAqB,QAEjC,CAAC;AACF,eAAO,MAAM,qBAAqB,QAEjC,CAAC;AACF,eAAO,MAAM,mBAAmB,QAE/B,CAAC;AACF,eAAO,MAAM,uBAAuB,QAEnC,CAAC;AACF,eAAO,MAAM,mBAAmB,QAE/B,CAAC;AACF,eAAO,MAAM,eAAe,QAA+C,CAAC;AAC5E,eAAO,MAAM,gBAAgB,QAAgD,CAAC;AAC9E,eAAO,MAAM,gBAAgB,QAAgD,CAAC;AAG9E,eAAO,MAAM,kBAAkB,QAA2C,CAAC;AAC3E,eAAO,MAAM,mBAAmB,QAA4C,CAAC;AAC7E,eAAO,MAAM,UAAU,QAAkC,CAAC;AAC1D,eAAO,MAAM,gBAAgB,QAAyC,CAAC;AACvE,eAAO,MAAM,gBAAgB,QAAyC,CAAC;AACvE,eAAO,MAAM,qBAAqB,QAEjC,CAAC;AACF,eAAO,MAAM,mBAAmB,QAA4C,CAAC;AAG7E,eAAO,MAAM,kBAAkB,QAA8B,CAAC;AAC9D,eAAO,MAAM,kBAAkB,QAA8B,CAAC;AAC9D,eAAO,MAAM,iBAAiB,QAAgC,CAAC;AAC/D,eAAO,MAAM,qBAAqB,QAAoC,CAAC;AAMvE,eAAO,MAAM,sBAAsB,QAAsC,CAAC;AAC1E,eAAO,MAAM,kBAAkB,QAAkC,CAAC;AAClE,eAAO,MAAM,oBAAoB,QAAoC,CAAC;AACtE,eAAO,MAAM,uBAAuB,QAEnC,CAAC;AAEF;;GAEG;AACH,wBAAgB,aAAa,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/templates/trellis/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAcH,eAAO,MAAM,WAAW,QAAsC,CAAC;AAG/D,eAAO,MAAM,UAAU,QAA6C,CAAC;AACrE,eAAO,MAAM,WAAW,QAA0C,CAAC;AACnE,eAAO,MAAM,eAAe,QAA8C,CAAC;AAC3E,eAAO,MAAM,gBAAgB,QAAgD,CAAC;AAC9E,eAAO,MAAM,eAAe,QAA+C,CAAC;AAC5E,eAAO,MAAM,eAAe,QAA+C,CAAC;AAC5E,eAAO,MAAM,gBAAgB,QAAgD,CAAC;AAC9E,eAAO,MAAM,gBAAgB,QAAgD,CAAC;AAC9E,eAAO,MAAM,YAAY,QAA2C,CAAC;AACrE,eAAO,MAAM,QAAQ,QAAuC,CAAC;AAC7D,eAAO,MAAM,SAAS,QAAwC,CAAC;AAC/D,eAAO,MAAM,SAAS,QAAwC,CAAC;AAC/D,eAAO,MAAM,WAAW,QAA0C,CAAC;AACnE,eAAO,MAAM,WAAW,QAA0C,CAAC;AACnE,eAAO,MAAM,iBAAiB,QAAiD,CAAC;AAChF,eAAO,MAAM,uBAAuB,QAEnC,CAAC;AACF,eAAO,MAAM,eAAe,QAA+C,CAAC;AAC5E,eAAO,MAAM,kBAAkB,QAE9B,CAAC;AACF,eAAO,MAAM,oBAAoB,QAEhC,CAAC;AACF,eAAO,MAAM,wBAAwB,QAEpC,CAAC;AACF,eAAO,MAAM,qBAAqB,QAEjC,CAAC;AACF,eAAO,MAAM,qBAAqB,QAEjC,CAAC;AACF,eAAO,MAAM,mBAAmB,QAE/B,CAAC;AACF,eAAO,MAAM,uBAAuB,QAEnC,CAAC;AACF,eAAO,MAAM,mBAAmB,QAE/B,CAAC;AACF,eAAO,MAAM,eAAe,QAA+C,CAAC;AAC5E,eAAO,MAAM,gBAAgB,QAAgD,CAAC;AAC9E,eAAO,MAAM,gBAAgB,QAAgD,CAAC;AAG9E,eAAO,MAAM,kBAAkB,QAA2C,CAAC;AAC3E,eAAO,MAAM,mBAAmB,QAA4C,CAAC;AAC7E,eAAO,MAAM,UAAU,QAAkC,CAAC;AAC1D,eAAO,MAAM,gBAAgB,QAAyC,CAAC;AACvE,eAAO,MAAM,gBAAgB,QAAyC,CAAC;AACvE,eAAO,MAAM,qBAAqB,QAEjC,CAAC;AACF,eAAO,MAAM,mBAAmB,QAA4C,CAAC;AAG7E,eAAO,MAAM,kBAAkB,QAA8B,CAAC;AAC9D,eAAO,MAAM,kBAAkB,QAA8B,CAAC;AAC9D,eAAO,MAAM,iBAAiB,QAAgC,CAAC;AAC/D,eAAO,MAAM,qBAAqB,QAAoC,CAAC;AAMvE,eAAO,MAAM,sBAAsB,QAAsC,CAAC;AAC1E,eAAO,MAAM,kBAAkB,QAAkC,CAAC;AAClE,eAAO,MAAM,oBAAoB,QAAoC,CAAC;AACtE,eAAO,MAAM,uBAAuB,QAEnC,CAAC;AAEF;;GAEG;AACH,wBAAgB,aAAa,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CA8CnD;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAOlD"}
|
|
@@ -46,6 +46,7 @@ export const commonTasks = readTemplate("scripts/common/tasks.py");
|
|
|
46
46
|
export const commonTaskContext = readTemplate("scripts/common/task_context.py");
|
|
47
47
|
export const commonContextProjection = readTemplate("scripts/common/context_projection.py");
|
|
48
48
|
export const commonTaskStore = readTemplate("scripts/common/task_store.py");
|
|
49
|
+
export const commonTaskPlanning = readTemplate("scripts/common/task_planning.py");
|
|
49
50
|
export const commonSessionContext = readTemplate("scripts/common/session_context.py");
|
|
50
51
|
export const commonContinuationRecord = readTemplate("scripts/common/continuation_record.py");
|
|
51
52
|
export const commonOwnershipRecord = readTemplate("scripts/common/ownership_record.py");
|
|
@@ -102,6 +103,7 @@ export function getAllScripts() {
|
|
|
102
103
|
scripts.set("common/task_context.py", commonTaskContext);
|
|
103
104
|
scripts.set("common/context_projection.py", commonContextProjection);
|
|
104
105
|
scripts.set("common/task_store.py", commonTaskStore);
|
|
106
|
+
scripts.set("common/task_planning.py", commonTaskPlanning);
|
|
105
107
|
scripts.set("common/session_context.py", commonSessionContext);
|
|
106
108
|
scripts.set("common/continuation_record.py", commonContinuationRecord);
|
|
107
109
|
scripts.set("common/ownership_record.py", commonOwnershipRecord);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/templates/trellis/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/templates/trellis/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"""Task-local material plan revisions; chat approval remains coordinator-owned."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from datetime import datetime, timezone
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class PlanningError(ValueError):
|
|
11
|
+
"""A planning contract needs correction before a task can start."""
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def validate_meta_value(key: str, value: str) -> None:
|
|
15
|
+
if key == "planning" or key.startswith("planning."):
|
|
16
|
+
raise PlanningError("meta.planning is reserved; use task.py plan seal/approve")
|
|
17
|
+
choices = {
|
|
18
|
+
"execution_class": {"direct", "planned"},
|
|
19
|
+
"delivery_mode": {"change_bearing", "analysis_only"},
|
|
20
|
+
}
|
|
21
|
+
if key in choices and value not in choices[key]:
|
|
22
|
+
raise PlanningError(f"{key} must be one of: {', '.join(sorted(choices[key]))}")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def classification(data: dict[str, Any]) -> tuple[str, str]:
|
|
26
|
+
meta = data.get("meta", {})
|
|
27
|
+
if not isinstance(meta, dict):
|
|
28
|
+
raise PlanningError("task meta must be an object")
|
|
29
|
+
execution = meta.get("execution_class")
|
|
30
|
+
delivery = meta.get("delivery_mode")
|
|
31
|
+
if execution not in ("direct", "planned") or delivery not in ("change_bearing", "analysis_only"):
|
|
32
|
+
raise PlanningError("classify this task with execution_class=direct|planned and delivery_mode=change_bearing|analysis_only before start")
|
|
33
|
+
return execution, delivery
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def planning_record(data: dict[str, Any]) -> dict[str, Any]:
|
|
37
|
+
meta = data.get("meta", {})
|
|
38
|
+
if not isinstance(meta, dict):
|
|
39
|
+
raise PlanningError("task meta must be an object")
|
|
40
|
+
record = meta.get("planning", {})
|
|
41
|
+
if not isinstance(record, dict):
|
|
42
|
+
raise PlanningError("meta.planning must be an object")
|
|
43
|
+
revision = record.get("revision", 1)
|
|
44
|
+
if isinstance(revision, bool) or not isinstance(revision, int) or revision < 1:
|
|
45
|
+
raise PlanningError("planning revision must be a positive integer")
|
|
46
|
+
return {**record, "revision": revision}
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def invalidate_plan(data: dict[str, Any]) -> None:
|
|
50
|
+
record = planning_record(data)
|
|
51
|
+
data.setdefault("meta", {})["planning"] = {"revision": record["revision"] + 1}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _plan_documents(task_dir: Path, execution: str, delivery: str) -> list[str]:
|
|
55
|
+
names = ["prd.md"]
|
|
56
|
+
if execution == "planned":
|
|
57
|
+
names.append("design.md")
|
|
58
|
+
if delivery == "change_bearing":
|
|
59
|
+
names.append("implement.md")
|
|
60
|
+
for name in names:
|
|
61
|
+
document = task_dir / name
|
|
62
|
+
if document.is_symlink() or not document.is_file():
|
|
63
|
+
raise PlanningError(f"plan requires a regular task document: {name}")
|
|
64
|
+
if document.stat().st_size > 256 * 1024 or not document.read_text(encoding="utf-8").strip():
|
|
65
|
+
raise PlanningError(f"plan document must be bounded, non-empty UTF-8: {name}")
|
|
66
|
+
return names
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def seal_plan(data: dict[str, Any], task_dir: Path) -> int:
|
|
70
|
+
execution, delivery = classification(data)
|
|
71
|
+
documents = _plan_documents(task_dir, execution, delivery)
|
|
72
|
+
record = planning_record(data)
|
|
73
|
+
# Resealing an already sealed plan declares a material new version. A replan
|
|
74
|
+
# already advanced the revision; its first seal keeps that revision.
|
|
75
|
+
revision = record["revision"] + (1 if record.get("seal") else 0)
|
|
76
|
+
data.setdefault("meta", {})["planning"] = {
|
|
77
|
+
"revision": revision,
|
|
78
|
+
"seal": {
|
|
79
|
+
"revision": revision,
|
|
80
|
+
"task_id": data.get("id") or task_dir.name,
|
|
81
|
+
"task_name": task_dir.name,
|
|
82
|
+
"documents": documents,
|
|
83
|
+
"execution_class": execution,
|
|
84
|
+
"delivery_mode": delivery,
|
|
85
|
+
"sealed_at": datetime.now(timezone.utc).isoformat(),
|
|
86
|
+
},
|
|
87
|
+
}
|
|
88
|
+
return revision
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _require_seal(data: dict[str, Any], task_dir: Path) -> dict[str, Any]:
|
|
92
|
+
execution, delivery = classification(data)
|
|
93
|
+
record = planning_record(data)
|
|
94
|
+
seal = record.get("seal")
|
|
95
|
+
if not isinstance(seal, dict) or type(seal.get("revision")) is not int or seal.get("revision") != record["revision"]:
|
|
96
|
+
raise PlanningError("current material plan is not sealed; use task.py plan seal")
|
|
97
|
+
if seal.get("task_id") != (data.get("id") or task_dir.name) or seal.get("task_name") != task_dir.name:
|
|
98
|
+
raise PlanningError("sealed plan belongs to another task")
|
|
99
|
+
if seal.get("execution_class") != execution or seal.get("delivery_mode") != delivery:
|
|
100
|
+
raise PlanningError("plan classification changed; reseal the material plan")
|
|
101
|
+
if seal.get("documents") != _plan_documents(task_dir, execution, delivery):
|
|
102
|
+
raise PlanningError("sealed plan documents do not match this task")
|
|
103
|
+
return record
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def approve_plan(data: dict[str, Any], task_dir: Path, revision: int, basis: str) -> None:
|
|
107
|
+
record = _require_seal(data, task_dir)
|
|
108
|
+
if revision != record["revision"]:
|
|
109
|
+
raise PlanningError(f"approval revision must match current revision {record['revision']}")
|
|
110
|
+
if not basis.strip() or len(basis) > 1024 or "\x00" in basis:
|
|
111
|
+
raise PlanningError("approval basis must be non-empty bounded text without credentials")
|
|
112
|
+
record.update({
|
|
113
|
+
"approved_revision": revision,
|
|
114
|
+
"approval_basis": basis.strip(),
|
|
115
|
+
"approved_at": datetime.now(timezone.utc).isoformat(),
|
|
116
|
+
})
|
|
117
|
+
data["meta"]["planning"] = record
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def require_start_approval(data: dict[str, Any], task_dir: Path) -> None:
|
|
121
|
+
if data.get("status") not in {"planning", "in_progress"}:
|
|
122
|
+
raise PlanningError("start requires an active planning or in_progress task")
|
|
123
|
+
# Existing running tasks retain their authorization; only crossing the
|
|
124
|
+
# planning boundary is gated. Selecting a task never crosses that boundary.
|
|
125
|
+
if data.get("status") == "in_progress":
|
|
126
|
+
return
|
|
127
|
+
execution, delivery = classification(data)
|
|
128
|
+
if delivery == "analysis_only":
|
|
129
|
+
raise PlanningError("analysis_only tasks remain in planning and cannot be started")
|
|
130
|
+
if execution == "planned" and delivery == "change_bearing":
|
|
131
|
+
record = _require_seal(data, task_dir)
|
|
132
|
+
if type(record.get("approved_revision")) is not int or record.get("approved_revision") != record["revision"] or not record.get("approval_basis"):
|
|
133
|
+
raise PlanningError("current sealed plan has no matching later implementation approval; use task.py plan approve only after the user's approval")
|