Repository navigation
v0.18.0
Added
-
Agents review their own diff before handing in.
chela task-finishedrefuses a code
PR whose body has no## Reviewsection (prose-only PRs are exempt). An agent that cannot
review passes--no-review-reason "…", which is recorded on the run and shown to the judge
and on the PR. (CMX-209) -
Every judge and self-check suite run now has its own memory ceiling. chela wraps each
run in its ownsystemd-run --user --scope(CHELA_SUITE_MEMORY_MAX, default 8.3G, which
is 3x the measured 2.75 GiB anon peak of a full suite run;0turns it off). A run that
blows through its ceiling is killed inside its scope and reported as CANNOT VERIFY ("suite
exceeded 8.3G"), never as a pass. chela claims the ceiling only when the suite's own
/proc/self/cgroupnames the scope. Whatever a suite leaks into its scope, such as a
daemon that ignores SIGTERM, is SIGKILLed when the run ends. (CMX-119) -
At most K suite runs at once, judges and agents together. Each suite run takes one of
K slots first (CHELA_SUITE_SLOTS, default half the RAM over the measured peak: 2 on a
16 GB box). A queued run says⏳ waiting for a suite slot, is served first come first
served, and is never reported as stuck, and the wait does not count against its budgets or
the judge's wall. (CMX-119) -
Spike: chela's merge gate and event posting as a Claude Code Mod.
mods/chela-hooks/
runs the same gate script and posts the same event bodies as the shell plugin. It needs
Claude Code 2.1.301+, chela never installs it, and it replaces nothing.
docs/MODS_SPIKE.mdholds the side-by-side measurements: it posts faster than the shell
plugin, missed no events, and denied the same commands. Its recommendation is to build
the in-session UI items as an opt-in Mod and to keep the shell plugin as the baseline.
(CMX-157) -
The judge keeps a failing suite's output. Every non-green infrastructure suite run in a
judge round (the baseline, its coverage run, and the base-branch diagnostic) is written to
$CHELA_DIR/judge-logs/<task>/<round>-suite-<n>.logwith the command, cwd, env diff vs the
operator, start/end time, exit code and up to 200,000 chars of output. Before this, only a
one-line summary survived, so acannot_verifycaused by a flaky baseline could not be
diagnosed later.chela judge showlists each round's logs, and acannot_verifyverdict
line and PR comment start with a~-relative pointer to them. A green baseline writes
nothing. Logs older than 30 days, or past the newest 20 per task, are pruned whenever a
new one is written. (CMX-163) -
Settings > Dispatch has "Agents at once" and "Judges at once" rows. One stepper per
workflow sets an operator-local override of WORKFLOW.md'sconcurrency.max(Reset goes
back to the file value), and one stepper sets the judge count, which now wins over
CHELA_JUDGE_MAX_CONCURRENT. Both are bounded 1 to 8, validated by
/api/config/concurrency, read on the dispatcher's next tick (no file edit, no restart),
logged to the event log assettings.concurrency(who, old, new), and shown with what the
count means for memory (N x the measured judge peak, next to available memory). Lowering
a count never stops a running agent or judge.judge_max_concurrentleft the generic
Dispatch knob list for this row. (CMX-164) -
chela judge sweepmutates TypeScript and TSX. A changed.ts/.tsxfile is no
longer UNKNOWN: it gets the JS mutations (if, comparison flips,return 0/1) plus
&&↔||,return true↔false, a ternary's condition forced each way, and JSX
conditional rendering ({c && <X/>}→{false && <X/>}). Every mutant is parse-checked
with the repo's owntypescriptpackage; one that does not parse is skipped, never counted
as killed or survived. With notypescriptto parse with, the sweep reports it cannot
verify..d.tsfiles and*.spec.ts/__tests__/files are not mutated. (CMX-173) -
A TS mutation runs only its related Vitest tests. When
judge.test_cmdis one Vitest
invocation (pnpm exec vitest run), the baseline also writes Vitest's JSON report, and each
mutated.ts/.tsxfile runs only the test files whose import graph reaches it, directly or
through other modules (./a,./a.jsand./dirresolve toa.ts/a.tsx/dir/index.ts).
A TS file no test reaches is UNTESTED: it still runs the full suite, and a survivor there
is reported as[UNTESTED], a survivor-class finding. (CMX-174) -
The judge and the self-check grade TypeScript.
chela judge run,chela judge self-checkandtask-finished --self-check-experimentsapply experiments to.ts/.tsx
files with the sweep's parse proof and Vitest test selection, within the same budgets. A
TS-only PR getsblockedorcleanlike any other. If the repo has notypescript(or no
node) to prove a TS file parses, the run is cannot-verify, never clean. Vitest's
Test Filessummary line no longer counts as tests run. (CMX-175) -
A React + TypeScript frontend scaffold for the dashboard in
chela/dashboard/frontend/
(Vite, strict TypeScript, Vitest, React Testing Library, ESLint), installed by the root
pnpm installas a workspace package. dependency-cruiser rules keep eachsrc/features/*
andsrc/shared/*module reachable only through its root entry files and forbid cycles. CI
gains afrontendjob (typecheck, lint, module boundaries, test), and the Vitest suite runs
inside pytest too, as do tests that feed each boundary, type and lint rule a deliberate
violation. Nothing is wired into the dashboard page yet; users still need no Node.
(CMX-176) -
The dashboard frontend now builds into Flask's static dir, and the build is committed.
pnpm -C chela/dashboard/frontend buildwrites hashed files plusmanifest.jsonto
chela/dashboard/static/app/, served understatic/v/<version>/app/with the same
cache headers as every other asset. Because the output is tracked, a wheel, an sdist and
a plaingit clone+uv syncall serve it with no Node installed. CI and pytest fail
when it differs from a fresh build ofsrc/, andchela releaserebuilds it before it
commits.chela release --dry-runruns every release step (frontend build included) and
leaves no commit, tag, request or push. (CMX-177) -
The dashboard loads its first React island. The page now loads the built frontend
(named by the Vite manifest) before the legacy modules, and the sidebar foot shows the
dashboard build version, rendered by React from/api/version. Legacy code publishes the
agents list and the sidebar layout to a small shared store React reads, so components can
be ported one at a time.docs/FRONTEND.mddocuments the stack, islands, the store, the
dev loop (pnpm dev) and how the judge grades TypeScript. (CMX-178) -
chela's status line as an opt-in Mod. On Claude Code 2.1.301 or later,
mods/chela-hooks/draws a status line with the context bar, the window id and an
orchestrator or dispatched-run marker, through$.ui.status. After every turn it also
writes the context cache the dashboard reads, through the same
scripts/cache-statusline.sh, so loading the Mod needs nosettings.jsonedit.
chela install-statuslineis unchanged and stays the default. The Mod's event posting
now runs only whenCHELA_MOD_HOOKS_URLis set, so a chela pane that loads the Mod does
not file each event twice. (CMX-181) -
A "needs you" band in the opt-in Mod. With
mods/chela-hooks/loaded (Claude Code
2.1.301 or later), the orchestrator session shows one line above the prompt with the
pending override approvals, access requests and runs inneeds_human: counts plus the
oldest, each linking to the dashboard. It polls the new read-onlyGET /api/needs-you
every 10 s, shows nothing when the daemon is down, and hides a reading older than 30 s.
Approving stays in Telegram, the dashboard or a plain terminal. (CMX-183) -
Classifier denials reach the inbox straight from the opt-in Mod. With
mods/chela-hooks/loaded, aclassic.PermissionDeniedis POSTed to the new
POST /api/inbox/denial, which files CMX-155's orchestrator-inbox notice (tool, one-line
reason, session, window) without tailing the event log. Withplugin/loaded too, one
denial is still one notice: both copies are keyed on itstool_use_id. A dead daemon
never affects the tool call. Under--permission-mode dontAskno denial fires in either
layer.plugin/drops nothing yet (docs/MODS_SPIKE.md§8.0). (CMX-184) -
Settings > Telegram topics. "Telegram: delete a topic when its window closes" (off by
default) makes the bridge delete a dead window's topic instead of archiving it, falling
back to archive if the delete is refused. "Delete archived topics (N)" deletes every
remembered archived topic after a confirm, never a bound one or General. Both apply on
the bridge's next tick, no restart. (CMX-204) -
Risk tiers for the merge gate. A PR whose every changed file is prose (text under
docs/, achangelog.d/fragment, a top-level*.mdother thanWORKFLOW.md/CLAUDE.md/
AGENTS.md, or an image or video) gets no judge round. The dispatcher records the verdict
prose_onlyon its head, andchela mergeaccepts it once CI is green and GitHub says
MERGEABLE. A PR that touches an always-human path (WORKFLOW.md,.github/workflows/**,
plugin/**, the merge gate and release code, plus any workflow'smerge.always_human)
needs an operator-approved--overrideeven with a clean judge, and both the refusal and
the override question name the path. If the file list cannot be read, the PR is never
treated as prose. (CMX-208) -
chela dispatch --pause --allquiets the whole fleet. It holds the queue, then
presses Escape in each dispatched agent's window and tells it to start nothing until
"resume". An agent insidetask-finished,judge sweeporjudge self-checkgets the
message without the Escape, so it finishes that command first.--resumesends "resume"
to each agent it paused, and--hold-statuslists them. Judges already running still
finish. (CMX-214) -
chela ops scan: a read-only anomaly scan of the fleet. Six rules, each from a real
incident: a test run sitting at ≤1% CPU for 20 minutes, a process left behind under a
finished or orphaned run's worktree, a CI red that names no job or fired while checks still
ran, the same test failing the judge baseline of two tasks in a day, a judge whose survivors
move to a new file every round, and the same notice for one run three times in an hour. It
prints findings with their evidence (--jsonfor scripts). The daemon runs it every 30
minutes as a niced, detached child with a 10-second budget, skips it during a fleet pause,
appends findings to~/.chela/ops/findings.jsonl, and sends the orchestrator one inbox
notice only when a finding is new (or still open after 6 hours). (CMX-260) -
Known-flake quarantine.
tests/flaky.tomlregisters flaky tests, each with a tracker
issue and an expiry date. When a PR's CI is red only on registered tests, the dispatcher
re-runs the failed jobs up to 2 more times, after CMX-255's one re-run. If it is still red
only on those tests, the PR is not sent back for rework: the run goes to the judge, records
ci_known_flake, and the inbox notice names the issue. The flaky test still runs and
reports. The registry is read from the orchestrator's own checkout, so a PR cannot
quarantine its own red.chela mergestill needs green CI, so a known-flake PR is judged
but must be re-run green (or overridden) before it merges. A CI test fails when an entry is
expired, malformed, or its issue is Done, Canceled or missing.chela ops scan's
repeat_flakerule names a "quarantine candidate" when the same test flakes on 2 or more
PRs within 48 hours. (CMX-282, #797) -
The nightly mutation sweep's scheduler (1 of 3). Once a day (WORKFLOW.md
sweep.nightly_at, default02:00local;off,never,falseorsweep.enabled: false
turns it off) the daemon spawnschela judge nightly-sweep: one at a time, outside the
dispatch slots and in the judge's shared memory slice. It fetchesorigin, reads
origin/<workspace.base_branch>, and sweeps the lines that still blame to a STANDARD-tier
commit merged in the last 24 h (core, always-human and prose merges are not swept). After a
night that could not verify (or left mutations unrun), the next one catches up from the last
night that did, at most a week back. Each surviving guard is handed to a filer once; the filed keys live in
$CHELA_DIR/sweep-nightly/filed.jsonand survive restarts. Until the filing slice lands,
the filer files nothing and records what it would have filed in
$CHELA_DIR/sweep-nightly/would-file.jsonl. It is skipped while
chela dispatch --pause --allis active. Its budget issweep.budget_seconds/
sweep.max_mutations, else the judge's sweep budget. (CMX-303) -
The nightly mutation sweep groups its survivors (2 of 3). Survivors are grouped by
category — the mutation's shape plus the guard line's stripped text — so one weak guard
copied to five files is one group, and one line that survives two shapes is two. Each group
lists every same-shape site in the checkout, found with a fixed-stringgit grep -F(a
line holding.*[(orv[0]keeps all its sites). A group whose sites cannot be listed, or
whose listing misses one of its own survivors, is not filed, and the next night retries it.
A night with no survivor that left mutations past the budget now reportsunrun, and one
with a merged file no mutator handles reportsunknown. Neither reportsclean, and
chela judge nightly-sweepexits 2 for both. (CMX-304) -
The nightly mutation sweep files its survivors and tells the inbox (3 of 3). Each
survivor group becomes ONE tracker issue, labelledrisk:normalandsweep, listing every
mutation that survived and every same-shape site. A group is skipped only when every
surviving mutation in it was filed before; a new site of a filed category files the group
again. The filed keys are written after each issue, so a filing that fails part-way keeps
the ones before it. A tracker that cannot create issues records what it would have filed in
$CHELA_DIR/sweep-nightly/would-file.jsonlinstead. Each run queues ONE inbox notice: the
issues filed (or "nothing filed"), already filed, not filed and why, clean, or skipped and
why, with the run's UNRUN and UNKNOWN counts. (CMX-305) -
Linear's
create_issuetakes label names. They are added to a parent's inherited labels,
never in place of them. A missing label is created on the team, and is used only when Linear
answerssuccess: true. A label that can be neither found nor created is a warning, and the
issue is still created. (CMX-305) -
Standing agent rules. A workflow can name a short rules file with
agent.standing_rules: <path relative to the repo>, and the prompt body renders it
wherever{{standing_rules}}appears; the built-in rework prompt carries it too. A missing or
unreadable file renders as nothing and logs a warning instead of failing the dispatch.
chelamux seedsdocs/AGENT_STANDING_RULES.mdwith the rules the orchestrator used to
repeat to each agent by hand. (CMX-317) -
chela park <run> --reason "…" [--until <time|run:ID>]andchela unpark <run>. An
agent the orchestrator holds on purpose no longer draws "looks STUCK" notices: the stuck
detector skips a parked run and sends ONE notice if the park outlives its--untilor
CHELA_PARK_MAX_S(default 6h). A run waiting for a suite slot counts as parked
automatically ("waiting for a suite slot (holder: …)"). Parks show inchela dispatch-runs
and on the Work card's flags line and the desktop Rows table's state column ("parked · ·
" or "waiting · …", dim, never orange: the hold is on purpose). An agent cannot
park itself: the command refuses inside the run's own worktree and from any Claude Code
session that is not the orchestrator. (CMX-318) -
A Linear workflow can be scoped to one project of a team (
tracker.project). Every
read and write it makes (the open set, the ID refresh, state writes, New task, size-gate
splits) is limited to that project, and issues it creates go into it, so several repos
can share one Linear team. Two dispatched workflows on one team must each set a
differentproject:, or both refuse to claim work andchela doctorsays why. The
archive sweep stays team-wide, runs once per team, and keeps the largestkeep_done
set on the team. (CMX-326) -
scripts/check_fresh_install.shruns the README install block verbatim in a clean
Ubuntu container (docker or podman, as your own uid, a stubclaude), times it, and
checks the dashboard answers andchela doctorpasses. The test suite runs it wherever
docker or podman answers; without one it exits 77 and the test reports SKIPPED. (CMX-92) -
A one-minute demo of the merge gate. An agent opens a PR, the judge corrupts its guard
and the PR's test stays green,chela mergerefuses, the agent adds a boundary test, the
judge comes back clean and the merge goes through. The alternative ending is one Telegram
tap on an override. The clip runs chela's real judge, merge gate and Telegram card against
a throwaway repo, with only GitHub faked. It is in the README and on the landing page, and
scripts/demo/record-gate.shre-records it. (CMX-93)
Changed
-
The phone's session pills follow the sidebar's order: group order, Move up / Move
down, custom groups and each group's sort, shared by every device. A session the sidebar
collapses or filters out keeps its pill at its group's position, and one the sidebar has
not drawn yet goes at the end. The header swipe steps in the same order. (CMX-165) -
The frontend's Vitest suite runs 4 workers, not one per core minus one. On a 20-core
box that default was 19 jsdom workers and 4.3 GB, most of a full test run's peak. (CMX-119) -
The task modal's review timeline moved to the wide column. The main column now has
Brief | Review (N) tabs, and the modal opens on Review while a verdict is waiting to be
read. Each review round is a collapsible card, newest first and open. A collapsed round
shows its verdict, its visible and held-out survivor counts, and its time. The sidebar
keeps the details and adds a round list that jumps to a round. On a phone the sidebar
stacks above the tabs, so the modal has only one scroll area. On a Linear workflow, the
task key in the header links to the Linear issue. (CMX-122, #721) -
The judge Action is pinned to a release tag, not
main. The README now uses
Devail1/chelamux@v0and says to pin@v0.17.0or a commit SHA for a reproducible job.
chela release X.Y.Z, after the approved push ofmainandvX.Y.Z, force-moves the
major tag (v0for 0.y.z,vXin general) to the release commit under the same approval.
It is the release flow's only forced push and touches only that tag. If the move fails, the
release stays pushed and chela reports it in the log, event log, notification and inbox.
chela release X.Y.Z --major-tag-onlypoints the major tag at an existing release (used
once to createv0on 0.17.0). (CMX-158) -
The launch pages read as written, not generated. Every em dash (and spaced en dash)
in the landing page, the docs page, the README,action.yml, every skill, the five docs
the landing links (Getting started, Config, Hooks, Trust and approval posture, Risk
levels) and the dashboard's visible copy was rewritten sentence by sentence, as a
period, colon, comma or parentheses.tests/test_no_em_dashes.pygoes red if one comes
back. (CMX-160) -
orchestrateasks for blocking edges. Every task an orchestrator files declares its
blockers as native tracker links (Linear blocked by, ordepends:inTODO.md), and a
multi-slice task is filed as one chained issue per slice under a tracking parent. Adapted
fromto-ticketsinmattpocock/skills. (CMX-187) -
chela task-finishedrefuses a branch that is behind or conflicts withorigin/<base>.
It fetches the base first, and before the size gate, self-check and sweep it refuses a
branch that would not merge cleanly (listing the conflicting files) or that is behind
(with the exactgit fetch/git mergecommands; no rebase).--allow-behindlets a
deliberate behind-but-clean hand-in through, recorded on the run and shown to the judge.
A remote that cannot be reached fails closed. (CMX-189) -
The rework cap counts only review rounds. A judge block or a reviewer's changes
requested still spends a rework round. A CI send-back, or a rework agent that died without
handing in (window gone, stalled, launch failed), no longer does: those are counted as
infra_restarts, which has its own cap (max_infra_restarts/
CHELA_MAX_INFRA_RESTARTS, default 8). Reaching that cap escalates with a reason that
names infra instead of "the PR still fails review". Achela retrydoesn't spend
either counter. (CMX-192) -
Telegram auto-topics reuse a folder's old topic instead of creating a new one. chela
now remembers every topic it creates ($CHELA_DIR/telegram-topics.json: thread, window,
name, cwd, created and closed time). A new agent window first reopens a closed topic made
for the same cwd, renaming it only if the name changed, and creates one only when there
is none (or the reopen fails, which drops the stale entry). Two live windows in one folder
still get two topics, and both come back next time. (CMX-204) -
chela dispatch --pause --allnow freezes running judges too. Each judge's process
tree gets SIGSTOP, and--resumesends SIGCONT. Time spent frozen does not count against
the judge's suite deadline or the daemon watchdog's wall. The watchdog leaves a frozen
judge alone, and after a thaw its progress clock starts again from the thaw. A reaped judge
tree that was stopped also gets SIGCONT, so it can act on its SIGTERM.--hold-status
lists each suspended judge. (CMX-214 slice 3, CMX-251, #775) -
The dashboard's JS suites (
tests/*.test.mjs) run under Vitest instead of
node --test, through the rootvitest.config.mjs(one process per file, as before), so a
dashboard module can later import a ported TypeScript one. pytest still runs each file as
its own item with the same fences: a missing Vitest is a loud skip, or a failure under
CHELA_REQUIRE_JS_TESTS=1, and a Vitest run that finds no suite is red. The collab and
browser suites stay onnode --test. (CMX-216) -
The dashboard's data layer is TypeScript (CMX-212 P03). The refresh loop, the one
Server-Sent Events stream, the HTTP client and the page state moved frommain.js,
sse.jsandutil.jstofrontend/src/shared/(scheduler,events,api,store).
Each feature registers its own polls and event handlers, the page reads its config from one
JSON block instead of three inline globals, and legacy imports are extensionless. With
CHELA_TERMINALS_ENABLED=falsethe page now really runs without terminals: it used to read
the missing global as "on". (CMX-217) -
The dashboard's chrome is React now: the approvals and access-request pills and sheets,
the agent-status warning, the CPU/RAM/Disk strip, the run toasts, the "chela was updated"
banner, the New session menu (with its favorites picker), the gear menu with its usage
readouts, the phone "+", and the agent detail with its send-message dialog. They look and
behave as before, with three differences: the menus and sheets are real keyboard menus and
dialogs (focus moves in, Escape closes, focus returns), their rows are buttons you can tab
to, and the icons are Lucide icons instead of emoji. (CMX-219, part of CMX-212) -
Settings, the ⌘K palette and the keyboard shortcuts sheet are React now: every Settings
tab (General, Timing, Dispatch, Notifications, Cost, Appearance, Collaboration) and its
search, the palette (also driven from the sidebar's "Jump to session" box) and the shortcuts
sheet. They work as before, with these differences: Settings and the palette are real
dialogs (focus moves in and returns, Escape closes the top one); the tab rail is buttons you
reach with Tab and move along with the arrow keys; the selects and switches are the kit's
(keyboard menus and switches); a narrow Settings stacks each label above its control; and
clicking the Cost tab's view or window you are already on refreshes it. (CMX-221, part of
CMX-212) -
The Decisions inbox and the task-detail modal are React (CMX-212 P07-P09). The inbox
button, its owner dot and unread badge, the log dialog (owner chip, re-register, search,
click-through) and the task modal (Brief and Review tabs, rounds, details) run on the
dashboard's UI kit: real dialogs with focus trapping, and keyboard tabs. The re-register
picker and button are the kit's Select and Button. (CMX-228) -
The owner chip in the Decisions inbox has its border and padding back. A comment in
the stylesheet closed early and swallowed the chip's own rule. (CMX-228) -
Work's Runs and Schedules segments, New task and Init a repo are React now. They look and
behave as before, with three differences: the dialogs are real keyboard dialogs (focus moves
in, Escape closes, focus returns to the button that opened them), the form fields line up in
one column, and the icons are Lucide icons instead of symbols. (CMX-231, part of CMX-212) -
The Work view is React now: the board, its cards and actions, the project filter, the phone
layout and the toolbar. It looks and behaves as before, with three differences: a board
column keeps a normal width (about 400px at most), so one visible column no longer stretches
across the screen and many columns scroll sideways instead of squeezing; the project pills
are a multi-select (pick any set of projects; All clears it, and the choice is remembered);
and the card chips use Lucide icons instead of symbols. (CMX-233, part of CMX-212) -
The Wall's tile model, wire geometry and window-id parsing are TypeScript (CMX-212 P22).
wallmodel,wireandwindowidare converted in place understatic/js/, typechecked
strictly with the frontend. A room badge on a Wall tile now shows a plug icon instead of the
🔌 emoji. (CMX-236) -
A plain shell pane says it is a shell. A shell opened from the dashboard showed its tmux
window name (liavedunix-2) and anidlechip, so it looked like an idle Claude session.
Until Claude starts in it, its pane header now readsShell · <cwd>with ashellchip;
onceclauderuns there, the header shows the session's name and state as before. The tmux
window is never renamed (its name feeds the Telegram topic and--remote-control).
/api/agentscarries the pane's cwd asshell_cwd(home written~) while no Claude runs
in it. (CMX-241) -
The Wall's pane header is React (CMX-212 P26): the title and rename, the state pill and
status dot, the web chip and UNSANDBOXED banner, the ⋯ menu (Wire, Share, Orchestrator, Pin,
now keyboard-reachable), minimize, maximize, the phone's fill-screen button and kill with its
confirm. Its share-mode marks are icons instead of emoji. (CMX-241) -
The pane footer shows in every pane mode. A pane filling the phone's screen hid its
footer; it now keeps the window id, model, branch and context % there too, as on a wall
tile and a maximized pane. (CMX-242) -
The focus ring no longer hides the context bar. The thin context-% strip along a pane's
bottom edge sat under the focused pane's highlight, so it vanished on exactly the pane you
were looking at. It is drawn inside the ring now. (CMX-242) -
The Minimized tray is one row. It used to wrap to a second and third row as panes were
minimized, taking height from every pane on the wall. Its chips now scroll sideways (a
vertical mouse wheel scrolls them too), a fade marks the edge that hides more, and the label
counts them:Minimized (5). (CMX-242) -
The rest of the Wall's chrome is React (CMX-212 P27, P28, P30, P32): the pane footer
(the window id that copies itself, model, PR, cost, branch, context % and the Files chip),
the Answer / Approve / Review bar and the recap line; the Minimized tray and the lazy dock for
dispatched workers; the layout toolbar (presets, lock, auto-arrange, Focus); and the wire
that puts two agents in a room. The footer's branch and PR flag, the action bar's marks and
the wire's toast are icons or plain text instead of emoji. (CMX-242) -
The dashboard page is one React root.
templates/index.htmlis now its<head>, the
pre-paint sidebar restore, the boot block and<div id="root">; the app shell
(frontend/src/features/shell) renders the sidebar, the canvas, the phone controls, the
safety float and the overlays, with the same classes and ids, so the look does not change.
The view switch, the view registry and the tab title and favicon badge moved to TypeScript
with it, and the last plain-JS modules (nav.js,views.js,viewreg.js,util.js,
icons.js) are gone.style.cssis the build entry's stylesheet, linked from the manifest.
window.chelaholds only what a terminal pane calls,openPaletteandtoggleSettings.
(CMX-248, CMX-212 P34) -
A red CI re-runs its failed jobs once before sending a PR back. On the first red on a
head commit the dispatcher runsgh run rerun <id> --failedand spends no rework round. If
the re-run goes green, the flaky test is recorded on the run (runs.ci_flakes) and the
orchestrator gets one "CI flake, re-run green" notice. If it goes red again, the PR is sent
back for rework as before, and the verdict says the re-run failed too. At most one re-run
per head sha; a new push resets it. A re-run GitHub will not start falls straight through
to the rework. (CMX-255) -
The judge no longer blocks on survivors in tool config that CI enforces. A mutation
that survives ineslint.config.*,.dependency-cruiser.*,tsconfig*.json,
vite.config.*,vitest.config.*,package.jsonscriptsor.github/workflows/*is
listed in the verdict as advisory. It still blocks when a test the PR changes names that
file, because a guard the PR claims must be real. Code experiments now run before
tool-config ones in the judge and inchela judge self-check, so a budget runs out on
config first.judge.tool_configin WORKFLOW.md replaces the list, and[]turns the
policy off. (CMX-259) -
A dispatched PR that goes CONFLICTING no longer just sits. Once GitHub has said
CONFLICTING on two ticks in a row, the dispatcher mergesorigin/<base>into the PR's branch
in a scratch worktree (never the run's own). A clean merge runs the targeted tests (the PR's
own test files plus the tests importing the files it touches); if they are green, the merge
commit is pushed and noted on the PR ("base merged automatically"), and the judge grades the
new head as usual. A real conflict, or a merge whose targeted tests go red, goes back to the
agent as a rework that does not count against the rework cap, with the conflicting files
listed. A textual conflict is never resolved automatically. Once per head sha. (CMX-262) -
The CONFLICTING-PR auto-merge (CMX-262) is now wired into the dispatcher. At most three
conflict reworks per run are free; past that a conflict round is charged against the rework
cap like any other, so a run that cannot resolve its conflict still reaches a human. (CMX-264) -
The sidebar's session list is React now, with title-first rows. Each row gives its title
the width; the metadata beside it is icons with tooltips (a crown for the orchestrator, a
pull-request icon and number, a flask and3/6while a judge tests, the context % from
50%). The row no longer spells out its state: the dot's shape carries it, and its tooltip
and screen-reader name say it. The window id shows on hover. The Dispatched group is always
the last group. (CMX-276, part of CMX-172) -
One control for archived sessions. When anything is archived, the sidebar shows a
collapsedArchived · Ngroup at the bottom; its open or closed state is remembered on each
device like any other group. The "Show archived" / "Hide archived" link under the list is
gone: View ▸ Status ▸ Active hides the group entirely, and All (now the default) shows it. A
view saved before this release keeps the Status it saved: pick View ▸ Status ▸ All to see the
group. (CMX-277, part of CMX-172) -
The sidebar's group, row and View menus are keyboard menus now. Each
⋯and the View
button is a real button: Enter or the arrow keys open its menu, the arrows move, Enter picks,
Escape closes. A right-click on a group header or a row still opens its menu. The View menu's
custom groups page listsRename <group>andDelete <group>as items of their own. -
The sidebar is resizable. Drag its right edge (or focus the edge and use the arrow keys,
Home and End) to widen or narrow it; a double-click restores the default. The width is saved
with the sidebar layout, so it follows you to your other devices. The default is a little
wider than before (288px). The phone drawer is unchanged. (CMX-278, part of CMX-172) -
Hover over a session row, or press and hold it on a phone, to read its whole title.
-
Recent sessions never lists a session that is running. A session resumed after a restart
is matched by its session id, so it no longer shows as closed under its old window address.
A judge's session never offers Resume: it sits under "Show hidden dispatcher sessions", since
chela owns its lifecycle. -
The rest of the sidebar (the Wall and Work rows, Recent sessions, the collapse control and
Alt+1..9) now runs on the same React code as the session list. Nothing else changes. -
The sweep and the judge no longer mutate generated files. One shared list
(chela/generated.py: the committedchela/dashboard/static/app/**bundle,uv.lock,
chela/dashboard/frontend/pnpm-lock.yaml,**/*.min.js) is excluded fromchela judge sweep's mutation candidates and from the judge's experiments, and the hand-in size gate
never counts those files. Each round says how many it skipped. A React port slice used to
spend almost all of its sweep budget on minified JS that no test can guard; CI's
"committed build is fresh" step is that bundle's guard. (CMX-284) -
A
standardrisk tier: product and UI PRs get a judge round with no held-out experiments.
chela/risktier.pynow sorts a judged PR intocore(it touches the dispatcher, the judge,
the workflow loader, the sources orchela/context.py; a workflow adds paths with
merge.core) orstandard(everything else). A standard run asks the judge for no held-out
experiments and runs any it tags anyway as visible; a core run keeps today's quota. The tier
is stamped on the run and shown bychela judge show. Prose and always-human are unchanged.
(CMX-288) -
A clean verdict survives a merge from the base branch. When a judged PR's head moves
and the move touches none of the PR's own files, the dispatcher keeps the clean verdict,
re-stamps it on the new head with the reasoncarried: base-only merge, and spawns no
judge;chela mergeaccepts it. When a PR file did change, the re-judge re-runs the
previous round's experiments first and the judge is asked for new ones only on the lines
changed since, instead of drawing a fresh sample of lines already judged. (CMX-289) -
A
standard-tier PR gets no judge round. A PR that touches no core path (the
dispatcher, the judge, the workflow loader, the sources,chela/context.py) and no
always-human path is stampedstandard_no_judgeand is merge-ready on green CI +
MERGEABLE, likeprose_only.chela mergeaccepts it only while its live diff is still
standard. Itschela task-finishedrunshand_in.lint_cmdand one full suite, with no
self-check battery and no sweep. Core PRs are unchanged, and the core set grows to cover
the rest of the merge and containment machinery (automerge,overrides,gateanswer,
reviewgate,sizegate,basesync,sweep*,toolconfig,sandbox*,procguard)
and anyCLAUDE.md/AGENTS.md, since those no longer meet a judge as standard. A workflow keeps the judge on
every PR withmerge: {core: ["*"]}. (CMX-296) -
The phone pane looks like the Claude mobile app. The pane is full-bleed (no margin,
rounded border or focus outline) and fills the screen from the top to the key bar. Its header
is the one title bar: the drawer's hamburger on the left, the session name over its subtitle,
and the status chip and ⋮ on the right. Close moved into the ⋮ menu, maximize and the
fill-screen toggle are gone (the pane is always full), and the floating "+" is removed: New
session is in the sidebar drawer. Desktop is unchanged. (CMX-313) -
The Work view on a phone has one title bar. The six rows of controls above the board
(coloured buttons, project pills, a layout toggle, column chips) are gone. The bar holds the
drawer's hamburger, the current project as the title with its running and queued counts, New
task and a ⋯ menu. Tapping the title opens a project switcher sheet with each project's counts
and an "N needs you" mark. ⋯ holds the layout, Init a repo, Pause dispatch and Pause all agents,
which now asks to confirm. Rows (one list grouped by status) is the phone's default; the Board
layout swipes one column per screen under underlined status tabs. Cards are compact: the
title, one id/PR/state line and one flags line, with the branch, delete and Close & requeue
moved into the card's ⋮ menu. Desktop is unchanged, except that a queued task's card now
shows its risk label there too. (CMX-315) -
The Work view on desktop has one header row. The two rows above the board (the tabs
with four coloured buttons, then the project pills) are one: the current project as a
dropdown (search, All projects, one row per project with its running and queued counts, and
"dispatch on" or "paused" under the title), Board | Runs | Schedules, Pause dispatch as a quiet
outline that reads Resume while held, New task as the one primary button, and a ⋯ menu with
Init a repo, Pause all agents (which now asks to confirm), the Board | Rows layout and the
board's column options. On the board, Todo, In Progress and In Review are fixed 320px columns;
Backlog and Done (and Archived) collapse to narrow rails at the edges and open on a click,
and the board remembers which ones you left open for each project. The "Hidden columns"
panel is gone: hide a column from ⋯ › Column options. Cards are the phone's compact card:
the branch, merge, delete and Close & requeue live in the card's ⋮. Rows on desktop is a
table (ID, title, state, PR, CI, risk, age) grouped by status, sortable by any column head and
walkable with the arrow keys. (CMX-316) -
Hook events store a preview, not the tool's body. Each
tool_input/
tool_responsevalue keeps 200 characters, andclipped_bytesrecords the original
sizes, so a 1 MB Write is a ~1 KB line instead of a ~2.5 KB one. AnAskUserQuestion/
ExitPlanModeinput is still kept whole for the Telegram gate. (CMX-328) -
chela task-finishedno longer re-runs its whole battery when the base moved during it.
Whenorigin/<base>moved while the self-check and sweep ran and merges cleanly, the hand-in
merges it in, runs only the tests that observe the files the new base commits touched, and
re-runs the self-check experiments only on PR files those commits also touched. A red test,
a surviving experiment or a git error still refuses. The judge is told the base was merged
mid-check. (CMX-334) -
One-command install. The README and docs/GETTING_STARTED.md now install with
uv tool install "chelamux[dashboard] @ git+https://github.com/Devail1/chelamux", then
start tmux, the daemon and the wall: four commands, with the wall up about 15s after the
first on a clean Ubuntu box.gh, Telegram, Linear and pm2 are follow-ups, not
prerequisites. (CMX-92) -
chela doctorseparates required from optional checks. Telegram and Linear read
"not configured (optional)" when absent, an error from either no longer fails the run,
and a clean run ends withrequired checks: all pass. (CMX-92)
Removed
-
The generic planning skills are retired.
blindspot-pass,interview-meand
implementation-planwere adaptations of widely used skills, so chela no longer ships
them. Usegrill-me,to-specandwayfinderfrom Matt Pocock'smattpocock/skills
instead; docs/GETTING_STARTED.md now has a "Recommended skills" section. chela keeps
chela-setup,orchestrate,handoff,telegram-setupandtelegram-send. (CMX-187) -
Terminal reply timestamps are retired. Claude Code's own turn footer now prints the
completion time on every reply ("✻ Brewed for 7s · done 8:55 AM"), so chela no longer
stamps**[HH:MM]**into replies.CHELA_TERMINAL_TIMESTAMPSis gone and is ignored if
set, and the plugin no longer registers aMessageDisplayhook (plugin 0.2.12). The
unreleased timestamp band in the opt-in Mod (mods/chela-hooks, CMX-182) is removed with
it; the Mod's status line is unchanged. 0.x carries no compatibility promise. (CMX-266)
Fixed
-
Terminals reconnect by themselves. A wall tile whose socket dropped (iOS suspending a
background tab or a locked screen, a wifi/4G switch) no longer stops at ttyd's "Press ⏎ to
Reconnect". While visible it retries after 0.5 s, 1 s, 2 s, 5 s, then every 10 s; while
hidden it waits until the page is visible again or the network returns. After repeated
failures a quiet "Reconnecting…" line shows, and Enter still retries at once. A frame chela
suspended on purpose stays suspended. (CMX-167) -
A Telegram photo is only reported "sent" once the agent actually submitted it. The
bridge now re-reads the agent's prompt after delivering a photo and presses Enter once more
if the[Image #N]chip is still sitting there. If the prompt never clears, the topic gets
"⚠️ Image delivered but not submitted" instead of "📎 Image sent". A text, photo or file
that arrives while the prompt still holds an unsubmitted message is refused with a notice
instead of being merged into it, and a turn that carries the inbound📎 image:marker
anywhere is no longer echoed back to the topic. (CMX-117) -
Human sessions come back even when tmux restarts twice in a row. The boot restore pass
used to look only at the epoch that just ended. If tmux died again before the roster had
sampled the relaunched windows, that epoch was empty and the orchestrator stayed dead (the
inbox sat held until someone relaunched it by hand). The pass now walks back to the newest
ended epoch that recorded any windows, and no further. It also carries the sessions it
resumed itself inrestore-boot.jsonuntil a later epoch samples them live, and never
relaunches a session that is already live. The inbox notice says "tmux restarted twice;
recovered from epoch X" when it used an older epoch. (CMX-152) -
Phone: the terminal fits its tile on first view and after a tile switch. With the keyboard
open, iPhone Safari shrank the pane but the terminal inside kept its taller row count, so a
multi-line prompt showed only its first lines and Claude's status line was clipped below the
tile footer until you scrolled. The dashboard now re-fits the pane's terminal itself after
every phone fit and whenever a pane's terminal finishes loading. Desktop is unchanged. (CMX-154) -
A killed
chela judge self-checkorjudge runno longer leaves a mutation in your
files. The judge's apply → measure → restore loop now uses the sweep's signal handling
and kill guardian. SIGTERM, SIGHUP and SIGINT restore the file before the process exits,
and after a SIGKILL a guardian process restores it from the recovery marker. (CMX-162) -
The merge gate also denies GitHub MCP merge tools, not only
Bash. A session with the
GitHub MCP (the official GitHub plugin, for one) could callmerge_pull_requeston a
dispatched PR and skip the judge, because thePreToolUsegate was matched toBash
only. A newmcp__.*entry runs the same script, which deniesmerge_pull_request, and
push_files/create_or_update_file/delete_fileto a protected branch, in a chela
repo named by the call's ownowner/repo. Every other MCP tool is allowed. Plugin
0.2.10: re-install it so existing sessions pick up the new hook. (CMX-180) -
A dispatched run can no longer launch another Claude Code agent. Agents and judges run
unsandboxed, and a dispatched agent routed around a classifier denial by relaunching
claudein another permission mode on a private tmux server. The dispatcher now exports
CHELA_RUN_ID/CHELA_RUN_ROLEinto every pane it spawns, and the plugin'sBashhook
denies aclaudelaunch there, however wrapped (tmux new-session "…",env,nohup,
timeout,bash -c,npx @anthropic-ai/claude-code, …).claude --version,
claude plugin test|validateandclaude mcp liststay allowed; human sessions and the
orchestrator are unaffected. Plugin 0.2.11. (CMX-190) -
The judge wastes fewer rounds on "cannot verify". A baseline that is red only because of
named test failures now re-runs just those tests once. If they all pass, the judge records
them as flakes (baseline_flakesin the private round record) and the round continues. A
test that fails both times is still red. A suite killed at its timeout now saysTIMEOUT
with the elapsed time and host load, instead of "NOT GREEN", and no longer spends a second
full run on the same budget. A round where the judge proposed no experiments is parked for a
human once instead of being retried with the same inputs. A verdict that arrives after its
run was already merged or closed is still recorded, but no longer reaches the inbox as an
action item.test_zz_no_pm2_daemon_survives_this_files_testsno longer counts another
concurrent suite's live test daemon as its own leak. (CMX-195) -
An override approval survives GitHub's
UNKNOWNmergeability. Approving an override
right after the base branch moved used to fail onmergeable: UNKNOWN(GitHub computes it
lazily) and burn the approval. The merge now polls for up to ~30s; if GitHub still has not
decided, the request staysapprovedfor the same head and the daemon retries it at most
once a minute with no new human approval (a new push still expires it).CONFLICTING
still fails as before. A PR that is already merged answers "already merged as "
instead of announcing a failure.chela override listshows approved requests. (CMX-205) -
Three tests that failed only under load now pass every time. Two judge tests could
resolve their judge worktree to the operator's real~/.chela/worktrees/default, so they
failed whenever another suite running at the same time held the judge lock there. Five
more_spawn_judgetests had the same hazard and are fixed too. The peer-transport doctor
test bound a socket whose path ran past the 108-byte AF_UNIX limit on xdist worker gw10
and above. (CMX-257) -
A daemon restart during a judge launch no longer costs a judge round. On SIGINT/SIGTERM
the daemon finishes the launch already in progress and launches nothing new: no judge, no
rework, no claim. A launch interrupted anyway (a SIGKILL after PM2'skill_timeout, or a
launch whose hook the restart's signal killed) no longer reads as "the judge's window
disappeared" CANNOT VERIFY. The next daemon relaunches it as the same round, and no retry
is spent. The run row records who is launching its judge (judge_launch_owner) until the
launch is through.examples/ecosystem.config.jsnow giveschela-daemon
kill_timeout: 90000andtreekill: false. Copy both into your own ecosystem file, then
pm2 delete chela-daemon && pm2 start <ecosystem> --only chela-daemon(a plain restart
keeps the old kill settings). (CMX-263) -
A prose-only PR no longer waits for a judge slot. The prose-only check used to run
only once a judge slot was free, so a docs-only PR could sit "queued for the judge" behind
running judges andchela mergerefused it with judge "never ran". The trigger now
classifies the PR's head commit before the slot gate, and a prose-only PR gets
prose_onlyon the next tick however many judges are busy. (CMX-265) -
A prose-only PR's inbox notice no longer says "merged". It fired when the verdict was
stamped, while the PR was still open, so a pending PR read as done. It now says the PR is
merge-ready on green CI with no judge round needed, and names thechela mergecommand that
lands it. (CMX-272) -
A dispatched agent's leftover test runs no longer burn cores for hours. When an agent
hands in (chela task-finished), is closed, escalated, merged or respawned for a rework,
chela now stops every process still running in its worktree, except the agent's own
Claude Code and its shell: SIGCONT, then SIGTERM, then SIGKILL after a 5 s grace, each pid
and its command line logged.chela ops scanalso reports a test run under a running
agent that is older than the suite timeout, and the agent brief runs the suite under
timeout. (CMX-274) -
A crash restore no longer brings back a session you closed. A session the restore had
resumed once, and that you then closed, was revived by the next crash. chela now records a
window that disappears while tmux is still running as closed by you, with the time, and
neither the automatic restore after a reboot norchela restore --resumerelaunches it.
chela restorelists those sessions as skipped. A window that vanishes in tmux's last
minute is treated as the crash tearing it down, so it is still restored. (CMX-275) -
chela ops scanno longer reports a live test's own tmux server or helper script as a
leaked process. A tmux server always re-parents to pid 1, and so does a script a test
detaches. A tmux server on a-L chelatest-<pid>-…socket now counts as owned while that
test pid is alive, and any other orphan whose working directory is a worktree counts as
owned while a test run of that worktree is alive. Either is still reported once its owner
is gone, or once it is older than the suite timeout. An orphaned test run is still
reported as before. (CMX-279) -
Tapping a session in the phone sidebar opens it again. Since the sidebar moved to React,
a tap on a session row on a phone showed the row's tooltip ("window: …") and the session
never opened. The dashboard's touch tooltip now leaves anything clickable alone, so the tap
opens that session full-screen and closes the drawer. (CMX-301) -
Hebrew reads right to left in the wall's terminal panes again, in Claude's replies and
in the prompt box. ttyd now starts xterm.js on its DOM renderer
(--client-option rendererType=dom); the WebGL renderer it used by default paints every
cell left to right and has no bidi, so Hebrew came out letter-reversed. Running ttyds keep
their old options:pm2 restart chela-agent-terminalsrespawns them.
CHELA_TERM_RENDERER=webglrestores the faster renderer for a wall that shows no RTL text.
(CMX-302) -
On a phone, every dialog, sheet and the ⌘K palette opens on top and closes by touch.
Settings opened from the sidebar drawer's gear came up behind the still-open drawer, and the
palette could not be closed. Now opening any of them shuts the drawer first, they all draw
above it, each has a close button (a 44px target on a touch screen; the palette gained one)
and a tap on the backdrop closes it. (CMX-306) -
A run whose task leaves the tracker while its PR is still open is no longer marked done.
Linear's GitHub integration closes an issue when any merged PR names it, so a task could
leave the tracker while its own PR sat open; the dispatcher read that as done, killed the
window, and nothing could bring the run back. Now the run keeps its state, its card shows
"issue left the tracker's ready states; PR still open", and it finishes only when the PR
merges or closes.chela reopenalso accepts adonerun on a linear tracker whose PR is
still open: there is no struck line to check there, so the PR state decides. (CMX-308) -
Menu item icons are back to the label's size. The icons in the gear menu, the pane
menu and every other dashboard menu drew at 24px, too big next to their labels. They now
draw at 14px, as they did before the React port. (CMX-309) -
The Settings modal no longer resizes or jumps when you switch tabs or search. It has a
fixed height (up to 640px on a desktop, 92% of the screen on a phone) and the tab's
content scrolls inside it, so the header, tab rail and footer stay put. The "No settings
match" message now shows in the panel instead of pushing the tabs down. (CMX-310) -
A PR waiting on CI no longer gets a "no judge round has started" notice. No judge starts
before CI is green, and a standard or prose PR never gets a judge at all, yet a run that sat
20 minutes on a slow or re-running CI job was told "NO judge round has started … is the
queue held or the daemon down? Or review it yourself". The review-wait notice now names CI
as the wait (still running, re-running a flake, red, or unreadable). A standard or prose PR
is never "queued for the judge", and if one is green with no verdict the notice asks about
its no-judge verdict, not a judge. (CMX-312) -
test_a_detached_judge_survives_the_daemon_being_killedno longer flakes in CI. It
waited for the pid file to exist, butwrite_textcreates the file empty before it writes,
so on a slow runner the kill landed in that gap and the test read/proc//stat
(=/proc/stat) and failed withIndexError. The pid is now published by rename. The
other process tests intests/test_ticklock.pynow wait on what they can observe instead
of fixed gaps and tight deadlines: the stand-in daemon ends its tick when the test says so,
and a "survives the kill" check samples the process for a second instead of looking once.
(CMX-314) -
A pane's terminal no longer jumps in font and size while it opens. ttyd painted at its
own default font, then at the server's, then at your Settings > Terminal size (after a
one-step size flicker). The terminal now stays hidden until your font and size are set and
rendered, and shows within 1.5 s even if a font never loads. Your size and face are also
handed to ttyd's own preferences, so a reconnect no longer drops back to the server
default. (CMX-321) -
A wall pane opens much faster, most of all on a phone. Every pane used to download
ttyd's ~720 kB client uncompressed, revalidate every font, and wait for a 2.3 MB icon
font before applying the terminal font. ttyd's client is now served once, gzipped and
cached for good; the terminal fonts are woff2 at content-hashed,immutableURLs; the
icon font is split into small blocks a pane only downloads when it shows one of their
glyphs; and the font apply no longer waits for icons. On a throttled phone profile, a
pane with a warm cache shows its terminal in about 0.75 s (was 1.35 s), and a cold pane
has its fonts applied in 1.5 s (was 5 s). (CMX-322) -
The Decisions dialog keeps its history when agents are busy. Every non-hook event
(merges, send-backs, judge verdicts, stuck notices) is also written to a small
events-core.jsonl, and an event-log read that asks only for non-hook types is served
from it. Before, the hook firehose rolled the 8 MB log every ~7 h and the dialog said
"No decisions logged yet" after a day full of them.chela events --type …and the ops
scan read the same core log. On the first daemon start, the core log is seeded from the
rolled files. (CMX-328) -
A wall pane's title bar shows the move cursor only when you can drag it. In Focus and
Auto the wall lays the tiles out itself and dragging is off, yet the header still showed the
four-arrow move cursor. It now shows only in the free/grid layout; in Focus a strip pane's
header shows a pointer (a click brings it forward), and elsewhere the default cursor.
(CMX-329) -
A crash restore no longer brings back sessions from an older tmux epoch. After a
restore, a later crash could resume the epoch restored two restarts ago next to the one
that replaced it, so a stale orchestrator and a stale agent woke up with a days-old plan.
The boot pass now retires a carried session once the newer epoch's roster shows its window,
and resumes at most one session per window lineage (name and cwd), taking the newest
epoch's. The roster also records the window's LIVE session (hooks or Claude Code's own
registry), so a session that a/clearmoved on comes back under its new id, not the
--resumeid it was launched with. (CMX-337) -
A re-judge re-runs at most a cap's worth of the previous round's experiments. A round
now runs at most twice the risk-level cap: up to the cap of new experiments, plus up to the
cap re-run from the previous round, its survivors first. Before this, from the third round
the previous round's re-runs could fill the whole grown cap and leave the judge's new
experiments unrun. (CMX-338) -
chela dispatch --pause --allno longer freezes an unrelated agent. A run that had
merged while its judge ran keptjudge_state=runningand a window id from before a crash.
tmux display-message -tnever fails on a missing window: it answers with the current one.
So the pause resolved that id to another agent's claude and SIGSTOPped its whole tree,
running suite included, until the resume. Three changes: the window-to-pid lookups
(claude_pid,is_claude_running) now check that the window tmux answered with is the
one asked for. The freeze skips a run that is done, closed or failed, and signals a
judge window's claude only when the window's tmux epoch is the current one and the
claude's cwd is that run's judge worktree. The daemon now cancels a finished run's dead
judge, and every judge cancel clearsjudge_window_id. A judge still running on a
merged PR is left to finish, so it can still record a blocked-race verdict. (CMX-340)
Security
-
The Telegram bridge accepts updates only from allow-listed senders. Set
CHELA_TELEGRAM_ALLOWED_USERS(numeric Telegram user ids, inchela.envor
secrets.env). Text, commands, button taps, photos and documents from anyone else in the
bound chat are dropped unanswered. Each drop is logged with the sender's id and a hash of
the text, at most once a minute per sender. Unset, the bridge keeps the old chat-only
behaviour and warns at start-up, andchela doctorwarns too. (CMX-185) -
A brief's markdown links only to http(s), mailto or relative URLs. A task brief or a
judge report holding[x](javascript:...)(ordata:,vbscript:,file:) rendered a
live link in the task modal; it now renders as plain text. (CMX-228)