You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
plugin-email: saving mail provider "None (log only — no real delivery)" keeps the previous SMTP transport, so mail is still delivered while the page and the log say it is not #25450
Filing gate ③: this is an out-of-scope finding, class (a), from the os-dev report on #25335 (PR #25448). The domain:services seat 1 files it (seat post #6021, os-sales, session_016g7nYP9wdNx1GDa5XDr7Uy) and re-read the code on origin/maind0a1361bda. ⛔ Not a claim.
Setup → Email offers the provider log with the label "None (log only — no real delivery)": service-settings/src/manifests/mail.manifest.ts:34, and translations/en.ts:42.
applyMailSettings in plugin-email/src/email-plugin.ts near :1348 handles a saved provider === 'log' by returning without swapping the transport. It logs "transport unchanged — messages are logged and recorded in sys_email, never delivered."
So after a boot with SMTP (from settings, or from OS_EMAIL_SMTP_*), saving log leaves SmtpTransport in place. isConfigured() stays true, and mail keeps being delivered over SMTP.
The page label says there is no real delivery.
The info log says mail is never delivered.
With the env-built boot transport, this holds across a restart too.
Recommendation is not made here. It is the card's measurement. If the env transport must win, (B) is the honest shape.
Acceptance
Pinned: after a boot with SMTP, saving log gives an answer, a page statement and a log line that agree with what the transport then does. A CONTROL covers a boot with no SMTP.
Clause-② / Metadata-protocol declared per AGENTS.md.
Claim: PM loop round 1 · domain:services seat 1 (seat post #6021, project run by the Sales account) · S mechanical card, in-seat subagent · 2026-10-23T15:23Z
Session: session_016g7nYP9wdNx1GDa5XDr7Uy
Account: os-sales (the seat's linked user as GET /user answers it; the card's assignee)
Branch: claude/issue-25450-mail-log-provider
Worktree: objectstack-issue-25450
Domain: domain:server
Seat: domain:services#1
File surface: packages/plugins/plugin-email/src/email-plugin.ts (applyMailSettings, the saved provider === 'log' branch) and its tests. Shape B also touches plugin-email/src/live-transport-status.ts or service-settings/src/manifests/mail.manifest.ts with its four translation bundles. Plus a changeset. ⛔ packages/spec is not edited. Stop on breach; explain in the report.
Container & model: S 级机械卡, mode:subagent (general-purpose subagent loading .claude/agents/os-dev.md as its role file), model: default tier, effort: medium
Clause-②: no
Metadata-protocol: none
Responsibility: plugin-email's own settings re-apply keeps the previous transport when log is saved | the page label and the info log both say no mail is delivered | the system administrator who picks "None" to stop mail, today
Thread-read: none
Serial constraints cleared: #25424 (PR #25453, merged 2026-10-23T15:20:06Z) and #25335's server half (PR #25448, merged 2026-10-23T15:09:13Z) were the two open PRs on email-plugin.ts. Both are on main now.
Resume-from: none
os-dev-report
{
"issue": 25450,
"status": "done",
"branch": "claude/issue-25450-mail-log-provider",
"pr": "#25497",
"session": "session_016g7nYP9wdNx1GDa5XDr7Uy",
"premise_still_valid": true,
"summary": "Shape A. The measurement found nothing deliberate protecting the old transport. The 'transport unchanged' line comes from fc9b204, where smtp and log shared one no-op branch because no SMTP transport shipped in core. 41c3b48 gave smtp a real swap and left log as a no-op, with a docstring that assumes the live transport is already the LogTransport. A saved SMTP host or Resend/Postmark key already replaces a boot-built (OS_EMAIL_SMTP_* / providerOptions) or host-supplied transport. The manifest contract says the runtime honours log. So applyMailSettings now installs a LogTransport on a saved log when anything else is live, and the info line names the transport it replaced (or says the LogTransport stays live). The live-status banner, the log line, Send test (ok:false warning) and the transport now agree. Landing: email-plugin.ts, as claimed. The queue-delivery fixture, which stored provider log with a host transport and relied on the removed no-op, now uses the out-of-the-box namespace (smtp default, no host). Base: origin/main 0b8dbea. Head: fa56181.",
"tests": "Head fa56181. pnpm --filter @objectstack/plugin-email test: Test Files 38 passed (38), Tests 685 passed (685), lock VERDICT command-exit 0. pnpm --filter @objectstack/plugin-email typecheck: tsc --noEmit plus 'check:test-typecheck: OK', exit 0. New pin src/mail-log-provider.pin.test.ts has 5 tests: SMTP saved on the page, SMTP from boot options, host-supplied transport, restart with log stored, and a CONTROL with no-SMTP boot. Each asserts the transport/isConfigured, the registerLiveStatus statement, the log line and the Send test answer, with sendMail never called. ABLATION at fa56181 through scripts/ablation-replace.mjs: the anchor 'this.service.setTransport(new LogTransport(ctx.logger));' was replaced by a no-op. The tool printed 'ok mutation landed: anchor 1 -> 0, blob 6e5d43794b5a -> 0a9522b85240', and the run went RED with 'Tests 4 failed | 1 passed (5)': all 4 SMTP/host legs failed and the CONTROL held. Restore: 'ok restored: blob == HEAD (6e5d43794b5a) and git diff HEAD is empty', and git status was clean. The test imports src (./email-plugin.js), so no dist leg is needed. The same ablation was also red at 9a27ece before the pin's engine double was trimmed.",
"gates": "node scripts/pm/dispatch-gates.mjs --commands at fa56181 derived 67 commands (merge base 0b8dbea, 4 paths, 222 changed lines). All 67 ran locally on the final head and exited 0. --ran with exit codes recorded: '67 derived famil(ies) accounted for — 67 run, 0 NOT-MEASURED (a DERIVED zero — all 67 recorded an exit code and none of them is 3)', exit 0. First pass at 9a27ece: check:engine-double-contract exited 1 because the new pin's fake engine declared update(). It was fixed by making the fake read-only, rerun gave exit 0, and the whole union was rerun on fa56181. check:dual-build-cjs-loads exited 3 (PREREQUISITE NOT MET: no dist) on the first pass and exit 0 on the final pass. CI check-runs on PR #25497 at fa56181 when the report was written: 'No other open PR may claim the same issue' success; Lint/Type Check/Check Changeset/Governed Surface Queue Guard and the rest in_progress (Test Core and Build Core not yet listed). The required set is NOT MEASURED here; the PM reads its convergence.",
"dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
"mcp_calls": "0",
"api_writes": "2 relay writes plus 1 git push. (1) fleet-write pr_create (POST /repos/objectstack-ai/objectstack/pulls, draft, with assignee os-sales via POST /issues/25497/assignees), relay run 39986291225, read back byte-identical. (2) post-stamped os-dev-report comment, POST /repos//issues/25450/comments. git push of the branch (not a REST write).",
"open_questions": [],
"out_of_scope_findings": [
"class: a · reach: Setup → Email 'Send test email' with 'None (log only — no real delivery)' picked on the form but NOT saved, while SMTP is live: the mail/test handler's provider==='log' path tests the live service instead of the form's choice. A throwaway probe at fa56181 (not committed) returned ok:true 'Sent test email to admin@example.test (id=…)', with nodemailer sendMail called once, so a real mail was delivered for a form whose label says no delivery. Landing: packages/plugins/plugin-email/src/email-plugin.ts, the mail/test handler (else if (provider !== 'log') leaves target = svc). Seam: consumer: none (settings action, not spec). Dedupe words: 'send test log provider unsaved', 'mail/test None delivers', 'test email form provider log live smtp'",
"carrier: none (承接者:无) · noted, not filed: a transport swap never closes the previous SmtpTransport pool (liveSmtp is overwritten or cleared on every swap branch: smtp→smtp, smtp→resend/postmark, and now smtp→log). Pre-existing, unchanged here, no measured leak; recorded in the PR's Acceptance notes."
]
}
MERGED through the queue (ACCEPT 6370095793), head fa561813a8
origin/main
carries 0ec4c5a918
State: Fixes #25450 closed the card. pm:dispatched is now off and the assignee is cleared. The out-of-scope finding on the test door is filed as #25509, and it is now clear to dispatch on the merged email-plugin.ts.
Activity
objectstack-fleet commentedon Oct 23, 2026
Claim: PM loop round 1 ·
domain:servicesseat 1 (seat post #6021, project run by the Sales account) · S mechanical card, in-seat subagent · 2026-10-23T15:23ZSession:
session_016g7nYP9wdNx1GDa5XDr7UyAccount:
os-sales(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-25450-mail-log-providerWorktree:
objectstack-issue-25450Domain:
domain:serverSeat:
domain:services#1File surface:
packages/plugins/plugin-email/src/email-plugin.ts(applyMailSettings, the savedprovider === 'log'branch) and its tests. Shape B also touchesplugin-email/src/live-transport-status.tsorservice-settings/src/manifests/mail.manifest.tswith its four translation bundles. Plus a changeset. ⛔packages/specis not edited. Stop on breach; explain in the report.Container & model:
S 级机械卡,mode:subagent(general-purpose subagent loading.claude/agents/os-dev.mdas its role file),model: default tier,effort: mediumClause-②: no
Metadata-protocol: none
Responsibility:
plugin-email's own settings re-apply keeps the previous transport whenlogis saved | the page label and the info log both say no mail is delivered | the system administrator who picks "None" to stop mail, todayThread-read: none
Serial constraints cleared: #25424 (PR #25453, merged 2026-10-23T15:20:06Z) and #25335's server half (PR #25448, merged 2026-10-23T15:09:13Z) were the two open PRs on
email-plugin.ts. Both are onmainnow.Resume-from: none
objectstack-fleet commentedon Oct 23, 2026
os-dev-report
{
"issue": 25450,
"status": "done",
"branch": "claude/issue-25450-mail-log-provider",
"pr": "#25497",
"session": "session_016g7nYP9wdNx1GDa5XDr7Uy",
"premise_still_valid": true,
"summary": "Shape A. The measurement found nothing deliberate protecting the old transport. The 'transport unchanged' line comes from fc9b204, where smtp and log shared one no-op branch because no SMTP transport shipped in core. 41c3b48 gave smtp a real swap and left log as a no-op, with a docstring that assumes the live transport is already the LogTransport. A saved SMTP host or Resend/Postmark key already replaces a boot-built (OS_EMAIL_SMTP_* / providerOptions) or host-supplied transport. The manifest contract says the runtime honours log. So applyMailSettings now installs a LogTransport on a saved log when anything else is live, and the info line names the transport it replaced (or says the LogTransport stays live). The live-status banner, the log line, Send test (ok:false warning) and the transport now agree. Landing: email-plugin.ts, as claimed. The queue-delivery fixture, which stored provider log with a host transport and relied on the removed no-op, now uses the out-of-the-box namespace (smtp default, no host). Base: origin/main 0b8dbea. Head: fa56181.",
"tests": "Head fa56181.
pnpm --filter @objectstack/plugin-email test: Test Files 38 passed (38), Tests 685 passed (685), lock VERDICT command-exit 0.pnpm --filter @objectstack/plugin-email typecheck: tsc --noEmit plus 'check:test-typecheck: OK', exit 0. New pin src/mail-log-provider.pin.test.ts has 5 tests: SMTP saved on the page, SMTP from boot options, host-supplied transport, restart with log stored, and a CONTROL with no-SMTP boot. Each asserts the transport/isConfigured, the registerLiveStatus statement, the log line and the Send test answer, with sendMail never called. ABLATION at fa56181 through scripts/ablation-replace.mjs: the anchor 'this.service.setTransport(new LogTransport(ctx.logger));' was replaced by a no-op. The tool printed 'ok mutation landed: anchor 1 -> 0, blob 6e5d43794b5a -> 0a9522b85240', and the run went RED with 'Tests 4 failed | 1 passed (5)': all 4 SMTP/host legs failed and the CONTROL held. Restore: 'ok restored: blob == HEAD (6e5d43794b5a) andgit diff HEADis empty', and git status was clean. The test imports src (./email-plugin.js), so no dist leg is needed. The same ablation was also red at 9a27ece before the pin's engine double was trimmed.","gates": "
node scripts/pm/dispatch-gates.mjs --commandsat fa56181 derived 67 commands (merge base 0b8dbea, 4 paths, 222 changed lines). All 67 ran locally on the final head and exited 0.--ranwith exit codes recorded: '67 derived famil(ies) accounted for — 67 run, 0 NOT-MEASURED (a DERIVED zero — all 67 recorded an exit code and none of them is 3)', exit 0. First pass at 9a27ece: check:engine-double-contract exited 1 because the new pin's fake engine declared update(). It was fixed by making the fake read-only, rerun gave exit 0, and the whole union was rerun on fa56181. check:dual-build-cjs-loads exited 3 (PREREQUISITE NOT MET: no dist) on the first pass and exit 0 on the final pass. CI check-runs on PR #25497 at fa56181 when the report was written: 'No other open PR may claim the same issue' success; Lint/Type Check/Check Changeset/Governed Surface Queue Guard and the rest in_progress (Test Core and Build Core not yet listed). The required set is NOT MEASURED here; the PM reads its convergence.","dogfood_battery": "n/a — no
!in the PR title and no (narrowing) arm","mcp_calls": "0",
"api_writes": "2 relay writes plus 1 git push. (1) fleet-write pr_create (POST /repos/objectstack-ai/objectstack/pulls, draft, with assignee os-sales via POST /issues/25497/assignees), relay run 39986291225, read back byte-identical. (2) post-stamped os-dev-report comment, POST /repos//issues/25450/comments. git push of the branch (not a REST write).",
"open_questions": [],
"out_of_scope_findings": [
"class: a · reach: Setup → Email 'Send test email' with 'None (log only — no real delivery)' picked on the form but NOT saved, while SMTP is live: the mail/test handler's provider==='log' path tests the live service instead of the form's choice. A throwaway probe at fa56181 (not committed) returned ok:true 'Sent test email to admin@example.test (id=…)', with nodemailer sendMail called once, so a real mail was delivered for a form whose label says no delivery. Landing: packages/plugins/plugin-email/src/email-plugin.ts, the mail/test handler (
else if (provider !== 'log')leaves target = svc). Seam: consumer: none (settings action, not spec). Dedupe words: 'send test log provider unsaved', 'mail/test None delivers', 'test email form provider log live smtp'","carrier: none (承接者:无) · noted, not filed: a transport swap never closes the previous SmtpTransport pool (liveSmtp is overwritten or cleared on every swap branch: smtp→smtp, smtp→resend/postmark, and now smtp→log). Pre-existing, unchanged here, no measured leak; recorded in the PR's Acceptance notes."
]
}
objectstack-fleet commentedon Oct 23, 2026
Landed ·
domain:servicesseat 1 (seat post #6021) ·os-sales·session_016g7nYP9wdNx1GDa5XDr7Uy· 2026-10-23T17:17Z6370095793), headfa561813a8origin/main0ec4c5a918State:
Fixes #25450closed the card.pm:dispatchedis now off and the assignee is cleared. The out-of-scope finding on the test door is filed as #25509, and it is now clear to dispatch on the mergedemail-plugin.ts.Generated by Claude Code