Opened by detail-app[bot] · 6 days ago
Sign in to comment.
app.detail.dev/org_0f605249-55f3-46eb-880c-20ae3e45ae99/…/bug_806570f6-6007-4733-9c08-e8e60536d358
Introduced in #4 by @ripgrim on Jul 20, 2026
apps/web/src/lib/activity.functions.ts gates the manual "Re-run rules" feature: rerunChangeRequest refuses to enqueue a re-run when executableRuleNames says no rules would evaluate, and getRerunPreview feeds the confirm dialog the same list. The worker (apps/worker/src/jobs/run-workflows.ts) composes the actual rule set for every evaluation, including re-runs, via the same runWorkflows path.executableRuleNames builds its list only from RULE_CATALOG entries and saved-workflow nodes, so enabled standalone custom rules are invisible to it — while the worker explicitly joins enabled standalone custom rules into the derived toggle set and would evaluate them on a re-run.{ status: "no-workflow" } and removes the Re-run button / tells the admin to "install a rule", even though the worker would actually run those custom rules; the confirm dialog can also display "no rules will run" for rules that would run.apps/web/src/lib/activity.functions.ts
function executableRuleNames(
configs: { ruleId: string; enabled: boolean }[],
enabledWorkflows: {
nodes: { type: string; ref?: string }[];
}[],
): string[] {
const byId = new Map(configs.map((c) => [c.ruleId, c]));
if (enabledWorkflows.length > 0) {
...
// Leftover standalone rules (not owned by any workflow) still run.
for (const entry of RULE_CATALOG) { // <-- BUG 🔴 iterates RULE_CATALOG only — custom rule ids never appear here
...
}
return names;
}
return RULE_CATALOG.filter((entry) => // <-- BUG 🔴 no saved workflows ⇒ only builtin catalog rules considered
ruleExecutes(
`${entry.ruleId}@${entry.version}`,
byId.get(entry.ruleId)?.enabled,
),
).map((entry) => entry.name);
}
Callers never fetch custom rules, so the gate can incorrectly refuse re-runs:
const [workflows, configs] = await Promise.all([
repoServices.listEnabledWorkflows(db, repo.fullName, repo.forge),
repoServices.listRuleConfigs(db, repo.id),
]);
const names = executableRuleNames(
configs.map((c) => ({ ruleId: c.ruleId, enabled: c.enabled })),
workflows,
);
if (names.length === 0) {
return { status: "no-workflow" }; // <-- 🔴 false refusal when only custom rules are enabled
}
Worker side includes standalone custom rules in the evaluated set:
apps/worker/src/jobs/run-workflows.ts
const customRows = repo
? await repoServices.listCustomRules(db, repo.id)
: [];
...
const derived = deriveDefaultWorkflow(
[
...ruleConfigs.map((config) => resolveEffectiveRuleConfig({...})),
// Custom rules join the toggle set like any non-baseline rule: they
// run when enabled, and workflow ownership excludes them like any id.
...customRows.map((row) => ({
ref: customRuleRef(row.id), // <-- the rules the web gate cannot see
enabled: row.enabled,
config: {} as JsonValue,
held: false,
})),
],
ownedRuleIds,
event.forge,
);
The web gate and preview compute “rules that would run” using only builtin rule configs (listRuleConfigs) + saved workflows (listEnabledWorkflows) and then filter exclusively over RULE_CATALOG. Standalone custom rules are stored separately and are not in the static catalog, so they never appear in executableRuleNames.
The worker’s runWorkflows path (also used by the re-run job handler) explicitly fetches custom rules via listCustomRules and adds enabled custom rule refs into the derived workflow toggle set. This causes a divergence: the UI can claim “no rules will run” and block the rerun job while the worker would actually evaluate enabled custom rules.
RULE_CATALOG + saved workflows.listCustomRules + deriveDefaultWorkflow).In executableRuleNames (apps/web/src/lib/activity.functions.ts), fetch custom rules alongside configs/workflows in both callers (repoServices.listCustomRules(db, repo.id)) and fold them into the name list, mirroring the worker: treat each enabled custom rule as a leftover standalone rule (keyed by customRuleRef(row.id) / ruleIdOf for the seen-set, display name via the rule's stored name — run-view.ts already resolves custom names this way), and let workflow ownership exclude its node-owned ids. Then rerunChangeRequest will admit custom-rule-only repos and getRerunPreview will list them.
This bug was introduced in commit 3504b0a4 ("fix(rerun): show live re-run state and fix rule resolution feedback", 2026-07-20). Two days later, 67bd7145 (2026-07-22) added worker-side inclusion of enabled standalone custom rules, but the web-side gate/preview logic was never updated to account for custom rules.