9 ACCEPT candidates from the v1.3 IDEATE pass (REQ-INB-28..34,
36, 37) decompose into 3 waves of 8 PRs / 8 commits:
Wave 1 (P0 closures):
- T1.1 (1 PR, 2 files): fix(docs+scripts) — close cto-inboxos
owner drift in docs/gitea-actions.md:95 +
scripts/setup_gitea_secrets.sh:4,14,18,28 (REQ-INB-28 +
REQ-INB-37, same P0 class as v1.1 sweep commits 7c20d5d +
d530921; also closes v1.1 P1-2 carryover)
- T1.2 (1 PR, 1 file): ci(ci-pr) — wire 6 v1.1 offline tests
into ci-pr fast-tests job (REQ-INB-34; test_papra_link_parity
uses continue-on-error per v1.0/v1.1 deferred-runtime
precedent)
Wave 2 (doc-hygiene, 5 PRs):
- T2.1 (XS): docs(runbook §footer) — next-review pointer
v1.1 → v1.3 (REQ-INB-29)
- T2.2 (XS): docs(runbook §4.5) — alerting forward-pointer
v1.2 → v1.3 (REQ-INB-30)
- T2.3 (XS): docs(papra-write-policy) — test count 13 → 15
(REQ-INB-31)
- T2.4 (S): docs(extraction-contract) — refresh 4 stale
pipelines/common.py: line refs to post-v1.1 positions
(REQ-INB-32)
- T2.5 (XS): docs(runbook §6) — renumber 6.1 → 6.2 → 6.3 →
6.4 (REQ-INB-36, closes v1.1 P1-1)
Wave 3 (env-file drift, 1 PR):
- T3.1 (S): chore(env) — add 7 missing workflow tunables
to .env.example (REQ-INB-33: OLLAMA_API_KEY,
INBOXOS_LLM_BACKEND, PAPRA_DRY_RUN, PAPRA_BODY_WRITE_MODULES,
PAPRA_METADATA_ONLY_MODULES, MBOX_BUCKET, OPENCODE_NPM_VERSION)
3 DEFER candidates (REQ-INB-35 .gitignore opencode.json,
REQ-INB-38 apply_schemas.py papra_index --check, REQ-INB-39
test_papra_link_parity imapclient shim) are filed for v1.4+
in .ciagent/inboxos/ideation/v1.3-candidates.md §9.
Total: 8 PRs / 8 commits / 9 unique file touches. Net diff
≈ 30-50 lines. 0 new code, 0 new tests, 0 schema changes,
0 new dependencies. v1.3 is the smallest source-tree delta
since v1.0 close.
---ci---
project: inboxos
phase: 1
milestone: v1.3
status: plan
waves:
wave_1:
name: "P0 closures (cto-inboxos + CI test wiring)"
tasks: [T1.1, T1.2]
prs: 2
files: 3
wave_2:
name: "Doc-hygiene sweep (5 candidates)"
tasks: [T2.1, T2.2, T2.3, T2.4, T2.5]
prs: 5
files: 3
wave_3:
name: "Env-file drift closure"
tasks: [T3.1]
prs: 1
files: 1
totals:
tasks: 8
prs: 8
commits: 8
unique_files: 9
new_code: 0
new_tests: 0
schema_changes: 0
new_dependencies: 0
candidates_accepted: [REQ-INB-28, REQ-INB-29, REQ-INB-30, REQ-INB-31, REQ-INB-32, REQ-INB-33, REQ-INB-34, REQ-INB-36, REQ-INB-37]
candidates_deferred: [REQ-INB-35, REQ-INB-38, REQ-INB-39]
candidates_rejected: []
backlog_closures:
v1_1_P1_1: REQ-INB-36
v1_1_P1_2: REQ-INB-37
---/ci---
InboxOS
Self-hosted personal ERP / CRM pipeline. A single Gmail inbox, polled over IMAP, archived to SeaweedFS, classified and extracted by a local LLM (Ollama), and written to NocoDB as structured records. Curated attachments are mirrored to Papra. No third-party SaaS touches PII.
Where to start
- Operations runbook —
docs/operations-runbook.md. System overview, service inventory, workflow operations, observability, failure modes, security, cost, glossary. The first thing to read on day 1 of incident response. - Extraction contract —
docs/extraction-contract.md. How a raw email becomes a NocoDB record. Source of truth for theinboxos:classify+inboxos:extract-<module>prompt family. - NocoDB schema —
docs/schemas.md. The four module tables (finance,crm,intel,promotions) and the Message-ID idempotency contract. - Gitea Actions setup —
docs/gitea-actions.md. Runner provisioning (including the second runner for live + historical parallelism), repo secrets, the Basic Auth model. - Pipelines —
pipelines/README.md. Flow diagrams, idempotency model, rollback procedure for the live and historical pipelines. - Papra write policy —
docs/papra-write-policy.md. When the pipeline writes to Papra (attachments + selected bodies), and why the first ship is dry-run.
Quick layout
inboxos/
├── README.md # this file
├── .env.example # placeholder env (no real secrets)
├── docker/
│ └── docker-compose.yml # self-hosted stack: gitea, runners, nocodb, seaweedfs, papra, ollama
├── .gitea/workflows/
│ ├── live-pipeline.yml # cron */5 * * * *, polls IMAP
│ ├── historical-pipeline.yml # workflow_dispatch, streams .mbox
│ └── ci-pr.yml # fast-tests + 100MB e2e + live Ollama smoke
├── pipelines/ # shared Python: common.py, live_pipeline.py, historical_pipeline.py, mbox_stream.py
├── .opencode/commands/ # version-controlled extraction prompts (prompt-as-code)
├── config/ # n8n config (legacy, no longer in use)
└── docs/ # this runbook + schemas + contracts + reports
Status
Phase 5 — Full Deployment. Parent goal: INB-1. Current phase issue: INB-7. The runbook in docs/operations-runbook.md is the canonical reference for ongoing operation.
Historical archive (Google Takeout .mbox)
The full Google Takeout .mbox export lives in SeaweedFS at the canonical S3 URI:
s3://inboxos/eml/historical/all-mail.mbox
That is the value to pass as the mbox_object_key workflow_dispatch input to historical-pipeline.yml. To verify it exists, run from any host with mc configured against the inboxos bucket:
mc ls --recursive inboxos/inboxos/ | grep '\.mbox$'
The historical pipeline streams this object message-by-message, UPSERTs each into NocoDB on Message-ID, and writes the raw .eml backup under s3://inboxos/eml/historical/<yyyy>/<mm>/<dd>/<Message-ID>.eml. See docs/operations-runbook.md §3.2 for the dispatch recipe and §4.4 for the full bucket layout.