fix(lanes,reload): count a pipeline run by declarations, and stop the two reload commands merging

Closes #26, #25.

`runs` is the denominator every pass rate on the Workflows page is read
against, and any backwards move opened a new one. Stage detection bounces by
design — one feature reads `implement → tests → implement` as the session
edits, runs the suite and edits again — so a single feature was recorded as
five or six runs, and a lane in constant use reported a handful of passes
spread across dozens of phantom runs. A backwards move now opens a run only
when it is DECLARED; a backwards detection continues the run in flight.
Reaching the final node still closes the run for either, since work after
`done` is new work however it was observed. Replayed over this repo's own lane
the rule takes 26 recorded runs down to the 5 features that actually shipped
through it. The panel itself is unchanged: passes are still declarations only.

`/reload-plugins` and `/reload-skills` were typed back to back. Claude Code
opens its slash-command menu the moment `/` is typed and the Enter that follows
lands on that menu, so the second command was appended to the line still in the
composer and the pane submitted `/reload-plugins/reload-skills` — which Claude
Code runs as `/reload-plugins` with an argument, never reloading skills, and
leaving a stray prompt turn in the scrollback. Reproduced and fixed against a
live pane: SETTLE_MS (1s) between the two sends, which makes
`reloadOpenSessions` async rather than block the event loop, and the same pause
in the detached update-all worker.
This commit is contained in:
2026-09-17 16:01:59 +07:00
parent 02ee46c862
commit 686b80bd99
11 changed files with 132 additions and 35 deletions
+3 -3
View File
@@ -334,7 +334,7 @@ router.post("/plugins/action", async (req, res) => {
let reloaded;
if (reload && result.ok) {
try {
reloaded = reloadOpenSessions();
reloaded = await reloadOpenSessions();
} catch {
/* a reload that cannot be delivered must not fail the update itself */
}
@@ -351,7 +351,7 @@ router.post("/plugins/action", async (req, res) => {
* sessions to re-read what is already there — so it neither queues on the CLI
* mutation chain nor emits `cc_config_changed`.
*/
router.post("/reload", (req, res) => {
router.post("/reload", async (req, res) => {
const { kinds } = req.body || {};
if (kinds !== undefined && !Array.isArray(kinds)) {
return res.status(400).json({
@@ -359,7 +359,7 @@ router.post("/reload", (req, res) => {
});
}
try {
res.json({ ok: true, ...reloadOpenSessions({ kinds }) });
res.json({ ok: true, ...(await reloadOpenSessions({ kinds })) });
} catch (err) {
res.status(500).json({
error: { code: "ERELOAD", message: err.message || String(err) },