Skip to content

Setup → Email shows provider SMTP with an empty host while mail goes to the development log sink and "Invitation sent" toasts appear — say on the page which transport is live (hotcrm#2194 T2-U12) #25335

Closed

Description

@objectstack-fleet

维护者速读

  • 事由: 邮件设置页显示 SMTP、主机为空,而平台邮件实际进了开发日志、状态记为「已发送」,邀请提示「已发送」但什么都发不出去。
  • 要做: 页面上说明当前生效的传输方式,例如「开发日志——邮件不会离开本机」。
  • 完成标准: 管理员在邮件设置页能看出邮件会不会真的发出。

Filing gate ③ a task the maintainer set: the maintainer's ruling on objectstack-ai/hotcrm#2194, recorded there at 6354968262 (the director seat, posting from the maintainer's account): 「25251 B hotcrm B」, 2026-10-23T01:05:47Z. That is letter B for all three rounds, exactly as the seat recommended, and it adopts T2-U12. Filed by the repo:hotcrm seat 1 (os-elon-musk, session session_014xanhPEmGTMTfVH4UokwJT) under objectstack-ai/hotcrm#2194's After the ruling. ⛔ Not a claim.

Grade: triage's. The seat suggests priority:p3: an adopted suggestion with a measured cost on an existing surface, no wrong data and no contract change.

Reader (who acts): objectstack triage routes and grades it. The landing below is the seat's reading of origin/main; paths under src/ are HotCRM's (objectstack-ai/hotcrm).

Governing text

Why (measured)

Landing

Read on objectstack origin/main 7b047b44:

Done when

  • Setup → Email states which transport is live; with the log transport it says nothing leaves this machine.
  • The statement follows a transport swap made from the same page.
  • A pin on the page's reading of isConfigured().

Size: S.

Dedupe

Method: every open objectstack-ai/objectstack issue (108) and every issue updated since 2026-10-19 (544, any state), listed through the repo-scoped issues endpoint (paged) at 2026-10-23T01:20Z and matched case-insensitively on their titles. Words: transport, log sink, email, mail. 27 hits on the broad words; the related ones are closed: #25065 (the contract fact this card reads) and #24767 (send-test recipients). The open ones (#25293, #25190, #25189, #24465) are other email subjects. New.


Generated by Claude Code

Activity

  1. added
    area:workflowApprovals and automation — the work that runs without a person driving it
    bugSomething isn't working
    domain:servicesRetired 2026-10-22, alias of domain:server (services surface); in-flight cards keep it until landed
    pm:queueReady for the PM dispatch loop
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Oct 23, 2026
  2. objectstack-fleet commented on Oct 23, 2026

    @objectstack-fleet
    ContributorAuthor

    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-23T12:19Z
    Session: session_016g7nYP9wdNx1GDa5XDr7Uy
    Account: os-sales (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/issue-25335-email-transport-statement
    Worktree: objectstack-issue-25335
    Domain: domain:services
    Seat: domain:services#1
    File surface: expected packages/plugins/plugin-email/src/ (the mail settings' served reading of IEmailService.isConfigured() / the live transport) and, if the Setup → Email page's settings descriptor lives there, packages/services/service-settings/src/; plus tests, bundles and a changeset. ⛔ packages/spec is not edited (isConfigured?() already exists). If the real producer is elsewhere, fix it there and name the landing and reason in the report and PR body.
    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: n/a — not a defect card (adopted suggestion, filing gate ③ on hotcrm#2194's ruling)
    Thread-read: none
    Serial constraints cleared: #25190 (plugin-email render exports) is on hold with no open PR; #25345 (in flight) touches service-sms and runtime/src/flow-clone.ts only.
    Resume-from: none

  3. objectstack-fleet commented on Oct 23, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 25335,
      "status": "done",
      "branch": "claude/issue-25335-email-transport-statement",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/25448",
      "session": "session_016g7nYP9wdNx1GDa5XDr7Uy",
      "premise_still_valid": true,
      "summary": "Server half landed, as a draft PR marked 'Part of #25335'. service-settings gains SettingsService.registerLiveStatus(namespace, provider) and liveStatusOf(). GET /api/settings/:namespace asks the provider on every read and heads manifest.specifiers with a spec-valid info_banner (reserved id live_status); the registered manifest and in-process getNamespace() are unchanged. plugin-email registers the mail statement from EmailService.isConfigured(): false reads 'No mail leaves this machine' (warning), and true names the live transport, e.g. 'Mail is delivered through SMTP (host:port)'. The statement waits for any mail re-apply still in flight, so a read made right after a save names the new transport. The landing matches the claim. No packages/spec edit was needed, because info_banner already exists and the console already renders it. The SMS page had no live-sender reader to copy, so the hook is namespace-generic for service-sms to adopt. Not done: in-place follow after Save needs a console change. @object-ui/console 18.0.0 SettingsView, read from the npm tarball (this session has no objectui repo access), merges only the PUT values after a save and never refetches the manifest, so the banner updates on the next page load. That half is reported below with its Seam line, and the card stays open via 'Part of'.",
      "tests": "At HEAD 4789209944: `pnpm --filter @objectstack/plugin-email test` gave Test Files 37 passed (37), Tests 676 passed (676), and `pnpm --filter @objectstack/plugin-email typecheck` (tsc --noEmit && check:test-typecheck) exited 0. `pnpm --filter @objectstack/service-settings test` gave Test Files 60 passed (60), Tests 995 passed (995), and its typecheck (tsc --noEmit) exited 0. Both ran under os-verify-lock, VERDICT command-exit 0. The full-package runs were taken at 9ffc78df34. The only later commit (4789209944) changes the new pin test file alone, and that file re-passed at 4789209944 (5/5) along with plugin-email typecheck, VERDICT command-exit 0. New pins: plugin-email src/live-transport-status.pin.test.ts (5) and service-settings src/settings-routes.live-status.pin.test.ts (6). Ablations ran through scripts/ablation-replace.mjs on src-resolved imports (no dist in the path), and every leg was restored with blob == HEAD and git diff HEAD empty. (1) The branch rewritten to 'instanceof LogTransport' gave 5 red tests, but as ReferenceError: LogTransport is not defined. That is a crash, not the mutation, so it is void and reported as such. (2) The branch rewritten to 'transport.constructor.name === LogTransport' gave 1 failed and 4 passed; the red test is 'the branch is isConfigured(), not the transport class'. (3) 'if (false)' gave 4 failed and 1 passed. (4) Deleting 'await Promise.allSettled([...applying])' gave 1 failed, the swap test: expected 'warning' to be 'info'. test:repo was not run locally; it belongs to CI, and the diff touches no repo-project test.",
      "gates": "Local, at 4789209944: `node scripts/pm/dispatch-gates.mjs --commands` derived 70 commands, and all 70 exited 0 (exit codes captured before any pipe). `--ran` with an exit-coded record answered: '70 derived famil(ies) accounted for — 70 run, 0 NOT-MEASURED (a DERIVED zero — all 70 recorded an exit code and none of them is 3)'. Two problems on the first sweep, both resolved. check:engine-double-contract flagged the new pin's fake engine update(); the fake was trimmed to find() only, and the gate re-ran green. check:dual-build-cjs-loads first printed PREREQUISITE NOT MET (exit 3) on an unbuilt tree and passed on the final sweep. check-platform-checklist was not required: exports were added only, none renamed or removed. CI on PR #25448 at head 4789209944, read right after the PR opened: 'No other open PR may claim the same issue' success; '18.x upgrade harness (opt-in)' and 'Packed-tarball smoke (opt-in)' skipped. Every other check-run was in_progress, including the Type Check lanes, Lint lanes, Governed Surface Queue Guard and Check Changeset; Test Core and Build Core were not listed yet. in_progress, not yet converged.",
      "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
      "mcp_calls": "0",
      "api_writes": "3 — (1) fleet-write relay pr_create, which landed as POST /repos/objectstack-ai/objectstack/pulls (draft #25448) plus POST /repos/objectstack-ai/objectstack/issues/25448/assignees (os-sales); read-back identical, 6232 bytes sent and stored. (2) post-stamped issue comment POST /repos/objectstack-ai/objectstack/issues/25335/comments (this os-dev-report). git push is direct and not counted.",
      "open_questions": [
        {
          "question": "The claim's line is 'Clause-②: no', and I copied it verbatim into the PR body and the changeset. The diff does widen an exported surface: @objectstack/service-settings gains SettingsService.registerLiveStatus and liveStatusOf, and exports SettingsLiveStatus, SettingsLiveStatusProvider and SETTINGS_LIVE_STATUS_BANNER_ID. A sibling changeset in the tree spells an added exported method 'Clause-②: yes (widening: ...)'. Which line should stand?",
          "options": [
            "A: keep 'Clause-②: no' as claimed. Zero writes; Check Changeset judges it.",
            "B: the seat re-declares 'Clause-②: yes (widening: service-settings exports registerLiveStatus / liveStatusOf and three new type/const exports; nothing is refused)'. This costs one changeset commit, which I can push on request, plus a seat-written PR-body edit (the dev does not PATCH the body)."
          ],
          "recommendation": "B, because the declaration should describe the diff, and the exports are additive and public. The level stays patch either way, since Metadata-protocol is none."
        }
      ],
      "out_of_scope_findings": [
        "carrier: #25335 (the PR is 'Part of', so the card stays open) · the console half of done-when item 2. @object-ui/console 18.0.0 SettingsView (npm tarball dist/assets/SettingsView-*.js; the save handler sets state to the old payload merged with the PUT response's values) never refetches GET /api/settings/:namespace after a save or an action, so the live_status banner keeps the pre-save statement until reload. Fix: refetch the namespace after a successful save or action. Not measured at the .objectui-sha pin 5fef032b (no objectui access in this session). Seam: runtime:service-settings GET /api/settings/:namespace manifest.specifiers[0] (info_banner id live_status) → renderer:@object-ui/console SettingsView save handler (merges PUT values only; manifest never re-read) · dedupe words: settings page refetch after save, settings manifest stale after save, live_status banner",
        "class: a · reach: named producer: packages/plugins/plugin-email/src/email-plugin.ts applyMailSettings, the provider === 'log' branch. Measured on this branch with a throwaway probe (not committed): boot with provider smtp + smtp_host, then save provider=log. The transport stays SmtpTransport, isConfigured() stays true, and mail keeps being delivered over SMTP. Meanwhile the option label reads 'None (log only — no real delivery)' and the plugin logs 'transport unchanged — messages are logged and recorded in sys_email, never delivered'. That log line is false whenever the previous transport delivers, and it also holds across a restart when OS_EMAIL_SMTP_* built the boot transport. With this PR the page banner truthfully says 'Mail is delivered through SMTP', which makes the mismatch visible · dedupe words: mail provider log still delivers, None log only no real delivery, transport unchanged never delivered",
        "carrier: none (承接者:无) · noted, not filed · Setup → SMS states no live sender either. When the constructor/env provider is twilio or aliyun, the stored default shows 'None (log only)' while the live transport delivers. service-sms can adopt the same statement by calling settings.registerLiveStatus('sms', ...) from ISmsService.isConfigured(). Recorded in the PR's One-pattern paragraph.",
        "carrier: none (承接者:无) · noted in the PR's Acceptance notes, not filed · the live_status banner has no zh-CN, ja-JP or es-ES text. Its text is computed per request, and the console's banner translation lookup by id would replace it with a fixed string."
      ]
    }

    Generated by Claude Code

  4. objectstack-fleet commented on Oct 23, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report addendum

    {
      "issue": 25335,
      "addendum_to": "issuecomment-6366550562",
      "ruling_applied": "open question Clause-② resolved as B by the seat",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/25448",
      "new_head": "39af42e75a",
      "change": "One commit (39af42e75a) touches .changeset/25335-email-live-transport-statement.md only. Clause-② now reads: yes (widening: service-settings exports SettingsService.registerLiveStatus / liveStatusOf, SettingsLiveStatus, SettingsLiveStatusProvider and SETTINGS_LIVE_STATUS_BANNER_ID; nothing is refused). Both packages stay at patch, and Metadata-protocol stays none. The PR body was not edited; the seat changes its Clause-② line.",
      "changeset_gates": "At 39af42e75a all six exited 0: check-empty-changeset --base origin/main, check-adr-0087-registration --base origin/main, check-changeset-no-major --base origin/main, pnpm check:changeset-gate-self-tests, pnpm check:pm-changeset-deadline-census, node scripts/check-changeset-fixed.mjs. check-changeset-no-major notes that locally no pull_request payload was available to read the declaration from; CI's Check Changeset reads it from the PR body.",
      "ran_reconciliation": "At 39af42e75a dispatch-gates derived the same 70 commands. The first pass in the recreated worktree had no built dist, so 4 gates exited 3: check:dts-closure, check:dual-build-cjs-loads, check:lean-entry-closure, check:sourcemap-no-sources-content. After a cache-backed turbo build (76/76 tasks, 75 cached), all 4 re-ran and exited 0. --ran answered: 70 derived famil(ies) accounted for — 70 run, 0 NOT-MEASURED (a DERIVED zero — all 70 recorded an exit code and none of them is 3).",
      "api_writes": "1 more — the post-stamped comment POST /repos/objectstack-ai/objectstack/issues/25335/comments (this addendum)",
      "mcp_calls": "0"
    }

    Generated by Claude Code

  5. objectstack-fleet commented on Oct 23, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed, server half · domain:services seat 1 (seat post #6021) · os-sales · session_016g7nYP9wdNx1GDa5XDr7Uy · 2026-10-23T15:16Z

    reading value
    PR #25448 MERGED through the queue (TIER_DEFAULT record 6367450381 PASS, ACCEPT 6367463668), head 39af42e75a
    origin/main carries ab510f42e7

    State: the card stays open and dispatched (Part of #25335) for the console half. After Save, @object-ui/console SettingsView merges only the PUT values and never re-reads GET /api/settings/:namespace, so the new live_status banner updates only on the next page load. That half is routed to the ui lane seat (objectui#5560) through the coordinator. The server already answers the new statement on every read.


    Generated by Claude Code

  6. objectstack-fleet commented on Oct 24, 2026

    @objectstack-fleet
    ContributorAuthor

    Release: claim 6364710482 · session session_016g7nYP9wdNx1GDa5XDr7Uy (domain:services seat 1, seat post #6021) · because the maintainer voided that seat · to domain:server seat 1 (seat post #6367), same account

    • Provenance: who — the maintainer (Sales); what, verbatim — 「services 席位也作废了」; where — the Sales account's project, 2026-10-24T13:23Z. That project then summoned domain:server seat 1 (verbatim: 「开」) to take over the in-hand server cards.
    • Assignee: unchanged, os-sales (same account).
    • Handover record: the server half landed as PR feat(settings,plugin-email): state the live mail transport on Setup → Email #25448 (ab510f42e7, landing note 6368209700); no server branch is open. What remains is the console half (Settings re-reads the namespace after Save), routed to the ui lane; nothing of it is pushed. The claim follows as its own comment.

    Generated by Claude Code

  7. objectstack-fleet commented on Oct 24, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: card takeover, holding · domain:server seat 1 (seat post #6367, project run by the Sales account, 由Sales账户的项目负责) · p2 · the card stays Part of #25335 for its console half · 2026-10-24T13:40Z
    Session: session_01JrXUZYyTV4i588KLZRxSvJ
    Account: os-sales (the seat's linked user; the card's assignee)
    Branch: claude/issue-25335-email-transport-statement (server half, merged as PR #25448; no new server branch)
    Worktree: objectstack-issue-25335
    Domain: domain:server
    Seat: domain:server#1
    File surface: none on the server side. The remaining half is the console's Settings view (re-read GET /api/settings/:namespace after Save), which is ui lane work; this seat holds the card until that half lands or the card is routed there, and closes it then.
    Container & model: S, mode:subagent, model: default tier (nothing dispatched by this claim), effort: medium
    Clause-②: no
    Metadata-protocol: none
    Responsibility: n/a — the server half's defect is landed; the console half's owner is the ui lane.
    Thread-read: 6368209700
    Serial constraints cleared: none (no server file is touched).
    Resume-from: none


    Generated by Claude Code

  8. objectstack-fleet commented on Oct 24, 2026

    @objectstack-fleet
    ContributorAuthor

    Release: claim 6384920448 · session session_01JrXUZYyTV4i588KLZRxSvJ (domain:server seat 1, seat post #6367) · because the maintainer closed the server lane's shift in the Sales account's project · to the queue, no successor named

    • Provenance: who — the maintainer (Sales); what, verbatim — 「server 车道合并后下班」; where — the Sales account's project, 2026-10-24T15:48Z.
    • Landed: the server half, PR feat(settings,plugin-email): state the live mail transport on Setup → Email #25448 (ab510f42e7, landing note 6368209700). No server branch is open and nothing else is pushed.
    • Remaining: the console half only: after Save, the Settings view merges the PUT values and never re-reads GET /api/settings/:namespace, so the live_status banner updates only on the next page load. That is ui lane work (the console is moving into this repo), so the card goes back to pm:queue with pm:retriage for its lane: the domain:services label names a voided lane.
    • Labels and assignee, same act: pm:dispatched → pm:queue + pm:retriage; assignee os-sales cleared.
    • Resume-from: none (the console half has no branch).

    Generated by Claude Code

  9. added
    pm:queueReady for the PM dispatch loop
    pm:retriageQuestion for triage, answered each fire; coexists with the standing pm:* label; no dispatch
    and removed
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    on Oct 24, 2026
  10. objectstack-fleet commented on Oct 24, 2026

    @objectstack-fleet
    ContributorAuthor

    Retriage answer: both halves have landed — closing completed · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-24T15:55Z

    The answer to 6386247938. ⛔ Not a claim.


    Generated by Claude Code

  11. removed
    pm:queueReady for the PM dispatch loop
    pm:retriageQuestion for triage, answered each fire; coexists with the standing pm:* label; no dispatch
    on Oct 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:workflowApprovals and automation — the work that runs without a person driving itbugSomething isn't workingdomain:servicesRetired 2026-10-22, alias of domain:server (services surface); in-flight cards keep it until landedpriority:p2Medium: important, M3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions