instar 1.3.680 → 1.3.682
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/server.d.ts.map +1 -1
- package/dist/commands/server.js +32 -11
- package/dist/commands/server.js.map +1 -1
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +25 -1
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/types.d.ts +3 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/messaging/ColdStartFallbackReply.d.ts +68 -0
- package/dist/messaging/ColdStartFallbackReply.d.ts.map +1 -0
- package/dist/messaging/ColdStartFallbackReply.js +91 -0
- package/dist/messaging/ColdStartFallbackReply.js.map +1 -0
- package/dist/monitoring/AgentWorktreeReaper.d.ts +8 -0
- package/dist/monitoring/AgentWorktreeReaper.d.ts.map +1 -1
- package/dist/monitoring/AgentWorktreeReaper.js +1 -0
- package/dist/monitoring/AgentWorktreeReaper.js.map +1 -1
- package/dist/monitoring/agentWorktreeGit.d.ts +26 -0
- package/dist/monitoring/agentWorktreeGit.d.ts.map +1 -1
- package/dist/monitoring/agentWorktreeGit.js +83 -1
- package/dist/monitoring/agentWorktreeGit.js.map +1 -1
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +7 -0
- package/dist/scaffold/templates.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +19 -19
- package/src/scaffold/templates.ts +7 -0
- package/upgrades/1.3.681.md +43 -0
- package/upgrades/1.3.682.md +55 -0
- package/upgrades/coldstart-lifeline-fallback.eli16.md +49 -0
- package/upgrades/reaper-squash-merge-aware.eli16.md +43 -0
- package/upgrades/side-effects/coldstart-lifeline-fallback.md +85 -0
- package/upgrades/side-effects/reaper-squash-merge-aware.md +75 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Cold-Start Lifeline Fallback — ELI16 overview
|
|
2
|
+
|
|
3
|
+
## The problem in plain words
|
|
4
|
+
|
|
5
|
+
When you send a message to one of your agent's topics, the agent has to *start a
|
|
6
|
+
session* to read and answer it. Usually that just works. But sometimes it can't
|
|
7
|
+
start one right then — maybe too many sessions are already running (a hard limit),
|
|
8
|
+
maybe the machine is low on memory or under heavy load, or maybe the startup hit an
|
|
9
|
+
unexpected error.
|
|
10
|
+
|
|
11
|
+
Before this change, when that happened you'd either get silence, or a bare, unhelpful
|
|
12
|
+
line like "Having trouble starting a session right now." Worse, one version told you
|
|
13
|
+
to "increase maxSessions in your config" — asking *you* to go edit a settings file.
|
|
14
|
+
That's backwards: the agent is the thing with the tools to free up resources and fix
|
|
15
|
+
the problem. You shouldn't have to.
|
|
16
|
+
|
|
17
|
+
## What this change does
|
|
18
|
+
|
|
19
|
+
Now, when the agent genuinely can't start (or restart) a session for a topic, you get
|
|
20
|
+
ONE clear, friendly reply that does three things:
|
|
21
|
+
|
|
22
|
+
1. **Says WHY in plain English** — "I'm already running the maximum number of sessions
|
|
23
|
+
at once," or "the machine is under resource pressure right now," or an honest
|
|
24
|
+
"unexpected start-up error." No jargon, no config keys.
|
|
25
|
+
2. **Points you to your Lifeline topic** — the one topic that's guaranteed to always be
|
|
26
|
+
reachable. That's the place to go when a normal topic is stuck.
|
|
27
|
+
3. **Hands you a ready-to-paste message** — a pre-written line you can copy straight
|
|
28
|
+
into the Lifeline that describes exactly what failed, so the agent can diagnose and
|
|
29
|
+
free resources fast. You don't have to explain anything yourself.
|
|
30
|
+
|
|
31
|
+
If the failing topic *is* your Lifeline, it doesn't tell you to go elsewhere — it says
|
|
32
|
+
you're already in the right place and it'll start freeing resources. If you have no
|
|
33
|
+
Lifeline configured, it still tells you why and reassures you that your message isn't
|
|
34
|
+
lost — just resend once things settle.
|
|
35
|
+
|
|
36
|
+
## Why it's built this way
|
|
37
|
+
|
|
38
|
+
This is the "G1" half of a constitutional standard called **"The Agent Is Always
|
|
39
|
+
Reachable."** The core idea: the agent must never go silently unreachable, because the
|
|
40
|
+
agent itself is the solution — it holds the tools to diagnose and free resources, so it
|
|
41
|
+
has to stay reachable to use them. The reply is sent on a *deterministic* delivery path
|
|
42
|
+
(a direct send), never through the smart message-review gate, because that gate could
|
|
43
|
+
itself fail under the very resource pressure we're trying to report.
|
|
44
|
+
|
|
45
|
+
It's an always-on safety floor with no on/off switch — the standard forbids hiding
|
|
46
|
+
reachability behind a flag. Under the hood it's a small, well-tested message builder
|
|
47
|
+
wired into the two places a session-start can fail (a fresh spawn and a restart), plus
|
|
48
|
+
a CLAUDE.md note so any agent can explain "why did I get a go-to-lifeline message?" if
|
|
49
|
+
you ask.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Squash-merge-aware worktree reaper — ELI16 overview
|
|
2
|
+
|
|
3
|
+
## The problem in plain words
|
|
4
|
+
|
|
5
|
+
When the agent works on a change, it makes a "worktree" — a full copy of the codebase
|
|
6
|
+
in its own folder — so different jobs don't trip over each other. Each one is hundreds
|
|
7
|
+
of megabytes. After the change is merged and shipped, that folder is dead weight: the
|
|
8
|
+
work is safely in the main branch, so the copy can be deleted.
|
|
9
|
+
|
|
10
|
+
A background cleaner (the "reaper") is supposed to delete those finished folders. But it
|
|
11
|
+
had a blind spot. To decide "is this branch's work already in main?", it compared the
|
|
12
|
+
branch's commits to main commit-by-commit (using a git trick called patch-id). That
|
|
13
|
+
works when a branch is merged normally — but this project **squash-merges**: it mashes
|
|
14
|
+
all of a branch's commits into ONE new commit on main. That single squashed commit has a
|
|
15
|
+
different fingerprint than the original commits, so the reaper couldn't recognize the
|
|
16
|
+
work as merged. It played it safe and **kept the folder forever**.
|
|
17
|
+
|
|
18
|
+
Result: merged-but-unrecognized folders piled up — on this machine, hundreds of them,
|
|
19
|
+
over 100 GB of disk. That bloat is part of what made the machine slow and, eventually,
|
|
20
|
+
contributed to a crash.
|
|
21
|
+
|
|
22
|
+
## What this change does
|
|
23
|
+
|
|
24
|
+
The reaper now has a second way to tell a branch is merged: it asks GitHub. Once per
|
|
25
|
+
cleanup sweep it fetches the list of **merged pull requests** and the exact commit each
|
|
26
|
+
one merged. If a folder's branch has a merged PR, AND the folder is sitting on exactly
|
|
27
|
+
that same commit, the reaper knows the work is in main and the folder is safe to delete.
|
|
28
|
+
|
|
29
|
+
Two safety rules make this trustworthy:
|
|
30
|
+
- **Exact-commit match.** If you added new commits to the branch AFTER its PR merged,
|
|
31
|
+
the folder's commit won't match the merged one, so it's still KEPT — new work is never
|
|
32
|
+
thrown away.
|
|
33
|
+
- **Fail-safe.** If GitHub can't be reached (offline, not logged in, not a GitHub repo),
|
|
34
|
+
the reaper simply falls back to the old commit-by-commit check and keeps the folder.
|
|
35
|
+
It never deletes on a guess.
|
|
36
|
+
|
|
37
|
+
It's still off by default and runs in dry-run first (it tells you what it WOULD delete
|
|
38
|
+
before it deletes anything), and there's an off-switch (`githubMergeCheck: false`) to
|
|
39
|
+
turn off the GitHub call entirely. The GitHub lookup is just ONE call per sweep, cached,
|
|
40
|
+
so it's cheap.
|
|
41
|
+
|
|
42
|
+
The net effect: when you turn the reaper on, it can finally reclaim the squash-merged
|
|
43
|
+
folders that were stuck forever — recovering the disk that was quietly disappearing.
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Side-Effects Review — Cold-Start Lifeline Fallback (G1)
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `coldstart-lifeline-fallback`
|
|
4
|
+
**Date:** `2026-06-26`
|
|
5
|
+
**Author:** `echo`
|
|
6
|
+
**Tier:** 1 (enhances an EXISTING always-on user-notice on the inbound cold-start
|
|
7
|
+
failure path; reuses the already-merged "Agent Is Always Reachable" standard #1288;
|
|
8
|
+
no new authority, no new gate, no protocol/security change)
|
|
9
|
+
|
|
10
|
+
## Summary of the change
|
|
11
|
+
|
|
12
|
+
G1 (the user-facing arm) of the constitutional standard **"The Agent Is Always
|
|
13
|
+
Reachable"** corollary (2), *no silent resource rejection*. When a user messages a
|
|
14
|
+
topic and the system genuinely cannot start (cold spawn) OR restart a session for it,
|
|
15
|
+
the existing catch already sent a generic notice — but it (a) did not point to the
|
|
16
|
+
Lifeline, (b) did not hand a copy-paste debug message, and (c) leaked dev jargon
|
|
17
|
+
("increase maxSessions in your config"). This change extracts a pure, unit-tested
|
|
18
|
+
message builder (`src/messaging/ColdStartFallbackReply.ts`) that classifies WHY the
|
|
19
|
+
start failed (`session-limit` / `resource-pressure` / `start-failure`) and composes a
|
|
20
|
+
plain-English reply with the Lifeline pointer + a pre-written copy-paste debug block,
|
|
21
|
+
and wires it into BOTH inbound failure paths in `src/commands/server.ts`. Migration
|
|
22
|
+
parity: a `migrateClaudeMd` section (existing agents) + a `generateClaudeMd` template
|
|
23
|
+
section (new agents) so any agent can explain the behavior. Files:
|
|
24
|
+
`src/messaging/ColdStartFallbackReply.ts` (new), `src/commands/server.ts` (two catch
|
|
25
|
+
sites), `src/core/PostUpdateMigrator.ts` (CLAUDE.md section), `src/scaffold/templates.ts`
|
|
26
|
+
(template section), plus three test files (logic, wiring-integrity, migration).
|
|
27
|
+
|
|
28
|
+
## Decision-point inventory
|
|
29
|
+
|
|
30
|
+
- The two inbound failure catches (`onTopicMessage` cold-spawn `.catch` and restart
|
|
31
|
+
`.catch`) — **pass-through**. They already FIRED a notice; this only changes the
|
|
32
|
+
*content* of the message (and replaces the string-match branch with the classifier).
|
|
33
|
+
No control-flow change: still one `telegram.sendToTopic(topicId, …)` on failure.
|
|
34
|
+
|
|
35
|
+
## 1. Over-block
|
|
36
|
+
|
|
37
|
+
**No block/allow surface — not applicable.** The change produces a user-facing message
|
|
38
|
+
on an already-failing path; it never blocks, delays, or rejects any session, message,
|
|
39
|
+
or spawn. The spawn/restart decision is entirely unchanged (the failure already
|
|
40
|
+
happened by the time the builder runs).
|
|
41
|
+
|
|
42
|
+
## 2. Under-block
|
|
43
|
+
|
|
44
|
+
**Not applicable** — no gate. The closest "miss" is a misclassified reason word, which
|
|
45
|
+
only changes the wording (the notice still fires and still points to the Lifeline). The
|
|
46
|
+
classifier is a SIGNAL (help text), not authority — both sides of each classification
|
|
47
|
+
branch are unit-tested.
|
|
48
|
+
|
|
49
|
+
## 3. Level-of-abstraction fit
|
|
50
|
+
|
|
51
|
+
Correct layer. The failure reason (the thrown error) and the delivery primitive
|
|
52
|
+
(`telegram.sendToTopic`, `telegram.getLifelineTopicId`) are both already in scope at the
|
|
53
|
+
catch site; composing the message there — via a pure builder that has no I/O — is the
|
|
54
|
+
right level. The builder is isolated in `src/messaging/` so it is unit-testable without
|
|
55
|
+
standing up the server.
|
|
56
|
+
|
|
57
|
+
## 4. Signal vs authority compliance
|
|
58
|
+
|
|
59
|
+
The reply is **pure signal** — a help message on a deterministic delivery path. It
|
|
60
|
+
carries zero blocking authority: it cannot hold, delay, or release a session. The
|
|
61
|
+
session's fate is decided entirely by the unchanged `SessionManager.spawnSession`
|
|
62
|
+
throw. This is the signal-vs-authority split the principle demands.
|
|
63
|
+
|
|
64
|
+
## 5. Interactions
|
|
65
|
+
|
|
66
|
+
- **Deterministic path (deliberate):** delivery is the direct `sendToTopic`, NOT the
|
|
67
|
+
LLM tone gate — per the standard, the notice must not be blockable by the very
|
|
68
|
+
pressure it reports (a tone gate failing closed under load would swallow it).
|
|
69
|
+
- **Double-fire:** unchanged. The existing `spawningTopics` guard + the single `.catch`
|
|
70
|
+
still fire at most one notice per failed attempt.
|
|
71
|
+
- **Same-topic-is-lifeline:** handled — when the failing topic IS the Lifeline, the
|
|
72
|
+
message does not tell the user to go elsewhere (tested).
|
|
73
|
+
- **No-lifeline-configured:** degrades to "why + honest retry guidance" (tested).
|
|
74
|
+
|
|
75
|
+
## 6. External surfaces
|
|
76
|
+
|
|
77
|
+
- **User:** the agent's own Telegram topic — same `sendToTopic` primitive, same
|
|
78
|
+
topic, same priority as the message it replaces. No new external call shape.
|
|
79
|
+
- **No new config, route, hook, or credential.** Always-on (the standard forbids
|
|
80
|
+
dark-shipping reachability); the only "surface" change is better message text.
|
|
81
|
+
|
|
82
|
+
## 7. Rollback
|
|
83
|
+
|
|
84
|
+
Pure code + docs. Reverting the commit restores the prior generic notice. No state
|
|
85
|
+
migration to undo (the CLAUDE.md section is idempotent and additive).
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Side-Effects Review — Squash-merge-aware worktree reaper
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `reaper-squash-merge-aware`
|
|
4
|
+
**Date:** `2026-06-26`
|
|
5
|
+
**Author:** `echo`
|
|
6
|
+
**Tier:** 1 — enhances an EXISTING merged-detection on the AgentWorktreeReaper (which
|
|
7
|
+
ships OFF + dry-run by default). Adds a fail-safe, conservative network SIGNAL + a config
|
|
8
|
+
off-switch. No new authority, no new gate, no new route. The reaper's safety contract
|
|
9
|
+
("NEVER delete unmerged work") is strengthened, not weakened.
|
|
10
|
+
|
|
11
|
+
## Summary of the change
|
|
12
|
+
|
|
13
|
+
The reaper's merged-check used `git cherry` (patch-id equivalence) ONLY, which the code
|
|
14
|
+
itself documents cannot detect MULTI-commit squash-merges — the disk-accumulation root
|
|
15
|
+
cause (squash-merged worktrees kept forever; ~118GB/290 observed). Added a SECOND,
|
|
16
|
+
conservative signal: `fetchMergedPrHeadOids()` does ONE `gh pr list --state merged`
|
|
17
|
+
call per sweep (cached 60s) → a `headRefName→headRefOid` map; `isMerged` treats a
|
|
18
|
+
worktree as merged only when its branch has a merged PR whose head OID EXACTLY matches
|
|
19
|
+
the worktree HEAD. Files: `agentWorktreeGit.ts` (the fetcher + the cached map + the
|
|
20
|
+
enhanced `isMerged`), `AgentWorktreeReaper.ts` (config field `githubMergeCheck`,
|
|
21
|
+
default true), `types.ts` (config type), `server.ts` (plumb the flag),
|
|
22
|
+
`PostUpdateMigrator.ts` (awareness). Tests: 9 new (`fetchMergedPrHeadOids` parse/fail-safe,
|
|
23
|
+
`isMerged` PR-map both sides + oid-mismatch keep + disabled-no-call).
|
|
24
|
+
|
|
25
|
+
## 1. Over-block (over-KEEP)
|
|
26
|
+
|
|
27
|
+
Possible: a genuinely-merged worktree is still KEPT (e.g. gh offline, PR list older than
|
|
28
|
+
`--limit 500`, branch renamed). This is the SAFE direction — a kept worktree wastes disk
|
|
29
|
+
but loses nothing. Fully acceptable; it is exactly today's behavior on any gh failure.
|
|
30
|
+
|
|
31
|
+
## 2. Under-block (the deletion-safety risk — the one that matters)
|
|
32
|
+
|
|
33
|
+
The hard rule is NEVER delete unmerged work. The new signal can only ADD a "merged"
|
|
34
|
+
verdict, so the question is: can it false-positive? Guards: (a) it requires an actual
|
|
35
|
+
MERGED PR for the branch (authoritative — the content is in main); (b) it requires the
|
|
36
|
+
worktree HEAD to EXACTLY equal the PR's merged head OID, so a branch with commits added
|
|
37
|
+
AFTER the merge (unmerged work on top) is KEPT (tested); (c) the upstream `isClean` gate
|
|
38
|
+
still independently blocks any uncommitted/untracked changes; (d) fail-safe to cherry-only
|
|
39
|
+
on any gh error. A reused branch name resolves to the NEWEST merged PR, and the exact-OID
|
|
40
|
+
match still gates deletion. Both sides tested.
|
|
41
|
+
|
|
42
|
+
## 3. Level-of-abstraction fit
|
|
43
|
+
|
|
44
|
+
Correct layer — the change lives entirely in the deps factory (`makeAgentWorktreeReaperDeps`);
|
|
45
|
+
the pure classifier (`AgentWorktreeReaper.evaluate`) is untouched and stays fake-testable.
|
|
46
|
+
The gh call is lazy (only when `git cherry` says unmerged) and cached (one call per sweep).
|
|
47
|
+
|
|
48
|
+
## 4. Signal vs authority compliance
|
|
49
|
+
|
|
50
|
+
The merged-PR map is a SIGNAL consumed by the existing deterministic reaper gate-chain;
|
|
51
|
+
it has no block/allow surface of its own. It can only contribute a conservative "merged"
|
|
52
|
+
input, which the existing in-use/clean/blast-radius/breaker gates still sit in front of.
|
|
53
|
+
|
|
54
|
+
## 5. Interactions
|
|
55
|
+
|
|
56
|
+
- **`isClean` ordering:** unchanged — `isMerged` (now with the gh path) is still only
|
|
57
|
+
reached AFTER the cheap protect-gates (in-use, dirty) clear, so no extra gh calls on
|
|
58
|
+
dirty/active worktrees.
|
|
59
|
+
- **Caching:** 60s TTL map + the existing 10s cwd cache — one sweep = one gh call.
|
|
60
|
+
- **Blast radius:** unchanged `maxReapsPerPass` (default 20) still caps deletions/sweep.
|
|
61
|
+
- **Breaker:** unchanged per-path removal-failure breaker still applies.
|
|
62
|
+
|
|
63
|
+
## 6. External surfaces
|
|
64
|
+
|
|
65
|
+
- **New external call:** `gh pr list` (read-only) against the repo's GitHub. Requires gh
|
|
66
|
+
installed + authed; absent → fail-safe KEEP. No write, no new credential (uses the
|
|
67
|
+
ambient gh auth). Off-switch: `agentWorktreeReaper.githubMergeCheck: false`.
|
|
68
|
+
- No new route, no user-facing message, no config migration required (the field defaults
|
|
69
|
+
true via `?? true`, so existing agents get the behavior on the dist update).
|
|
70
|
+
|
|
71
|
+
## 7. Rollback
|
|
72
|
+
|
|
73
|
+
Pure code + one config flag. `githubMergeCheck: false` restores cherry-only at runtime;
|
|
74
|
+
reverting the commit removes the path entirely. The reaper's OFF+dry-run default means no
|
|
75
|
+
agent reaps anything until explicitly enabled and reviewed.
|