Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b0844a5ce7 | ||
|
|
b096a16cce | ||
|
|
9182134e59 | ||
|
|
191dff32fb |
@@ -5189,3 +5189,13 @@ Before: managed project/thread verbs had no Console conversation area. After: fi
|
||||
2026-10-20 19:34:28 UTC | Gander row 196 build paused at design mismatch | Before: approved design promised Unix tester cleanup only for the owned inode. After: scratch test shows Node/libuv close unlinks a replacement inode before JS close handlers. Evidence agents/gander/work/queue-196/evidence/tester-cleanup-blocker.txt. Sent Sage/Duckworth gander-0016/0017, recommending explicit same-uid residual with private 0700 parent retained. Interpreter, concurrent peer, principal, human, peer-transport, transport, tester and process single files passed. Partial code uncommitted, tester transport not wired. No suites, mutant batches or live actions.
|
||||
|
||||
2026-10-21 18:45 UTC | Gander row 196 preservation before rebase | Sage-2051, Mos order mos-1193, explicitly authorizes commit and fast-forward push of the current build before deferred full verification. Product and evidence preserved together; prior focused checks and stopped gates are recorded in the row196 packet, with no claim of full acceptance. Next: rebase onto a36523713 on new gander/row196-r2, report range-diff/patch-id/conflicts, then await Sage's clear for M1-M29 and all requested suites. No live action, force push, branch deletion or tests during hold.
|
||||
|
||||
2026-10-21T21:11:44Z | Della mos-1213 / Vikunja 460 planning phase | Before: PRD 1.10 has no email feature; MP 95b0298 and EP 7a64921 are read-only prior inputs. Scope: independent post-v1 email plan on della/email-plan, no implementation/live access/MVP or queue changes. Gitea issue enrollment belongs to Mos/Sage because this seat has no API credential.
|
||||
|
||||
2026-10-21T21:12:18Z | Della mos-1213 / Vikunja 460 planning verification | After: docs/plans/2026-10-21_email-plan-della.md is 341 lines, with 13 post-v1 rows and six Jason questions. Verified 42 pinned citation anchors, ten requested coverage areas, identical prior src trees, append-only records, clean whitespace and repository queue guard. No configured CI pipeline or CI-queue wrapper found in the current foundation; documentation-only validation applies, no product suites or live tests run. Phase B/C and Gitea issue/queue enrollment remain with Mos/Sage. Only della/email-plan is authorized for push.
|
||||
|
||||
2026-10-21T21:37:07Z | Della mos-1220 / Vikunja 460 phase B cross-review | Before: independent plans Della 191dff32f and Ludwig 4a27b73ac delivered. After: docs/plans/2026-10-21_email-review-by-della.md reviews all 218 Ludwig plan lines, records ten findings, five grafts, six retained strengths, nine disagreements and eleven merged Jason questions. Ludwig review 143415779 read in full after ludwig-0002 arrived; reconciliation records new mail-access action, no agent reroute and correction of Della dataRoot credential example. Original plan unchanged. Planning only, no product/MVP/queue/live change.
|
||||
|
||||
2026-10-21T21:43:02Z | Della mos-1223 / Vikunja 460 phase C synthesis | Before: independent plans/reviews complete, Mos selected Della base. After: docs/plans/2026-10-21_email-plan.md merges Ludwig grafts and accepted corrections, with 16 post-v1 rows (eight READY, eight WAIT/optional), eleven deduplicated Jason questions, per-row test paths and explicit Q210 non-v1 scheduling. Original Della plan/review preserved; Ludwig originals remain on their branch. Static checks cover graft/row/question/citation anchors and original blobs. No source code, suite, mailbox, secret file, service, MVP or queue change. Candidate goes to Ludwig before Sage landing/queue handoff, copying Mos.
|
||||
|
||||
2026-10-21T21:45:20Z | Della mos-1223 / ludwig-0004 focused corrections | Before: b096a16cc left content-key location implicit and E05 core registration marked ready. After: merged plan 399 lines fixes external configDir secret key/lifecycle namespace outside dataRoot backup/restore, E01 schema and E03 missing/expired-key restore tests, and E05/E06 test-local registry readiness with core kinds.mjs registration gated on approved E01/Q4. Sixteen rows and eleven questions retained; originals unchanged. Focused static checks and queue guard apply, no product/live changes. Reviewer will check changed lines only.
|
||||
|
||||
@@ -1432,3 +1432,7 @@ are never rewritten or removed; corrections are new entries.
|
||||
2026-10-21T19:46:12Z | Dewey (Opus 5.5, 84fb346c) | row 222 r1 static review (megavolt-0095), c9aa9c2ee, manifest c2121b55 | APPROVE, comment 30138 on #1723, queue rev 2966; rulings: createObject=rename accepted, 5 s human timeout deferred with STATUS codes in the follow-up, credential.helper reset argv-only accepted; notes N1-N5; no tests run
|
||||
2026-10-21T19:49:37Z | Sage (Opus 5.5, 1ef1e4f8) | row 250 landing, row 196 r5 grant (mos-1198) and resume (mos-1200), F27 row 253 | 250 LANDED 1b6158ce2 (patch-id 29786caa, Filbert rev 2957, Gizmoduck slot green), #1755 closed c30139, Vikunja 450; 196 r5 91e3696ef hunk checked by Duckworth, webui 304/0; my queue add tripped Gander's busy check, not a stop (mos-1200), resume on 05e940117; row 253 #1758 brief 0cdead66c
|
||||
2026-10-21T20:46:03Z | Sage (Opus 5.5, 1ef1e4f8) | row 222 landing, PRD 1.9 and 1.10, Pocket ID design row 255, row 254 brief, 196 r6 grants | 222 LANDED 0b33f5e9d (megavolt-0098 gate, Dewey rev 2966, patch-id edb60b4f), #1723 closed c30147, done rev 2972; follow-up row 254 (#1759, after 196); PRD 1.9 f48725cfc (REQ-WEB-2, REQ-DEC-3 amended, mos-1202); 458 brief 7d08237e1 (#1760), row 255 rev 2976; PRD 1.10 add0f1aae REQ-INST-2 post-v1 (mos-1204, tasks 97/0, bus req 3/0); Vikunja 458 c478, 416 c479; queue-commit 21def2850 revs 2968-2976; 196 r6 gate gander-0066 2334/0, 38/38 killed; 244 granted sage-2147; 196 r6 resume granted sage-2145 after self-tee busy refusal (mos-1205). Miss: landed 222 before checking 196's manifest; 196 carried results voided. Class rule now (mos-1204, replaces the mos-1200 interface grep): before any landing push, compare every in-review and in-progress row's manifest against the landing's paths.
|
||||
2026-10-21T21:12:18Z | Della (GPT-6.1-Sol, mos-1213) | Vikunja 460 independent post-v1 email plan, della/email-plan from c3d88840c | 341-line proposal, 13 post-v1 rows, six Jason questions, 42 pinned citation anchors verified. Canonical queue next della: nothing. Planning only, no live mail/service/MVP/queue change; phase B/C with Mos, no Ludwig plan read. Commit and branch push authorized by dispatch.
|
||||
2026-10-21T21:37:07Z | Della (GPT-6.1-Sol, mos-1220) | Vikunja 460 phase B email cross-review, della/email-plan | Ludwig plan 4a27b73ac and review 143415779 read in full. Cross-review has ten findings and eleven merged questions; original Della plan unchanged. Canonical queue next della: nothing. Static verification only; review push followed by direct della-0002 handoff to both threads under Q210. Phase C base choice remains with Mos.
|
||||
2026-10-21T21:43:02Z | Della (GPT-6.1-Sol, mos-1223) | Vikunja 460 phase C merged email plan, della/email-plan | Mos-selected base plus Ludwig grafts; original plans/reviews unchanged. Sixteen post-v1 rows, eight READY and eight WAIT/optional; eleven Jason questions. Canonical queue next della: nothing. Docs-only checks. Commit/push then immediate della-0003 handoff to Ludwig/Mos; Sage landing/queueing follows approval.
|
||||
2026-10-21T21:45:20Z | Della (GPT-6.1-Sol, mos-1223 / ludwig-0004) | merged email plan focused review fixes | External content-key/expiry location and restore tests specified; fake registry work READY, production registry gated on PRD approval/Q4. Merged plan 399 lines, original plans/reviews unchanged. Commit/push and focused Ludwig review before Sage handoff.
|
||||
|
||||
@@ -0,0 +1,341 @@
|
||||
# Post-v1 email processing and monitoring plan (Della)
|
||||
|
||||
**Status:** proposal for Mos phase A, Vikunja 460, mos-1213. Planning only.
|
||||
**Goal:** receive mail for named agents and users through Stack-owned connections,
|
||||
then expose authorized reading, routing and drafting equally to every harness.
|
||||
**Boundary:** every build row below is **post-v1**. None is required for the
|
||||
2026-10-27 MVP. This document changes neither PRD 1.10 nor the live Stack.
|
||||
No service, mailbox connection, live-mail read, account, DNS change or spend is authorized.
|
||||
R21 binds the specialist: **“The email specialist reads and drafts, never sends.”**
|
||||
|
||||
## Evidence and reuse
|
||||
|
||||
Citations are pinned, not claims about deployed behavior. Prefixes below identify
|
||||
the repository and commit for every `file:line` citation. Both prior clones were
|
||||
inspected read-only. No prior application or test suite was executed.
|
||||
|
||||
| Prefix | Pinned evidence |
|
||||
| --- | --- |
|
||||
| S | Stack `c3d88840c5b6ffa62d2a6a4aa4385d388234d777`, this worktree |
|
||||
| MP | `mail-processor` `95b02989f4d4276dded00c90ffd74ebb7d03ae8b` |
|
||||
| EP | `email-processor` `7a6492190cd376058e29a04fd0593e86bae1a8e5` |
|
||||
| SM | `origin/huey/system-manager-design` at `2ee68cd69337bb933a8fabeb04bcd71835170171`, Q198 r6, superseding r5 `fd44f7c60` |
|
||||
|
||||
**F1 Existing mail code:** recursive comparison found identical `src/` trees in MP
|
||||
and EP. Reuse once, not as two running products. Both implement normalized messages
|
||||
and a connector boundary (`src/email_processor/connectors/base.py:8,30`), M365
|
||||
pagination/rate-limit handling (`connectors/m365.py:81,138`, under that same source
|
||||
directory), ordered AND/OR rules (`src/email_processor/rule_engine.py:81,124`), and
|
||||
bounded preview/cold-start safety (`src/email_processor/poller.py:101,122,152`).
|
||||
Port these concepts and selected synthetic tests into Stack-native modules after
|
||||
dependency/license review. Neither copy proves IMAP or POP3 support: connector
|
||||
selection handles only M365 (`src/email_processor/poller.py:48`).
|
||||
|
||||
**F2 Prior intent:** EP `docs/PRD.md:25,39` describes a single-organization M365
|
||||
application, not the Stack add-on. MP `docs/PRD.md:26,66,204,235` specifies generic
|
||||
Graph/IMAP/POP3 drivers, separated intake/execution, fail-closed activation and
|
||||
atomic staging/checkpoints. Those are useful design requirements, not implemented
|
||||
features. Adopt the safety properties and conformance cases, not its independent
|
||||
tenant/profile product, bootstrap authority or mandatory standalone web setup.
|
||||
|
||||
**F3 Drop unsafe coupling:** in both repos, `src/email_processor/poller.py:186,200,301`
|
||||
checks for duplicates before effects and advances a timestamp to wall-clock now.
|
||||
`src/email_processor/models.py:266` has indexes, not a unique mailbox/message key.
|
||||
Replace these with durable provider cursors, unique identities and transactional
|
||||
delivery. Drop connector caches keyed just by mailbox name (`poller.py:48`), direct
|
||||
GLPI/ntfy effects (`action_executor.py:30,69,159`) and source category writes
|
||||
(`connectors/m365.py:170`, `poller.py:359`). Drop direct model calls and content logs
|
||||
(`classifiers/llm.py:79,105,109,123`), replacing decisions through Q207.
|
||||
Do not import deployment manifests, credential settings, existing accounts or data.
|
||||
|
||||
**F4 Stack enforcement:** S `packages/harness/src/tools.mjs:1` already maps typed
|
||||
tools through the runner socket with broker checks. Its MCP bridge carries no
|
||||
capability (`packages/harness/src/mcp-server.mjs:1,34`). Reuse that path for all
|
||||
harnesses, rather than giving each one mailbox credentials or a monitoring plugin.
|
||||
S `packages/bus/src/broker.mjs:690,701,1601,1666` supplies human-approved grant
|
||||
consumption and action authority. `packages/bus/src/credentials.mjs:142,358,379`
|
||||
supplies scoped references and protected use. Mail kinds and grants do **not** exist:
|
||||
S `packages/business/src/vocabulary.mjs:109,119` accepts Gitea, Vikunja and host kinds.
|
||||
Mail requires reviewed extensions, not relabelling mail credentials as an existing kind.
|
||||
|
||||
**F5 Product constraints:** S `docs/prd/mosaic-stack.md:432,454,477,496` defines
|
||||
chief-raised grants, exact human approval, one-time consumption and immediate local
|
||||
revocation. Its `:507,542,582,607` covers decision evidence, thread binding and hard
|
||||
waits. PRD “inbox” is the decision inbox, not email. Mail needs a separately approved
|
||||
post-v1 PRD addendum. S `packages/plugins/src/kinds.mjs:9,32` has no mail-source kind.
|
||||
Follow REQ-PLUG-1/2 (S PRD `:876,910`) for provider adapters.
|
||||
|
||||
**F6 Monitoring precedent:** S `tools/agent-watch/jarvis-email-watch-liveness.sh:8,26,53`
|
||||
checks timer state, failed execution and staleness. Reuse the distinction between
|
||||
alive, failed and unknown, not its legacy unit names or fleet wiring. SM
|
||||
`docs/plans/2026-10-21_q198-system-manager.md:262,291,304,326` supplies protected mode,
|
||||
independent probes and controlled resume. It is a design dependency, not shipped proof.
|
||||
|
||||
## Architecture and sources
|
||||
|
||||
**D1 One owner per instance.** Add an optional supervised `packages/mail/` service,
|
||||
with a single leased owner per configured source. It owns connection pools, OAuth
|
||||
refresh, scheduling, cursors and intake. A harness can stop, restart or change without
|
||||
losing monitoring. Multiple accounts are isolated by instance/business/source ID.
|
||||
“One service” does not mean one global credential, mailbox or shared authorization.
|
||||
Default installation is disabled and opens no connection.
|
||||
|
||||
Use Node modules aligned with the Stack, adapting the Python design rather than
|
||||
embedding both applications and their separate databases/authentication/UI. A Python
|
||||
sidecar would reuse more syntax but duplicate operational boundaries. Harness-native
|
||||
connectors would repeat connection state and fail Jason's cross-harness requirement.
|
||||
|
||||
**D2 Source contract.** Propose a reviewed `mail-source` plugin kind, list-selected,
|
||||
with `descriptor()`, `scan({source,cursor,limits,signal})`,
|
||||
`fetch({source,item,part,limits,signal})` and `health({source,signal})` exports.
|
||||
`scan` returns bounded observations, continuation, terminal cursor and identity epoch.
|
||||
Adapters receive protected credential-use callbacks, never agent-provided secret paths.
|
||||
Core owns grants, leases, persistence, rule evaluation and audit. No adapter sends,
|
||||
deletes, labels, moves or marks messages read. Use the existing channel kind only
|
||||
where its dispatch contract fits presentation, not as a substitute polling interface.
|
||||
|
||||
| Source | Proposed implementation and boundary |
|
||||
| --- | --- |
|
||||
| Microsoft 365 | Graph read-only access, per-folder delta/continuations and immutable message IDs. Replace timestamp polling. Restrict application rights to named mailboxes and verify effective scope, including any additive Entra permissions. |
|
||||
| IMAP | TLS with certificate verification, OAuth where supported, otherwise a separately approved service/app credential. Read-only selection and `BODY.PEEK`; UIDVALIDITY plus UID, never sequence number. IDLE is a wake-up hint with periodic reconciliation. |
|
||||
| Other providers | Prefer a conforming IMAP source initially. Gmail API and JMAP are later adapters only when a named need justifies them. No new provider account is implied. |
|
||||
| POP3 | Deferred optional row. Require stable UIDL, TLS and read-only retrieval. No `DELE`. Reject automatic activation without safe identity/reconciliation. |
|
||||
|
||||
Graph delta is folder scoped and cursors are opaque ([Microsoft delta documentation](https://learn.microsoft.com/en-us/graph/delta-query-messages)).
|
||||
Immutable IDs survive moves within a mailbox, not archive-mailbox moves or reimports
|
||||
([Microsoft ID documentation](https://learn.microsoft.com/en-us/graph/outlook-immutable-id)).
|
||||
Mailbox-scoped Application RBAC and unscoped Entra grants are independent
|
||||
([Microsoft RBAC documentation](https://learn.microsoft.com/en-us/exchange/permissions-exo/application-rbac)).
|
||||
IMAP identity, read-only selection and PEEK semantics follow [RFC 9051](https://www.rfc-editor.org/rfc/rfc9051.html).
|
||||
These are protocol references checked 2026-10-21, not evidence of installed support.
|
||||
|
||||
**D3 Addressing.** Version a route table mapping trusted delivery evidence to a stable
|
||||
Stack user or role-instance ID, never a display name or a newly invented role.
|
||||
Aliases on an approved ingress mailbox are the recommended inexpensive start.
|
||||
Plus-addresses are acceptable only after fixtures and a later authorized provider test
|
||||
prove the exact recipient survives delivery. They are not a security boundary.
|
||||
Dedicated mailboxes provide stronger provider-side separation for confidential users
|
||||
or agents, at the cost of account management and possible licenses.
|
||||
|
||||
Bind routes to source identity plus provider-controlled original-recipient evidence.
|
||||
`To`/`Cc`, forwarded headers and arbitrary `Delivered-To` headers are insufficient
|
||||
unless the enrolled delivery path establishes their provenance. Bcc or ambiguous
|
||||
alias delivery goes to restricted operator review, never guessed routing. Unknown
|
||||
addresses do not create agents. Multiple recipients produce independently authorized
|
||||
deliveries without revealing other recipients' mail. Jason approves domains, names,
|
||||
aliases, mailboxes and each provider-side change before provisioning. No SMTP/MX
|
||||
server, catch-all domain or public webhook is proposed for the initial increment.
|
||||
|
||||
## Processing, data and retention
|
||||
|
||||
**D4 Intake pipeline:** approved source → bounded discovery → bounded parse → durable
|
||||
observation/dedup → versioned deterministic rules → authorized delivery outbox →
|
||||
Stack notification/work request. Provider cursor advancement and durable observations
|
||||
commit together under the source lease/generation. Outbox delivery has a stable key
|
||||
and a recipient-side unique receipt, so a crash after delivery but before acknowledgment
|
||||
does not create a second task/message. Never promise exactly-once external effects.
|
||||
|
||||
Default candidates: poll every 60 seconds, at most 50 observations and 10 MiB fetched
|
||||
per slice, fair scheduling between sources. Persist continuation when the slice fills.
|
||||
Cap each source at 1,000 pending deliveries and 100 MiB of retained content initially.
|
||||
Persist quota accounting across restarts and slices. Reaching a quota holds new intake
|
||||
and raises an alert, while read-only health checks continue.
|
||||
First activation establishes a from-now baseline without actions on old mail. Historical
|
||||
import is a separate approved time/count window, previewed before activation. Cap
|
||||
violations, disk-full and unavailable audit storage hold work without advancing past
|
||||
unstaged mail. These limits are proposed settings to validate, not measured capacities.
|
||||
|
||||
Provider identity is scoped to business/source/account: Graph immutable ID, IMAP
|
||||
folder/UIDVALIDITY/UID or POP3 UIDL. RFC Message-ID, thread ID and content similarity
|
||||
are secondary correlation signals, never sole proof of uniqueness. Preserve distinct
|
||||
messages with missing or reused Message-ID. An IMAP move, epoch reset, expired delta
|
||||
cursor, archive move or reimport enters bounded reconciliation. Record aliases only
|
||||
with sufficient identity evidence. Otherwise hold possible duplicate actions for review.
|
||||
An edit is a new observation version, not automatic permission to rerun prior actions.
|
||||
Preserve dedup tombstones beyond content expiry. Replay needs a new authorized intent,
|
||||
parent link and current grants. A rule edit never retroactively executes preview work.
|
||||
|
||||
**D5 Parsing and rules.** Preserve provenance for normalized sender, recipients,
|
||||
timestamp, subject, text, thread references and attachment descriptors. Bound MIME
|
||||
depth to 10, parts to 100 and normalized text to 256 KiB. Decode safely, flag malformed
|
||||
or truncated content and quarantine failures. Prefer text, sanitize HTML for display,
|
||||
disable remote images/URLs and never execute active content. Rules use finite allowlisted
|
||||
fields/operators, stable priority and first-match by default. Reject executable rules
|
||||
and unbounded regex. Record rule/version and routing reason. Unmatched mail is held
|
||||
for its authorized owner, not dropped or copied fleet-wide. Routing a user notification
|
||||
does not launch an agent. Agent wake-up is a separate admission-controlled work request.
|
||||
|
||||
Optional classification and any mail-triggered decision go through the Stack's one
|
||||
decision module under DECISIONS Q207. The mail service neither selects nor falls back
|
||||
to a model locally. Core policy still determines permitted actions and human hard waits.
|
||||
Use minimal, explicitly permitted content and an allowlisted result schema. A model
|
||||
result is advisory data, never a grant. Missing model service leaves deterministic
|
||||
intake working and model-dependent work held. Summaries/drafts run as ordinary governed
|
||||
agent work, not an extra mail-service model client.
|
||||
|
||||
**D6 Attachments.** Initially enumerate metadata only. An authorized explicit fetch
|
||||
quarantines bytes under an opaque ID, with a 10 MiB per-part and per-message total cap.
|
||||
Never trust filenames as paths, unpack archives, execute macros or follow embedded
|
||||
links. Content extraction requires isolated no-network processing, resource bounds,
|
||||
scanner success and a separate read right. Failed, encrypted or unsupported files stay
|
||||
quarantined. A clean scan does not make attachment instructions trustworthy.
|
||||
|
||||
**D7 Storage.** Proposed `<dataRoot>/mail/<business>/` contains encrypted content blobs,
|
||||
SQLite observation/outbox state, encrypted cursors, local drafts and append-only event evidence.
|
||||
No mail content enters git or images. Service-only directories are 0700, files 0600.
|
||||
Use scoped keyed fingerprints for private dedup fields. Separate content keys from
|
||||
provider slots. Keep tokens, delta URLs and authentication errors out of ordinary logs.
|
||||
Default durable records hold opaque IDs, state, versions and counts, not bodies,
|
||||
subjects, recipient lists or attachment names. Content access is explicit and audited.
|
||||
|
||||
Recommended content policy for an enabled pilot: encrypted bodies and local drafts
|
||||
for 30 days, explicitly fetched attachments for 7 days, minimal metadata/tombstones for
|
||||
365 days. Without Jason's approved content/retention policy, persist metadata only and
|
||||
show that later content fetch requires the source online. Expired content returns an
|
||||
explicit unavailable result. Define backup/key expiry and restore suppression together:
|
||||
restoring an old backup must not resurrect expired content or redeliver old work.
|
||||
Local pruning needs the Stack's `data.delete` approval path or an explicitly approved
|
||||
future retention authority. Do not silently grant automatic deletion. Write receipts,
|
||||
preserve legal holds and do not rewrite `<dataRoot>/runs/` or existing audit evidence.
|
||||
|
||||
## Access and security
|
||||
|
||||
**D8 Identical access contract.** Proposed tools are `mail_list(source?,cursor?,limit)`,
|
||||
`mail_get(id,view)`, `mail_attachment_get(id,part)`, `mail_route(id,target,expect)`,
|
||||
`mail_ack(id,expect)`, `mail_draft_create(id,text)` and `mail_health(source?)`.
|
||||
All map to typed broker verbs via the existing tool socket/MCP bridge. Local CLI and
|
||||
authenticated Console use the same handlers. List is paginated (default 20, max 100).
|
||||
`ack` changes Stack-local state only. Drafts are local unsent records, not Graph drafts.
|
||||
Health returns only authorized source metadata. No arbitrary URL, shell, source secret
|
||||
path, provider query language or send tool is accepted.
|
||||
|
||||
Separate rights for metadata, body, attachment, draft and routing per business,
|
||||
source, address binding and named principal. Recheck current role/grant on every read,
|
||||
notification and action, including caches and derived summaries. Revoke immediately
|
||||
fences new uses, invalidates fetch handles and cancels owned in-flight reads before
|
||||
returning content. Agents receive only mail explicitly routed to their grant scope.
|
||||
User mail requires that user's or explicitly delegated access. An email specialist is
|
||||
not implicitly allowed to read every user's mail. Cross-business IDs return no data.
|
||||
|
||||
New mail access grants follow the existing chief → `credential.mint` → authenticated
|
||||
human Approve chain, with exact target, rights and expiry bound once. Add the mail
|
||||
grant method and action routing only through a Jason-approved PRD version and reviewed
|
||||
vocabulary/role changes. Until then new actions remain hard waits. Within-role mail
|
||||
access can become routine only inside those approved grants. Manual-mode actions still
|
||||
need per-use approval. A hand-edited grant or approving email creates no authority.
|
||||
|
||||
**D9 Credentials and hostile input.** Distinguish a source service principal's
|
||||
provider connection grant from an agent's mediated mail-access grant. Recommend
|
||||
dedicated read-only ingress credentials per source/business, held by protected Stack
|
||||
code and never shared with agents. This system-service ownership is a new approved
|
||||
extension, not an exception silently taken to REQ-CRED-1. Configuration names only
|
||||
an approved absolute slot path, for example
|
||||
`<dataRoot>/credentials/mail/<business>/<source>/oauth`; no value or partial value is
|
||||
read by a planner, returned through tools or embedded in a plan. Missing/expired/revoked
|
||||
slots fail closed. Validate owned non-symlink files, rotation and revocation behavior.
|
||||
|
||||
Treat mail, headers, filenames, OCR and quoted replies as untrusted external data.
|
||||
Sender addresses, even authenticated messages, cannot approve decisions, change policy,
|
||||
mint grants or impersonate the CLI/Console human. Delimit model input and strip tool
|
||||
authority, but rely on broker denial, sandboxing and least privilege for enforcement.
|
||||
An email asking an agent to fetch secrets, forward documents or run commands must not
|
||||
gain those rights. External action proposals use existing approval paths and provenance.
|
||||
No automatic link fetches. Provider endpoints are enrolled allowlists with TLS and
|
||||
redirect checks, not values selected from mail content. Logs use reason codes and IDs.
|
||||
|
||||
**D10 Sending.** Out of scope: SMTP submission, Graph send, forwarding, auto-replies,
|
||||
DSNs generated by this service and emailed digests. Block them in adapter interfaces,
|
||||
credentials and network policy. R21 stays intact even if a future separate sender role
|
||||
is approved. Human export of an unsent draft is not authorization for Stack delivery.
|
||||
|
||||
## Monitoring and system-manager integration
|
||||
|
||||
**D11 Health:** report desired/actual connection state, last successful scan and durable
|
||||
cursor commit, lag, backlog, lease owner, retry-after, credential status, quarantine,
|
||||
outbox age and disk pressure. Never report “healthy” merely because the process lives.
|
||||
Candidate stale threshold: three missed scan intervals. Alert once per incident, on
|
||||
severity change and recovery through existing internal notifications/Console, not mail.
|
||||
Show alert delivery failure locally. Alerts include IDs and reason codes, no content.
|
||||
|
||||
Register dependency scopes `mail-source/<business>/<source>` with Q198's manager.
|
||||
Network/provider outages pause only affected intake and dependent fetch/dispatch.
|
||||
Independent sources and authorized stored-mail reads can continue. Disk/audit failure
|
||||
halts new intake/effects. Auth/config denial stays held for correction, never retries
|
||||
under another credential. Malformed messages quarantine individually, not as outages.
|
||||
|
||||
Apply Q205–Q207 through protected mode, not the human kill switch: mechanical source
|
||||
probes run independently at 60, 120, 180 seconds, increasing by 60 to a 600-second
|
||||
ceiling, indefinitely, while respecting longer Retry-After. No JEV/model availability
|
||||
gate and no model call for a mail health check. Mail health proves TLS/auth and scoped
|
||||
source access without fetching message bodies. One lease owns retries, so adapter and
|
||||
manager loops cannot multiply them. Disabled sources make no probes.
|
||||
|
||||
On recovery reconcile cursors/uncertain delivery receipts, pass one synthetic pipeline
|
||||
canary, then resume eligible work in manager-controlled batches without approval per
|
||||
session. Never send test mail or use a live message as a synthetic canary. Uncertain
|
||||
external effects stay fenced. Human kill remains human-only to clear. These mail
|
||||
extensions to SM D13–D15 need contract review and tests before operational reliance.
|
||||
|
||||
## Ordered build rows (all post-v1, not current queue edits)
|
||||
|
||||
Each row is a separately reviewable deliverable. Write failing synthetic cases first,
|
||||
implement only that row, then run its named tests and affected existing suites. Commands
|
||||
below name proposed tests. They are not tests run during this planning assignment.
|
||||
Shared touch points include `packages/{bus,business,harness,plugins,cli,webui}/`.
|
||||
No rollout uses an implementation candidate until normal review and re-pin gates pass.
|
||||
|
||||
| Row | Deliverable / files | Acceptance evidence |
|
||||
| --- | --- | --- |
|
||||
| E01 **post-v1** | PRD addendum and source/data schemas, `docs/prd/`, `packages/mail/schemas/`. Resolve Q1–Q6 before activating corresponding behavior. | Schema fixtures reject unknown fields, missing scopes and secret values. Disabled add-on makes zero connections and v1 startup works without it. Obtain Jason's policy approval, not implementation's implied consent. |
|
||||
| E02 **post-v1** | Protected source slots and mail-access grants, `packages/bus/`, `packages/business/`. Build D8/D9 on existing grant executor. Depends E01. | `node --test packages/bus/tests/mail-grants.test.mjs`: deny non-chief raises, nonhuman approvals, altered targets, duplicate consumption, hand grants and revoked/in-flight access. Secret sentinel absent from all outputs. Existing grant/approval suites pass. |
|
||||
| E03 **post-v1** | Durable intake ledger, leases, outbox and disabled supervisor, `packages/mail/src/{store,intake,service}.mjs`. Depends E02. | `node --test packages/mail/tests/intake.test.mjs`: crash at each transaction/outbox boundary, concurrent owners, disk-full, cap exhaustion and restart. No skipped cursor or duplicate recipient effect, no source calls while disabled. |
|
||||
| E04 **post-v1** | Bounded MIME parsing and quarantine, `packages/mail/src/{parse,attachments}.mjs`. Depends E03. | `node --test packages/mail/tests/parse.test.mjs`: malformed MIME, oversized/deep parts, traversal filenames, remote HTML, archives and encrypted attachments. Limits enforced before expansion, zero execution/network, explicit truncation/quarantine. |
|
||||
| E05 **post-v1** | M365 driver and first `mail-source` kind, `packages/plugins/src/kinds.mjs`, `packages/plugins/mail-source/m365/`. Depends E03/E04. | `node --test packages/mail/tests/m365.test.mjs`: fake Graph pagination/delta, immutable ID moves, cursor expiry, token expiry, 429, restarts and scoped-denial cases. Read-only request allowlist, no skipped/repeated delivery. Existing plugin tests pass. |
|
||||
| E06 **post-v1** | IMAP driver, `packages/plugins/mail-source/imap/`. Depends E05 contract. | `node --test packages/mail/tests/imap.test.mjs`: fake TLS/IMAP server, certificate denial, IDLE disconnect, sequence renumbering, UIDVALIDITY reset, folder moves and missed wake-ups. No Seen/STORE/EXPUNGE, uncertain identities hold effects. |
|
||||
| E07 **post-v1** | Address table, bounded rules and delivery, `packages/mail/src/{addresses,rules,route}.mjs`. Depends E03–E06. | `node --test packages/mail/tests/routing.test.mjs`: aliases, plus tags, Bcc, forged recipient headers, retired agents, multiple recipients, missing/reused Message-ID and duplicate provider pages. No cross-principal disclosure, no duplicate work, no authority from sender. |
|
||||
| E08 **post-v1** | Broker verbs, MCP/typed tools and local drafts, `packages/bus/`, `packages/harness/`, `packages/mail/src/access.mjs`. Depends E02/E07. | `node --test packages/harness/tests/mail-tools.test.mjs`: identical schema/results/refusals through every supported harness adapter, read/attachment/draft rights separated, manual mode and expiry enforced, send impossible. Existing MCP/tools/approval suites pass. |
|
||||
| E09 **post-v1** | Decision-module integration, `packages/mail/src/decisions.mjs`, central decision interface. Depends E08 and approved Q198/Q207 interface. | `node --test packages/mail/tests/decisions.test.mjs`: model outage leaves rules active, one central fallback path, schema-invalid suggestions held, no mail-local model client, hostile mail cannot approve/grant/spend/send. Minimal-context privacy and budget checks pass. |
|
||||
| E10 **post-v1** | Health, alerts and protected-mode recovery, `packages/mail/src/health.mjs`, Q198 manager adapter. Depends E03/E08 and implemented/verified SM D13–D15. | `node --test packages/mail/tests/outage.test.mjs`: fake clock/provider verifies outage scope, full cooldown sequence, Retry-After, parallel probes, no model probe, no outage DLQ, restart continuity, canary/batched resume, uncertain delivery fencing and human kill persistence. |
|
||||
| E11 **post-v1** | Authorized Console/CLI mail views, retention/export/prune/restore, `packages/{mail,cli,webui}/`. Depends E08/E10 and Q4 authority. | Focused `mail-retention` and `mail-ui` tests prove ACLs on lists/search/export, expired-content denial, exact delete approval, holds, backup-expiry restore suppression and no old-work replay. Decision inbox remains distinct, alerts survive mail outage. |
|
||||
| E12 **post-v1** | Opt-in operator runbook and acceptance packet under `docs/`. Depends E01–E11. | Offline end-to-end synthetic message → parse → route → authorized tools → local draft, under every supported harness. Demonstrate revoke, outage recovery and disable/rollback preserving evidence. Live pilot is a separate Jason-authorized step after named address/account/scope/retention approvals. No external mail sent. |
|
||||
| E13 **post-v1 optional** | POP3 adapter, `packages/plugins/mail-source/pop3/`, only for an approved named need. Depends E12. | Fake server tests UIDL absence/reuse, restart and TLS failure, retrieval caps, reconciliation and zero DELE. Unsupported identity refuses activation. Other adapters remain unaffected. |
|
||||
|
||||
## Questions for Jason (recommendations, not assumed approvals)
|
||||
|
||||
1. **Q1 Source and address pilot:** which provider, domain and existing or new mailbox
|
||||
should receive agent/user mail? **Recommend** one separately approved read-only
|
||||
ingress mailbox, aliases for named agent/user recipients, no catch-all or MX change.
|
||||
Use dedicated mailboxes for confidential separation. Prove preserved delivery
|
||||
identity before enabling alias/plus routing. New addresses/accounts wait for Jason.
|
||||
2. **Q2 Grant ownership:** approve a protected source service principal plus separately
|
||||
scoped per-agent/user mail-access grants? **Recommend yes**, through a post-v1 PRD
|
||||
amendment and existing chief/human approval model. No agent gets provider secrets
|
||||
or implicit access to personal mail. Enable only the explicitly requested sources.
|
||||
3. **Q3 Automation:** may rules route mail and wake a granted agent without per-message
|
||||
human approval? **Recommend yes** for reviewed within-role routes, subject to manager
|
||||
admission and manual mode. Reading/drafting does not authorize requested external
|
||||
effects. Any model-needed decision uses the one Q207 module.
|
||||
4. **Q4 Content and deletion:** approve the 30-day body/draft, 7-day attachment and
|
||||
365-day minimal-evidence policy, including encrypted backups and deletion authority?
|
||||
**Recommend these pilot limits**, metadata-only until approved, with an exact
|
||||
`data.delete` approval binding for pruning. Do not weaken the hard-waits list.
|
||||
Jason must identify any business retention/hold requirements before live intake.
|
||||
5. **Q5 Exposure and costs:** allow provider webhooks or paid mailbox infrastructure?
|
||||
**Recommend no initially**. Use outbound Graph polling/IMAP, existing internal
|
||||
notifications and no new exposure. Each future webhook, DNS/MX change or spend
|
||||
gets its own explicit authorization.
|
||||
6. **Q6 Sending:** should any future role send email? **Recommend keep sending out
|
||||
of this add-on**. Retain R21 permanently for the specialist. Any later sender is a
|
||||
separate design with exact-recipient approval, scoped credentials and effect evidence,
|
||||
not a toggle that silently turns drafts into delivery.
|
||||
|
||||
## Planning receipt and handoff
|
||||
|
||||
Verified pinned commits, source-tree equality, cited code boundaries and ten requested
|
||||
coverage areas. No implementation, dependency installation, legacy test execution,
|
||||
mail connection or live-stack change occurred. Canonical `queue next della` returned
|
||||
`nothing` after the linked-worktree refusal. No queue/MVP row changed. Vikunja 460 is
|
||||
the supplied tracking task. Mos/Sage owns any required Gitea issue and future queue
|
||||
enrollment because this seat has no Gitea API credential. Phase B review and phase C
|
||||
synthesis remain with Mos. Della has not read Ludwig's plan. This branch push delivers
|
||||
the planning artifact only, not approval to build or deploy it.
|
||||
@@ -0,0 +1,399 @@
|
||||
# Post-v1 email processing and monitoring plan
|
||||
|
||||
**Status:** phase C candidate, mos-1223, Vikunja 460. Ludwig reviews before Sage lands it.
|
||||
**Goal:** Stack-owned mail intake, monitoring, authorized reading/routing and local drafting for every harness.
|
||||
**Boundary:** all rows post-v1, none required for 2026-10-27. No PRD 1.10 or live change here.
|
||||
Q210 ready rows go to non-v1 seats after handoff. No ready label authorizes live connections,
|
||||
addresses/accounts, DNS/MX, exposure, spend or external mail. Original plans/reviews stay unchanged.
|
||||
R21: **“The email specialist reads and drafts, never sends.”**
|
||||
|
||||
## Evidence and reuse
|
||||
|
||||
Citations are pinned, not claims about deployed behavior. Prefixes below identify the repository and commit
|
||||
for every `file:line` citation. Both prior clones were inspected read-only. No prior application or test suite
|
||||
was executed.
|
||||
|
||||
| Prefix | Pinned evidence |
|
||||
| --- | --- |
|
||||
| S | Stack `c3d88840c5b6ffa62d2a6a4aa4385d388234d777`, this worktree |
|
||||
| MP | `mail-processor` `95b02989f4d4276dded00c90ffd74ebb7d03ae8b` |
|
||||
| EP | `email-processor` `7a6492190cd376058e29a04fd0593e86bae1a8e5` |
|
||||
| SM | `origin/huey/system-manager-design` at `2ee68cd69337bb933a8fabeb04bcd71835170171`, Q198 r6, superseding r5 `fd44f7c60` |
|
||||
|
||||
**F1 Existing mail code:** recursive comparison found identical `src/` trees in MP and EP, including identical
|
||||
tests. MP contains EP plus three commits. Reuse once. Both implement normalized messages and a connector
|
||||
boundary (`src/email_processor/connectors/base.py:8,30`), M365 pagination/rate-limit handling
|
||||
(`connectors/m365.py:81,138`, under that same source directory), ordered AND/OR rules
|
||||
(`src/email_processor/rule_engine.py:81,124`), and bounded preview/cold-start safety
|
||||
(`src/email_processor/poller.py:101,122,152`). Port all 36 rule-engine and 15 predeployment safety test cases
|
||||
as synthetic JS cases after dependency/license review. Remove subject logging (`rule_engine.py:147`). Neither
|
||||
copy proves IMAP or POP3 support: connector selection handles only M365 (`src/email_processor/poller.py:48`).
|
||||
|
||||
**F2 Prior intent:** EP `docs/PRD.md:25,39` describes a single-organization M365 application, not the Stack
|
||||
add-on. MP `docs/PRD.md:26,66,204,235` specifies generic Graph/IMAP/POP3 drivers, separated intake/execution,
|
||||
fail-closed activation and atomic staging/checkpoints. Those are useful design requirements, not implemented
|
||||
features. Deployment manifests target USC, not proof of a currently running service. Do not enroll an employer
|
||||
tenant by default or change any existing service. Adopt the safety properties and conformance cases, not its
|
||||
independent tenant/profile product, bootstrap authority or mandatory standalone web setup.
|
||||
|
||||
**F3 Drop unsafe coupling:** in both repos, `src/email_processor/poller.py:187-195,200,301` checks for
|
||||
duplicates before effects and advances a timestamp to wall-clock now. `src/email_processor/models.py:266` has
|
||||
indexes, not a unique mailbox/message key. Replace these with durable provider cursors, unique identities and
|
||||
transactional delivery. Drop connector caches keyed just by mailbox name (`poller.py:48`), direct GLPI/ntfy
|
||||
effects (`action_executor.py:30,69,159`) and source category writes (`connectors/m365.py:170`,
|
||||
`poller.py:359`). Drop direct model calls and content logs (`classifiers/llm.py:79,105,109,123`), replacing
|
||||
decisions through Q207. Do not import deployment manifests, credential settings, existing accounts or data.
|
||||
Graph already selects `body` and normalizes HTML (`connectors/m365.py:27,129`), although `body_text` is empty.
|
||||
|
||||
**F4 Stack enforcement:** S `packages/harness/src/tools.mjs:1` already maps typed tools through the runner
|
||||
socket with broker checks. Its MCP bridge carries no capability (`packages/harness/src/mcp-server.mjs:1,34`).
|
||||
Reuse that path for all harnesses, rather than giving each one mailbox credentials or a monitoring plugin. S
|
||||
`packages/bus/src/broker.mjs:690,701,1601,1666` supplies human-approved grant consumption and action
|
||||
authority. `packages/bus/src/credentials.mjs:142,358,379` supplies scoped references and protected use. Mail
|
||||
kinds and grants do **not** exist: S `packages/business/src/vocabulary.mjs:109,119` accepts Gitea, Vikunja and
|
||||
host kinds. Mail requires reviewed extensions, not relabelling mail credentials as an existing kind.
|
||||
|
||||
**F5 Product constraints:** S `docs/prd/mosaic-stack.md:432,454,477,496` defines chief-raised grants, exact
|
||||
human approval, one-time consumption and immediate local revocation. Its `:507,542,582,607` covers decision
|
||||
evidence, thread binding and hard waits. PRD “inbox” is the decision inbox, not email. Mail needs a separately
|
||||
approved post-v1 PRD addendum. S `packages/plugins/src/kinds.mjs:9,32` has no mail-source kind. Follow
|
||||
REQ-PLUG-1/2 (S PRD `:876,910`) for provider adapters.
|
||||
|
||||
**F6 Monitoring precedent:** S `tools/agent-watch/jarvis-email-watch-liveness.sh:8,26,53` checks timer state,
|
||||
failed execution and staleness. Reuse the distinction between alive, failed and unknown, not its legacy unit
|
||||
names or fleet wiring. SM `docs/plans/2026-10-21_q198-system-manager.md:262,291,304,326` supplies protected
|
||||
mode, independent probes and controlled resume. It is a design dependency, not shipped proof. S
|
||||
`packages/discord/src/connector.mjs:1-12` and `journal.mjs:60,71` provide authorize → journal → engine →
|
||||
deliver and intent/result reconciliation precedents. Reuse principles, not Discord nonce assumptions or its
|
||||
old approval-button path for mail authorization.
|
||||
|
||||
**F7 Secret-scan evidence:** Ludwig reports a pattern-based scan of both prior trees and full histories with
|
||||
no candidate credential value found (his plan `4a27b73ac`, `docs/plans/2026-10-21_email-plan-ludwig.md:35`).
|
||||
Della did not independently repeat that scan. It proves neither absence of secrets nor that rotation is
|
||||
unnecessary. Any later finding is reported by path and commit only, never by value or partial value.
|
||||
|
||||
## Architecture and sources
|
||||
|
||||
**D1 One owner per instance.** Add an optional supervised `packages/mail/` service, with a single leased owner
|
||||
per configured source. It owns connection pools, OAuth refresh, scheduling, cursors and intake. A harness can
|
||||
stop, restart or change without losing monitoring. Multiple accounts are isolated by instance/business/source
|
||||
ID. “One service” does not mean one global credential, mailbox or shared authorization. Default installation
|
||||
is disabled and opens no connection.
|
||||
|
||||
Use Node modules aligned with the Stack, adapting the Python design rather than embedding both applications
|
||||
and their separate databases/authentication/UI. A Python sidecar would reuse more syntax but duplicate
|
||||
operational boundaries. Harness-native connectors would repeat connection state and fail Jason's cross-harness
|
||||
requirement.
|
||||
|
||||
**D2 Source contract.** Propose a reviewed `mail-source` plugin kind, list-selected, with `descriptor()`,
|
||||
`scan({source,cursor,limits,signal})`, `fetch({source,item,part,limits,signal})` and `health({source,signal})`
|
||||
exports. `scan` returns bounded observations, continuation, terminal cursor and identity epoch. Adapters
|
||||
receive protected credential-use callbacks, never agent-provided secret paths. Core owns grants, leases,
|
||||
persistence, rule evaluation and audit. No adapter sends, deletes, labels, moves or marks messages read. Use
|
||||
the existing channel kind only where its dispatch contract fits presentation, not as a substitute polling
|
||||
interface.
|
||||
|
||||
| Source | Proposed implementation and boundary |
|
||||
| --- | --- |
|
||||
| Microsoft 365 | Graph `Mail.Read`, read-only access, per-folder delta/continuations and immutable message IDs. Replace timestamp polling. Restrict application rights to named mailboxes and verify effective scope, including any additive Entra permissions. |
|
||||
| IMAP | TLS with certificate verification, OAuth where supported, otherwise a separately approved service/app credential. Read-only selection and `BODY.PEEK`; UIDVALIDITY plus UID, never sequence number. IDLE is a wake-up hint with periodic reconciliation. |
|
||||
| Other providers | Prefer a conforming IMAP source initially. Gmail API and JMAP are later adapters only when a named need justifies them. No new provider account is implied. |
|
||||
| POP3 | No build row until Jason names a POP-only need. A future adapter requires stable UIDL, TLS, read-only retrieval and safe identity/reconciliation. No `DELE`. |
|
||||
|
||||
Graph delta is folder scoped and cursors are opaque ([Microsoft delta
|
||||
documentation](https://learn.microsoft.com/en-us/graph/delta-query-messages)). Immutable IDs survive moves
|
||||
within a mailbox, not archive-mailbox moves or reimports ([Microsoft ID
|
||||
documentation](https://learn.microsoft.com/en-us/graph/outlook-immutable-id)). Mailbox-scoped Application RBAC
|
||||
and unscoped Entra grants are independent ([Microsoft RBAC
|
||||
documentation](https://learn.microsoft.com/en-us/exchange/permissions-exo/application-rbac)). IMAP identity,
|
||||
read-only selection and PEEK semantics follow [RFC 9051](https://www.rfc-editor.org/rfc/rfc9051.html). These
|
||||
are protocol references checked 2026-10-21, not evidence of installed support.
|
||||
|
||||
**D3 Addressing.** Version a route table mapping trusted delivery evidence to a stable Stack user or
|
||||
role-instance ID, never a display name or a newly invented role. The org file holds this human-approved table
|
||||
through its versioned audited write path. No agent reroutes messages or widens its own route. A route grants
|
||||
no read right. The first source follows Q1’s provider. Offline adapters need not wait for that choice. Review
|
||||
maintained exact-pinned IMAP/MIME dependencies in their owning rows, initially Ludwig’s
|
||||
`imapflow`/`mailparser` candidates. Do not write either protocol/parser in-house. Aliases on an approved
|
||||
ingress mailbox are the recommended inexpensive start. Plus-addresses are acceptable only after fixtures and a
|
||||
later authorized provider test prove the exact recipient survives delivery. They are not a security boundary.
|
||||
Dedicated mailboxes provide stronger provider-side separation for confidential users or agents, at the cost of
|
||||
account management and possible licenses.
|
||||
|
||||
Bind routes to source identity plus provider-controlled original-recipient evidence. `To`/`Cc`, forwarded
|
||||
headers and arbitrary `Delivered-To` headers are insufficient unless the enrolled delivery path establishes
|
||||
their provenance. Bcc or ambiguous alias delivery goes to restricted operator review, never guessed routing.
|
||||
Unknown addresses do not create agents. Multiple recipients produce independently authorized deliveries
|
||||
without revealing other recipients' mail. Jason approves domains, names, aliases, mailboxes and each
|
||||
provider-side change before provisioning. No SMTP/MX server, catch-all domain or public webhook is proposed
|
||||
for the initial increment.
|
||||
|
||||
## Processing, data and retention
|
||||
|
||||
**D4 Intake pipeline:** approved source → bounded discovery → bounded parse → durable observation/dedup →
|
||||
versioned deterministic rules → authorized delivery outbox → Stack notification/work request. Provider cursor
|
||||
advancement and durable observations commit together under the source lease/generation. Outbox delivery has a
|
||||
stable key and a recipient-side unique receipt, so a crash after delivery but before acknowledgment does not
|
||||
create a second task/message. Never promise exactly-once external effects.
|
||||
|
||||
Default candidates: poll every 60 seconds, at most 50 observations and 10 MiB fetched per slice, fair
|
||||
scheduling between sources. Persist continuation when the slice fills. Cap each source at 1,000 pending
|
||||
deliveries and 100 MiB of retained content initially. Persist quota accounting across restarts and slices.
|
||||
Reaching a quota holds new intake and raises an alert, while read-only health checks continue. First
|
||||
activation establishes a from-now baseline without actions on old mail. Historical import is a separate
|
||||
approved time/count window, previewed before activation. Cap violations, disk-full and unavailable audit
|
||||
storage hold work without advancing past unstaged mail. These limits are proposed settings to validate, not
|
||||
measured capacities.
|
||||
|
||||
Provider identity is scoped to business/source/account: Graph immutable ID, IMAP folder/UIDVALIDITY/UID or
|
||||
POP3 UIDL. RFC Message-ID, thread ID and content similarity are secondary correlation signals, never sole
|
||||
proof of uniqueness. Preserve distinct messages with missing or reused Message-ID. An IMAP move, epoch reset,
|
||||
expired delta cursor, archive move or reimport enters bounded reconciliation. Record aliases only with
|
||||
sufficient identity evidence. Otherwise hold possible duplicate actions for review. An edit is a new
|
||||
observation version, not automatic permission to rerun prior actions. Preserve dedup tombstones beyond content
|
||||
expiry. Replay needs a new authorized intent, parent link and current grants. A rule edit never retroactively
|
||||
executes preview work.
|
||||
|
||||
**D5 Parsing and rules.** Preserve provenance for normalized sender, recipients, timestamp, subject, text,
|
||||
thread references and attachment descriptors. Bound MIME depth to 10, parts to 100 and normalized text to 256
|
||||
KiB. Decode safely, flag malformed or truncated content and quarantine failures. Prefer text, sanitize HTML
|
||||
for display, disable remote images/URLs and never execute active content. Rules use finite allowlisted
|
||||
fields/operators, stable priority and first-match by default. Reject executable rules and unbounded regex.
|
||||
Record rule/version and routing reason. Unmatched mail is held for its authorized owner, not dropped or copied
|
||||
fleet-wide. Routing a user notification does not launch an agent. Agent wake-up is a separate Q198-admitted
|
||||
work request. Wake requires an approved route, current access grant, sender allowlist and trusted aligned
|
||||
DMARC authentication. Strip/ignore sender-supplied Authentication-Results. Ambiguous forwarded/list mail or
|
||||
failed authentication goes to restricted review.
|
||||
|
||||
Optional classification and any mail-triggered decision go through the Stack's one decision module under
|
||||
DECISIONS Q207. The mail service neither selects nor falls back to a model locally. Core policy still
|
||||
determines permitted actions and human hard waits. Use a reviewed mail decision schema inside SM D12’s 4 KiB
|
||||
request limit (`:503-512`). Initial input is metadata only: opaque eligible route IDs, work/capability/error
|
||||
enums and bounded numeric state. Sender domain/auth fields or any new mail-derived field need explicit schema
|
||||
approval and redaction tests; no free-form rule summaries or body. If metadata cannot decide, hold/unknown
|
||||
rather than invent confidence. Body-based summaries and drafts are separate governed work, not mail-local
|
||||
routing decisions. A model result is advisory data, never a grant. Missing model service leaves deterministic
|
||||
intake working and model-dependent work held. Summaries/drafts run as ordinary governed agent work, not an
|
||||
extra mail-service model client.
|
||||
|
||||
**D6 Attachments.** Initially enumerate metadata only. An authorized explicit fetch quarantines bytes under an
|
||||
opaque ID, with a 10 MiB per-part and per-message total cap. Raw quarantine fetch needs no scanner. It never
|
||||
implies permission to expose extracted content. Read-only files are not execution prevention.
|
||||
Scanner/extraction is gated E16. Never trust filenames as paths, unpack archives, execute macros or follow
|
||||
embedded links. Content extraction requires isolated no-network processing, resource bounds, scanner success
|
||||
and a separate read right. Failed, encrypted or unsupported files stay quarantined. A clean scan does not make
|
||||
attachment instructions trustworthy.
|
||||
|
||||
**D7 Storage.** Proposed `<dataRoot>/mail/<business>/` contains encrypted content blobs, SQLite
|
||||
observation/outbox state, encrypted cursors, local drafts and append-only event evidence. No mail content
|
||||
enters git or images. Service-only directories are 0700, files 0600. Use scoped keyed fingerprints for private
|
||||
dedup fields. Content keys live at `<configDir>/secrets/mosaic-stack/mail-content/<business>/<key-id>`,
|
||||
outside dataRoot and its backup set, separate from provider slots, with 0600/no-symlink checks.
|
||||
Protected key expiry/retirement metadata also stays outside the mail-backup restore domain. Keep tokens, delta URLs and authentication errors out
|
||||
of ordinary logs. Default durable records hold opaque IDs, state, versions and counts, not bodies, subjects,
|
||||
recipient lists or attachment names. Content access is explicit and audited.
|
||||
|
||||
Recommended content policy for an enabled pilot: encrypted bodies and local drafts for 30 days, explicitly
|
||||
fetched attachments for 7 days, minimal metadata/tombstones for 365 days. Without Jason's approved
|
||||
content/retention policy, persist metadata only and show that later content fetch requires the source online.
|
||||
Expired content returns an explicit unavailable result. Define backup/key expiry and restore suppression
|
||||
together: restoring an old backup must not resurrect expired content or redeliver old work. Use tested
|
||||
backup-copy deletion or crypto-erasure with expiry of every relevant key copy/escrow, plus restore
|
||||
suppression. Encryption or one key deletion alone proves neither. Local pruning needs the Stack's
|
||||
`data.delete` approval path or an explicitly approved future retention authority. Do not silently grant
|
||||
automatic deletion. Write receipts, preserve legal holds and do not rewrite `<dataRoot>/runs/` or existing
|
||||
audit evidence.
|
||||
|
||||
## Access and security
|
||||
|
||||
**D8 Identical access contract.** Proposed tools are `mail_list(source?,cursor?,limit)`, `mail_get(id,view)`,
|
||||
`mail_thread(id,cursor?,limit?)`, `mail_attachment_get(id,part)`, `mail_ack(id,expect)`,
|
||||
`mail_draft_create(id,text)` and `mail_health(source?)`. Thread views recheck every message and omit
|
||||
unauthorized members/recipients. No reroute tool. All map to typed broker verbs via the existing tool
|
||||
socket/MCP bridge. Local CLI and authenticated Console use the same handlers. List is paginated (default 20,
|
||||
max 100). `ack` changes Stack-local state only. Drafts are local unsent records, not Graph drafts. Health
|
||||
returns only authorized source metadata. No arbitrary URL, shell, source secret path, provider query language
|
||||
or send tool is accepted.
|
||||
|
||||
Routing is human/system-only. Separate metadata, body, attachment and draft rights per business/source and
|
||||
named principal. Recheck current role/grant on every read, notification and action, including caches and
|
||||
derived summaries. Revoke immediately fences new uses, invalidates fetch handles and cancels owned in-flight
|
||||
reads before returning content. Agents receive only mail explicitly routed to their grant scope. User mail
|
||||
requires that user's or explicitly delegated access. An email specialist is not implicitly allowed to read
|
||||
every user's mail. Cross-business IDs return no data.
|
||||
|
||||
New mediated access uses proposed `mail.access`, distinct from provider credential creation through
|
||||
`credential.mint`. The chief raises it to the authenticated human Approve path, with exact
|
||||
principal/target/rights/expiry bound once. Add its access grant schema and action routing only through a
|
||||
Jason-approved PRD version and reviewed vocabulary/role changes. Until then new actions remain hard waits.
|
||||
Grant creation stays human-only. Read/draft actions become routine only inside approved grants. Manual mode needs
|
||||
per-use approval. A hand-edited grant or approving email creates no authority.
|
||||
|
||||
**D9 Credentials and hostile input.** Distinguish a source service principal's provider connection grant from
|
||||
an agent's mediated mail-access grant. Recommend dedicated read-only ingress credentials per source/business,
|
||||
held by protected Stack code and never shared with agents. This system-service ownership is a new approved
|
||||
extension, not an exception silently taken to REQ-CRED-1. Configuration names only an approved absolute slot
|
||||
under `<configDir>/secrets/mosaic-stack/mail/<business>/<source>/`. Proposed service-principal schema: `{kind:
|
||||
"mail-service", business, source, provider, mailbox, scopes, slotPath, rotateBy, approvalId, revision}`.
|
||||
Identity comes from approved configuration, not an agent argument. It grants source reads only, not human
|
||||
input, grant issuance or general bus writes. Reject wildcard/unknown fields, expired approval, changed source
|
||||
identity and slots inside repo/dataRoot. This extends existing schemas explicitly (S
|
||||
`packages/business/src/credentials.mjs:95-102`, `packages/webui/src/sso.mjs:33`). No value or partial value is
|
||||
read by a planner, returned through tools or embedded in a plan. Missing/expired/revoked slots fail closed.
|
||||
Validate owned non-symlink files, rotation and revocation behavior.
|
||||
|
||||
Treat mail, headers, filenames, OCR and quoted replies as untrusted external data. Sender addresses, even
|
||||
authenticated messages, cannot approve decisions, change policy, mint grants or impersonate the CLI/Console
|
||||
human. Delimit model input and strip tool authority, but rely on broker denial, sandboxing and least privilege
|
||||
for enforcement. An email asking an agent to fetch secrets, forward documents or run commands must not gain
|
||||
those rights. External action proposals use existing approval paths and provenance. No automatic link fetches.
|
||||
Provider endpoints are enrolled allowlists with TLS and redirect checks, not values selected from mail
|
||||
content. Logs use reason codes and IDs. Mail tool results carry `untrusted: true` plus a notice modeled on S
|
||||
`packages/bus/src/broker.mjs:109`. Sanitize hidden HTML, comments and control-character tricks for display
|
||||
without treating sanitization as authorization. Test body, subject, display-name, filename, hidden-text and
|
||||
spoofed-internal-sender injection. Mail-woken work uses a fresh isolated profile, intersected with its
|
||||
existing grants: mail reads/draft, scoped fleet messages and decision.raise only, no git/credential/
|
||||
external-message tools. Broker enforcement matches the tool list. Decisions and onward messages retain
|
||||
`source: mail`; downstream seats cannot launder mail into authority. A powerful existing thread is never
|
||||
resumed as the restricted mail turn.
|
||||
|
||||
**D9a Service delivery:** add a protected business-scoped delivery principal/API that accepts only approved
|
||||
recipients and stable mail receipt keys, with fixed actor `mail`. S `packages/bus/src/principal.mjs:8` has no
|
||||
mail principal today. Do not impersonate a user/agent or call an unauthenticated general bus write. Journal
|
||||
delivery acceptance and recipient receipts atomically in the bus through this API, with grant/revoke
|
||||
checks. The separate local outbox retries the same receipt key after a crash, not a distributed transaction.
|
||||
|
||||
**D10 Sending.** Out of scope: SMTP submission, Graph send, forwarding, auto-replies, DSNs generated by this
|
||||
service and emailed digests. Block them in adapter interfaces, credentials and network policy. R21 stays
|
||||
intact even if a future separate sender role is approved. Human export of an unsent draft is not authorization
|
||||
for Stack delivery.
|
||||
|
||||
## Monitoring and system-manager integration
|
||||
|
||||
**D11 Health:** report desired/actual connection state, last successful scan and durable cursor commit, lag,
|
||||
backlog, lease owner, retry-after, credential status, quarantine, outbox age and disk pressure. Never report
|
||||
“healthy” merely because the process lives. Candidate stale threshold: three missed scan intervals. Alert once
|
||||
per incident, on severity change and recovery through existing internal notifications/Console, not mail. Show
|
||||
alert delivery failure locally. Alerts include IDs and reason codes, no content.
|
||||
|
||||
Register dependency scopes `mail-source/<business>/<source>` with Q198's manager. Network/provider outages
|
||||
pause only affected intake and dependent fetch/dispatch. Independent sources and authorized stored-mail reads
|
||||
can continue. Disk/audit failure halts new intake/effects. Auth/config denial stays held for correction, never
|
||||
retries under another credential. Permit at most one refresh/retry of a rejected authentication attempt, then
|
||||
stop and alert to prevent account lockout. Missing grants/config and explicit scope denials get no retry.
|
||||
Malformed messages quarantine individually.
|
||||
|
||||
Apply Q205–Q207 through protected mode, not the human kill switch: mechanical source probes run independently
|
||||
at 60, 120, 180 seconds, increasing by 60 to a 600-second ceiling, indefinitely, while respecting longer
|
||||
Retry-After. No JEV/model availability gate and no model call for a mail health check. Mail health proves
|
||||
TLS/auth and scoped source/folder access without bodies: IMAP login/NOOP alone and Graph directory `GET
|
||||
/users/{id}` do not prove it. The latter needs directory permissions, not Mail.Read ([Microsoft user
|
||||
API](https://learn.microsoft.com/en-us/graph/api/user-get?view=graph-rest-1.0)). Use minimal scoped mailbox
|
||||
operations and test denied folders/mailboxes. Retention or provider deletion can lose source mail during
|
||||
outages; detect/report gaps, never promise losslessness. One lease owns retries, so adapter and manager loops
|
||||
cannot multiply them. Disabled sources make no probes.
|
||||
|
||||
On recovery reconcile cursors/uncertain delivery receipts, pass one synthetic pipeline canary, then resume
|
||||
eligible work without approval per session in manager-controlled batches at
|
||||
most five with 30-second healthy intervals, and current capacity/admission checks (SM `:326-339`). After three
|
||||
consecutive failed automatic exits, persist `recovery-escalated`; probes continue, work does not. Only the
|
||||
authorized `system.recovery.release` path with incident/revision and diagnosis clears that latch, never the
|
||||
kill switch (SM `:341-358`). Never send test mail or use a live message as a synthetic canary. Uncertain
|
||||
external effects stay fenced. Human kill remains human-only to clear. These mail extensions to SM D13–D15 need
|
||||
contract review and tests before operational reliance.
|
||||
|
||||
## Ordered build rows (all post-v1)
|
||||
|
||||
READY means no Jason decision blocks the stated offline deliverable. It does not mean its code prerequisites
|
||||
have landed. Sage queues it now and assigns an available non-v1 seat when prerequisites/review gates pass,
|
||||
without a scheduled start or calendar wait. WAIT Qn identifies the positive authority needed before that row's
|
||||
governed integration. Unanswered questions never block independent READY work. All rows remain disabled by
|
||||
default, use synthetic mail/fake providers and run named tests plus affected existing suites. No ready row
|
||||
enables a real source or weakens current grants/hard waits. Q3/Q9/Q10 propose keeping restrictive defaults,
|
||||
not gates on code that preserves them. Q7 is a technical dependency review, not a new permission gate. Q11
|
||||
affects neither build nor pilot because no old repository/service is altered. Queue enrollment/landings belong
|
||||
to Sage after Ludwig approves this plan, with the v1 path and protected files preserved.
|
||||
|
||||
| Row / gate | Deliverable and dependencies | Acceptance tests (proposed filenames, not run here) |
|
||||
| --- | --- | --- |
|
||||
| E01 **post-v1 READY**; Jason: none for draft | PRD addendum draft, source/data/access/service and external content-key location/lifecycle schemas in `docs/prd/`, `packages/mail/schemas/`. Core mail-source registration/policy activation awaits approved addendum/Q4; content policy awaits E13/Q6. | `packages/mail/tests/schema.test.mjs`: unknown fields, wildcards, secret values, incomplete scopes and mismatched principal/source rejected; key location excludes dataRoot/backups, mode/symlink checks, disabled schema compatible with v1. Every requirement maps to a row. |
|
||||
| E02 **post-v1 WAIT Q4** | Protected slot reader, source-service enrollment/credential lifecycle and separate `mail.access` grant action in `packages/{bus,business}/`. After E01 and approved PRD. No real credential enrollment in this build row. | `packages/bus/tests/mail-grants.test.mjs`, `packages/bus/tests/mail-principal.test.mjs`: non-chief/nonhuman approval, modified targets, expiry, duplicate consume, hand grants, failed issuance undo, manual mode, immediate revoke/in-flight fencing and secret sentinel leakage. Reject repo/dataRoot slots; existing grant tests pass. |
|
||||
| E03 **post-v1 READY**; Jason: none | Durable scoped ledger, encrypted state, leases, cursor/continuation, outbox and quota accounting in `packages/mail/src/{store,intake,service}.mjs`. After E01. No dependency on live grants. | `packages/mail/tests/intake.test.mjs`, `packages/mail/tests/store.test.mjs`: crash every staging/delivery boundary, concurrent owners, missing/reused Message-ID, source-scoped identity collisions, 1,000/100-MiB quotas, disk/audit failure, restart and zero-connection disabled startup. Missing/expired key fails closed; restored backup with expired key returns unavailable, never plaintext. No skips or duplicate recipient effects. |
|
||||
| E04 **post-v1 READY**; Jason: none, Q7 advisory | Bounded MIME parsing/metadata quarantine in `packages/mail/src/{parse,attachments}.mjs`. After E01; independently of grants. Review license/advisories and exact-pin maintained MIME dependency. | `packages/mail/tests/parse.test.mjs`, `packages/mail/tests/parser-corpus.test.mjs`: multipart, bad charset, missing IDs, S/MIME, 30-MiB input, depth/part/text limits, traversal, hidden HTML, comments, bidi/zero-width display tricks, archive and encrypted attachment quarantine. Fuzz corpus; no expansion/execution/network, no content logs. |
|
||||
| E05 **post-v1 READY** offline; core registration **WAIT Q4** | Graph descriptor/module in `packages/plugins/mail-source/m365/` against test-local registry `packages/mail/tests/fixtures/mail-source-registry.mjs`. After E01/E03/E04. `packages/plugins/src/kinds.mjs` stays unchanged until E01 addendum approval/Q4; core entry lands together with or after that approved addendum, with its first production module. Q1 selects pilot, not offline work. | `packages/mail/tests/m365.test.mjs`, `packages/mail/tests/source-contract.test.mjs`: fake Graph delta/paging, immutable moves, token/cursor expiry, 401 single refresh, 429/5xx scheduling, restart, denied mailbox, additive-scope check and alias/plus/Bcc provenance. No source writes; test-local registry works with unchanged core registry before approval. Existing plugin suite passes. |
|
||||
| E06 **post-v1 READY**; Jason: none, Q7 advisory | IMAP module `packages/plugins/mail-source/imap/`. After E05's test-local shared contract; no core registration before approved E01/Q4. Review/license/exact-pin maintained IMAP dependency. | `packages/mail/tests/imap.test.mjs`: local fake TLS server, certificate denial, selected-folder scope denial, BODY.PEEK/no Seen or STORE/EXPUNGE, IDLE drop/poll fallback, renumbering, UIDVALIDITY/move reconciliation and alias/plus/Bcc/forged-header provenance. Ambiguity holds effects. |
|
||||
| E07 **post-v1 READY**; Jason: none for pure evaluation | Address/rule evaluator `packages/mail/src/{addresses,rules,route}.mjs`. After E01/E03/E04; no org-file activation or delivery privilege. | `packages/mail/tests/rules.test.mjs`, `packages/mail/tests/routing.test.mjs`: port all 36 prior rule cases; bounded operators, rule version/order, previews never execute, history import window, recipient ambiguity, multiple recipients, retired agents, repeated identical mail and no content logs. |
|
||||
| E08 **post-v1 WAIT Q4/Q5** | Approved org route writer and narrow service delivery API/principal in `packages/{bus,business}/`, `packages/mail/src/delivery.mjs`. After E02/E03/E07. No agent route writer/reroute. | `packages/bus/tests/mail-delivery.test.mjs`, `packages/bus/tests/mail-routes.test.mjs`: exact human approval and versioned changes, fixed actor/business, recipient receipt idempotency, spoofed principal refused, route addition grants no read, revoke before dispatch fences content, no human-input impersonation. |
|
||||
| E09 **post-v1 WAIT Q4** | Mediated typed/MCP tools, per-message thread view and local drafts in `packages/{bus,harness}/`, `packages/mail/src/access.mjs`. After E02/E08. | `packages/harness/tests/mail-tools.test.mjs`, `packages/mail/tests/access.test.mjs`: same schema/results/refusals on all supported harnesses, separate metadata/body/attachment/draft rights, page caps, thread filtering, cross-business ID denial, revoke/cache races, manual mode, local ack/draft and absence of send/reroute tools. |
|
||||
| E10 **post-v1 WAIT Q5** | Restricted mail-triggered launch profile, sender-auth gate and origin propagation in `packages/{mail,harness,bus}/`. After E08/E09 and Q198 admission interface. | `packages/mail/tests/mail-turn.test.mjs`, `packages/mail/tests/injection.test.mjs`: fresh context/grant intersection, no git/credential/external tools at broker and harness, body/subject/display-name/filename injection, forged auth headers, aligned DMARC/allowlist, forwarded ambiguity, no resume of powerful thread, no authority laundering through fleet messages/decisions. |
|
||||
| E11 **post-v1 WAIT Q4** (mail decision schema approval) | `packages/mail/src/decisions.mjs` using the one approved Q207 decision API; no local model client. After E07/E09 and implemented Q198 decision module. | `packages/mail/tests/decisions.test.mjs`: 4-KiB closed schema, no body/free text, extra metadata denied until approved, out-of-enum/missing/uncalibrated confidence held, central fallback only, unavailable model leaves deterministic intake working; useful metadata-only outcomes or explicit unknown. |
|
||||
| E12 **post-v1 READY**; Jason: none for synthetic recovery | Health/manager adapter `packages/mail/src/health.mjs`. After E03/E08 and implemented/verified SM D13-D15; fixtures can precede integration. | `packages/mail/tests/health.test.mjs`, `packages/mail/tests/outage.test.mjs`: mailbox/folder scope proof, full 60..600 cooldown and Retry-After, independent probes/one retry owner, bad-auth single retry then stop, no model probes or outage DLQ, synthetic canary, five-item/30-second batches, three-exit latch/release, human kill, alert-once/recovery and source gap reporting. |
|
||||
| E13 **post-v1 WAIT Q4/Q6** | Authorized CLI/Console mail/draft/digest views and content/export/retention handlers in `packages/{mail,cli,webui}/`. After E09/E12. No live purge; keep `data.delete` hard wait. | `packages/mail/tests/retention.test.mjs`, `packages/cli/tests/mail.test.mjs`, `packages/webui/tests/mail.test.mjs`: per-user ACL on every item/export, separate decision inbox, exact deletion approval/holds/receipts, expiry of backup content and all relevant keys, restore suppression/tombstones, local draft ownership and metadata-only digest. |
|
||||
| E14 **post-v1 READY**; Jason: none for offline proof | Operator runbook/acceptance packet in `docs/`; offline end-to-end harness test. After E01-E13. | `packages/mail/tests/predeploy-safety.test.mjs`, `packages/mail/tests/end-to-end.test.mjs`: port all 15 safety-gate cases; synthetic intake → route → authorized tools → local draft across supported adapters, zero source connections while disabled, from-now baseline, bounded preview, revoke/outage/restart and disable/rollback with evidence preserved. |
|
||||
| E15 **post-v1 WAIT Q1/Q4/Q5/Q6**, plus Q2 if personal mail | Explicit separately authorized live pilot: enrolled agent ingress source, approved grant/route/content policy and reviewed re-pin. After E14. No v1 gate or external test mail. | `packages/mail/tests/pilot-reconcile.test.mjs` validates a redacted identity-level acceptance manifest; authorized operator supplies live receipts for delivered/held/missing identities, recipient proof, health, restart/revoke and bounded catch-up. Counts alone prove neither loss nor duplication. Complete on coverage/evidence, not a mandatory week or scheduled wait. |
|
||||
| E16 **post-v1 OPTIONAL WAIT Q8**, plus Q4 access | Isolated scanner/content extractor only if Jason chooses attachment content support, `packages/mail/src/extract.mjs`. After E04/E09, separate reviewed dependency/resource boundary. | `packages/mail/tests/extract.test.mjs`: no-network/resource isolation, scan failure/unavailable/encrypted/unsupported hold, execution/archive expansion prohibited, hostile extracted text marked untrusted, access/revoke checked before release. No scanner implies no extracted-content feature. |
|
||||
|
||||
Run proposed Node files with `node --test <filename>`; owning reviewers select affected existing suites and
|
||||
require green evidence before commit/landing. Pure store/parser/rule rows can proceed independently of
|
||||
approval work. Later adapters Gmail API/JMAP/POP3 require a named need and separate rows, not a committed
|
||||
expansion of this add-on.
|
||||
|
||||
## Questions for Jason (merged list, recommendations are not approvals)
|
||||
|
||||
1. **Q1 Pilot provider/domain/addresses:** which controlled domain, provider and agent ingress mailbox,
|
||||
aliases or plus-tags? Recommend one approved non-USC agent mailbox, aliases after recipient proof, no
|
||||
catch-all/MX change. Dedicated mailbox only for approved isolation. Its protocol pilots first; build both
|
||||
adapters offline meanwhile.
|
||||
|
||||
2. **Q2 Personal mail scope:** which existing user mailbox, if any, may be read? Recommend none initially.
|
||||
Later enrollment names user/mailbox and exact consent or delegation, separately from agent ingress.
|
||||
|
||||
3. **Q3 Source mutation:** mark read, flag, label, move or save provider drafts? Recommend no, read-only
|
||||
sources and Stack-local drafts/acknowledgments.
|
||||
|
||||
4. **Q4 Service/access grants:** approve protected source-service credential ownership, distinct
|
||||
per-agent/user `mail.access` approval, mail-source kind and closed mail decision schema? Recommend yes through reviewed
|
||||
post-v1 PRD/vocabulary changes, chief/human approval, exact targets/rights/expiry/revoke. New access action
|
||||
stays a hard wait until the approved PRD explicitly routes it. Provider credential creation remains
|
||||
distinct.
|
||||
|
||||
5. **Q5 Unattended wake:** which senders/routes may wake granted agents without per-message approval?
|
||||
Recommend allowlist plus trusted aligned DMARC, approved route, restricted fresh profile and Q198
|
||||
admission. Manual mode overrides this. Ambiguous/failing mail quarantines. Neither identity nor email
|
||||
approves external effects.
|
||||
|
||||
6. **Q6 Content/retention/deletion:** what may be retained, for how long, with what holds? Recommend encrypted
|
||||
30-day bodies/drafts, 7-day explicitly fetched attachments, 365-day minimal evidence/tombstones,
|
||||
metadata-only until approved. Keep exact `data.delete` authorization, backup/key expiry and restore
|
||||
suppression.
|
||||
|
||||
7. **Q7 Dependencies:** maintained IMAP/MIME packages after license/security review and exact pins rather than
|
||||
custom clients/parsers? Recommend yes, investigate Ludwig's imapflow/mailparser candidates in owning rows.
|
||||
Routine technical review unless an existing repository policy adds an approval gate; no calendar wait for
|
||||
package review.
|
||||
|
||||
8. **Q8 Attachments:** allow bytes/extraction and require scanning? Recommend metadata first, explicit capped
|
||||
raw fetch into quarantine, no scanner for opaque quarantine. Extracted content waits for isolated
|
||||
no-network processing, resource limits and a scanner. Do not enable arbitrary execution or automatic
|
||||
archive expansion.
|
||||
|
||||
9. **Q9 Exposure/spend:** new listener/webhook, DNS/MX, paid infrastructure or model spend? Recommend none
|
||||
initially. Outbound monitoring/internal alerts suffice. Every later exposure, address/account change or
|
||||
spend needs its own authorization.
|
||||
|
||||
10. **Q10 Sending:** should this add-on send/forward/reply? Recommend no. R21 remains permanent for the
|
||||
specialist. A future separate sender needs its own approved plan and exact-recipient human approval
|
||||
through `message.external`.
|
||||
|
||||
11. **Q11 Prior repositories/service:** is the configured USC deployment still running, and should either
|
||||
repo/service be archived or retired? Recommend no operational change here. Preserve both histories, use MP
|
||||
as consolidated design reference. Any archival/retirement is a separate authorized task after actual-state
|
||||
verification.
|
||||
|
||||
## Verification and handoff
|
||||
|
||||
Sources: Della 191dff32f, Ludwig 4a27b73ac, reviews 9182134e5/143415779, Mos's routing ruling.
|
||||
Verify citations, coverage, row gates/tests and unchanged original blobs. No product suite,
|
||||
live mail, secret-file read, install or service change here. After Ludwig approval, send Sage
|
||||
this pin for refactor landing and READY-row queueing on non-v1 seats, copying Mos.
|
||||
Resolve review changes without a calendar wait. Jason-gated work waits on its numbered decision.
|
||||
@@ -0,0 +1,272 @@
|
||||
# della-0002: phase B review of Ludwig's email plan
|
||||
|
||||
2026-10-21. Mos assignment mos-1220, Vikunja 460. Planning review only.
|
||||
**Recommendation:** neither proposal is ready for implementation approval. Preserve
|
||||
Ludwig's ingress/turn-isolation ideas and Della's grant/durability/retention controls.
|
||||
Mos chooses the base in phase C. Neither original plan was edited for this review.
|
||||
|
||||
## Evidence pins
|
||||
|
||||
Every `file:line` below resolves at its named commit. Abbreviations:
|
||||
|
||||
| Ref | Repository/commit and document |
|
||||
| --- | --- |
|
||||
| L | Stack `4a27b73ac`, `docs/plans/2026-10-21_email-plan-ludwig.md`, read all 218 lines |
|
||||
| D | Stack `191dff32f`, `docs/plans/2026-10-21_email-plan-della.md`, 341 lines |
|
||||
| S | Stack `c3d88840c`, the implementation and PRD 1.10 base for both plans |
|
||||
| MP | `mail-processor` `95b0298` |
|
||||
| EP | `email-processor` `7a64921` |
|
||||
| SM | Stack `2ee68cd69`, `docs/plans/2026-10-21_q198-system-manager.md` r6 |
|
||||
|
||||
MP contains EP plus three commits. `src/` and `tests/` compare identical. There are
|
||||
36 rule test definitions and 15 predeployment safety test definitions. This confirms
|
||||
L `:17,28,33`, not that any tests pass today. No prior app or suite was executed.
|
||||
|
||||
## 1. Wrong or unsupported claims and material gaps
|
||||
|
||||
**F1 (factual): Graph already fetches body content.** L `:25` says it never fetches
|
||||
a body. MP and EP `src/email_processor/connectors/m365.py:27` explicitly select
|
||||
`body`; `:118,129` stores HTML body content and `:135` retains the provider object.
|
||||
`body_text=""` is a normalization gap, not absence of body retrieval. Correct before
|
||||
using that claim to estimate migration scope or privacy exposure.
|
||||
|
||||
**F2 (high): role plus route is not the complete grant model.** L `:139,145,147`
|
||||
makes those the access conditions, but its rows `:185-195` omit mail grant creation,
|
||||
exact human approval, expiry, failed-issuance undo, revoke races and manual mode.
|
||||
S `docs/prd/mosaic-stack.md:432,454,477,496,565,607` requires those controls and
|
||||
keeps new actions hard waits until an approved PRD says otherwise. S
|
||||
`packages/bus/src/broker.mjs:690,701` consumes approved grants once. Add explicit
|
||||
source-service enrollment and per-principal mail-access grant work and tests.
|
||||
Routing changes must never grant body access merely by adding a seat to a route.
|
||||
Use a new approved mail-access action for mediated rights, not `credential.mint`
|
||||
unless an operation actually creates a provider credential. Both use authenticated
|
||||
human decision records, with new action routing governed by REQ-WAIT-3.
|
||||
|
||||
**F3 (high): dedup conflates content, identity and authorization.** L `:121` gives
|
||||
Graph an apparently unscoped immutable-ID unique key and makes `(source_id,sha256)`
|
||||
unique. L `:123` merges across sources by Message-ID plus body hash. Distinct deliveries
|
||||
can have identical content or reused IDs, and two businesses must not share a mail
|
||||
record/visibility decision. MP `docs/PRD.md:300,306,310,313` explicitly requires scoped
|
||||
identity, keyed fingerprints and move/epoch reconciliation. Retain source-scoped
|
||||
observations and recipient-specific delivery receipts. Correlation may link records
|
||||
only inside an authorized scope, never collapse them or widen access by itself.
|
||||
Test forged/reused Message-ID, identical repeated mail and cross-business collisions.
|
||||
|
||||
**F4 (high): durable mail storage does not prove durable delivery.** L `:118-127`
|
||||
advances the cursor after a row commit, then routes and delivers. It does not define a
|
||||
durable work state/outbox, recipient idempotency receipt, source lease or atomic
|
||||
ownership claim. Crash after bus acceptance and before local acknowledgment can
|
||||
duplicate work. A unique mail row alone cannot prevent that. MP `docs/PRD.md:334,343,349`
|
||||
separates staging, concurrency and effects. S `packages/bus/schema.sql:70,83` has unique
|
||||
bus message IDs and delivery history but no automatic mail-source idempotency binding.
|
||||
Add transactional pending work, leased intake and stable per-recipient effect keys.
|
||||
|
||||
**F5 (high): mail health probes do not establish mailbox health.** L `:173` proposes
|
||||
Graph `GET /users/{id}`, which checks a directory user, not access to the mailbox.
|
||||
Its application permission is `User.Read.All`, not the proposed `Mail.Read`
|
||||
([Microsoft Get user documentation](https://learn.microsoft.com/en-us/graph/api/user-get?view=graph-rest-1.0)).
|
||||
Use an approved minimal mailbox-scoped read with no bodies. Likewise IMAP login/NOOP
|
||||
does not prove access to the selected folder. Both checks need enrolled source identity,
|
||||
scope and capability validation, with no new privilege merely for probing.
|
||||
|
||||
L `:173` also resumes after one successful probe without stating the synthetic canary,
|
||||
effect reconciliation, batch limits and observation intervals required by SM `:326-339`.
|
||||
Its mention of three failed exits at L `:176` needs those exit stages to exist. Add the
|
||||
persisted escalation latch/release behavior from SM `:341-358`. “Mail is not lost ...
|
||||
because the server keeps it” is unsupported: retention, deletion, cursor expiry and
|
||||
provider failure can remove mail. Report detected gaps, never promise losslessness.
|
||||
|
||||
**F6 (high): retention receipts are not deletion authority.** L `:131,186` proposes
|
||||
`mail prune` with receipts/holds but does not bind it to the human `data.delete` approval
|
||||
path. S `docs/prd/mosaic-stack.md:582-605` makes deletion a hard wait. Retain receipts
|
||||
and holds, add exact deletion authorization, and test backup restore, content expiry,
|
||||
dedup tombstone survival and non-redelivery. Choosing a retention period does not
|
||||
silently route deletion within-role.
|
||||
|
||||
**F7 (medium): Q198 metadata allowlist is mis-cited and not automatically reusable.**
|
||||
L `:66` points to SM `:420` for the 4 KiB restriction, but D12 is SM `:503-512`.
|
||||
L `:126` adds sender domain, authentication results, subject length and rule summary.
|
||||
Those fields are not explicitly in D12's closed allowlist of work/resource/queue/error/
|
||||
capability/opaque-route fields. Require an approved, tested mail decision schema.
|
||||
Metadata-only is a useful default, not proof of compliance or semantic classification
|
||||
quality. Show deterministic or unknown routing when permitted metadata is insufficient.
|
||||
Do not send a free-form rule summary that can contain user content.
|
||||
|
||||
**F8 (medium): the mechanical mail principal is new authority work.** L `:92,127`
|
||||
assigns bus actor `mail`, but S `packages/bus/src/principal.mjs:8-11` verifies only
|
||||
human/tester/user transport identities, and `packages/bus/src/broker.mjs:2965-2985`
|
||||
checks session action authority for message sends. The design needs a narrowly scoped
|
||||
trusted service delivery API/principal, not a spoofed agent session or human input.
|
||||
Specify business identity, fixed actor provenance, recipient checks and allowed verbs.
|
||||
This is a missing implementation contract, not evidence that service delivery is impossible.
|
||||
|
||||
**F9 (medium): recipient evidence needs a Graph gate too.** L `:108` correctly rejects
|
||||
`To` as envelope proof, then suggests Graph recipient fields plus plus-tag without
|
||||
showing they preserve the original alias/Bcc recipient. MP and EP
|
||||
`src/email_processor/connectors/m365.py:115-126` only normalize visible To/Cc fields.
|
||||
L M4 tests IMAP, while M5 `:189` has no recipient-provenance case. Test alias/plus/Bcc
|
||||
preservation for every enrolled source and reject sender-supplied delivery headers.
|
||||
When evidence is absent, quarantine rather than infer from `To` or a guessed tag.
|
||||
|
||||
**F10 (factual/qualification): narrow the evidence claims.** L `:19,54,209,218` treats
|
||||
deployment configuration as current USC deployment evidence. It establishes intended
|
||||
targeting, not that anything is currently running. No live check is authorized here.
|
||||
L `:35` qualifies its secret scan, then concludes “Nothing to rotate.” A pattern scan
|
||||
cannot establish that conclusion. Say no candidate found by the reported scan and
|
||||
make no credential-validity/rotation claim. I have not repeated a full-history scan.
|
||||
L `:68` says no mail code anywhere, but S
|
||||
`tools/agent-watch/jarvis-email-watch-liveness.sh:2,18` is mail-watcher liveness code.
|
||||
Say no integrated Stack mail intake/access service instead. L `:28,43` calls the rule
|
||||
engine pure: MP `src/email_processor/rule_engine.py:147` logs the subject. Port rule
|
||||
semantics, remove content logging, and do not describe the existing function as pure.
|
||||
|
||||
## 2. Ludwig strengths to graft into either base
|
||||
|
||||
- **G1 Restricted mail-triggered work:** L `:155` proposes mail-turn authority,
|
||||
`source: mail` decision provenance and no git/credential/external-message tools.
|
||||
D has least privilege but no explicit restricted mail-triggered launch profile.
|
||||
Adopt a fresh isolated profile whose rights are the intersection with existing
|
||||
grants. Do not resume a powerful ongoing thread or let fleet messaging launder
|
||||
mail-origin requests into authority elsewhere. Broker enforcement must match tools.
|
||||
- **G2 Trusted sender evidence before wake:** L `:127,158,213` adds provider-origin
|
||||
Authentication-Results, allowlists and aligned authentication. D lacks an explicit
|
||||
sender/authentication gate. Adopt it for unattended wake. A DMARC pass authenticates
|
||||
a domain, not a human decision or permission. Define forwarded/list-mail handling as
|
||||
quarantine/manual review initially. Strip forged auth headers at the trust boundary.
|
||||
- **G3 Read-only thread tool:** L `:141` explicitly offers `mail.thread`. D has no
|
||||
equivalent. Add a paginated view restricted per message, not an entire provider thread
|
||||
that might contain recipients or messages the caller cannot see.
|
||||
- **G4 Dependency investigation and parser corpus:** L `:88,186,202,215` names the
|
||||
IMAP/MIME dependency work and concrete hostile fixtures. Carry that explicit package
|
||||
review, exact pins and case migration into the row that first needs each dependency.
|
||||
This review does not endorse an unverified package version or install anything.
|
||||
- **G5 Explicit future live acceptance:** L M12 `:196` gives the eventual pilot a
|
||||
visible row. D E12 leaves it a separately authorized step without a build row.
|
||||
Preserve an explicit gated post-v1 pilot row, after synthetic evidence and Jason's
|
||||
source/access/content approvals. Replace a fixed week/server-count comparison with
|
||||
identity-level reconciliation, crash/outage/revoke cases and a defined coverage target.
|
||||
Q210 favors immediate authorized progress, not a mandatory calendar wait.
|
||||
|
||||
## 3. Della strengths missing or materially weaker in Ludwig
|
||||
|
||||
- **K1 Grant lifecycle:** D `:212-235,290` separates source credentials from mediated
|
||||
access, enforces human grant approval, expiry, revoke races and manual mode (F2).
|
||||
- **K2 Crash-safe work:** D `:131-156,291` defines leases, cursor/outbox atomicity,
|
||||
recipient receipts, bounded reconciliation and separate replay generations (F3/F4).
|
||||
- **K3 Data minimization:** D `:176-199` uses metadata-only baseline, explicit fetch,
|
||||
encrypted scoped storage/cursors, keyed fingerprints, backup expiry, holds and
|
||||
deletion authority. L's always-raw plaintext store does not provide these controls.
|
||||
- **K4 Resource bounds:** D `:138-146,158-164` names slice, retained-byte, pending-work,
|
||||
MIME-depth/part/text bounds and holds under audit/disk failure. L names a raw-size cap
|
||||
but leaves per-run limits unspecified and parser expansion/cumulative quotas uncovered.
|
||||
- **K5 Manager contract:** D `:260-277,298` includes one retry owner, source/business
|
||||
scope, synthetic canary, uncertain-effect fencing, manager batches and human-kill
|
||||
persistence. Keep these and add Ludwig's explicit repeated-exit escalation.
|
||||
- **K6 Security acceptance:** D E02/E08/E09/E11 `:290,296,297,299` tests hand grants,
|
||||
stale approval, manual mode, revoke/in-flight races, cross-business access and backup
|
||||
restore. L's cross-seat test and reduced tool list do not exercise these boundaries.
|
||||
|
||||
## 4. Disagreements and recommended resolution
|
||||
|
||||
| Ref | Difference | Side and reason |
|
||||
| --- | --- | --- |
|
||||
| D1 | Graph first in D E05 vs IMAP first in L M4 | Choose the first approved pilot source, not a protocol mandate. Prefer IMAP for a personally controlled IMAP mailbox. Prefer Graph if the approved mailbox is M365. Both remain post-v1 adapters. Reusing design does not require retaining USC infrastructure. |
|
||||
| D2 | D aliases first vs L plus-addressing first | Take D's requirement for provider-verified original-recipient evidence. Choose aliases or plus-tags only after that proof and Jason's address ruling. Neither is an access boundary. |
|
||||
| D3 | D metadata-only/explicit content vs L stores every raw message/attachment | Take D's default and scoped encryption. Raw retention is an explicit optional approved policy, not unconditional intake. Keep raw-fidelity fixtures to test parsers without retaining every live message. |
|
||||
| D4 | D 30-day bodies/drafts, 7-day attachments vs L 90 days for all content | Recommend D's shorter pilot limits, 365-day minimal evidence in both. Jason decides business retention/holds. Add quotas and backup expiry rather than relying on retention days alone. |
|
||||
| D5 | D scanner before isolated extraction vs L no scanner initially | Start with metadata only. Fetched bytes stay quarantined. Require isolated extraction and scanning before model-visible extracted content, or explicitly limit the first release to opaque retrieval without extraction. A read-only workspace bind does not stop code from reading/executing bytes. |
|
||||
| D6 | D minimal explicitly permitted content vs L metadata-only model classification | Take L's metadata-only baseline, corrected by F7. Both need an approved mail decision schema and useful-result fixtures. A later content field needs explicit approval, not an alternate decision client. |
|
||||
| D7 | D optional POP3 row vs L no POP3 build | Take L's deferral until Jason names a POP-only need. Preserve the compatibility boundary and safe UIDL/no-DELE requirements as a documented future condition, not a committed implementation row. |
|
||||
| D8 | D ordinary governed agent work vs L restricted mail-turn authority | Take L's explicit restriction with G1's fresh-context and propagation conditions. Preserve a human-approved route for later promotion to ordinary work. |
|
||||
| D9 | L's instance-secret slot vs D's dataRoot slot example | Take L's config-directory placement, with an explicit new service-principal schema. D `:233` is wrong against S `packages/business/src/credentials.mjs:95-102`, which forbids token files in dataRoot. Correct that example in phase C, not the validator. S `packages/bus/src/credentials.mjs:216-239` also constrains loaded locations. |
|
||||
|
||||
Neither interface spelling wins on safety. In phase C choose one naming scheme and
|
||||
declare exact schemas once. Add descriptor/capability discovery to L's source exports.
|
||||
Keep broker-mediated tools identical across harnesses, local-only drafts and no send.
|
||||
Neither plan's later live row is authorization to connect now.
|
||||
|
||||
## 5. Merged Jason questions (no duplicate decisions)
|
||||
|
||||
These replace, rather than append to, both original question lists. Origin labels
|
||||
refer to the original question numbers, not the new numbers below.
|
||||
|
||||
1. **Q1 Pilot provider/domain/addresses:** which controlled domain, provider and agent
|
||||
ingress mailbox should be enrolled, and aliases or plus-tags? **Recommend** one
|
||||
approved non-USC agent mailbox, no catch-all/MX change, with proven recipient
|
||||
preservation. Build its protocol first. Dedicated mailboxes only for an approved
|
||||
isolation need. (L Q1/Q2, D Q1.)
|
||||
2. **Q2 Personal mail scope:** which existing user mailbox, if any, may be read?
|
||||
**Recommend none initially**. Later enrollment names the user/mailbox and exact
|
||||
consent/delegation, separately from agent ingress. (L Q8, D Q1/Q2 personal scope.)
|
||||
3. **Q3 Source mutation:** should intake mark read, flag, label, move or save drafts
|
||||
into the provider? **Recommend no**, read-only source access and local drafts.
|
||||
(L Q3 and provider-draft portion of Q4.)
|
||||
4. **Q4 Service/access grants:** approve a protected mail-service credential owner and
|
||||
distinct per-agent/user access grants under the chief/human approval lifecycle?
|
||||
**Recommend yes**, via reviewed post-v1 PRD/vocabulary changes, a new mail-access
|
||||
hard-wait action distinct from provider credential creation, and exact targets,
|
||||
rights, expiry and immediate local revoke. (D Q2.)
|
||||
5. **Q5 Unattended wake:** which senders/routes may wake agents without per-message
|
||||
approval? **Recommend** explicit sender allowlist plus trusted aligned authentication,
|
||||
approved route/access grant, restricted mail-turn profile and manager admission.
|
||||
Manual mode overrides it. Authentication failures/ambiguity quarantine for review.
|
||||
None of this approves external effects or user decisions. (L Q5, D Q3.)
|
||||
6. **Q6 Content/retention/deletion:** what content may be retained, for how long, and
|
||||
what holds apply? **Recommend** encrypted 30-day body/draft, 7-day explicitly fetched
|
||||
attachments, 365-day minimal evidence/tombstones, metadata-only until approved.
|
||||
Preserve exact `data.delete` authorization, backup expiry and restore suppression.
|
||||
(L Q6, D Q4.)
|
||||
7. **Q7 Dependencies:** accept maintained IMAP/MIME dependencies after review and exact
|
||||
version pins rather than custom protocol/parser implementations? **Recommend yes**,
|
||||
assess Ludwig's named candidates during their owning rows. This is routine technical
|
||||
review unless repository policy adds a dependency approval gate. (L Q7.)
|
||||
8. **Q8 Attachment handling:** may attachment bytes be fetched/extracted, and is a
|
||||
scanner required? **Recommend metadata-only first**, explicit quarantined fetch,
|
||||
isolated no-network extraction plus scanning before exposing extracted content.
|
||||
Do not enable arbitrary attachment execution or automatic archive expansion. (L Q9.)
|
||||
9. **Q9 New exposure/spend:** authorize any new listener/webhook, DNS/MX change, paid
|
||||
infrastructure or model spend? **Recommend none initially**. Outbound monitoring
|
||||
and existing internal notifications suffice. Each later change has its own approval.
|
||||
(D Q5, overlapping listener boundary in L Q2.)
|
||||
10. **Q10 Sending:** should this add-on ever send/forward/reply? **Recommend no**.
|
||||
R21 remains for the specialist. A future separate sender needs its own approved
|
||||
plan and exact-recipient human approval through `message.external`. (L Q4, D Q6.)
|
||||
11. **Q11 Prior repositories/service:** archive the older repo or change any existing
|
||||
USC service? **Recommend no operational change in this assignment**. Keep both
|
||||
histories. Treat MP as the consolidated design reference. Any archival or service
|
||||
retirement is a separate authorized task after verifying actual deployment state,
|
||||
which this review did not inspect. (L Q10.)
|
||||
|
||||
## Cross-review reconciliation received before publication
|
||||
|
||||
Ludwig's `ludwig-0002` arrived during finalization. Read all 102 lines at Stack
|
||||
`143415779:docs/plans/2026-10-21_email-review-by-ludwig.md` (LR). The original plans
|
||||
remain unchanged. These are phase C recommendations, not changes to their text.
|
||||
|
||||
- **C1 Agreed corrections:** LR `:12-19` identifies D's duplicate-check line nit,
|
||||
invalid dataRoot credential slot and overbroad `credential.mint` wording. Correct
|
||||
them in phase C. This review's F2 and Q4 now explicitly distinguish access rights
|
||||
from provider credential issuance. LR `:62` recommends no agent reroute. Adopt that
|
||||
initial tool boundary: human-approved address rules route intake, and agents cannot
|
||||
widen readership by rerouting a message. Keep any later reroute/delegation design
|
||||
separate, with exact access authorization and no content laundering through bus text.
|
||||
- **C2 Shared graft:** LR `:26-39,63-72` agrees with most durability, grant, retention,
|
||||
quota, provider-order and attachment controls above. Its secret-scan result may be
|
||||
included only as Ludwig's attributed, pattern-based finding, not my independent scan
|
||||
and not proof of absence or that no rotation could be needed (F10).
|
||||
- **C3 Keep qualifications:** LR `:65` says key deletion is the only way expiry reaches
|
||||
backups. That is too absolute: a design may delete backup copies or use crypto-erasure,
|
||||
but crypto-erasure works only if all relevant key copies/escrows expire too. Specify
|
||||
the actual restoration/expiry controls. LR `:67` must not imply aliases avoid delivery
|
||||
provenance proof. Both aliases and plus-addressing require that proof (F9/D2).
|
||||
|
||||
## Review receipt
|
||||
|
||||
Read Ludwig's complete pinned plan, compared both proposals and checked the cited
|
||||
implementation/PRD/manager passages. Confirmed repository ancestry and equal source/test
|
||||
trees. Public Graph documentation was checked for F5 on 2026-10-21. No live mailbox,
|
||||
secret file, service, implementation, dependency or MVP/queue change occurred. Canonical
|
||||
`queue next della` returned `nothing`. Static checks cover review structure, cited
|
||||
line existence, question-source coverage and unchanged original plan blob. No product
|
||||
suite or live validation was run. Commit/push this review on `della/email-plan`, then
|
||||
deliver della-0002 immediately to Mos and Ludwig under Q210. Phase C remains Mos's call.
|
||||
Reference in New Issue
Block a user