@erclx/aitk 3.40.0 → 3.41.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/claude/.claude-plugin/plugin.json +1 -1
- package/claude/skills/claude-autoship/SKILL.md +5 -1
- package/claude/skills/claude-orchestrate/REQUIREMENT.md +10 -0
- package/claude/skills/claude-orchestrate/SKILL.md +12 -1
- package/claude/skills/claude-orchestrate/references/orchestrator-poll.md +15 -1
- package/claude/skills/claude-orchestrate/scripts/watch.sh +110 -0
- package/claude/skills/claude-worker/REQUIREMENT.md +54 -0
- package/claude/skills/claude-worker/SKILL.md +58 -0
- package/docs/operating-model.md +40 -8
- package/package.json +1 -1
- package/src/claude/cases/claude-workflow.ts +5 -0
|
@@ -29,7 +29,11 @@ The baseline is unusable when no merge base resolves against either ref. Stop: `
|
|
|
29
29
|
|
|
30
30
|
The base equalling HEAD stays usable here, unlike in the four read-only siblings carrying this section. Step 5 runs before anything is committed, since `git-stage` commits at Step 7, so the base equals HEAD on every ordinary run. The classifier diffs the base against the working tree rather than against HEAD, which keeps the uncommitted work in the set at correct scope. Do not port the sibling `base == HEAD` stop into this skill.
|
|
31
31
|
|
|
32
|
-
## Step 0: enter a worktree
|
|
32
|
+
## Step 0: take the role, then enter a worktree
|
|
33
|
+
|
|
34
|
+
Invoke `aitk:claude-worker` first, whatever the worktree state. This session is about to build one branch under one plan, and that skill states the boundaries, the lifetime, and the channel obligations the role carries. A dispatched worker reaches the role here and nowhere else, since the launch names this chain rather than the role, and a hand-launched one reaches it on the same path.
|
|
35
|
+
|
|
36
|
+
Report it rather than proceeding silently when `aitk:claude-worker` does not resolve. It ships with the plugin, so a session running this chain from a project holding the CLI alone builds with no role asserted.
|
|
33
37
|
|
|
34
38
|
If `git rev-parse --git-dir` equals `git rev-parse --git-common-dir`, the session is in the main worktree. Invoke `aitk:claude-worktree` before continuing. The wrapper handles name derivation and branch alignment. Do not call `EnterWorktree` directly.
|
|
35
39
|
|
|
@@ -25,6 +25,8 @@ The ban on writing at all fails on a different axis, which is that nothing enfor
|
|
|
25
25
|
|
|
26
26
|
That removes the independent pass the repository built to catch what a self-review misses, and the vantage does not come back, since no later session can review the change without re-deriving the context that produced it. A correction no open task owns has nowhere to go either, so the route matters as much as the ban.
|
|
27
27
|
|
|
28
|
+
The contract also stops one state short of what the session holds. Its sections cover what the human launches and what they review, which was the whole state space while every build was hand-launched, and a self-dispatching session adds a third that sits between them for ten to thirty minutes. A session holding four running workers reports them under no section and invents a shape per report, which the same block forbids while offering no term to use, and the launch section keeps recommending a plan a worker is already building because the plan file sits in place for the whole build.
|
|
29
|
+
|
|
28
30
|
Output drifts everywhere the contract stops. The specified shape covers invocation alone, so a sweep report, a board report, and an analysis each end in a decision the human owns and each buries it under the evidence they would have skipped. A compaction is the same failure one step earlier, since the file that survives it has a stated reader and no stated writer, and the session improvises what to save and loses the reasoning the board never held.
|
|
29
31
|
|
|
30
32
|
Naming that reader without naming the way in fails on the next axis, and it fails circularly. The command that routes a resume request lives in this body, which is the surface a long session is likeliest to have dropped, and dropping it is the condition the handoff exists to survive, so the session holding a complete handoff cannot reach the runbook that reads it back. The review poll is lost in the same moment and recovered by neither, since it is session-scoped and the resume performs no restart, which leaves a reader who follows the whole handoff with a board and no trigger.
|
|
@@ -35,6 +37,8 @@ Every one of them therefore has to ship inside the skill, the four covering the
|
|
|
35
37
|
|
|
36
38
|
The dispatch's return leg is unplaced for the same reason. A reply reaches this session as a message and settles a finding, corrects which session holds which branch, or names a gate a worker's edits pass through, and reading it is the whole of what happens to it. Both sessions then end, so the half that belonged on the pull request never reaches it and the half that belonged on the board is rediscovered by whoever needs it next.
|
|
37
39
|
|
|
40
|
+
Both halves of that channel are written here and one of them is performed elsewhere, so a worker owes what nothing it loads states. The review trigger inherits the same asymmetry: it fires on a dispatch as well as on a pull request, spending an interval against a building worker that has none, which five consecutive runs measured as reporting nothing across roughly fifteen minutes. The one reading that would close the gap is the roster read the poll script cannot make, since a worker that finishes goes idle and one that crashes vanishes, so a trigger matching pull requests alone stays silent through the second. The loop that did make it ran for an afternoon in a session's temporary directory against a hardcoded repository path, and it died with that session with no runbook naming it.
|
|
41
|
+
|
|
38
42
|
Cost never enters the decision to widen a wave. The rule weighing how many tracks to open measures collision and review attention, and both describe what a worker writes rather than what the loop spends. Claude Code delivers a message from another session as a fresh turn carrying the whole accumulated context, and a recurring poll bills that same window on its own interval, so a wave's spend tracks this session's context rather than the work coming back. The one setting that bounds it reads as available to whoever finds it, and the two values that would bound it break the handback the loop runs on.
|
|
39
43
|
|
|
40
44
|
The session also records nothing of what it learns. Both other callers of memory capture are ship-chain skills and this one never ships, so the session taking every operator correction is the session with no moment that writes one down. Hanging that moment on the merge sweep answers it and bills the operator a capture pass per batch of merges while nothing is being built, which is a cost paid on the days shipping is fastest.
|
|
@@ -54,6 +58,11 @@ The session also records nothing of what it learns. Both other callers of memory
|
|
|
54
58
|
- Write the priority file with an editing tool that errors on a non-match, since the board is where a silent failure costs a dispatch
|
|
55
59
|
- Carry a plan pointer in the Plan column and a file set in the Touches column, so a row's readiness and disjointness claims stay checkable
|
|
56
60
|
- Re-resolve the board's plan pointers after any archive, since the archiving skill rewrites the task file and knows nothing about the board
|
|
61
|
+
- Carry a slot for this session's own workers mid-build, one row apiece naming the session, the branch, the model, and a progress reading taken from the worktree, since the two other slots cover what a human launches and what they review and nothing between
|
|
62
|
+
- Withhold a plan from the launch slot once a row in flight names it, since the plan file sits in place for the whole build and listing it recommends handing off work already underway
|
|
63
|
+
- Start the review poll on an open pull request rather than on a dispatch, and keep the dispatch as a fallback for an announcement that never arrives, since the script reads pull requests and a building worker has none
|
|
64
|
+
- Point at the worker's own skill for the announcement, the block message, and the board ban, rather than holding the only written copy of obligations this session does not perform
|
|
65
|
+
- Ship the roster-and-pull-request watch as a script beside the poll, since the loop that covered the gap lived in one session's temporary directory against a hardcoded path and died with that session
|
|
57
66
|
- Lead a sweep report, a board report, and an analysis with the state, the open decisions, and the next action, keeping the evidence below them
|
|
58
67
|
- Write the pre-compact handoff with what no other file carries, and name the runbook that reads it back
|
|
59
68
|
- Carry the resume invocation and the owed poll restart in the handoff file itself, not in the runbook that writes it alone, since the file is what survives a compaction and the runbook is what a session has to already be holding to read
|
|
@@ -106,4 +115,5 @@ The session also records nothing of what it learns. Both other callers of memory
|
|
|
106
115
|
- Writing the plan itself, which `claude-feature` owns and this session runs rather than reimplements
|
|
107
116
|
- Reviewing a worker's pull request, which `claude-pr-review` owns
|
|
108
117
|
- Entering the worktree a build runs in, which the worker opens for itself whether a human launched it or this session dispatched it
|
|
118
|
+
- The worker's half of the channel and the boundaries a building session holds, which `claude-worker` states where that session reads them
|
|
109
119
|
- The operating model this enacts, which the toolkit's own docs hold
|
|
@@ -66,6 +66,11 @@ Ready to build (hand each to its own worker):
|
|
|
66
66
|
no plan yet. A handoff without a plan has no scope
|
|
67
67
|
→ /claude-feature here first
|
|
68
68
|
|
|
69
|
+
In flight (this session's workers, building now):
|
|
70
|
+
|
|
71
|
+
<session name> <branch> <model>
|
|
72
|
+
task: <row>, <n> commits, <n> files changed
|
|
73
|
+
|
|
69
74
|
In review (your turn):
|
|
70
75
|
|
|
71
76
|
PR #<n> <title>
|
|
@@ -78,6 +83,10 @@ Next: <the single most useful action>
|
|
|
78
83
|
|
|
79
84
|
Omit any section with nothing in it. Recommend a handoff only for a plan whose file set is disjoint from every track already in flight, per Parallelism below.
|
|
80
85
|
|
|
86
|
+
`In flight` covers the state between the other two, which lasts ten to thirty minutes and is most of what an operator sees once this session dispatches its own workers. Before it existed, a session holding running workers reported them under no section and invented a shape per report, which the block below forbids two paragraphs down while giving it no term to use. Read the progress figures off each worker's worktree rather than from the worker, since a busy status says a session is alive and nothing about whether it is moving, and name the model because a dispatcher now picks one per row.
|
|
87
|
+
|
|
88
|
+
Leave a plan out of `Ready to build` once a row in flight names it. The plan file stays in `.claude/plans/` for the whole build, so listing it there recommends handing off work already underway, and the disjointness rule below withdraws the recommendation only for a reader who already knows what is running.
|
|
89
|
+
|
|
81
90
|
The block opens on the board rather than on a version, because no committed file states one. Adding a version line here would restate what a reader can already see on the rows, dated by nothing, which is how the retired sequencing surface produced an unsourced claim on every run.
|
|
82
91
|
|
|
83
92
|
### Every later turn
|
|
@@ -114,7 +123,9 @@ Write no shape for a correction. A correction is a sentence, and a format for ad
|
|
|
114
123
|
|
|
115
124
|
A session is reachable when it appears in a live listing, which reads what each session registered on disk rather than probing it, and a message carries plain text and no authority. When no live session holds the branch, report the invocation for the human, naming the branch, the pull request, and the skill to run, then stop. Retrying or waiting leaves the loop believing it is open while nothing acts on it. Every dispatch in the trial behind this step found a live session, so this branch stands on reasoning rather than on observation.
|
|
116
125
|
|
|
117
|
-
The channel runs both ways and the return leg carries what the pull request cannot. A worker answering a posted finding by naming the plan question that had already declined it changes the outcome in the moment, where a thread comment waits on whoever reads it next. Read what a worker volunteers as part of the review rather than as an aside.
|
|
126
|
+
The channel runs both ways and the return leg carries what the pull request cannot. A worker answering a posted finding by naming the plan question that had already declined it changes the outcome in the moment, where a thread comment waits on whoever reads it next. Read what a worker volunteers as part of the review rather than as an aside, and read a refusal the same way, since the corrections that landed on this session's model of the world arrived as a worker arguing back rather than complying.
|
|
127
|
+
|
|
128
|
+
What the worker owes on its own side is stated in the `claude-worker` skill that session loads, being the pull-request announcement, the message a block goes out as before it becomes a prompt, and the ban on writing the board. Do not restate any of it here. This half held the only written copy of a channel that runs both ways, which put every obligation on the side that does not perform it.
|
|
118
129
|
|
|
119
130
|
What arrives there does not become a record by being read, so place it by what it changes. An answer that settles a finding goes onto the pull request through the next pass, which withdraws or regrades that finding and names the fact behind it, per `claude-pr-review`. An answer that changes what this session believes about the world instead, which is a mapping correction or a constraint on what a worker can do, settles no finding and reaches no thread, so route it the way Boundaries below routes a change found while orchestrating, which lands it on the task owning the surface it describes. Writing a tracked file to hold either is forbidden here, which leaves the pull request and the board as the two surfaces this session writes.
|
|
120
131
|
|
|
@@ -9,7 +9,11 @@ Run the orchestrator's review trigger. The poll reports pull request movement an
|
|
|
9
9
|
|
|
10
10
|
## When to run it
|
|
11
11
|
|
|
12
|
-
Start the poll on
|
|
12
|
+
Start the poll on an open pull request and stop it when the last one merges with nothing else out. That is the whole condition and it resolves from `gh pr list` without asking the operator. A release pull request alone does not qualify, since its sweep carries no findings.
|
|
13
|
+
|
|
14
|
+
A dispatch no longer starts it. The script reads pull requests and a building worker has none, so every interval between the launch and the push is a fixed cost returning nothing, measured as five consecutive runs reporting no movement across roughly fifteen minutes while one worker built. What replaces it is the worker announcing its own pull request the moment it opens one, per the `claude-worker` skill that session loads, which covers exactly the transition the poll cannot observe.
|
|
15
|
+
|
|
16
|
+
The dispatched-worker condition survives as a fallback rather than as a trigger. Start the poll against any dispatch still out after thirty minutes with no announcement, which is past the upper end of the ten to thirty minutes a row here takes. The announcement is newer than the narrowing it justifies, so a silent failure would otherwise leave a finished worker unnoticed the way one already sat idle for eighteen minutes, and the fallback keeps the cover a straight narrowing would have removed. `${CLAUDE_SKILL_DIR}/scripts/watch.sh` covers the same window at a lower cost, since it reads the roster alongside the pull request list and reports a worker that vanished as well as one that finished.
|
|
13
17
|
|
|
14
18
|
A session holding a recurring-prompt scheduler starts the loop itself on that condition and cancels it on the same test, without waiting for the operator. Both halves belong to whoever holds the loop, since a session that can start one can stop one, and a runbook stating only the start leaves the always-on failure unaddressed on the side that causes it.
|
|
15
19
|
|
|
@@ -61,3 +65,13 @@ The five review headings the script matches are written by `claude-pr-review` an
|
|
|
61
65
|
`STALLED` is the one state the script derives from a heading rather than from a commit or a count, since `claude-pr-review` posts the open heading exactly when a dispatch is owed, per the threshold that skill states. The heading alone cannot carry it, because an open pass means a dispatch was owed and made, so the ordinary healthy thread is a worker still working. The age of that pass is the third test: the state fires when the open pass covers the head, nothing has followed it, and it is older than the `STALE_AFTER` seconds set at the top of the script. It reports once per entry and fires again after any commit or reply resets the thread. A project whose workers run longer than the default two hours raises that number.
|
|
62
66
|
|
|
63
67
|
The state reaches every stalled dispatch, since one threshold governs the heading and the dispatch alike and a pass carrying anything posts the open heading. A minors-only pass therefore reports here on the same terms as a blocking one, which widens the state from what it caught while the two were split. It stays a heading test rather than a count test, so nothing here pins the summary line, which is a second string this script does not own.
|
|
68
|
+
|
|
69
|
+
## The watch beside it
|
|
70
|
+
|
|
71
|
+
`${CLAUDE_SKILL_DIR}/scripts/watch.sh` is a long-running loop rather than a scheduled prompt. It reads the open pull request list and the session roster together every sixty seconds and prints one line per new pull request, per worker whose status changed, and per worker that dropped out of the roster. Start it in the background and read what it emits. It writes nothing.
|
|
72
|
+
|
|
73
|
+
Coverage is what it buys over the poll. A worker that finishes goes idle and a worker that crashes disappears, so a trigger reading pull requests alone stays silent through the second, and the roster read is the half `poll.sh` cannot make. It ran a full afternoon over four concurrent workers and caught every transition, while the three-minute poll it replaced reported no movement five times in a row.
|
|
74
|
+
|
|
75
|
+
It classifies nothing and routes nothing. A line it prints says a pull request opened or a worker moved, and the routing block above is still what decides whether a review follows, so the two compose rather than replace each other.
|
|
76
|
+
|
|
77
|
+
Every session in the repository holding a branch other than the base one counts as a worker, whoever launched it. The prototype matched the `orchestrator-` prefix instead, which reads a dispatched worker and misses every hand-launched one. A failed read of either source reports itself on a `watch:` line and leaves the baseline untouched, since reading an empty result as current state would report every worker gone on the pass after.
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Reports worker and pull request transitions as they happen. Reads only.
|
|
3
|
+
#
|
|
4
|
+
# The poll answers "what moved since the last run" on a schedule the session
|
|
5
|
+
# holds. This answers "what just changed" on a loop of its own, and it exists
|
|
6
|
+
# because the two readings a dispatch needs are not one reading: a worker that
|
|
7
|
+
# finishes goes idle and a worker that crashes vanishes, so a watch matching
|
|
8
|
+
# only the pull request list stays silent through the second.
|
|
9
|
+
#
|
|
10
|
+
# `set -e` is deliberately not set. This runs for hours, and one transient `gh`
|
|
11
|
+
# failure aborting the loop is a silent stop rather than a reported one. Every
|
|
12
|
+
# read below states its own failure instead.
|
|
13
|
+
set -uo pipefail
|
|
14
|
+
|
|
15
|
+
# Seconds between passes. A pass costs two cheap reads and the transitions it
|
|
16
|
+
# watches take minutes, so the number trades staleness against little. Raise it
|
|
17
|
+
# in a project whose workers run long.
|
|
18
|
+
INTERVAL=60
|
|
19
|
+
|
|
20
|
+
# Resolving the main worktree root rather than this file's folder keeps a watch
|
|
21
|
+
# started from a linked worktree reading the same repository as one started from
|
|
22
|
+
# main. The repository field on a roster row is that root's `.git`.
|
|
23
|
+
MAIN_ROOT="$(git worktree list --porcelain 2>/dev/null | awk '/^worktree /{print $2; exit}')"
|
|
24
|
+
if [ -z "$MAIN_ROOT" ]; then
|
|
25
|
+
echo "watch: not a git repository, so nothing is watched" >&2
|
|
26
|
+
exit 1
|
|
27
|
+
fi
|
|
28
|
+
REPOSITORY="$MAIN_ROOT/.git"
|
|
29
|
+
|
|
30
|
+
# The base branch is read rather than assumed, since this ships to projects that
|
|
31
|
+
# do not all call it `main`. It is what separates a worker from the controlling
|
|
32
|
+
# session, which sits on the base branch in the main worktree.
|
|
33
|
+
BASE_REF="$(git symbolic-ref --quiet --short refs/remotes/origin/HEAD 2>/dev/null || true)"
|
|
34
|
+
if [ -z "$BASE_REF" ]; then
|
|
35
|
+
BASE_REF="origin/$(gh repo view --json defaultBranchRef --jq .defaultBranchRef.name 2>/dev/null || echo main)"
|
|
36
|
+
fi
|
|
37
|
+
BASE_BRANCH="${BASE_REF#origin/}"
|
|
38
|
+
|
|
39
|
+
# Every session in this repository holding a branch other than the base one is a
|
|
40
|
+
# worker, whoever launched it. The prototype matched the `orchestrator-` prefix
|
|
41
|
+
# instead, which reads a dispatched worker and misses every hand-launched one,
|
|
42
|
+
# and those are the ordinary shape whenever the operator is launching.
|
|
43
|
+
read_workers() {
|
|
44
|
+
aitk sessions list --json 2>/dev/null | tail -1 | jq -r \
|
|
45
|
+
--arg repository "$REPOSITORY" --arg base "$BASE_BRANCH" '
|
|
46
|
+
.sessions[]?
|
|
47
|
+
| select(.repository == $repository)
|
|
48
|
+
| select(.branch != null and .branch != $base)
|
|
49
|
+
| "\(.name) \(.branch) \(.status)"
|
|
50
|
+
' 2>/dev/null | sort
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
read_pulls() {
|
|
54
|
+
gh pr list --state open --json number,headRefName,title --jq \
|
|
55
|
+
'.[] | "PR-OPEN #\(.number) \(.headRefName) \(.title)"' 2>/dev/null | sort
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
# A failed read is reported and the previous baseline kept. Reading an empty
|
|
59
|
+
# result as the current state would report every worker gone and every pull
|
|
60
|
+
# request new on the pass after, which is the one failure that makes a detection
|
|
61
|
+
# tool worth less than no tool.
|
|
62
|
+
#
|
|
63
|
+
# The two sources carry a baseline flag apiece rather than sharing one. A read
|
|
64
|
+
# that fails on the first pass leaves its baseline empty while the other source
|
|
65
|
+
# establishes one, and a single flag would then report that source's whole list
|
|
66
|
+
# as new on the pass after it recovers.
|
|
67
|
+
prev_pulls=""
|
|
68
|
+
prev_workers=""
|
|
69
|
+
prev_names=""
|
|
70
|
+
pulls_seen=0
|
|
71
|
+
workers_seen=0
|
|
72
|
+
|
|
73
|
+
while true; do
|
|
74
|
+
pulls="$(read_pulls)"
|
|
75
|
+
pulls_read=$?
|
|
76
|
+
workers="$(read_workers)"
|
|
77
|
+
workers_read=$?
|
|
78
|
+
|
|
79
|
+
if [ "$pulls_read" -ne 0 ]; then
|
|
80
|
+
echo "watch: the open pull request list failed to load, so none is classified this pass"
|
|
81
|
+
fi
|
|
82
|
+
if [ "$workers_read" -ne 0 ]; then
|
|
83
|
+
echo "watch: the session roster failed to load, so no worker is classified this pass"
|
|
84
|
+
fi
|
|
85
|
+
|
|
86
|
+
names="$(printf '%s\n' "$workers" | awk 'NF {print $1}' | sort -u)"
|
|
87
|
+
|
|
88
|
+
if [ "$pulls_seen" -eq 1 ] && [ "$pulls_read" -eq 0 ]; then
|
|
89
|
+
comm -13 <(printf '%s\n' "$prev_pulls") <(printf '%s\n' "$pulls") | grep . || true
|
|
90
|
+
fi
|
|
91
|
+
|
|
92
|
+
if [ "$workers_seen" -eq 1 ] && [ "$workers_read" -eq 0 ]; then
|
|
93
|
+
comm -13 <(printf '%s\n' "$prev_workers") <(printf '%s\n' "$workers") |
|
|
94
|
+
grep . | sed 's/^/WORKER /' || true
|
|
95
|
+
comm -23 <(printf '%s\n' "$prev_names") <(printf '%s\n' "$names") |
|
|
96
|
+
grep . | sed 's/^/WORKER-GONE /' || true
|
|
97
|
+
fi
|
|
98
|
+
|
|
99
|
+
if [ "$pulls_read" -eq 0 ]; then
|
|
100
|
+
prev_pulls="$pulls"
|
|
101
|
+
pulls_seen=1
|
|
102
|
+
fi
|
|
103
|
+
if [ "$workers_read" -eq 0 ]; then
|
|
104
|
+
prev_workers="$workers"
|
|
105
|
+
prev_names="$names"
|
|
106
|
+
workers_seen=1
|
|
107
|
+
fi
|
|
108
|
+
|
|
109
|
+
sleep "$INTERVAL"
|
|
110
|
+
done
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: claude-worker
|
|
3
|
+
description: What a building session is, the shared surfaces it may not write, and the two messages it owes the session that dispatched it
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Claude worker requirement
|
|
7
|
+
|
|
8
|
+
## Gap
|
|
9
|
+
|
|
10
|
+
Without this skill, a building session is told what to do and never what it is. One shipped body asserts a session role and it is the orchestrator, so worker behavior spreads across three skills that each own a step and none of them names the role or the channel it runs on. The handback works because a session reading plain text tends to follow it rather than because anything says it should, which held while a person launched every worker and stopped holding the first time one ran with nobody watching.
|
|
11
|
+
|
|
12
|
+
The session then writes surfaces nothing told it were shared. One worker split a concern its own run could not reach, hand-wrote the priority board from a copy taken minutes earlier, and reverted a promotion with nothing reporting it, on a board that still validated afterwards because a reverted row is a well-formed board. A second added a row and labelled it with an identifier another row already held. The rule against both sits inside the controlling session's own body, which no worker loads.
|
|
13
|
+
|
|
14
|
+
Standing in a linked worktree fails one step earlier and reports confidently while it does. The plan folder is gitignored, so `git worktree add` never creates it, and a worker reading it from the build tree reports that the task has no plan. That is true from where the session stands and wrong about the world, and a reader holding no second tree cannot tell those apart. Three workers hit it in one day and an absolute path unblocked all three.
|
|
15
|
+
|
|
16
|
+
A blocked worker reaches nobody at all. Two dispatched sessions opened an interactive prompt inside a background session no operator was attached to, and the roster read both as alive throughout. An answer relayed afterwards arrives and does not land, since a queued message drains at the next tool round and a session waiting on input never reaches one, so the channel closes the gap only when it is used before the prompt opens. One of the two held uncommitted work across three files, which made stopping and relaunching a destructive recovery rather than a free one.
|
|
17
|
+
|
|
18
|
+
Finishing is invisible from the other side. A worker that opens a pull request goes idle rather than exiting, so the one push signal available fired for a worker that was stopped and never fired for the one that finished, which then sat unnoticed for eighteen minutes. Polling covers that interval by spending a fixed cost against a worker with no pull request to report, measured as five consecutive runs reporting no movement across roughly fifteen minutes while one worker built.
|
|
19
|
+
|
|
20
|
+
Nothing tells the session that refusing is allowed either. Four occasions across two days had a worker halt correctly with the cost falling on the dispatcher, and the correction that mattered most reached the right place because a worker argued back with evidence rather than complying. A role written only as report-upward suppresses exactly that.
|
|
21
|
+
|
|
22
|
+
## Must
|
|
23
|
+
|
|
24
|
+
- Assert what a building session is, what it may not do, and how long the role lasts, since the three step-owning skills each carry a procedure and none carries a role
|
|
25
|
+
- State that session scratch resolves at the main worktree root, since a linked worktree holds none of those folders and their absence reads as a missing artifact rather than as a wrong vantage
|
|
26
|
+
- State the shared board as read-only for a building session and name what it does instead when a row is owed, since a worker cannot pick a free label without reading every task file and every archive entry
|
|
27
|
+
- Owe an announcement the moment the pull request opens, carrying the number, the branch, and the task, since that transition is the one interval only the worker can observe
|
|
28
|
+
- Owe a message before a block becomes an interactive prompt, since a session already waiting on input never reaches the tool round an inbound message drains at
|
|
29
|
+
- Name the addressee as the session the launch named, falling back to the roster and saying so, since an operator's own launch names nobody
|
|
30
|
+
- Keep refusing a dispatcher a first-class move carrying its evidence, since the halts measured so far were correct and the cost of each fell where it belonged
|
|
31
|
+
- State that a held body may be older than the branch under it, since a plugin skill loads from the marketplace cache rather than from the working tree
|
|
32
|
+
- Point at `claude-worktree`, `claude-autoship`, and `claude-address-review` for the steps each already owns
|
|
33
|
+
|
|
34
|
+
## Must not
|
|
35
|
+
|
|
36
|
+
- Restate a procedure any of the three step-owning skills carries, since thinness is what keeps one body correct for a dispatched worker reading it as its whole contract and for a hand-launched one reading it beside a person
|
|
37
|
+
- Restate a boundary the orchestrator already states about itself
|
|
38
|
+
- Report progress through the channel, which rebuilds on the sender's side the poll the announcement exists to retire
|
|
39
|
+
- Write the priority board or the backlog, at any size
|
|
40
|
+
- Be a skill nothing invokes but its author typing the name. `claude-autoship` Step 0 invokes it on every build, dispatched or hand-launched, so a stretch where only a typed invocation reaches it is the signal that the role never took.
|
|
41
|
+
|
|
42
|
+
## Guards
|
|
43
|
+
|
|
44
|
+
- Plan path does not resolve from inside the worktree: report it unreadable from here and name the main-root path, rather than reporting the task as having no plan
|
|
45
|
+
- Plan carries a suggestion written as needing the operator's call: halt and message, rather than answering it
|
|
46
|
+
- Launch named no controlling session: resolve from the roster, address the match, and say the addressee was inferred
|
|
47
|
+
- No live session holds the other end: report what would have been sent and stop, rather than retrying or waiting
|
|
48
|
+
|
|
49
|
+
## Out of scope
|
|
50
|
+
|
|
51
|
+
- Entering the worktree, which `claude-worktree` owns
|
|
52
|
+
- The chain from implement through pull request, which `claude-autoship` owns
|
|
53
|
+
- Answering a posted review, which `claude-address-review` owns
|
|
54
|
+
- The controlling session's half of the channel, the review poll, and the watch beside it, which `claude-orchestrate` and its runbooks hold
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: claude-worker
|
|
3
|
+
description: Asserts the worker role for a building session, holding the boundary set, the lifetime, and the two channel obligations a session owes whoever dispatched it. Use when asked to "be the worker", "you are a worker session", at the start of a dispatched or hand-launched build, or when a building session needs to know what it may not write. Do NOT use to plan the next feature, to run the independent review pass, or to merge.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Claude worker
|
|
7
|
+
|
|
8
|
+
This session is a worker: one cold session building one branch under one plan. It
|
|
9
|
+
implements, verifies, opens a pull request, and answers what the review posts
|
|
10
|
+
back.
|
|
11
|
+
|
|
12
|
+
It does not plan the next feature, it does not stand in for the independent
|
|
13
|
+
review pass, and it does not merge. Those belong to the controlling session and
|
|
14
|
+
to the human.
|
|
15
|
+
|
|
16
|
+
This body states the role, the boundaries, and the channel, and it starts no
|
|
17
|
+
step of its own. `claude-worktree` enters the tree, `claude-autoship` chains the
|
|
18
|
+
build, and `claude-address-review` answers a posted review, so read each step
|
|
19
|
+
from the skill that owns it and invoke none of the three from here. The ordinary
|
|
20
|
+
path arrives through `claude-autoship` Step 0, which means that chain is already
|
|
21
|
+
running and re-invoking it would restart the build.
|
|
22
|
+
|
|
23
|
+
## Where the session stands
|
|
24
|
+
|
|
25
|
+
- Resolve `.claude/plans/`, `.claude/tasks/`, `.claude/review/`, and `.claude/memory/` at the main worktree root, never against the linked worktree this session builds in. Those folders are gitignored, so `git worktree add` never creates them and the copy beside the build is absent rather than empty.
|
|
26
|
+
- Report a plan that fails to resolve as unreadable from here, naming the main-root path. Reporting the task as having no plan is true where this session stands and wrong about the world, and a reader with no second tree to check cannot separate the two.
|
|
27
|
+
- Build the plan the launch named. Do not write a second one when the path fails to resolve, since a row that cites a plan already has one and drafting another produces two plans for one row.
|
|
28
|
+
|
|
29
|
+
## The board is read-only
|
|
30
|
+
|
|
31
|
+
- Never write `.claude/tasks/priority.md` or `.claude/tasks/backlog.md`. The controlling session is their only writer apart from `aitk tasks archive`, and both are gitignored, so an overwrite drops a row with no history to recover it from.
|
|
32
|
+
- Report a row this build turns up rather than adding it. Picking a free label means reading every task file and every archive entry, which this session has not done, so a label it invents collides with one already taken.
|
|
33
|
+
- Write the task file this build closes and the plan it ran under. The ban covers the shared board rather than the artifacts of the row in hand.
|
|
34
|
+
|
|
35
|
+
## The channel
|
|
36
|
+
|
|
37
|
+
The controlling session cannot watch this one build, so two messages are owed and
|
|
38
|
+
nothing else.
|
|
39
|
+
|
|
40
|
+
- Announce the pull request as the ship chain's pull request step returns, carrying the number, the branch, and the task it closes. That transition is the one moment only this session knows, and the controller's review poll no longer starts on a dispatch because of it.
|
|
41
|
+
- Send a block out as a message before it becomes an interactive prompt. A session already waiting on input never reaches the tool round that drains an inbound message, so a relayed answer arrives under the open question and changes nothing.
|
|
42
|
+
- Send nothing on progress. A worker reporting progress rebuilds, on this side of the channel, the poll the announcement retired on the other.
|
|
43
|
+
|
|
44
|
+
Address the session the launch named. Fall back to `aitk sessions list --json`
|
|
45
|
+
and the `orchestrator-` prefix when the launch named nobody, which is what an
|
|
46
|
+
operator's own launch looks like, and say the addressee was inferred so the
|
|
47
|
+
reader can correct it.
|
|
48
|
+
|
|
49
|
+
## Refusing is part of the job
|
|
50
|
+
|
|
51
|
+
- Refuse an instruction the tree contradicts, and carry the evidence with it. Name the commands read and the consequences of complying, rather than reporting reluctance.
|
|
52
|
+
- Halt on a plan question written as needing the operator's call. The plan standard defines that as a stop for an executing session, so the dispatch that sent it unattended is the defect and the halt is not.
|
|
53
|
+
- Halting is cheap and guessing is not. Four measured halts each cost the dispatcher one reply, and the correction that mattered most reached the right place because a worker argued back rather than complying.
|
|
54
|
+
|
|
55
|
+
## Lifetime
|
|
56
|
+
|
|
57
|
+
- The role runs from the worktree entry to the merge of the branch it built, and nothing carries across to a second row. A worker handed another row after that is a new build under a new plan.
|
|
58
|
+
- Treat this body as possibly older than the branch under it. A plugin skill loads from the marketplace cache rather than the working tree, so a session building a change to its own skills may be holding the copy from before the last update.
|
package/docs/operating-model.md
CHANGED
|
@@ -17,14 +17,27 @@ order, port collisions), see [Claude Code and git worktrees](../wiki/claude/clau
|
|
|
17
17
|
|
|
18
18
|
The split is by vantage, not by capability. Both are Claude Code sessions.
|
|
19
19
|
|
|
20
|
-
| Role | Session | Owns
|
|
21
|
-
| ------------ | ------------------------------------- |
|
|
22
|
-
| Orchestrator | One warm, long-lived session | Planning, deep PR review, merge order
|
|
23
|
-
| Worker | One cold worktree session per feature | Implement, self-check, open PR
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
`claude-orchestrate
|
|
27
|
-
|
|
20
|
+
| Role | Session | Owns | Does not |
|
|
21
|
+
| ------------ | ------------------------------------- | ------------------------------------------------------- | ----------------------------- |
|
|
22
|
+
| Orchestrator | One warm, long-lived session | Planning, deep PR review, merge order | Edit tracked files, merge PRs |
|
|
23
|
+
| Worker | One cold worktree session per feature | Implement, self-check, open PR, answer the orchestrator | Write the shared board, merge |
|
|
24
|
+
|
|
25
|
+
Each role is asserted explicitly rather than inferred. The orchestrator loads
|
|
26
|
+
`claude-orchestrate` at the start of its session, and a worker loads
|
|
27
|
+
`claude-worker`, which `claude-autoship` invokes at Step 0 so a dispatched build
|
|
28
|
+
and a hand-launched one reach it on the same path. Both are framing and
|
|
29
|
+
boundaries rather than logic.
|
|
30
|
+
|
|
31
|
+
Refusing is part of the worker's job rather than a failure of it. A worker that
|
|
32
|
+
halts on a plan question it may not answer, or argues back against an
|
|
33
|
+
instruction the tree contradicts, is working correctly. The four measured halts
|
|
34
|
+
to date each cost the dispatcher one reply and each was right.
|
|
35
|
+
|
|
36
|
+
What the worker may not do is write `.claude/tasks/priority.md` or the backlog
|
|
37
|
+
beside it. Those are gitignored, so an overwrite drops a row with no history to
|
|
38
|
+
recover it from, and a worker cannot pick a free task label without reading
|
|
39
|
+
every task file and every archive entry. It reports the row it needs and lets
|
|
40
|
+
the orchestrator write it.
|
|
28
41
|
|
|
29
42
|
The orchestrator's cell reads every tracked file rather than every feature, and
|
|
30
43
|
it offers no exception for a small one. A correction found while orchestrating
|
|
@@ -73,6 +86,25 @@ reply that changes an outcome, such as a worker naming the plan question that
|
|
|
73
86
|
already declined a finding, still belongs back on the PR, since the session
|
|
74
87
|
holding it ends and the thread is what a later reader opens.
|
|
75
88
|
|
|
89
|
+
Two messages travel the other way and the worker owes both. It announces its own
|
|
90
|
+
pull request as the ship chain opens one, naming the number, the branch, and the
|
|
91
|
+
task it closes. That is the one transition only that session can observe, since
|
|
92
|
+
a worker that finishes goes idle rather than exiting, and one sat unnoticed for
|
|
93
|
+
eighteen minutes before the announcement existed.
|
|
94
|
+
|
|
95
|
+
The second reports a block before that block becomes an interactive prompt. A
|
|
96
|
+
queued message drains at the next tool round and a session already waiting on
|
|
97
|
+
input never reaches one, so an answer relayed to an open prompt renders beneath
|
|
98
|
+
the question and changes nothing. Nothing is sent on progress, which would
|
|
99
|
+
rebuild the poll on the sender's side of the channel.
|
|
100
|
+
|
|
101
|
+
The announcement is what let the review poll narrow. It used to start on a
|
|
102
|
+
dispatch as well as on an open pull request, and the script reads pull requests
|
|
103
|
+
while a building worker has none, so five consecutive runs reported no movement
|
|
104
|
+
across roughly fifteen minutes while one worker built. An open pull request is
|
|
105
|
+
now the whole trigger, with a dispatch still out after thirty minutes and silent
|
|
106
|
+
kept as a fallback.
|
|
107
|
+
|
|
76
108
|
Two rules put it there rather than leaving that to whoever remembers. A finding
|
|
77
109
|
the worker declines carries the fact that settled it in the same reply body that
|
|
78
110
|
already maps every finding, and a pass accepting that argument states the
|
package/package.json
CHANGED
|
@@ -118,6 +118,11 @@ export const CLAUDE_WORKFLOW_CASES: readonly SkillCase[] = [
|
|
|
118
118
|
prompt: 'Tell me the paint and layout cost of this page right now.',
|
|
119
119
|
expect: 'claude-ux-measure',
|
|
120
120
|
},
|
|
121
|
+
{
|
|
122
|
+
prompt:
|
|
123
|
+
'I am building this branch for another session. What am I on the hook for, and what is off limits?',
|
|
124
|
+
expect: 'claude-worker',
|
|
125
|
+
},
|
|
121
126
|
{
|
|
122
127
|
prompt: 'Get me set up in a fresh Claude Code worktree for this branch.',
|
|
123
128
|
expect: 'claude-worktree',
|