Skip to content

fix: deny always blocks; rule link id, resume labels, moderation copy#13

Merged
ripgrim merged 1 commit into
mainfrom
fix/moderation-deny-blocks
Jul 24, 2026
Merged

fix: deny always blocks; rule link id, resume labels, moderation copy#13
ripgrim merged 1 commit into
mainfrom
fix/moderation-deny-blocks

Conversation

@ripgrim

@ripgrim ripgrim commented Jul 24, 2026

Copy link
Copy Markdown
Collaborator

what

four fixes from one debugging session, confirmed against prod (read-only) on scratch #83.

deny always blocks (the security one)

a maintainer's deny produced a passing check. the edited workflow's send-to-moderation node routed its deny edge to a discord action with no block anywhere, so deny resumed to pass. the T4 deny-floor only checked that a deny edge existed, not that it blocked. now the verdict is the maintainer's decision, not the graph's traversal: deny always blocks, side-effect edges still conduct (discord/webhook fire), and a block action floors in when the graph produced none. approve resumes to the graph's verdict. logic lives in a pure resolveResumeOutcome.

rule "edit in workflow" link

the link built the workflow definition's wire id, not the editor route's row id — two separate generateId() calls at create, so the link 404'd at a workflow that "doesn't exist". map wire->row via listEnabledWorkflowRows. the badge was always accurate; only the link id was wrong.

resume step labels

resumed steps carry a :resume suffix the snapshot label map doesn't, so the moderation node and its downstream actions rendered as bare action. strip the suffix before lookup; label the synthetic deny-floor step as block.

moderation toast copy

names the outcome ("denied — this change is blocked" / "approved — this change can proceed"), not the mechanism.

tests

  • resume-verdict.test.ts (new): deny -> non-block action (discord) -> still blocks. the exact prod shape. green.
  • moderation.integration.test.ts: floor path end-to-end; updated the deny-floor assertion string. needs docker; ci runs it.
  • typecheck + biome: green on the changed files.

scope note

scoped to 7 files. this windows checkout has unrelated local changes (bun.lock rewritten by bun 1.3.2, nitro/dev shims, a pg-boss error handler, migrate.ts path fix) left uncommitted on purpose — not part of this work, and committing the lockfile would fork it.

four fixes from one debugging session, confirmed against prod (read-only)
on scratch #83.

- resume: the verdict is the maintainer's decision, not graph traversal.
  deny always blocks — a deny edge drawn to a non-block action (discord),
  or to nowhere, no longer resumes to pass. side-effect edges still conduct;
  a block action floors in when the graph produced none. approve resumes to
  the graph's verdict. extracted resolveResumeOutcome (pure, unit-tested).
- rules: the "edit in workflow" link built the definition wire id, not the
  editor's row id (two separate generateId() calls at create), so it 404'd.
  map wire->row via listEnabledWorkflowRows.
- run view: resumed steps carry a :resume suffix the snapshot label map
  lacks — strip it so the moderation node and its actions label correctly;
  label the synthetic deny-floor step as block.
- moderation queue: toast names the outcome, not the mechanism.

regression: resume-verdict.test.ts covers deny -> non-block action -> block.
moderation.integration.test.ts needs docker; ci runs it.
@ripgrim
ripgrim merged commit cb06b91 into main Jul 24, 2026
1 check failed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant