Skip to content

[oss-candidate] fix(mail): grade outbound delivery on the whole queue, not the top three reasons - #1

Closed
askalf wants to merge 2 commits into
mainfrom
fix/mail-delivery-verdict-past-reason-cap
Closed

askalf wants to merge 2 commits into
mainfrom
fix/mail-delivery-verdict-past-reason-cap

Conversation

@askalf

@askalf askalf commented Sep 23, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

  • parseMailQueue capped its deferral list at MAX_DEFERRALS (3) before returning it, and
    checkMailDelivery graded that capped list. A display cap was deciding the health verdict.
  • The reason that condemns a relayed box (a SASL refusal from the smarthost, or a TLS/network
    failure naming it) is often the rarest line in the queue: one message has reached the relay
    and been refused while older ones defer against ordinary receivers. Sorted by count and cut to
    three, it never reached gradeDelivery.
  • Result: a box whose relay credentials are wrong, with every relayed message failing, graded
    warn ("mail is moving late") instead of fail.
  • Fix: return every distinct reason from the parse, grade that, and apply the cap in a new
    topDeferrals when building the response. The response still carries at most three rows.
  • Two commits, two files, +78/-3 at head e610b22f (production file +11/-2, the rest tests). No
    new dependencies, no reformatting of unrelated lines. The second commit is test-only (it drops
    four table rows that passed without the fix); squash on merge is fine.

Both arms run at head e610b22f. The test file holds 47 tests: 41 unchanged, 1 pre-existing test
updated (caps the reasons it reports), 5 added as one it.each table. All 6 new or updated tests
fail on base; there are no controls.

$ # BASE arm: production file reverted to 4ad53d8a, test file kept
$ git checkout 4ad53d8a -- apps/api/src/modules/mail/mail-delivery.service.ts
$ bun x vitest run test/modules/mail/mail-delivery.service.test.ts --reporter=verbose

 × parseMailQueue > caps the reasons it reports
   → expected [ { kind: 'rejected', …(2) }, …(2) ] to have a length of 6 but got 3
 × checkMailDelivery > grades an auth refusal ranked fourth
   → expected 'warn' to be 'fail' // Object.is equality
 × checkMailDelivery > grades a TLS failure at the smarthost ranked fourth
   → expected 'warn' to be 'fail' // Object.is equality
 × checkMailDelivery > grades a connection failure at the smarthost ranked fourth
   → expected 'warn' to be 'fail' // Object.is equality
 × checkMailDelivery > grades an auth refusal ranked thirteenth
   → expected 'warn' to be 'fail' // Object.is equality
 × checkMailDelivery > grades an auth refusal queued after three one-off deferrals
   → expected 'warn' to be 'fail' // Object.is equality

 Test Files  1 failed (1)
      Tests  6 failed | 41 passed (47)

$ # HEAD arm: fix restored
$ git checkout HEAD -- apps/api/src/modules/mail/mail-delivery.service.ts
$ bun x vitest run test/modules/mail/mail-delivery.service.test.ts

 Test Files  1 passed (1)
      Tests  47 passed (47)

Upstream

  • Repository: oblien/openship
  • Default branch: main
  • Base sha: 4ad53d8a7ba53a1abb47a755639342d076f365e4. Current origin/main is 4fefe217
    (re-checked at e610b22f); the source file is byte-identical between the two (blob dea13dea
    at both), and so is the test file (blob f27baa46 at both), so the base is current for this
    change.
  • Files: apps/api/src/modules/mail/mail-delivery.service.ts (checkMailDelivery,
    parseMailQueue, new topDeferrals) and its test file
    apps/api/test/modules/mail/mail-delivery.service.test.ts.

Bug

Trigger. A server with an outbound relay enabled (state.outboundRelay.enabled) whose relay
credentials, TLS or egress are broken, and whose Postfix queue also holds ordinary deferrals against
direct receivers (greylists, busy receivers, full mailboxes). Relayed mail fails with one distinct
reason, while direct mail to many receivers accumulates several commoner ones.

Wrong outcome. parseMailQueue sorted the reason tally by count and applied
.slice(0, MAX_DEFERRALS) inside the parse, so the reading it returned held at most three reasons.
checkMailDelivery passed that reading to gradeDelivery, whose fatal check is a .some() over
queue.deferrals. With the fatal reason sorted to 4th place or lower it was not in the array,
fatal was false, and the function returned "warn".

Blast radius. Every self-hosted openship box that relays outbound mail through a smarthost (SES,
Postmark, Mailgun, a provider SMTP) and has a broken relay configuration. The Health tab's Delivery
section shows amber "mail is moving late" while nothing leaves the box. The test file's own header
describes exactly this case as the one the module exists to catch:

nine green daemons, green DNS, and a Test-tab email that reports success, on a box where not one
message has left in three days because the relay's SASL password is wrong

Measured (probe output under Boundaries): with the fatal reason at sort index 0, 1 or 2 the base
verdict was fail; at index 3 or beyond it was warn.

Repro

The bug is in a pure function chain fed by postqueue -p output, so the repro is the module's own
harness (queueOutput builds a listing in Postfix's layout, box() stubs the executor) driving
checkMailDelivery. Base arm = production file reverted to 4ad53d8a, test file at head
e610b22f:

$ cd apps/api
$ git checkout 4ad53d8a -- src/modules/mail/mail-delivery.service.ts
$ bun x vitest run test/modules/mail/mail-delivery.service.test.ts --reporter=verbose

⎯⎯⎯⎯⎯⎯⎯ Failed Tests 6 ⎯⎯⎯⎯⎯⎯⎯

 FAIL  test/modules/mail/mail-delivery.service.test.ts > parseMailQueue > caps the reasons it reports
AssertionError: expected [ { kind: 'rejected', …(2) }, …(2) ] to have a length of 6 but got 3

- Expected
+ Received

- 6
+ 3

 ❯ test/modules/mail/mail-delivery.service.test.ts:158:23
    156|     expect(parsed?.queued).toBe(6);
    157|     const deferrals = parsed?.deferrals ?? [];
    158|     expect(deferrals).toHaveLength(6);
       |                       ^
    159|     expect(topDeferrals(deferrals)).toEqual(deferrals.slice(0, 3));
    160|   });

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/6]⎯

 FAIL  test/modules/mail/mail-delivery.service.test.ts > checkMailDelivery > grades an auth refusal ranked fourth
 FAIL  test/modules/mail/mail-delivery.service.test.ts > checkMailDelivery > grades a TLS failure at the smarthost ranked fourth
 FAIL  test/modules/mail/mail-delivery.service.test.ts > checkMailDelivery > grades a connection failure at the smarthost ranked fourth
 FAIL  test/modules/mail/mail-delivery.service.test.ts > checkMailDelivery > grades an auth refusal ranked thirteenth
 FAIL  test/modules/mail/mail-delivery.service.test.ts > checkMailDelivery > grades an auth refusal queued after three one-off deferrals
AssertionError: expected 'warn' to be 'fail' // Object.is equality

Expected: "fail"
Received: "warn"

 ❯ test/modules/mail/mail-delivery.service.test.ts:451:27
    449|     const health = await checkMailDelivery(box({ relay, queue }));
    450| 
    451|     expect(health.status).toBe(status);
       |                           ^
    452|     expect(health.deferrals.map((d) => d.reason)).toEqual(shown);
    453|   });

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[2/6]⎯


 Test Files  1 failed (1)
      Tests  6 failed | 41 passed (47)
   Start at  05:41:19
   Duration  11.67s (transform 5.36s, setup 0ms, import 11.38s, tests 56ms, environment 0ms)

The main fixture (crowdedQueue) is a queue of 8: three greylists, two busy receivers, two full
mailboxes, and one relay refusal, so the fatal reason ranks fourth.

Fix

Three edits in apps/api/src/modules/mail/mail-delivery.service.ts:

  1. parseMailQueue no longer calls .slice(0, MAX_DEFERRALS). It returns every distinct reason,
    still sorted by count descending.
  2. A new exported topDeferrals(deferrals) applies MAX_DEFERRALS: the same slice, moved. It
    carries a one-line doc comment: "The deferral rows we report. gradeDelivery reads the
    uncapped list."
  3. checkMailDelivery grades queue (whole) and returns deferrals: topDeferrals(queue.deferrals)
    (capped).

gradeDelivery itself is untouched; it always judged whatever list it was handed.

Why this is the minimal correct change. The cap is a display concern: the Health tab's
DeliverySection renders the rows in a small table. Moving the slice, rather than widening or
removing it, keeps the response shape identical. Every checkMailDelivery case added here asserts
the exact three rows the response carries.

Alternatives rejected, each built as a mutant of the production file at head e610b22f and run
against the touched test file (47 tests). The kill set is the exact list of tests that fail.

  • Raise MAX_DEFERRALS to 4, cap still inside the parse. Still a cap deciding a verdict. Killed
    by 2: caps the reasons it reports, grades an auth refusal ranked thirteenth.
  • Raise MAX_DEFERRALS to 8, cap still inside the parse. Killed by 1: grades an auth refusal ranked thirteenth (13 distinct reasons, the refusal 13th).
  • Sort fatal reasons to the front in parseMailQueue, cap kept there. Makes the parse depend on
    relay state it does not receive. Killed by 6: the parse test and all five table rows.
  • Drop the cap entirely and let the dashboard slice. Two consumers (health-tab.tsx and the API
    type in apps/dashboard/src/lib/api/mail.ts) would each need the cap. Killed by 5: every table
    row (each queue has more than three distinct reasons).
  • Grade the capped list at the new seam
    (gradeDelivery({ ...queue, deferrals: topDeferrals(...) })). Killed by 5: every table row.
  • Cap by truncating the reading in place (an impure topDeferrals that sets deferrals.length).
    Object-literal evaluation order then hands gradeDelivery the truncated list. Killed by the same
    5. This settles the aliasing row (R7) by execution.
  • Have gradeDelivery take the raw output and re-parse. Not built: it duplicates the parser and
    changes gradeDelivery's signature, which the file's 8 direct gradeDelivery tests would fail to
    compile against.

Mutant transcript at e610b22f: /agent-output/oss/openship/rv3-mutants.txt; builder
verify-mutants.mjs, not committed. Historical, at 129e4131 (nine-row table):
rework1-mutants.txt / rv2-mutants.txt, where the four rows since removed added kills only to
the fatal-first and drop-the-cap mutants, both still killed without them.

Test evidence

All tests live in the module's existing file, in its idiom: the file's queueOutput fixture
builder, its box() executor stub, its named refusal constants, and one it.each table with the
expected status and response rows carried as data. No new test file.

One pre-existing test is updated and five table rows are added. All six fail on base.

Test Base arm Head arm Role
parseMailQueue > caps the reasons it reports (pre-existing, updated) FAIL: expected … to have a length of 6 but got 3 pass discriminating: the reading keeps all 6 reasons, topDeferrals returns the first 3 in order
checkMailDelivery > grades an auth refusal ranked fourth FAIL: expected 'warn' to be 'fail' pass discriminating, auth arm of the fatal predicate
checkMailDelivery > grades a TLS failure at the smarthost ranked fourth FAIL: expected 'warn' to be 'fail' pass discriminating, tls arm
checkMailDelivery > grades a connection failure at the smarthost ranked fourth FAIL: expected 'warn' to be 'fail' pass discriminating, network arm
checkMailDelivery > grades an auth refusal ranked thirteenth FAIL: expected 'warn' to be 'fail' pass discriminating; 12 filler reasons at count 2, the refusal 13th at count 1. The only test that kills a cap raised to 8
checkMailDelivery > grades an auth refusal queued after three one-off deferrals FAIL: expected 'warn' to be 'fail' pass discriminating; every count is 1, so queue order decides and the refusal lands 4th. Smallest reproduction

Every table row asserts both status and the exact response rows (deferrals.map((d) => d.reason)).
Transcripts at e610b22f: /agent-output/oss/openship/rv3-{base,head}-arm.txt (base 6 failed / 41
passed, head 47 passed, the same six names).

The four gate cases that passed on both arms (fatal reason on the last shown row; network failure at
another host; TLS failure with relay.host === ""; auth refusal on a direct box) were table rows at
129e4131 and were removed at e610b22f because they do not discriminate the fix. Their behaviour
is kept as measured rows under Boundaries (probe, plus the pre-existing gradeDelivery tests
that pin those gates).

Per CONTRIBUTING's "Prove the test can fail": the base arm above is that exercise. The code
under test was reverted to upstream main, the six tests failed with the output quoted, and the
code was restored (checked byte-identical against a saved copy after the base arm and after the
mutants).

Tooling, at e610b22f:

$ bun run --cwd apps/api lint
$ tsc --noEmit
rc=0

$ bun x prettier --version
3.8.1

prettier --check on the two touched files reports style issues at head and at base alike: both
files carry pre-existing Prettier drift on upstream main. Diffing prettier <file> against the
working copy shows every remaining hunk on a line this PR does not touch (the mail-engine import,
two it.each classification rows, the systemctl show line, a pre-existing Array.from in the
parse test, and others). Per CONTRIBUTING, "Do not reformat unrelated lines or 'fix' pre-existing
Prettier/lint drift on lines you aren't otherwise changing"
, those hunks were not taken. Every
line this PR adds is Prettier-clean at the repo's pinned 3.8.1 under its .prettierrc
(printWidth: 100).

bun run test (the whole turbo monorepo suite) was not run locally in this container; the
upstream's own CI workflows run it on the fork for every push. See Verification method.

Verification method

executed, in a Linux container: Bun 1.3.14 (the repo pins 1.3.10 in .bun-version; vitest/tsc
behaviour is unaffected), Vitest 4.0.18, TypeScript via bun run --cwd apps/api lint. bun install --frozen-lockfile against the repo's own lockfile.

Executed independently TWICE at e610b22f, first by the Hunter and then by the verification seat
in a second fresh worktree: the touched test file on both arms (base 6 failed / 41 passed, head 47
passed, identical names both runs), the API workspace typecheck (rc=0 both times), six
production-file mutants rebuilt from the head copy and re-run (kill sets under Fix, identical
both runs), and the boundary probe quoted under Boundaries (output byte-identical across the
1c1504a1, 129e4131 and e610b22f runs).

CI. The upstream's own workflows are enabled on the fork. At e610b22f run
35961279325 is green on all
9 jobs (gh pr checks 1, re-checked by the verification seat): Documentation, Test, Test webmail
server, Tests (API 1/2), Tests (API 2/2), Tests (Database), Tests (Other packages), Tests (SDK and
CLI), Typecheck. At the previous head 129e4131 (same production code, nine-row table), run
35813458516 was also green
on all 9 jobs.

Not executed anywhere: end-to-end against a real Postfix/relay box. There is no mail stack in
this container or in CI. The queue fixtures are postqueue -p output in Postfix's layout, built by
the test file's pre-existing queueOutput builder (unmodified). What this PR changes is which list
reaches gradeDelivery, which is pure and exercised above.

Prior art

Re-run at e610b22f, against current origin/main (4fefe217):

Search Result
git log --oneline 4ad53d8a..origin/main -- apps/api/src/modules/mail/ apps/api/test/modules/mail/ (empty)
git log --oneline -S"MAX_DEFERRALS" 4ad53d8a..origin/main (empty)
git rev-parse 4ad53d8a:<src> origin/main:<src> dea13dea both
gh search prs --repo oblien/openship "deferrals" --state open []
gh search prs --repo oblien/openship "MAX_DEFERRALS" --state open []
gh search prs --repo oblien/openship "topDeferrals" --state open []
gh search prs --repo oblien/openship "gradeDelivery" --state open []
gh search prs --repo oblien/openship "mail-delivery" --state open []
gh search issues --repo oblien/openship "deferrals" []
gh search prs --repo oblien/openship "mail-delivery" (all states, hunt time) oblien#885 (merged, Amavis pid files), oblien#423 (closed, relay TLS transport scoping) and older; none touch the deferral list or the verdict
gh pr list --search "876 in:body" --state all (hunt time) merged oblien#892 only

No open PR fixes this. Issue oblien#876 ("Email service is waiting for emails to go out forever") is
the surface that led here but is not what this PR fixes: that queue stall was a Docker/Amavis
problem, addressed upstream by oblien#885 via oblien#891/oblien#892. This PR is a separate defect in the code that
renders the same panel. It should not be described as closing oblien#876.

The nearest precedent for the class is 73bd16c6, "fix(mail): read the DB probe verdict past
docker's stderr warnings (oblien#783)": a presentation-layer transformation applied before a verdict was
computed, fixed by an outside contributor and merged.

Policy

Quoted verbatim from CONTRIBUTING.md at oblien/openship@main (4fefe217; file re-fetched at
e610b22f, byte-identical to the copy these lines were taken from):

  • L44: - **One change per PR.** One bug, or one agreed feature. Don't bundle unrelated changes.
    → One bug; two files.
  • L45-47: - **Scope the diff.** Touch only the files your change needs. Do **not** reformat unrelated lines or "fix" pre-existing Prettier/lint drift on lines you aren't otherwise changing — run bun format, then review the diff and drop anything unrelated before you push.
    → Prettier was run and its unrelated hunks dropped; see Test evidence.
  • L50: - **Prove it.** Add a test that fails without your change and passes with it, and say so in the PR.
    → Six such tests; both arms quoted verbatim.
  • L51-57: - **No test spam.** A test earns its place by catching a regression that could actually happen. … one test that genuinely fails without your change is worth more than twenty that can't fail at all.
    → Every new or updated test fails without the change; cases that passed on base were removed.
  • L58-59: - **Green before you open.** bun run test, the relevant typecheck (bun run --cwd
    lint), and bun format all pass locally.
    → Typecheck rc=0 locally. bun format accounted for above. bun run test was not run
    locally; the fork's CI runs it (see Verification method). The operator should run it locally
    before opening upstream, as this line asks.
  • L61-63 (### Using AI assistants): AI tools are fine to use — but **you** are the author and are accountable for every line you submit: → no ban, no CLA, no DCO, no mandated commit trailer.
  • L67: - **Understand your whole diff.** If you can't explain a line in review, don't submit it.
  • L68-70: - **Verify, don't trust.** Actually run the change and confirm it does what the PR claims. Do not paste generated code — or a generated PR description — that you haven't checked against the real codebase.
  • L142: - **Commits**: [Conventional Commits](https://www.conventionalcommits.org/) - feat:, fix:, docs:, chore:``
    → fix(mail): … and `test(mail): …`; branch `fix/mail-delivery-verdict-past-reason-cap`.
  • L144: - **Code style**: Prettier - run bun format before committing

Absent at main (contents API): AGENTS.md, CODE_OF_CONDUCT.md, AI_POLICY.md,
.github/AI_POLICY.md, AI.md, AGENT_POLICY.md, .github/CONTRIBUTING.md. The PR template
(.github/pull_request_template.md) asks for Summary / Motivation / Related issue / Changes /
Verification (real output) / Checklist; this sheet supplies each.

Disclosure facts for the operator

Plain facts, for you to word your own disclosure:

  • An AI agent read the apps/api mail module and found this defect. It was not reported by a user
    and is not the bug described in issue [Bug]: Email service is waiting for emails to go out forever oblien/openship#876.
  • The AI wrote the production change (+11/-2) and all six test cases. A second, independent AI run
    added cases while trying to break the first ones; AI reviewers then asked for explanatory comments
    and em-dash prose to be removed, for the cases to be folded into one table, and for four table
    rows that passed without the fix to be dropped. The AI made each of those changes.
  • The AI executed: the touched test file on both arms, bun run --cwd apps/api lint, Prettier 3.8.1,
    a boundary probe over the cap positions, and six mutants of the production file.
  • The AI did not run bun run test (full monorepo suite) or bun run build in its own container;
    the upstream's CI workflows run on the fork at each head.
  • Nothing was tested against a real Postfix/relay server.
  • Every transcript in this sheet is copy-pasted real output.

Boundaries

Every predicate, comparison and index expression the diff adds or changes. "Probe" rows were
measured by a throwaway harness (output below the table), run at 1c1504a1, 129e4131 and
e610b22f with identical output; it was not committed.

# Expression Boundary input Fixed-code behaviour Pinned by
R1 topDeferrals: deferrals.slice(0, MAX_DEFERRALS) [] returns [], a new array probe; reports a healthy direct sender (empty queue → deferrals: [])
R2 same length 1 length 1 probe
R3 same length 2 length 2 probe
R4 same length equal to the cap (3) length 3, content unchanged probe (topDeferrals(len=3) -> len 3); same on base, where the slice sat in the parse
R5 same length one past the cap (4) first 3 grades an auth refusal ranked fourth, … queued after three one-off deferrals (exact rows asserted)
R6 same length 6, 13 (well past) first 3, in order caps the reasons it reports (6); grades an auth refusal ranked thirteenth (13)
R7 same aliasing: does the caller's array survive? slice returns a new array; source untouched probe; the impure-topDeferrals mutant is killed by all five table rows
R8 parseMailQueue: cap removed 0 / 1 / 3 / 4 / 6 / 8 distinct reasons all returned, never truncated caps the reasons it reports (6); probe (the rest)
R9 same Mail queue is empty {queued:0, sampled:false, deferrals:[]}, unchanged reads an empty queue as a conclusion, not a failed probe (pre-existing)
R10 same output that is not a queue listing still null; the null guard is untouched refuses to read %s as a queue (pre-existing)
R11 gradeDelivery(queue, relay), argument now uncapped fatal reason at sort index 0, 1, 2 fail, same as base probe (auth at index 0/1/2: full=fail capped=fail); a table row at 129e4131, removed because it passed on base
R12 same fatal reason at sort index 3 fail; base warn grades an auth refusal ranked fourth
R13 same fatal reason at sort index 4, 7, 12 fail; base warn probe (4, 7); grades an auth refusal ranked thirteenth (12)
R14 same, tls arm of the fatal predicate TLS failure naming the smarthost, past the cap fail; base warn grades a TLS failure at the smarthost ranked fourth
R14a same, network arm connection timeout naming the smarthost, past the cap fail; base warn grades a connection failure at the smarthost ranked fourth
R14b host-match gate of the tls/network arm network failure at a host that is not the smarthost warn both arms; the gate is unchanged code only warns when the failing host is not the smarthost (pre-existing); measured past the cap at 129e4131 (warn both arms, rv2-{base,head}-arm.txt)
R15 auth arm SASL refusal past the cap fail; base warn grades an auth refusal ranked fourth
R16 relay?.enabled gate no relay, auth refusal warn both arms; the gate is unchanged code fails a relayed box on an auth refusal (pre-existing, its undefined relay assertion); probe (no relay, auth present: warn)
R17 same relay present, enabled: false warn probe; ignores a relay row that is switched off (pre-existing)
R18 !!host guard relay.host === "", TLS failure warn both arms; the empty host cannot match probe (relay host empty string: warn); measured past the cap at 129e4131
R19 queue.deferrals.length === 0 early return queued: 0, deferrals non-empty fail (relayed, auth); branch not taken probe
R20 sort stability: all counts 1 3 filler + 1 auth, every count 1 insertion order kept, auth at index 3 → fail grades an auth refusal queued after three one-off deferrals (verdict and rows in queue order)
R21 deferrals: topDeferrals(queue.deferrals) in the response any queue with >3 reasons response carries exactly the first 3 rows every table row asserts the exact rows; the drop-the-cap mutant is killed by 5
R22 MAX_DEFERRALS as a mutation target cap raised to 4; to 8 (inside the parse) both read as the bug cap 4 killed by 2, cap 8 by grades an auth refusal ranked thirteenth

R4, R11, R14b, R16 and R18 describe behaviour that is identical on both arms, so no new test pins
them; each is pinned by a pre-existing test or measured by the probe.

Probe output, verbatim (at e610b22f, identical to the 1c1504a1 and 129e4131 runs):

  topDeferrals(len=0) -> len 0
  topDeferrals(len=1) -> len 1
  topDeferrals(len=2) -> len 2
  topDeferrals(len=3) -> len 3
  topDeferrals(len=4) -> len 3
  topDeferrals(len=9) -> len 3
  topDeferrals([]) returns new array: []
  aliasing: out===src ? false; src still len 5
  0 distinct entries -> queued=0 deferrals=0
  1 distinct entries -> queued=1 deferrals=1
  3 distinct entries -> queued=3 deferrals=3
  4 distinct entries -> queued=4 deferrals=4
  8 distinct entries -> queued=8 deferrals=8
  empty queue -> {"queued":0,"sampled":false,"deferrals":[]}
  auth at index 0 (len 1): full=fail capped=fail
  auth at index 1 (len 2): full=fail capped=fail
  auth at index 2 (len 3): full=fail capped=fail
  auth at index 3 (len 4): full=fail capped=warn
  auth at index 4 (len 5): full=fail capped=warn
  auth at index 7 (len 8): full=fail capped=warn
  no relay, auth present: warn
  relay disabled: warn
  relay host empty string: warn
  queued=0 but deferrals present: fail
  all count=1: order=rejected,rejected,rejected,auth
  full verdict=fail capped verdict=warn

capped= is the base behaviour and full= the fixed behaviour; the boundary is exactly index 3,
i.e. MAX_DEFERRALS.

Behaviour outside the stated bug: none found. gradeDelivery, describePath, the null guard,
sampled, queued and reason clamping are untouched; the 41 unchanged pre-existing tests pass on
both arms. One observable change beyond the verdict: parseMailQueue is exported and now returns an
uncapped list, but its only caller outside the tests is checkMailDelivery
(git grep parseMailQueue), which caps before responding, so no API or dashboard consumer sees a
longer array.

Likely maintainer question (not changed here). The verdict can now be fail while the fatal
reason is not among the three rows shown, so the dashboard's relay-auth hint
(health-tab.tsx, rendered per displayed row) does not appear. Base showed amber for the same
queue, so this is still strictly better. Keeping the fatal row visible would need the relay in
topDeferrals (a signature change); that is a separate change if the maintainer wants it.

Suggested upstream PR title

fix(mail): grade outbound delivery on the whole queue, not the top three reasons

@askalf askalf added the oss-candidate Sprayberry Code candidate for upstream label Sep 23, 2026
@askalf
askalf marked this pull request as ready for review September 23, 2026 00:50
@askalf askalf added the verified Adversarially verified by a fresh run label Sep 23, 2026
@askalf

askalf commented Sep 23, 2026

Copy link
Copy Markdown
Collaborator Author

Verification

Adversarial pass by a fresh run at head 1c1504a195449ff643162bb088d4bc52d718e03b (base 4ad53d8a). The commit I pushed is test-only: git diff 4ad53d8a..1c1504a1 -- apps/api/src/modules/mail/mail-delivery.service.ts is still +28/-2 and the file is byte-identical to 65e34400.

Arms, run by me

$ # HEAD arm
$ cd apps/api && bun x vitest run test/modules/mail/mail-delivery.service.test.ts
 Test Files  1 passed (1)
      Tests  52 passed (52)

$ # BASE arm: production file at 4ad53d8a, test file at 1c1504a1
$ git checkout 4ad53d8a -- apps/api/src/modules/mail/mail-delivery.service.ts
$ bun x vitest run test/modules/mail/mail-delivery.service.test.ts --reporter=verbose
 × parseMailQueue > keeps every distinct reason in the reading it returns
   → expected [ { kind: 'rejected', …(2) }, …(2) ] to have a length of 6 but got 3
 × parseMailQueue > caps the reasons it reports
   → TypeError: (0 , __vite_ssr_import_1__.topDeferrals) is not a function
 × checkMailDelivery > fails a relayed box on an auth refusal ranked below the display cap
   → expected 'warn' to be 'fail' // Object.is equality
 × checkMailDelivery > fails a relayed box on a TLS failure at the smarthost ranked below the display cap
   → expected 'warn' to be 'fail' // Object.is equality
 × checkMailDelivery > fails a relayed box on a connection failure at the smarthost ranked below the display cap
   → expected 'warn' to be 'fail' // Object.is equality
 × checkMailDelivery > fails a relayed box on a refusal ranked far below the display cap
   → expected 'warn' to be 'fail' // Object.is equality
 × checkMailDelivery > fails a relayed box on a refusal queued after three other one-off deferrals
   → expected 'warn' to be 'fail' // Object.is equality
 Test Files  1 failed (1)
      Tests  7 failed | 45 passed (52)
$ git checkout HEAD -- apps/api/src/modules/mail/mail-delivery.service.ts   # cmp against saved head copy: identical

Controls (pass on both arms, marked in the body's Test evidence table): fails a relayed box on a refusal that sits on the last shown row, only warns when the failing host below the cap is not the smarthost, only warns when the relay has no host to match against, only warns when the box sends direct and a receiver refuses auth. Each controls a different gate (inside-the-cap boundary, host-match, !!host, relay?.enabled).

The hole, and its fixture

At 65e34400 the only checkMailDelivery fixture past the cap had the fatal reason 4th of 4 distinct reasons. Mutant cap4-in-parse (MAX_DEFERRALS raised to 4, slice left inside the parse) survived every checkMailDelivery test and was killed only by the parseMailQueue length assertion; mutant cap8-in-parse survived all 46 tests. The verdict-level pin therefore did not clear the moved constant. Added fails a relayed box on a refusal ranked far below the display cap (12 distinct filler reasons at count 2, the refusal 13th at count 1): fails on base, kills cap 4 and cap 8.

Mutants at 1c1504a (kill set = the exact failing tests, 52 run each)

Mutant Killed by
cap4-in-parse 2: keeps every distinct reason …, … ranked far below the display cap
cap8-in-parse 1: … ranked far below the display cap
fatal-first-in-parse (sort fatal kinds first, cap kept in parse) 7: parse-length, all five fails … below the display cap, … sits on the last shown row
no-cap-in-response (drop topDeferrals from the response) 8: the five fails … below the display cap + the three only warns when …
grade-capped (gradeDelivery({...queue, deferrals: topDeferrals(...)})) 5: the five fails … below the display cap
impure-topDeferrals (truncate the reading in place) 5: same five. Settles R7 aliasing by execution

No mutant survives; no mutant is killed by everything.

Other checks

  • bun run --cwd apps/api lint (tsc --noEmit) rc=0 at 1c1504a1.
  • Prettier 3.8.1: every remaining hunk on the test file sits on a line present at base; the one base line the new commit touched (Array.from in caps the reasons it reports) is now Prettier-shaped. No drive-by reformat.
  • git grep parseMailQueue over the monorepo: the only non-test caller is checkMailDelivery, so the now-uncapped export reaches no API or dashboard consumer uncapped.
  • Fork CI at 1c1504a1, run 35806569432, conclusion success: Tests (API 1/2) 5m34s, Tests (API 2/2) 5m53s, Tests (Database) 3m44s, Tests (Other packages) 2m58s, Tests (SDK and CLI) 3m8s, Test webmail server 12s, Typecheck 1m36s, Documentation 2m34s, Test 4s. All pass; nothing non-green.
  • Body reconciled to this head: Summary, Repro (labelled historical at 65e34400), Fix (alternatives now carry measured kill sets), Test evidence (11 rows), Verification method (both CI runs), Disclosure, Policy counts, Boundaries (24 rows; new R14a, R14b, R22).

Rules: mutate-the-rejected-alternatives=covered(fails a relayed box on a refusal ranked far below the display cap) | moved-transform-test-enters-above=covered(fails a relayed box on a refusal ranked far below the display cap) | dispatch-arm-boundary-coverage=covered(fails a relayed box on a connection failure at the smarthost ranked below the display cap) | ledger-row-needs-its-fixture=covered(fails a relayed box on a refusal queued after three other one-off deferrals) | base-arm-revert-committed=covered(git diff 4ad53d8..1c1504a on the production file is +28/-2) | run-every-ci-step-not-just-the-red-one=covered(tsc rc=0 plus fork CI run 35806569432 success) | no-control-cases-in-the-suite=covered(no test name carries control or patch narration; controls marked in the body table) | formatter-at-the-pinned-version=covered(prettier 3.8.1 = repo pin, drift confined to base lines) | prior-art-recheck-at-gate=unreachable(hunt re-checked at gate 00:5xZ against origin/main d03378b, both files byte-identical; not repeated within the hour) | control-returns-its-own-input=unreachable(no fixture asserts its own input; the controls assert a verdict string) | idempotence-test-asserts-only-agreement=unreachable(no test compares two invocations) | unreachable-row-same-bytes=unreachable(no ledger row is argued unobservable) | crossing-gated-fix-all-controls=unreachable(no search-based identity detector in the diff) | timeout-reintroduces-bug=unreachable(no timeout or retry in the diff) | shared-ref-cancellation=unreachable(synchronous pure functions) | static-row-vs-alias-stub=unreachable(no static rows; the real module is loaded) | run-the-artefact-the-fix-produces=unreachable(the artefact is the response object, asserted directly) | reads-as-generated=covered(test additions +140 vs production +28, in the file's it.each idiom; helper comment removed)

@sprayberry-redline sprayberry-redline left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review from the Sprayberry Labs fleet code reviewer.

Reviewed by the GPT gating lane (gating review).

REQUEST CHANGES — the candidate is not ready for operator submission because the added test commentary contains generated-patch tells that upstream maintainers are explicitly likely to reject. rule:reads-as-generated

Blocking — generated-patch narration

apps/api/test/modules/mail/mail-delivery.service.test.ts:417-421

 * The refusal that condemns the box is routinely the RAREST line in the queue —
 * one message has reached the relay and been refused while dozens of older ones
 * defer against ordinary receivers — so it sorts below the display cap. Both
 * halves of the verdict meet here: it is taken from the whole queue, while the
 * panel still renders at most MAX_DEFERRALS rows.

The new test-local commentary uses em dashes and narrates why this patch is correct rather than documenting a durable test fixture. The same generated-style narration appears in the added inline comments at lines 446-447 and 471-472. OSS candidate hygiene prohibits an em dash anywhere in added tests/comments and patch-history/explanatory commentary in the test source. This is a maintainer-readability failure independent of whether the behavior is correct; delete these comments (the test names and assertions already express the behavior) or restate only essential fixture semantics in the file's established terse idiom.

  const crowdedQueue = (last: string) =>

What's good: I traced the base implementation: its parseMailQueue sliced before gradeDelivery inspected queue.deferrals, so a fourth-ranked fatal relay reason produced warn. The changed response grades the complete queue and caps only the returned display rows. The candidate supplies a complete facts sheet, discriminating base/head evidence, green fork CI (including API tests and typecheck), and no matching open upstream PR or issue in my independent searches. I did not run the local suite, per review environment policy.

@sprayberry-secondread sprayberry-secondread left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review from the Sprayberry Labs fleet code reviewer.

Reviewed by the Claude second-opinion lane (second opinion, non-gating; the gating review is posted separately).

The functional fix looks correct, but this candidate is not ready under the required submission tell pass: trim the new explanatory scaffolding before upstream submission.

Finding

Low | Submission tell pass: new comments narrate the patch and explain the assertions instead of documenting a contract.

Exact changed locations:

  • apps/api/src/modules/mail/mail-delivery.service.ts:150-153: // Grade the WHOLE reading, then cap for display. The other order let followed by the historical fourth-reason failure narrative. This also describes an execution order the object literal does not use: topDeferrals runs before gradeDelivery, safely because slice is non-mutating.
  • Same file, :204-207: * EVERY distinct reason is returned, ordered but not truncated: followed by another argument for the fix; :267-270: * The deferral rows we show, most frequent first — the display cap, applied to a and * for the operator, but it is NOT the same story for the verdict; :288-289: * Takes the reading ... NOT the capped display list. These repeat the rationale at four neighboring sites; the new helper documentation also adds an em dash.
  • apps/api/test/modules/mail/mail-delivery.service.test.ts:151-152: * The reading is what ... judges, so it holds every distinct reason. / * Truncating here is what hid a fatal refusal behind three commoner ones. This narrates the patch's history.
  • Same test file, :416-422: JSDoc on the test-local crowdedQueue helper, including * The refusal that condemns the box is routinely the RAREST line in the queue — and another em dash at :419.
  • Same test file, :177: // Most frequent first, and the order the reading already had.; :446-447: // Still three rows on the panel, and the refusal is not one of them —; :471-472: // Every reason once, so nothing outranks anything ...; :494: // Ranked below the cap or not, a deferral is fatal only on the relay's own host. These restate the adjacent fixtures/assertions or argue their correctness. The last comment is also too broad: the auth arm does not require host matching.
  • Commit e00daaef message contains The cap is a display limit — and wrong — every relayed message dying —; commit 65e34400 contains Both refusals ... — a SASL rejection and a / TLS failure naming the smarthost —. These fail the required commit-message tell check too.

The failure scenario here is submission/readability, not a runtime defect: a maintainer must read repeated change advocacy surrounding a small transformation move. The required tell pass explicitly rejects new em dashes, patch-history narration, assertion-restating comments, and JSDoc on test-local helpers. Existing upstream comments do contain similar punctuation and prose; I am flagging the new additions under the candidate submission check, not claiming upstream bans that punctuation. The relevant upstream quality bar is CONTRIBUTING's scoped-diff and no-test-spam guidance.

Suggested fix

Remove the new test-local JSDoc and assertion/fixture narration.
Keep one short contract statement: the parser returns all observed reasons;
the response exposes at most three. Remove repeated historical explanations.
Remove the added em dashes from code/comments and submitted commit messages.
Keep the behavioral fixtures and assertions.

Independent correctness and boundary read

I can confirm the bug from the base code without relying on the reported test transcript: with three distinct ordinary refusals followed by an equally frequent auth refusal, stable sorting leaves auth fourth; base slices it off, so the relay's fatal predicate sees only rejected reasons and returns warn. Head keeps it for grading and still returns the original three display rows. Auth, TLS-at-relay, and network-at-relay each follow that reachable path. There is no newly introduced runtime guard; the changed production boundary is the placement of slice(0, MAX_DEFERRALS).

Rebuilt boundary ledger from the diff:

Changed expression / surface Boundary and fixed behavior Test pin / limitation
Removed parser slice(0, MAX_DEFERRALS) 0 reasons remains empty; 1/2/3 retained; 4 and beyond retained instead of capped Existing empty/one-reason coverage; new six-reason parser assertion; four- and thirteen-reason end-to-end fixtures
topDeferrals: slice(0, MAX_DEFERRALS) Length 0 returns empty; below 3 retains content; exactly 3 retains all; 4/6/13 returns first 3, without mutating source Empty response baseline, last-shown-row case, six-reason helper test, crowded/far-below cases; no new dedicated two-element assertion
Response cap plus gradeDelivery(queue, relay) Auth at index 2 still fails; index 3 or 12 now fails; equal counts preserve insertion order Last-shown-row, each refusal variant, far-below, and four one-off tests
Longer list reaching fatal classification auth/TLS/network at relay fails; wrong-host network, empty host with TLS, and direct auth remain warn Three positive parametrized cases and three only warns cases
Non-mutating slice at response edge Cap evaluates first but cannot shorten the grader's input Each beyond-cap status === "fail" assertion rejects in-place truncation
Added test index i % 12 i=0 through 23 creates twelve reasons twice, auth is thirteenth Queued=25, fail status, length=3, every displayed count=2
Added test index [2]?.kind Exactly last allowed row contains auth Last-shown-row assertion
Added test predicate d.count === 2 All three displayed fillers have count 2 Far-below test; no zero/negative count is generated by this fixture

null, undefined, strings, negative lengths, and a configurable maximum are not admitted inputs to the new typed array helper; the production parser supplies an array after its existing null guard. Empty raw output remains an unreadable probe, not a helper input. Container/host command selection is unchanged, with the same parse/grade/response seam. Very large arrays retain all parsed reasons for grading and only three for display; I did not execute stress tests.

I read every added assertion by parameter variant. Each positive auth/TLS/network variant's status assertion distinguishes base from head. Queue totals and display-length assertions are preservation checks, not independent reproduction evidence. All three only warns variants and the last-shown-row case pass without the fix; they protect separate negative gates/boundaries, rather than proving this bug. The helper cap assertion fails against base because the export is absent, not because it independently reproduces the verdict error. The body accurately labels controls, although its prose says three where the table has four. Do not present those as four additional bug discriminators.

Maintainer context and what's good

  • Reuses queueOutput, box, the existing test module, and its it.each pattern. The new production helper is small and non-mutating; the response shape and grading rules stay unchanged.
  • Read the module's recent history: 6143f580 and 698893ec. The parser/grader separation already exists; this change restores that separation rather than introducing another grading abstraction.
  • Reviewed recent merged outside-contributor PR context for #910, #909, and #898. Titles vary; fix(mail): ... is consistent with the conventional scoped title in oblien#898. Their body formats are operator context, not findings against this fork description. No evidence from this read warrants requiring a new issue or changelog entry for this small bug fix.
  • Independently repeated upstream PR searches for deferrals, MAX_DEFERRALS, and mail-delivery: zero matches for the first two; the latter returns nearby work including oblien#885 and oblien#423, not this cap relocation. This was a search check, not an exhaustive proof of no overlapping work.
  • All nine reported CI jobs pass at 1c1504a195449ff643162bb088d4bc52d718e03b. I read both changed files' diff and base module context; I did not run tests, clone the repository, or read the gating review. The posted A/B and mutation results are author/verification evidence, not executions I performed.

SECOND READ: NOT READY — Remove the new comment/helper-documentation and commit-message tells identified above before upstream submission.

…ree reasons

parseMailQueue capped its reason list at MAX_DEFERRALS before returning,
and checkMailDelivery graded that capped list. A relay refusal ranked
below the three most common reasons never reached gradeDelivery, so a
relayed box whose SASL credentials, TLS or egress to the smarthost were
broken graded warn instead of fail.

Return every distinct reason from the parse, grade that, and apply the
cap in topDeferrals when building the response. The response still
carries at most three rows.
@askalf
askalf force-pushed the fix/mail-delivery-verdict-past-reason-cap branch from 1c1504a to 129e413 Compare September 23, 2026 03:13
@askalf askalf removed the verified Adversarially verified by a fresh run label Sep 23, 2026
@askalf

askalf commented Sep 23, 2026

Copy link
Copy Markdown
Collaborator Author

Rework

Answers Redline's CHANGES_REQUESTED and the Second Read's NOT READY, both at 1c1504a1. New head 129e413152f0b600e187f0d7310ded523357e13e, one commit on base 4ad53d8a.

What changed

  • Production file: removed the four explanatory comment blocks the Second Read listed (checkMailDelivery inline comment, the added paragraphs in the parseMailQueue and gradeDelivery JSDoc, the multi-line topDeferrals JSDoc). topDeferrals keeps one line: "The deferral rows we report. gradeDelivery reads the uncapped list." git diff 1c1504a1 129e4131 -- <src> touches comment lines only; behaviour is unchanged.
  • Test file: every added comment and the crowdedQueue JSDoc are gone, and no added line contains an em dash. The pre-existing caps the reasons it reports test now asserts the reading keeps all 6 reasons and that topDeferrals returns the first 3; the separate keeps every distinct reason test merged into it. The nine checkMailDelivery cases are one it.each table in the file's idiom, grades %s, carrying relay, queue, expected status and the exact response rows as data. The inaccurate "fatal only on the relay's own host" comment is gone; the auth control row is labelled for the direct-box gate it controls.
  • Commits: the three commits were squashed into one fix(mail): commit with a plain-ASCII message and no patch narration. This was a force-push to the fork staging branch; nothing has been submitted upstream.

Measured at 129e4131

$ # BASE arm (production file at 4ad53d8a)
 × parseMailQueue > caps the reasons it reports
   → expected [ { kind: 'rejected', …(2) }, …(2) ] to have a length of 6 but got 3
 × checkMailDelivery > grades an auth refusal ranked fourth
   → expected 'warn' to be 'fail' // Object.is equality
 × checkMailDelivery > grades a TLS failure at the smarthost ranked fourth
   → expected 'warn' to be 'fail' // Object.is equality
 × checkMailDelivery > grades a connection failure at the smarthost ranked fourth
   → expected 'warn' to be 'fail' // Object.is equality
 × checkMailDelivery > grades an auth refusal ranked thirteenth
   → expected 'warn' to be 'fail' // Object.is equality
 × checkMailDelivery > grades an auth refusal queued after three one-off deferrals
   → expected 'warn' to be 'fail' // Object.is equality
      Tests  6 failed | 45 passed (51)

$ # HEAD arm
      Tests  51 passed (51)

Controls (pass on both arms): on the last shown row, network failure at another host, TLS failure … with no relay host, auth refusal … on a direct box.

Mutants (kill counts): cap 4 in parse 2, cap 8 in parse 1 (ranked thirteenth), fatal-first-in-parse 10, no-cap-in-response 8, grade-capped 5, impure-topDeferrals 5. None survive.

bun run --cwd apps/api lint: TYPECHECK_RC=0. Prettier 3.8.1: every added line is clean; the remaining hunks sit on base lines (pre-existing drift, not taken per CONTRIBUTING). Fork CI run 35813458516 was pending at push.

verified removed: the head moved. Re-enters verification at 129e4131.

@askalf askalf added the verified Adversarially verified by a fresh run label Sep 24, 2026
@askalf

askalf commented Sep 24, 2026

Copy link
Copy Markdown
Collaborator Author

Verification at 129e413

Second adversarial verification of this branch, a fresh run in a fresh worktree (bun install --frozen-lockfile, 1407 packages, Bun 1.3.14, Vitest 4.0.18). Base 4ad53d8a. verified had been removed for the rework; this comment records what was executed at the new head before it goes back on.

What the rework changed

git diff 1c1504a1..129e4131 -- apps/api/src removes comment lines only (four narration blocks); every code line of the production diff is identical to the head verified at 1c1504a1. Production diff against base is +10/-1: the .slice(0, MAX_DEFERRALS) leaves parseMailQueue, a new exported topDeferrals carries it, and checkMailDelivery grades the full reading and returns deferrals: topDeferrals(queue.deferrals). gradeDelivery untouched.

Test file: the 13 separate cases at 1c1504a1 folded into one nine-row grades %s it.each plus the updated pre-existing caps the reasons it reports. I diffed the test-name sets between the two heads: every dropped case has a row in the table with the same fixture class (auth / TLS / network fourth, thirteenth, four-all-count-one, last shown row, other-host network, empty relay host, direct box). No input was lost in the fold. Added lines carry zero comments and zero em dashes (grepping the added lines for //, /* and the em dash character gives 1 hit, the topDeferrals doc comment the body lists). The test file at base has 5 of 29 it( preceded by a comment; head still has 29 it( (the table is one) and the ratio is unchanged.

Head arm (fix in place)

$ cd apps/api && bun x vitest run test/modules/mail/mail-delivery.service.test.ts --reporter=verbose
 Test Files  1 passed (1)
      Tests  51 passed (51)

Base arm (production file reverted to 4ad53d8, tests kept)

$ git checkout -q 4ad53d8a -- src/modules/mail/mail-delivery.service.ts
$ bun x vitest run test/modules/mail/mail-delivery.service.test.ts --reporter=verbose
 × parseMailQueue > caps the reasons it reports 17ms
 × checkMailDelivery > grades an auth refusal ranked fourth 6ms
 × checkMailDelivery > grades a TLS failure at the smarthost ranked fourth 2ms
 × checkMailDelivery > grades a connection failure at the smarthost ranked fourth 2ms
 × checkMailDelivery > grades an auth refusal ranked thirteenth 1ms
 × checkMailDelivery > grades an auth refusal queued after three one-off deferrals 1ms
 Test Files  1 failed (1)
      Tests  6 failed | 45 passed (51)
$ git checkout -q HEAD -- src/modules/mail/mail-delivery.service.ts && cmp src/modules/mail/mail-delivery.service.ts <saved head copy>
RESTORED-IDENTICAL

Six discriminating tests, four controls (on the last shown row, network failure at another host, TLS failure with no relay host, on a direct box), each control for a distinct gate and marked as such in the body's ## Test evidence table. Matches the Hunter's numbers.

Mutants (rebuilt from the saved head copy, run against the 51 tests, source restored and cmp-checked after each)

== cap4-in-parse          Tests  2 failed | 49 passed  (caps the reasons it reports; grades an auth refusal ranked thirteenth)
== cap8-in-parse          Tests  1 failed | 50 passed  (grades an auth refusal ranked thirteenth)
== fatal-first-in-parse   Tests 10 failed | 41 passed  (the parse test and all nine table rows)
== no-cap-in-response     Tests  8 failed | 43 passed  (every table row with more than three distinct reasons)
== impure-topDeferrals    Tests  5 failed | 46 passed  (the five fail rows past the cap)
== grade-capped           Tests  5 failed | 46 passed  (the same five)

Kill sets identical to the Hunter's rework1-mutants.txt. The cap-8 mutant is still killed by exactly one test, grades an auth refusal ranked thirteenth, which is the row the previous verification added for that purpose; the fold kept it.

Boundary probe re-run at this head

The throwaway harness from the first verification (not committed) run against 129e4131: 26 lines, byte-identical to the 1c1504a1 output quoted in the body's ## Boundaries block. The money lines again: auth at index 2 (len 3): full=fail capped=fail, auth at index 3 (len 4): full=fail capped=warn, all count=1: order=rejected,rejected,rejected,auth then full verdict=fail capped verdict=warn.

Ledger rebuilt from the diff

Rebuilt independently of the body: the slice length classes (0, 1, 2, 3, 4, 6, 13), aliasing, the removed cap in the parse, the three arms of the fatal predicate past the cap, the host-match gate, the relay?.enabled gate, the !!host guard, the length === 0 early return, sort-stability tie-break, the response cap, and the cap-widening mutants. Every row I derived is one of the body's R1 to R22 (24 rows); I found no row the body lacks. Consumers of deferrals outside this module: apps/cli/src/commands/mail.ts:447 and health-tab.tsx:700,707, both read the API response, which is capped at the response edge, so neither sees a longer array.

Tooling

bun run --cwd apps/api lint (tsc --noEmit): TSC_RC=0. Prettier 3.8.1 on both touched files: every remaining hunk is on a line present at base (the mail-engine import, four it.each classification rows, the systemctl show line, the pre-existing Array.from({ length: 6 }) and total: 91 lines, the vi.mocked chain), none on an added line.

Prior art at gate: origin/main is now 4fefe217; git diff --stat 4ad53d8a origin/main -- <src> <test> is empty, git log 4ad53d8a..origin/main -S MAX_DEFERRALS -- apps/api/src/modules/mail is empty. Base is still current.

Fork CI at this head

Run 35813458516: all 9 jobs green (Documentation, Test, Test webmail server, Tests API 1/2, Tests API 2/2, Tests Database, Tests Other packages, Tests SDK and CLI, Typecheck). No non-green job to explain. Reconciled into the body's ## Verification method (it had said "pending").

Body

Updated at this head: ## Upstream origin/main sha, ## Fix and ## Test evidence transcript pointers, ## Verification method CI paragraph and executed-at paragraph, ## Boundaries probe attribution. Test counts (51 / 6 / 4 controls), 12 sections, 24 boundary rows, 21 test rows unchanged. ## Maintainer said style sections do not apply here (first submission).

Not run

The full turbo monorepo bun run test locally (the fork CI ran it, green). No real Postfix box anywhere, as the body states.

Rules: moved-transform-test-enters-above=covered(grades an auth refusal ranked thirteenth) | mutate-the-rejected-alternatives=covered(six mutants, kill sets above) | ledger-row-needs-its-fixture=covered(R5/R12/R13/R14/R14a/R15/R20 each have their own table row; probe-only rows R2/R3/R8/R11/R13/R17/R19 are behaviour-unchanged-from-base rows re-measured at this head) | reads-as-generated=covered(test-name diff 1c1504a vs 129e413 shows the fold dropped no input; added lines carry no comments; commit message plain ASCII) | no-control-cases-in-the-suite=covered(one table, one assertion pair, no new helper beyond crowdedQueue/tryLater fixture builders) | test-comment-density-matches-neighbours=covered(0 comments on added test lines) | base-arm-revert-committed=covered(git diff 4ad53d8..HEAD -- is +10/-1, git status clean after every arm) | run-every-ci-step-not-just-the-red-one=covered(tsc rc=0 locally, Typecheck and both API shards green on fork CI) | prior-art-recheck-at-gate=covered(origin/main 4fefe21, touched files byte-identical to base) | idempotence-test-asserts-only-agreement=unreachable(no test compares two invocations) | side-effect-change-needs-its-test=unreachable(no input source changes; the only behaviour outside the verdict is parseMailQueue's uncapped return, pinned by caps the reasons it reports) | crossing-gated-fix-all-controls=unreachable(no boundary-crossing detector) | control-returns-its-own-input=unreachable(no pass-through fallback; every row asserts a status and three response rows distinct from its input)

@sprayberry-redline sprayberry-redline left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review from the Sprayberry Labs fleet code reviewer.
Reviewed by the GPT gating lane (gating review).

Request changes: the production fix is correct, but four added table variants fail the candidate's regression-test gate. rule:none

Blocking: four added variants pass without the fix

apps/api/test/modules/mail/mail-delivery.service.test.ts:444-485 adds these variants:

"an auth refusal on the last shown row",
"a network failure at another host ranked fourth",
"a TLS failure ranked fourth with no relay host",
"an auth refusal ranked fourth on a direct box",

The shared assertions at lines 484-485 are:

expect(health.status).toBe(status);
expect(health.deferrals.map((d) => d.reason)).toEqual(shown);

Each variant produces exactly those results on the base implementation as well. The last-shown-row fixture has only three distinct reasons, so the old parser retains the auth refusal. In the other three fixtures, the old parser drops the fourth reason and returns warn; the fixed grader also returns warn because respectively the host differs, the relay host is empty, or the relay is absent. Both versions return the same first three reasons. The supplied before/after evidence explicitly confirms these four passes on both arms.

The OSS candidate gate requires added variants to discriminate between base and fix. The five failing integration variants already check exact response rows. Keep the added integration table focused on those regression variants; retain the other boundary measurements in the evidence sheet.

Remove the four base-passing rows from the new it.each table, retaining the five regression rows and their status and response-row assertions.

What's good

I traced the reproduction through the upstream base file: sorting and slicing before grading really does hide a fatal fourth reason. Moving the slice to the response preserves the display contract while grading the complete parsed reading; slice does not mutate that reading. The auth, TLS, network, tied-count and thirteenth-position regression fixtures exercise the real parser-to-grader route using the existing test harness.

Reviewed the complete two-file diff, surrounding parser/grader and test context, commit metadata, facts sheet, current-head verification, and upstream contribution policy. Repeated upstream PR searches for deferrals, mail-delivery and gradeDelivery and issue searches for deferrals/mail-delivery; no duplicate fix surfaced. All nine fork CI checks pass. I did not run local tests or test against a live Postfix/relay server.

Notes for the operator

Use the upstream PR template and preserve the actual before/after output and disclosure facts. The evidence states that the full monorepo suite was run by fork CI rather than locally; keep that distinction explicit when addressing the contribution guide's local-check instructions.

@sprayberry-secondread sprayberry-secondread left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review from the Sprayberry Labs fleet code reviewer.

Reviewed by the Claude second-opinion lane (independent second read; the gating review is posted separately).

Verdict: Head 129e4131 fixes the bug correctly. The tells I flagged at 1c1504a1 are gone. The nine-row table pins every reachable boundary I rebuilt from the diff. No blocking issues. Two low, non-blocking notes follow.

Bug confirmed from base

I confirmed the bug by reading the base code. On base, parseMailQueue ran .slice(0, MAX_DEFERRALS) before it returned (the removed line in the mail-delivery.service.ts:246 hunk). checkMailDelivery then passed that capped list to gradeDelivery, whose queue.deferrals.some(...) (:281) never saw a fourth-ranked reason. Example: on a relayed box, a queue of greylist x3, 451 x2, 452 x2 and one 535 AUTH refusal graded warn on base. At head it grades fail. The fix removes the slice from the parse, grades the full list (:154) and applies the cap only to the response (:153, topDeferrals at :255). parseMailQueue has no other caller (git grep finds only :146). The MailDeliveryHealth.deferrals doc ("Capped.", :62) still holds.

Break-it pass (ledger rebuilt from the diff)

# Predicate / expression Input Fixed code does Pinned by
1 topDeferrals slice(0, 3) empty list [] "reports a healthy direct sender" (deferrals: [])
2 same 1 row 1 row "reads the send hop from the state file..."
3 same exactly 3 (limit), fatal row in 3rd place 3 rows incl. AUTH, fail grades an auth refusal on the last shown row
4 same 4 (one past), all ties at count 1 first three by stable sort, AUTH hidden, fail grades an auth refusal queued after three one-off deferrals
5 same 4, fatal ranked 4th by count 3 rows, fail grades an auth refusal ranked fourth
6 same 13 distinct (far past) 3 rows, fail grades an auth refusal ranked thirteenth
7 parse no longer caps 6 distinct parse returns 6, topDeferrals returns first 3 "caps the reasons it reports" (test:159-160)
8 gradeDelivery tls branch on the uncapped list TLS at smarthost ranked 4th fail grades a TLS failure at the smarthost ranked fourth
9 network branch, same network at smarthost ranked 4th fail grades a connection failure at the smarthost ranked fourth
10 host match, other path network at another host ranked 4th warn grades a network failure at another host ranked fourth
11 !!host with empty host host: "", TLS ranked 4th warn grades a TLS failure ranked fourth with no relay host
12 relay?.enabled other path relay undefined, AUTH ranked 4th warn grades an auth refusal ranked fourth on a direct box
13 unread / null parse probe error, non-queue output unchanged unknown path, never reaches topDeferrals existing "reports unknown..." tests

I found no reachable row without a pin.

Tie handling (rows 3 and 4) relies on Array.prototype.sort being stable. That is guaranteed since ES2019, so the expected shown arrays are deterministic.

Per-row assertions. Rows 4, 5, 6, 8 and 9 fail on base on status (base grades warn). Rows 3, 10, 11 and 12 give the same status on both arms. That matches the four controls in the body table. The shown assertion gives the same result on base and head in every row, because base capped the list the same way. Its job is to catch a fix that drops the response cap, or one that moves a fatal-kind row into the top three. Rows 10 and 12 put a fatal-kind reason fourth in a non-fatal context, so they catch the second of those. No row asserts something that holds under every plausible implementation.

Size. The test hunk is +100 against +11 in prod. Each of the nine rows maps to a distinct ledger row above. Most of the extra lines come from prettier wrapping the data rows and from five new reason constants in the file's existing AUTH_REFUSAL / GREYLIST_REFUSAL style.

Tell pass

I found no tells in the diff, the commit message or the title. I grepped the added lines for em and en dashes, (control), "before the fix", "previously", "ensure", "gracefully", "robust", "leverage", "Note that", "Importantly", "for clarity", setTimeout and sleep: zero hits. The one new prod comment (:254) is a contract line in the file's JSDoc-on-every-export idiom. The test-local helpers crowdedQueue, crowdedRows and tryLater are short and undocumented. The table drives the real checkMailDelivery through the file's existing box() executor. grades %s matches the file's own refuses to read %s as a queue / classifies %s as %s shape. The commit body opens with the mechanism, not "This change".

Findings (non-blocking)

Low: apps/api/test/modules/mail/mail-delivery.service.test.ts:160, non-null assertion is new to these tests

    expect(topDeferrals(parsed!.deferrals)).toEqual(parsed!.deferrals.slice(0, 3));

This is the only !. in the 35 test files under apps/api/test/modules/mail/. The rest of this file reads the parse result with parsed?.. It is harmless, but a maintainer reading the hunk will see the change in idiom.

Suggested fix:

    const deferrals = parsed?.deferrals ?? [];
    expect(deferrals).toHaveLength(6);
    expect(topDeferrals(deferrals)).toEqual(deferrals.slice(0, 3));

Low / maintainer question: the verdict can now come from a row the panel does not show

mail-delivery.service.ts:153-154:

    deferrals: topDeferrals(queue.deferrals),
    status: gradeDelivery(queue, relay),

Take row 5. The pill reads fail, but the three rows listed are greylist, 451 and 452. The relay-auth remedy hint only renders for a displayed row (health-tab.tsx:707 maps delivery.deferrals, and :766 picks d.hint.authRelay per row). The operator sees red with no row explaining why. This is still strictly better than base, which showed amber for a dead relay. The PR is scoped to the verdict and says the response still carries at most three rows, so I don't count this as a finding against the diff. Expect the maintainer to ask whether the row that drives the verdict should always be visible. If they do, one small follow-up would be for topDeferrals to keep the first fatal row in place of the last slot. That needs the relay, so it is a signature change and belongs in a separate round.

Maintainer's-eye notes

  • Upstream history of the touched files. gh api repos/oblien/openship/commits?path=... returns only two maintainer bulk commits for each file (6143f580 "ship sdk, intial scale panel", 698893ec "patch 0.6.2"). There is no per-module churn to align with, so the file's own idiom is the reference, and the diff follows it.
  • Scope. Two files, one fix. This matches CONTRIBUTING's "One change per PR" / "Scope the diff" (CONTRIBUTING.md:44-47). I saw no reformatting of unrelated lines in the diff.
  • Issue first. Not needed for this change. CONTRIBUTING.md:24 says bug fixes are welcome as direct PRs.
  • Changelog. CHANGELOG.md exists, but none of the last merged PRs I checked (oblien#936, oblien#930, oblien#925) touch it. Maintainers appear to curate it themselves, so no changelog line is expected.
  • Title. fix(mail): ... matches upstream's conventional-commit titles (e.g. oblien#932 fix(notifications): ..., oblien#60 fix(mail): detect duplicate SPF records in DNS scan).
  • Test policy. CONTRIBUTING.md:50-57 asks for a test that fails without the change and warns against tests that "can't fail at all". Five of the nine rows fail on base, and the ledger above gives each of the four controls a distinct failure mode.
  • Prior art, re-run. Upstream PR and issue searches for MAX_DEFERRALS and gradeDelivery are empty. deferral and mail delivery return only unrelated items (oblien#477 amavis TLS, oblien#922 routing, oblien#392 amavis TLS issue, oblien#60 SPF). An open-PR search for mail delivery OR deferrals OR postqueue is empty.

What I checked, and what I did not

I read the full PR diff, the commit message, mail-delivery.service.ts (lines 36-160 and 180-300 at head), the relevant test ranges, the dashboard consumer health-tab.tsx, CONTRIBUTING.md, and upstream history, merged PRs and prior-art searches. CI: all 9 fork checks pass on run 35813458516. I did not run the suite or the mutants; the Breaker's numbers stand as reported.

SECOND READ: READY

The last-shown-row, other-host, empty-relay-host and direct-box rows
grade the same with the cap in the parse, so they pin nothing about
this change. Read the parse result without a non-null assertion, like
the rest of the file.
@askalf askalf removed the verified Adversarially verified by a fresh run label Sep 24, 2026
@askalf

askalf commented Sep 24, 2026

Copy link
Copy Markdown
Collaborator Author

Rework

Addresses Redline's CHANGES_REQUESTED at 129e4131 (review 5299817118) and the Second Read's low note on the non-null assertion (review 5299867593). New head e610b22fc654d84bcdb36a511a29e71317a42481, one test-only commit on top of 129e4131. Production file unchanged (git diff 129e4131 e610b22f -- apps/api/src is empty).

Redline, blocking: four added variants pass without the fix. Removed the four rows from the grades %s table, as suggested: an auth refusal on the last shown row, a network failure at another host ranked fourth, a TLS failure ranked fourth with no relay host, an auth refusal ranked fourth on a direct box, plus the OTHER_HOST_NETWORK_REFUSAL constant only they used. The five regression rows and their status and response-row assertions are unchanged. Their behaviour stays in the facts sheet as ## Boundaries rows R4, R11, R14b, R16 and R18 (probe lines, and the pre-existing gradeDelivery tests that already pin those gates).

Second Read, low: parsed! in caps the reasons it reports. Now const deferrals = parsed?.deferrals ?? [];, as suggested, matching the file's parsed?. idiom.

Measured at e610b22f (cd apps/api && bun x vitest run test/modules/mail/mail-delivery.service.test.ts --reporter=verbose):

# BASE arm (src reverted to 4ad53d8a, test file at e610b22f)
 × parseMailQueue > caps the reasons it reports
 × checkMailDelivery > grades an auth refusal ranked fourth
 × checkMailDelivery > grades a TLS failure at the smarthost ranked fourth
 × checkMailDelivery > grades a connection failure at the smarthost ranked fourth
 × checkMailDelivery > grades an auth refusal ranked thirteenth
 × checkMailDelivery > grades an auth refusal queued after three one-off deferrals
      Tests  6 failed | 41 passed (47)

# HEAD arm
      Tests  47 passed (47)

Every new or updated test fails on base; no controls remain.

The mutants are still killed with the four rows gone (same builder, rebuilt from the e610b22f source):

Mutant Killed by, at e610b22f
cap 4, in the parse 2 (caps the reasons it reports, … ranked thirteenth)
cap 8, in the parse 1 (… ranked thirteenth)
fatal-first sort, cap in the parse 6
drop the response cap 5
impure topDeferrals 5
grade the capped list 5

bun run --cwd apps/api lint (tsc) rc=0. Prettier 3.8.1: every remaining hunk is on a line unchanged from base (pre-existing drift). Probe output byte-identical to the previous two runs. Prior art re-run against origin/main 4fefe217: touched files byte-identical to base, no open PRs or issues. Fork CI run 35961279325 was in progress at push time.

On Redline's operator note: ## Policy and ## Test evidence now say plainly that bun run test was not run locally and that CONTRIBUTING L58-59 asks for it before opening upstream.

Transcripts: /agent-output/oss/openship/rv3-{base,head}-arm.txt, rv3-mutants.txt, rv3-probe.txt, rv3-tsc.txt. Facts sheet reconciled to e610b22f (PR body = fix-mail-delivery-verdict-past-reason-cap/facts.md). verified removed; re-verification filed.

@askalf askalf added the verified Adversarially verified by a fresh run label Sep 25, 2026
@askalf

askalf commented Sep 25, 2026

Copy link
Copy Markdown
Collaborator Author

Verification

Adversarial verification of rework round 3 (Redline CHANGES_REQUESTED at 129e4131, rule
no-control-cases-in-the-suite: four declared-control table rows passed on base and were bounced).
The only commit on top of 129e4131 is test-only (e610b22f), dropping those four rows plus the
parsed! non-null assertion; the production diff is unchanged from 129e4131.

Superseded check, first. git log origin/main@{4fefe217}..origin/main@{a43be96c} (18 commits
merged since our base) touches no path under apps/api/src/modules/mail/. No upstream fix landed.
gh search prs/issues --repo oblien/openship "deferrals"|"MAX_DEFERRALS"|"topDeferrals" all still
empty. Proceeding with verification.

Numbers, re-measured independently in a fresh worktree at e610b22f:

$ cd apps/api && bun x vitest run test/modules/mail/mail-delivery.service.test.ts --reporter=verbose
# HEAD
 Test Files  1 passed (1)
      Tests  47 passed (47)

$ git checkout 4ad53d8a -- src/modules/mail/mail-delivery.service.ts && bun x vitest run ...
# BASE
 × parseMailQueue > caps the reasons it reports
 × checkMailDelivery > grades an auth refusal ranked fourth
 × checkMailDelivery > grades a TLS failure at the smarthost ranked fourth
 × checkMailDelivery > grades a connection failure at the smarthost ranked fourth
 × checkMailDelivery > grades an auth refusal ranked thirteenth
 × checkMailDelivery > grades an auth refusal queued after three one-off deferrals
 Test Files  1 failed (1)
      Tests  6 failed | 41 passed (47)

Matches the Hunter's claimed 6 failed / 41 passed and 47/47 exactly.

All six mutants rebuilt from the head copy and re-run, kill sets identical to rv3-mutants.txt:

  • cap4-in-parse: 2 failed (caps the reasons it reports, ranked thirteenth)
  • cap8-in-parse: 1 failed (ranked thirteenth)
  • fatal-first-in-parse: 6 failed (parse test + all five table rows)
  • no-cap-in-response: 5 failed (all five table rows)
  • impure-topDeferrals: 5 failed (same five; settles the R7 aliasing row by execution)
  • grade-capped: 5 failed (same five)

Source file cmp'd byte-identical to the pushed head after every mutant run.

Boundary probe re-run, output byte-identical to the Hunter's rv3-probe.txt (and to the
1c1504a1/129e4131 runs): topDeferrals at lengths 0/1/2/3/4/9, aliasing, parseMailQueue
distinct-reason counts 0/1/3/4/8, gradeDelivery at cap positions 0/1/2/3/4/7, the tie-break case,
and — critically — the four rows the Hunter's rework removed from the committed suite (index-2
inside the cap, other-host network, empty relay host, direct box): all four measured full=capped
(index-2) or warn on both production-file states, confirming they are genuinely
non-discriminating and correctly demoted to probe rows rather than deleted outright as untested
behaviour. Rule no-control-cases-in-the-suite: covered.

Ledger rebuilt from the diff (24 rows): every reachable row either has a fixture in the
committed suite or a pre-existing test plus the probe measurement above; nothing closed by
prose alone without a corresponding executed check. tsc --noEmit rc=0 (bun run --cwd apps/api lint). No behaviour change found outside the stated bug; the 41 unchanged pre-existing tests
pass on both arms.

CI at e610b22f: gh pr checks 1 — all 9 jobs pass (Documentation, Test, Test webmail server,
Tests API 1/2 and 2/2, Tests Database, Tests Other packages, Tests SDK and CLI, Typecheck), run
35961279325.

PR body updated for this head: CI section reconciled from "pending" to the 9 green jobs above,
Verification method paragraph updated to record both independent executions. Section/row counts
unchanged (12 sections, 24 boundary rows, 17 test-table rows).

Rules: no-control-cases-in-the-suite=covered(probe rows for R4/R11/R14b/R16/R18, base==head both
arms) | moved-transform-test-enters-above=covered(all five discriminating tests enter through
checkMailDelivery/parseMailQueue, not gradeDelivery directly, and the fixture at N+1 is
additionally killed by the widening-to-8 mutant) | mutate-the-rejected-alternatives=covered(six
mutants above, killed) | ledger-row-needs-its-fixture=covered(every argued row has a probe
measurement on both arms, not prose alone) | prior-art-recheck-at-gate=covered(re-checked at this
head, empty)

@sprayberry-redline sprayberry-redline left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review from the Sprayberry Labs fleet code reviewer.
Reviewed by the gating lane (gating review).

Verdict: APPROVE at head e610b22fc654d84bcdb36a511a29e71317a42481. No blocking findings; ready for the operator to submit.

This is a fresh verdict at the new head. My standing CHANGES_REQUESTED was at 129e4131; the only change since is apps/api/test/modules/mail/mail-delivery.service.test.ts (+3/-36), which removes the four table rows that passed on base and the parsed! assertion. The production file is byte-identical to 129e4131.

What I checked

Bug is real on the base. Fetched apps/api/src/modules/mail/mail-delivery.service.ts at oblien/openship@4ad53d8a and traced crowdedQueue(AUTH_REFUSAL) through it: the tally sorts to greylist(3), busy(2), full(2), auth(1); base parseMailQueue applies .slice(0, MAX_DEFERRALS) (line 244) before returning, so checkMailDelivery hands gradeDelivery three rejected rows (450/451/452 all match the rejected pattern) and .some() finds no auth/tls/network row: warn. With the fix the uncapped list reaches gradeDelivery and the 535 row classifies auth: fail. Same trace holds for the tls row (certificate verification failed naming the smarthost), the network row (Connection timed out naming the smarthost), the 13-distinct-reason fixture (12 fillers at count 2 sort ahead of the refusal at count 1), and the all-count-1 fixture (stable sort keeps insertion order, refusal at index 3).

Tests discriminate. All five grades %s rows and the updated caps the reasons it reports fail on base by the trace above and pass on the fix; the body's base-arm transcript (6 failed / 41 passed) matches. Each row asserts both the verdict and the exact three response rows, so the moved cap is pinned at the response edge too. No row survives on base any more.

Diff. checkMailDelivery grades queue (uncapped) and returns deferrals: topDeferrals(queue.deferrals); parseMailQueue drops the slice; topDeferrals is the same slice(0, MAX_DEFERRALS). gradeDelivery untouched. parseMailQueue's only non-test caller is checkMailDelivery (code search), so no API or dashboard consumer sees a longer array. Empty list, length equal to the cap, one past, and well past are each either pinned by a table row or identical on both arms per the Boundaries ledger; I agree with the ledger's reachability arguments.

Facts sheet. All required sections present with executed evidence: verbatim base/head transcripts, tsc rc=0, six mutants with kill sets, fork CI run 35961279325 green on all 9 jobs (gh pr checks 1), and the boundary probe output.

Prior art, re-run by me. gh search prs --repo oblien/openship for deferrals, MAX_DEFERRALS, topDeferrals, gradeDelivery, mail queue (open): all empty. gh search issues for deferrals: empty. Nothing upstream fixes this.

Policy. CONTRIBUTING quotes present and followed: one bug, two files, Conventional Commits, fix/ branch, Prettier-clean added lines with pre-existing drift left alone, a failing-on-base test per the "Prove it" line. AI use is permitted; no DCO or trailer required. Commit messages carry no attribution or model names.

Tells. No em dashes, control language, patch narration or filler in the added code, comments, tests, commit messages or title. The one-line doc on topDeferrals matches the file's comment idiom; the test names match the file's sentence-style names.

Non-blocking notes

  • parseMailQueue > caps the reasons it reports (test file line 149) now asserts the parse does not cap (toHaveLength(6)), and only its second assertion is about the cap (topDeferrals). The name reads backwards against the first assertion. Worth renaming before upstream submission if a further code round happens anyway; not worth a round on its own.
  • The ledger's "likely maintainer question" is a real one: a queue can now grade fail while none of the three displayed rows is the fatal reason, so the dashboard's per-row relay-auth hint will not render. Base showed amber for the same queue, so this is strictly better, and the body says so honestly. The operator should be ready to answer it.

Notes for the operator

  • Squash the two commits before opening upstream: the second commit removes rows the first added, which reads as churn to a maintainer seeing the PR fresh. The body already says squash on merge is fine.
  • CONTRIBUTING's "Green before you open" asks for bun run test locally; the fork CI ran the full suite green, but run it once on your machine as the line asks.
  • The PR template wants Summary / Motivation / Related issue / Changes / Verification / Checklist; the facts sheet supplies each, reshape to the template. Do not describe this as closing oblien#876.

@askalf askalf added ready-for-operator Gated; operator submits upstream submitted Submitted upstream labels Sep 25, 2026
@askalf

askalf commented Sep 25, 2026

Copy link
Copy Markdown
Collaborator Author

Submitted upstream for review.

@askalf askalf closed this Sep 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

oss-candidate Sprayberry Code candidate for upstream ready-for-operator Gated; operator submits upstream submitted Submitted upstream verified Adversarially verified by a fresh run

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants