Skip to content

Commit d35ba2b

Browse files
missingbulbclaude
andauthored
Claudinite: refresh the vendored mount to canon 7e89c0c (#780)
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). Co-authored-by: Claude <noreply@anthropic.com>
1 parent bce14d2 commit d35ba2b

29 files changed

Lines changed: 706 additions & 215 deletions

File tree

.claudinite-checks.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
"delivery": "auto-merge"
3333
},
3434
"claudinite": {
35-
"updated": "2026-07-27T09:51:24.654Z",
36-
"ref": "526c9910fb0dc276db293e64b8b16b57bc90f565"
35+
"updated": "2026-07-28T10:26:42.699Z",
36+
"ref": "7e89c0cfe40c133739fdeb351e9d463f1d4b3097"
3737
}
3838
}

.claudinite/shared/engine/checks/helpers/repo-context.mjs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ export const PACK_ENTRY_KEYS = ['id', 'config', 'answers', 'rules', 'accept', 'v
124124
// read this one shape regardless of which form the file used.
125125
export function loadConfig(root) {
126126
const path = join(root, '.claudinite-checks.json');
127-
const empty = { packs: [], packEntries: [], rules: {}, accept: [], sharedConstants: [], packConfig: {}, taskScheduler: null, errors: [] };
127+
const empty = { packs: [], packEntries: [], rules: {}, accept: [], sharedConstants: [], packConfig: {}, taskScheduler: null, claudinite: null, maintenance: null, errors: [] };
128128
if (!existsSync(path)) return empty;
129129

130130
let raw;
@@ -271,6 +271,18 @@ export function loadConfig(root) {
271271
sharedConstants: Array.isArray(raw.sharedConstants) ? raw.sharedConstants : [],
272272
packConfig,
273273
taskScheduler,
274+
// Passed through as declared, not normalized: `claudinite` is the
275+
// vendored-mount provenance stamp the `stamp` signal reads, and `maintenance`
276+
// is the delivery preference. Both are in CONFIG_KEYS, so declaring them is
277+
// legal and raises no error — and omitting them from THIS shape is what made
278+
// the stamp invisible to the scheduler and silently killed baselining across
279+
// the whole fleet: every repo self-skipped as "no vendored mount (no stamp)"
280+
// while its scheduler runs went green. A key that validates but does not
281+
// survive the load is the worst kind — legal to write, impossible to read,
282+
// silent in both directions. The CONFIG_KEYS-survive-loadConfig test pins the
283+
// whole set so no future key can go the same way.
284+
claudinite: raw.claudinite ?? null,
285+
maintenance: raw.maintenance ?? null,
274286
errors,
275287
};
276288
}

.claudinite/shared/engine/scheduler/executor.md

Lines changed: 46 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
# Claudinite executor
22

33
> **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.
66
> If you cannot name exactly one, **run nothing and end the session** — the
77
> 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.
1112
1213
You are the per-repo **executor** — you run the scheduled **tasks** dispatched to
1314
this repo (per-project-scheduling DESIGN §5). A routine wired to a dispatch label
@@ -61,21 +62,34 @@ so use `engine/scheduler/`.
6162
node <engine>/scheduler/resolve-dispatch.mjs self # `fleet` if your trigger is ready-for-agent-fleet
6263
```
6364

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.
7083

7184
**Act on its exit code — that is the interface**, not the prose it prints:
7285

7386
| exit | verdict | what you do |
7487
| --- | --- | --- |
7588
| `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. |
7690
| `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. |
7993
| `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. |
8094

8195
**State the issue number before you act**, so everything after this has one
@@ -98,22 +112,25 @@ so use `engine/scheduler/`.
98112
`rearmDispatchIssues`) and escalates it to `needs-human` if it stays unrun past
99113
~2 of its scheduling periods. Leave it alone.
100114

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.
117134

118135
2. **Claim the issue — read, swap, then re-read to confirm you won.** The same
119136
issue can still be labeled twice (a re-arm that overlapped a slow session, a

0 commit comments

Comments
 (0)