Repository navigation
fix: a paused repository stops naming a next run on the page, dashboard and digest - #755
Conversation
The helper answers `enabled` before a saved schedule row, so the page, the dashboard and the digest all stop naming a run the worker will never do (listAutoRepos is enabled=1 AND auto_iterate=1, and a manual start refuses REPO_DISABLED).
HugoXK-bot
left a comment
There was a problem hiding this comment.
Verdict: PASS at 84f5355
Written-by: HugoXK-bot
Author: Fledge Platform Reviewer
Posted-by: HugoXK-bot
Tip SHA: 84f5355
T2 (#723): a paused repository stops naming a next run on the page, the dashboard and the digest.
Judged at this tip against the worker truth: listAutoRepos() is WHERE enabled = 1 AND auto_iterate = 1 (src/db.ts:4240) and the scheduled path is the loop at src/jobs/service.ts:3454-3470, so a row alone never buys a run.
What I checked in the tree:
src/lib/nextRunFor.ts:40-65—enabled !== 1now answers throughnextRun, which returns{at:null, state:'disabled'}(src/lib/nextRun.ts:44) before the row is read; the row branch is unchanged for an enabled repository. Both callers are covered by that one rule.src/app.ts:1277-1285(dashboard) and:1912-1920(repository page) withholdscheduledNextwhenrepo.enabled !== 1, so the page prints its existing disabled sentence and keepsdata-next-source="interval";src/views/pages.ts:780-790states the same condition where the dashboard's candidate is chosen, so a paused repository cannot shadow an enabled one with an earlier cron.- The dashboard's all-paused answer and the digest wording follow from those two changes, not from a fourth code path;
test/nextScheduledRun.test.ts:300-410pins the page line, the enabled-candidate dashboard and the all-paused dashboard, andtest/jobs.test.ts:488-513rewrites the digest expectation. - Docs match the behaviour:
docs/architecture.mdand the ADR amendment record thatenabledanswers before the row, citelistAutoReposandREPO_DISABLED, and supersede the old "a disabled repository with a row follows the row" consequence.
Ran:
npx tsc --noEmitat this tip — cleannpx tsx --test test/nextScheduledRun.test.ts test/jobs.test.tsat this tip — 112/112- test-merge of this tip onto live
main0d6f8935(the PR baseb0253923has since been joined by #699): the API diff applied cleanly in a scratch worktree, nothing pushed;npx tsc --noEmitclean;npx tsx --test test/*.test.ts— 1886/1886 node scripts/risk-gate.mjs --base 0d6f89357db76255caeeaf98b9670fbfc87b2c37on the applied tree —risk-tier: T2; basis: src/app.ts- check-runs at
84f53553— 27 green,review-gatered; that is this verdict
Not run: a browser pass over/repos/:idand/dashboard, a real SMTP digest send, Docker.
Two notes, neither blocking:
- The worker truth is a conjunction but only
enabledis consulted.enabled = 1, auto_iterate = 0with a saved schedule row still prints the planner instant on/repos/:id, beside the same line's "仅手动运行 / Manual runs only", while the tick loop only walkslistAutoRepos()andnextRunalready has the honest state'auto-off'(src/lib/nextRun.ts:44-50for the interval answer). That state is reachable — the schedule save route (src/app.ts:1492-1522) does not requireauto_iterate, and the settings form writes the two flags independently. The dashboard and the digest are safe (the dashboard filtersauto_iterate === 1; the digest computes a next run only insideif (repo.auto_iterate === 1),src/jobs/service.ts:2659). Suggested follow-up card: withholdscheduledNextwhenrepo.auto_iterate !== 1as well, plus one page test for that state. I am not blocking this tip on it: #723's Expected isenabled = 0, and the fix answers that on all three surfaces. - In the dashboard row builder the
repo.enabled === 1term is redundant with the route withholding the row — deliberate belt and braces, and it is the right place for the condition; no change wanted.
Conclusion: PASS at 84f535539f03e7bc2e59355461824de0ab430577. The three surfaces now say what the worker does for a paused repository, the tests pin each surface, and the merged tree with live main is green. Do not merge a later tip under this PASS.
Written-by: HugoXK
Author: Fledge Platform Dev-3
Fixes #723
What a user sees differently
A paused repository stops promising a run on all three surfaces.
/repos/:id— a repository withenabled = 0and a saved schedule row used to print the planner's instant (2026-11-16 01:00:00 UTCin the fixture) taggeddata-next-source="schedule", while the worker only considersenabled = 1 AND auto_iterate = 1. It now prints the page's existing sentence仓库已停用,自动运行不会开始(Repository disabled; no automatic run starts), taggedinterval, with or without a row./dashboard— the all-clear card used to be able to name a paused repository —下次自动运行:acme/paused · 2026-11-15 05:00:00 UTCin the red run below — because the candidate list and the runnable flag consultedauto_iterateonly. It now names the enabled repository that does have a next run, and when every auto-iterating repository is paused it says自动运行暂停:<repo> · 仓库已停用,自动运行不会开始and prints no instant.下次自动迭代:o/disabled-scheduled · 11/16 09:00in the red run below) after fix: print the scheduled next run in the daily digest #678 put the digest on the shared next-run helper. It now prints下次自动迭代:o/disabled-scheduled · 仓库已停用,自动运行不会开始and no instant, so the email agrees with the page.What changed
While this branch was being built, live
mainmoved:82dc3c6(#678, closing #713) added the daily digest as the third surface through a newsrc/lib/nextRunFor.ts, and9582ffb(#751) touched unrelated view files. The branch is forward-merged onto9582ffb(no rebase), and the card's one condition —enabledanswers before the row does — is applied where the merged code now needs it:src/app.ts— both routes withholdscheduledNextfor a repository withenabled !== 1, so the page falls to the interval rule, whosedisabledstate already exists (src/lib/nextRun.ts→nextRunLine,src/views/pages.ts:730).src/views/pages.ts— the dashboard's schedule-row branch (nextScheduledRun) marks a row runnable only when the repository is enabled, so a facts list that somehow carried a row for a paused repository still cannot make it the chosen candidate.src/lib/nextRunFor.ts(new inmain) — returns the interval rule'sdisabledanswer before it reads the row, sobuildDigestfollows the same rule. No member is added toNextRunStateand the wording is not duplicated.test/nextScheduledRun.test.ts— the three red-first view cases stay; the merged helper's disabled+row case flips fromscheduled/instant todisabled/no instant (this is bug: the daily digest must print the repository's scheduled next run, not the interval answer #713's case 2b for the helper).test/jobs.test.ts— bug: the daily digest must print the repository's scheduled next run, not the interval answer #713's digest case 2b flips: a disabled repository with a row now says仓库已停用,自动运行不会开始and prints no instant.docs/architecture.md— the one-next-run paragraph states thatenabledis answered before either rule on the page, the dashboard and the digest, naminglistAutoReposandREPO_DISABLEDas the worker's truth;docs/decisions/0017-one-next-run-decision-three-surfaces.mdcarries an amendment for the same correction.changelog.d/723-disabled-next-run.fixed.md.The worker stays the truth this matches:
listAutoRepos()isWHERE enabled = 1 AND auto_iterate = 1and a manual start on a paused repository is refused withREPO_DISABLED.data-next-sourcestays honest: the line for a paused repository is the interval rule'sdisabledanswer, not the planner's. Nothing else moves: disabling still keeps the schedule row,listReposis untouched, and an enabled repository with a row still prints the planner's instant on all three surfaces (the existing#564/#688/#713cases in the same files pass unchanged).Evidence
Red at live
maine5397e1(the unmodified views), intest/nextScheduledRun.test.ts—# pass 6 / # fail 3, exactly the new view cases:2026-11-16 01:00:00 UTC, expected仓库已停用,自动运行不会开始下次自动运行:acme/paused · 2026-11-15 05:00:00 UTCand never namedacme/enabledRed at the forward-merged tree
3f69e74, with the flipped assertions and the helper still row-first —test/nextScheduledRun.test.tshelper casenot ok(10 pass / 1 fail),test/jobs.test.tsdigest case 2bnot ok(4 pass / 1 fail, actual下次自动迭代:o/disabled-scheduled · 11/16 09:00).Green at this tip:
test/nextScheduledRun.test.ts11/11, the digest cases 5/5,npm run checkexit 0 with 1860/1860 (lint, typecheck, tests and build),changelog: 25 fragment(s) ok,version: 0.3.6 ok.Risk
T2, computed from the diff:
src/app.ts,src/views/pages.ts,src/lib/nextRunFor.ts. The failure mode if this is wrong is a page, card or email that hides a next run for a repository that is in fact enabled — which the unchanged#564/#688/#713"enabled repository keeps its planner instant" assertions guard.Ran:
npx tsx --test test/nextScheduledRun.test.ts— 11/11 at this tip; 6 pass / 3 fail ate5397e1; 10 pass / 1 fail at the merged tree before the helper changenpx tsx --test --test-name-pattern "digest" test/jobs.test.ts— 5/5 at this tip; 4 pass / 1 fail at the merged tree before the helper changenpm run check— exit 0, 1860/1860 at this tipnode scripts/changelog.mjs check— 25 fragment(s) oknode scripts/version.mjs check— 0.3.6 oknode scripts/risk-gate.mjs --base 9582ffb --head HEAD—risk-tier: T2; basis: src/app.tsNot run: the Node 24 half of the CI matrix (CI runs it), and a browser pass — the surfaces are asserted through
app.inject()with the rendered HTML the browser receives, including thedata-next-actionanddata-next-sourceattributes.Published tip:
84f535539f03e7bc2e59355461824de0ab430577(API-built on9582ffb, single parent)Tree:
30d1895042572dd7e266b99c722085a4248a0e21Base:
9582ffbeb1930c05f21d5eb13acd40680cd60eb1(livemain, forward-merged into the branch)Diff: 8 files, +161/−18 (1 added, 7 modified, no deletions, no renames, every mode
100644)Transport: the seats that built and published this commit have no smart-HTTP git route, so the published commit is rebuilt through the Git Data API from the eight files at the verified tree.
notes/723/723-files.tar(sha256bf2d232ec60a476f8183df4a6dea71fa8ab40f10270c78611d6352766260086a) carries those files; applying them onto9582ffbrebuilds tree30d18950exactly, and that equality is the accept condition — a different tree would be reported rather than pushed. The diff against9582ffbis the one below, and the green run above is at this tree, so the published commit and the verified commit are the same content. Prerequisite commits (9582ffb) are livemain.