Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@
- Treat `project.bootstrap.yaml` as the source of truth for repo governance, environments, CI policy, and home profile sync.
- If OpenClaw local skills are available, use the `omt-bootstrap` skill for manifest-first bootstrap work instead of rediscovering the workflow.
- Review `docs/bootstrap/onboarding.md` before first merge to confirm reviewers, runner labels, and environment gates match the project.
- The R1-R6 GitHub issues with the `release-v0.1` label drive the public v0.1.0 release.
39 changes: 39 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Changelog

All notable changes to MailPlus Intelligence are documented here.

The format follows Keep a Changelog, and this project uses the versioning
policy in [docs/versioning.md](docs/versioning.md).

## [Unreleased]

- Prepare public-release polish, release workflow, and repo metadata before the
v0.1.0 visibility/tagging gate.

## [0.1.0] - 2026-05-23

### Added

- Canonical-store boundary and privacy/redaction rules for keeping MailPlus as
the raw-message archive.
- Metadata fixture corpus, SQLite schema bootstrap, and index/search helpers.
- Deterministic thread reconstruction with confidence evidence.
- Classification lanes, noise suppression policy, and fixture coverage.
- Deterministic semantic extraction, LLM cassette playback, and optional live
LLM extraction surface.
- Selected-message text cache policy, promotion queue, dry-run exporters, and
scheduler locks.
- CLI commands for search, thread inspection, queue review, dry-run export,
sync status, and doctor checks.
- Live adapter and raw-fetch interface stubs for future credential-gated Phase
2 integration.

### Changed

- Public docs now call out fixture-mode support separately from live MailPlus
and production export work.

### Security

- Added fixture/privacy boundaries and fast validation gates sized for a public
repo before live MailPlus credential handling exists.
27 changes: 26 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,25 @@ Those distilled outputs can then feed:
- future entity/concept pages
- reminders/tasks when explicitly approved

## What's in v0.1

### Working end-to-end

v0.1 is a fixture-mode release. It supports metadata fixture sync, SQLite
schema bootstrap, deterministic thread reconstruction, lane classification,
noise suppression, deterministic semantic extraction, LLM extraction when the
optional `llm` extra and API key are configured, promotion queue review,
dry-run exporters, scheduler locks, CLI inspection, and `mpi doctor`.

### Stubbed / not-yet-wired

- Live MailPlus/IMAP ingestion is not connected yet. The public adapter shape
is documented in [the live MailPlus adapter guide](docs/integration/live-mailplus-adapter.md)
and tracked for Phase 2 in [issue #75](https://github.com/OMT-Global/mailplus-intelligence/issues/75).
- Production export to wiki, `memory/`, or reminders is dry-run only. The
future live export contract is documented in [the live export guide](docs/integration/live-export.md)
and tracked for Phase 2 in [issue #75](https://github.com/OMT-Global/mailplus-intelligence/issues/75).

## What should not go into memory

Avoid dumping:
Expand Down Expand Up @@ -125,7 +144,10 @@ Phase 1 should support practical operator questions like:

## Roadmap status

The current roadmap is tracked in GitHub issues for this repository.
The v0.1 public-release blockers are tracked with the
[`release-v0.1` label](https://github.com/OMT-Global/mailplus-intelligence/issues?q=is%3Aissue+is%3Aopen+label%3Arelease-v0.1).
Phase 2 live integration work is tracked in
[issue #75](https://github.com/OMT-Global/mailplus-intelligence/issues/75).

Primary epics include:

Expand All @@ -138,6 +160,9 @@ Primary epics include:
- memory/wiki promotion
- phase-1 medium-architecture delivery

Release history is tracked in [CHANGELOG.md](CHANGELOG.md), and versioning
policy is documented in [docs/versioning.md](docs/versioning.md).

## Agent execution

- [Agent execution playbook](docs/agent-execution/playbook.md)
Expand Down
38 changes: 38 additions & 0 deletions docs/integration/live-export.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Live Export Integration

Production export to wiki pages, `memory/`, or reminders is v0.2 territory.
v0.1 only writes dry-run artifacts so reviewers can inspect candidate output
before anything durable changes.

## Required Guarantees

A live exporter must honor:

- provenance: every durable write keeps source thread, message IDs, locators,
evidence references, confidence, and review status
- idempotency: rerunning the same approved candidate does not create duplicate
wiki, memory, or reminder entries
- rollback: every write records enough target metadata to undo or supersede it
- review boundary: only `approved` or `corrected` candidates may leave the
dry-run surface
- privacy boundary: raw message bodies and attachment payloads are never
exported into durable memory surfaces

See [promotion review workflow](../promotion-review-workflow.md) for the
human-review states that gate live writes.

## Target-Specific Notes

Wiki and `memory/` exporters should prefer stable page or note identifiers over
title matching. Reminder exporters should store the source artifact ID in the
target metadata when the target system allows it.

## Operator Flow

1. Run extraction and review candidates in the promotion queue.
2. Approve or correct only the artifacts that should become durable memory.
3. Run the exporter in dry-run mode and inspect the manifest.
4. Run the live exporter with an explicit target and rollback path.

Live export PRs are welcome once they include dry-run parity tests, rollback
evidence, and target-specific idempotency tests.
72 changes: 72 additions & 0 deletions docs/integration/live-mailplus-adapter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Live MailPlus Adapter Integration

The live adapter is v0.2 territory. v0.1 ships the stable boundary and fixture
mode, but `_fetch_messages()` intentionally returns an empty batch until a real
MailPlus or IMAP client is wired in.

## Expected Configuration

`src/mailplus_intelligence/live_adapter.py` reads these environment variables:

| Variable | Required | Purpose |
| --- | --- | --- |
| `MAILPLUS_HOST` | yes | MailPlus API or IMAP host |
| `MAILPLUS_USER` | yes | Mailbox identity |
| `MAILPLUS_TOKEN` | yes | OAuth token or app password |
| `MAILPLUS_MAILBOX` | no | Mailbox/folder root, default `INBOX` |
| `MAILPLUS_PAGE_SIZE` | no | Batch size, default `50` |

Missing required values raise `LiveAdapterNotConfigured`; fixture-mode tests
should continue to treat that as a gated state, not a failure.

## SyncBatch Shape

Live ingestion must return the same `SyncBatch` shape as fixture ingestion:

- `source_name`: stable source identifier such as `mailplus:<user>`
- `cursor`: previous cursor supplied by the scheduler or checkpoint
- `next_cursor`: cursor for the next incremental sync
- `messages`: list of metadata-only message dictionaries

Each message should include source account/mailbox/folder, stable UID, message
ID, references/in-reply-to headers when present, sender/recipients, subject,
sent date, labels/flags, locator fields, and attachment metadata. It must not
include raw message bodies.

## Worked IMAP Stub

```python
from mailplus_intelligence.live_adapter import LiveAdapterConfig
from mailplus_intelligence.sync import SyncBatch


def fetch_imap_metadata(config: LiveAdapterConfig, cursor: str = "") -> SyncBatch:
messages = [
{
"fixture_id": "imap:123",
"message_id": "<message@example.test>",
"subject": "Example subject",
"from": "sender@example.test",
"to": ["operator@example.test"],
"date": "2026-05-23T12:00:00Z",
"mailbox": config.user,
"folder": config.mailbox,
"locator": {
"export_id": "imap-demo",
"uid": "123",
"account": config.user,
},
"attachments": [],
}
]
return SyncBatch(
source_name=f"mailplus:{config.user}",
cursor=cursor,
next_cursor="123",
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Update SyncBatch example to avoid invalid next_cursor arg

The worked IMAP stub currently constructs SyncBatch with next_cursor, but SyncBatch only defines source_name, cursor, and messages in src/mailplus_intelligence/sync.py (SyncBatch dataclass). Anyone implementing Phase 2 by following this guide will hit TypeError: __init__() got an unexpected keyword argument 'next_cursor' at runtime, so the integration instructions are not executable as written.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@codex please address

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Summary

  • Addressed the PR review comment on docs/integration/live-mailplus-adapter.md by removing the invalid next_cursor field from the documented SyncBatch shape and clarifying that cursor is the value checkpointed after the batch.
  • Updated the worked IMAP stub to construct SyncBatch with valid fields only, setting cursor="123" and converting messages to a tuple so the example is executable against the current dataclass contract.
  • Confirmed the SyncBatch dataclass only accepts source_name, cursor, and messages, which is why next_cursor was invalid.

Testing

  • git diff -- docs/integration/live-mailplus-adapter.md
  • nl -ba docs/integration/live-mailplus-adapter.md | sed -n '22,75p'
  • git config core.hooksPath .githooks; git add docs/integration/live-mailplus-adapter.md; git commit -m "docs: fix SyncBatch example in live adapter guide"

View task →

messages=messages,
)
```

PRs are welcome for a real adapter once they preserve the metadata-only
boundary, fail closed on credential or locator drift, and include fixture-mode
tests that do not require live credentials.
22 changes: 22 additions & 0 deletions docs/versioning.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Versioning

MailPlus Intelligence uses semantic versioning after public release.

## 0.x Policy

While the project is in `0.x`, breaking changes can ship in minor releases
when they are documented in `CHANGELOG.md`. Patch releases should remain
backward compatible unless a security fix requires otherwise.

## Source Of Truth

The package version is declared in `pyproject.toml` and exposed at runtime as
`mailplus_intelligence.__version__` through `importlib.metadata`. Do not copy
the version into README examples or docs unless the surrounding text is release
history.

## Release Notes

Every release should update `CHANGELOG.md` with operator-facing changes, known
stubs, and any compatibility notes. Commit-level detail belongs in the GitHub
history, not the changelog.
8 changes: 8 additions & 0 deletions src/mailplus_intelligence/__init__.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
"""MailPlus Intelligence runtime foundations."""

from importlib.metadata import PackageNotFoundError, version

from .fixtures import MetadataFixtureCorpus, load_metadata_fixture_corpus
from .runtime import RuntimeProfile, default_runtime_profile
from .schema import apply_all_migrations, apply_schema_v0, current_schema_version
from .sqlite import connect_sqlite
from .suppression import SUPPRESSION_FAMILIES, SuppressionDecision, classify_noise_suppression

try:
__version__ = version("mailplus-intelligence")
except PackageNotFoundError:
__version__ = "0.0.0+local"

__all__ = [
"MetadataFixtureCorpus",
"RuntimeProfile",
"SUPPRESSION_FAMILIES",
"SuppressionDecision",
"__version__",
"apply_all_migrations",
"apply_schema_v0",
"classify_noise_suppression",
Expand Down
6 changes: 5 additions & 1 deletion tests/test_runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import unittest
from pathlib import Path

from mailplus_intelligence import connect_sqlite, default_runtime_profile
from mailplus_intelligence import __version__, connect_sqlite, default_runtime_profile


class RuntimeBaselineTests(unittest.TestCase):
Expand All @@ -16,6 +16,10 @@ def test_default_runtime_profile_is_python_sqlite_and_non_live(self) -> None:
self.assertEqual(profile.storage_engine, "sqlite")
self.assertFalse(profile.live_mailplus_access)

def test_package_version_is_exposed(self) -> None:
self.assertIsInstance(__version__, str)
self.assertGreater(len(__version__), 0)

def test_sqlite_connection_supports_index_style_round_trip(self) -> None:
with tempfile.TemporaryDirectory() as tmpdir:
database = Path(tmpdir) / "mailplus-intelligence.db"
Expand Down
Loading