You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Claudinite: refresh the vendored mount to canon 7e89c0c
Out-of-band refresh, pushed from a canon session, because this repo could not
perform it itself.
Its scheduler has been running green and skipping baselining every night with
"no vendored mount (no stamp)" — on a repo whose .claudinite-checks.json carries
one. The vendored `loadConfig` validated `claudinite` as a legal settings key and
then dropped it from the object it returned, so the `stamp` signal read undefined
and baselining took the branch meant for the canon and pre-adoption repos.
That made the fix unreachable by the normal route: baselining is what updates the
mount, and the bug was in the mount. Hence this manual converge.
Carried by this refresh (canon PRs #511, #516, #518):
- loadConfig keeps `claudinite` and `maintenance`, so the stamp is readable again
- the executor resolves its dispatch from the CCR trigger, not just the Actions
payload, and no longer guesses an issue when it cannot identify one
- FORCE_TASKS: a manual scheduler run can force a task past its slot gate
Mount converged whole-set and the wiring re-converged from the vendored stub
(the scheduler workflow gains the `overrides` input).
Copy file name to clipboardExpand all lines: .claudinite/shared/engine/scheduler/executor.md
+46-29Lines changed: 46 additions & 29 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,13 +1,14 @@
1
1
# Claudinite executor
2
2
3
3
> **Before anything else — name your one issue.** This session runs exactly one
4
-
> dispatch. Step 1 resolves it **in code** from the label event on disk and
5
-
> validates it; state the number it prints before you touch anything.
4
+
> dispatch. Step 1 resolves it **in code** from the trigger this session carries
5
+
> and validates it; state the number it prints before you touch anything.
6
6
> If you cannot name exactly one, **run nothing and end the session** — the
7
7
> scheduler re-arms an unrun dispatch on its next hourly pass, so stopping costs a
8
-
> delay while sweeping costs duplicated work. Never process a second issue in one
9
-
> session, under any circumstance — including when a listing shows several
10
-
> waiting. Those are other sessions' dispatches, already running.
8
+
> delay while guessing costs duplicated work. **There is no fallback**: never
9
+
> select a dispatch by listing the queue and taking one. Never process a second
10
+
> issue in one session, under any circumstance — including when a listing shows
11
+
> several waiting. Those are other sessions' dispatches, already running.
11
12
12
13
You are the per-repo **executor** — you run the scheduled **tasks** dispatched to
13
14
this repo (per-project-scheduling DESIGN §5). A routine wired to a dispatch label
@@ -61,21 +62,34 @@ so use `engine/scheduler/`.
61
62
node <engine>/scheduler/resolve-dispatch.mjs self # `fleet` if your trigger is ready-for-agent-fleet
62
63
```
63
64
64
-
(`<engine>` per Engine command paths above.) It reads the label event that
65
-
started this session from `$GITHUB_EVENT_PATH`, takes your issue and its body
66
-
straight out of that payload, and asserts in code — before any judgment of
67
-
yours — that the body names a legal task path, the file exists at HEAD, its
68
-
pack is declared, and its `task.mjs` sibling parses to a valid declaration. It
69
-
makes no GitHub calls; the payload plus this checkout is everything it needs.
65
+
(`<engine>` per Engine command paths above.) It finds the `issues.labeled`
66
+
trigger that started this session — from **either** transport, see below —
67
+
and asserts in code, before any judgment of yours, that the issue body names a
68
+
legal task path, the file exists at HEAD, its pack is declared, and its
69
+
`task.mjs` sibling parses to a valid declaration. It makes no GitHub calls of
70
+
its own.
71
+
72
+
**Your trigger arrives one of two ways, and the shell reads both:**
73
+
74
+
-**GitHub Actions** writes the whole webhook payload to `$GITHUB_EVENT_PATH`
75
+
— issue number, label, and body all in one file, so this resolves in a
76
+
single command.
77
+
-**Claude Code on the web (CCR)** writes no payload file. It delivers the
78
+
same trigger as environment variables — `CCR_TRIGGER_SOURCE`,
79
+
`CCR_TRIGGER_EVENT`, `CCR_TRIGGER_REPO`, `CCR_TRIGGER_ISSUE_NUMBER` — which
80
+
**name your issue but carry neither its labels nor its body**. So this
81
+
resolves in two commands: the shell exits `13` telling you the number, you
82
+
fetch **that one issue** over MCP, and you re-run with what you fetched.
70
83
71
84
**Act on its exit code — that is the interface**, not the prose it prints:
72
85
73
86
| exit | verdict | what you do |
74
87
| --- | --- | --- |
75
88
|`0`| valid dispatch, your scope | Go to step 2. The printed block is your brief: issue, task path, pack, task, model, outcome ceiling, `executionTimeout`. |
89
+
|`13`| CCR trigger, issue named, body needed | Fetch **the printed issue and only it** over MCP — its body and its current labels — write the body verbatim to a file, and re-run: `node <engine>/scheduler/resolve-dispatch.mjs self --issue-body-file <path> --issue-labels <csv>`. Then act on *that* run's exit code. |
76
90
|`10`| invalid dispatch | It never runs. Comment the printed `reason`, remove the ready label, add `needs-human`, end the session. |
77
-
|`11`| not yours | The trigger label is the *other* executor's, or no ready label at all. **Stop**: change nothing, comment nothing, end the session. |
78
-
|`12`| no event payload | Fall back — see below. |
91
+
|`11`| not yours | The trigger label is the *other* executor's, no ready label at all, or the issue no longer carries a ready label (another session already claimed it). **Stop**: change nothing, comment nothing, end the session. |
92
+
|`12`| no trigger at all |**Stop the session immediately.** See below. |
79
93
|`2`, `1`| bad invocation, internal fault | Comment what you saw, add `needs-human` if you know the issue, end the session. Do not proceed on a guess. |
80
94
81
95
**State the issue number before you act**, so everything after this has one
@@ -98,22 +112,25 @@ so use `engine/scheduler/`.
98
112
`rearmDispatchIssues`) and escalates it to `needs-human` if it stays unrun past
99
113
~2 of its scheduling periods. Leave it alone.
100
114
101
-
**Exit 12 — the fallback, and only on exit 12.** The event payload is how you
102
-
know which issue is yours; without it you must select one. List the open issues
103
-
under your ready label **solely to pick the single oldest**, run that one alone,
104
-
and continue at step 2. Say in your claim comment that you fell back and why: a
105
-
session reaching here means the payload did not arrive, and that is worth a
106
-
human noticing rather than silently degrading to "oldest first" forever.
107
-
108
-
**That listing is a selection step, never a work list.** This is the exact
109
-
point where the one-issue rule has been lost in practice: a session that
110
-
cannot identify its trigger enumerates the queue, sees N issues waiting, and
111
-
works its way through them — which is precisely the N-sessions-racing-over-N-issues
112
-
failure the rule exists to prevent, arrived at from the other direction. Every
113
-
other issue in that listing already has its own session. Take the oldest,
114
-
forget the rest, and if you cannot pick unambiguously, run nothing and end.
115
-
Never take an issue under the other ready label; that is the other executor's
116
-
to run.
115
+
**Exit 12 — stop the session. There is no fallback.** The trigger is how you
116
+
know which issue is yours. Without one you do not have a dispatch to run, and
117
+
**selecting one yourself is forbidden**: do not list the queue, do not take the
118
+
oldest, do not take *any*. Run nothing, change nothing, comment nothing, end
119
+
the session.
120
+
121
+
This is not caution for its own sake — a session that picks its own issue is
122
+
the duplicate-execution bug reached from the other direction. One scheduler run
123
+
files every due dispatch seconds apart, so **every** session that cannot name
124
+
its trigger builds the *same* list and races the others over it; the step-2
125
+
claim cannot save them, because they all read the list before any of them
126
+
claims. That is exactly how #772 was claimed twice, one second apart, on
127
+
2026-07-28. Stopping is cheap: the scheduler re-arms an unrun dispatch on its
128
+
next hourly pass and escalates it to `needs-human` if it stays unrun. Guessing
129
+
is not.
130
+
131
+
An exit 12 in a session that *was* triggered by a label event means the trigger
132
+
did not reach the shell — a real defect worth a human noticing. Say so plainly
133
+
in your final message rather than working around it.
117
134
118
135
2.**Claim the issue — read, swap, then re-read to confirm you won.** The same
119
136
issue can still be labeled twice (a re-arm that overlapped a slow session, a
0 commit comments