The v1.3 EXECUTE stage landed 8 PRs / 8 commits closing
9 ACCEPT candidates (REQ-INB-28, 29, 30, 31, 32, 33, 34,
36, 37). The 4-layer verification produces a clean PASS.
Structural layer: 8/8 must-haves PASS
- no cto-inboxos references in source-tree (T1.1)
- ci-pr.yml wires all 6 v1.1 offline tests (T1.2)
- extraction-contract.md line refs are post-v1.1 (T2.4)
- papra-write-policy.md test count is 15 (T2.3)
- runbook footer says v1.3 (T2.1)
- runbook §6 ordering is 6.1 → 6.2 → 6.3 → 6.4 (T2.5)
- runbook §4.5 forward-pointer says v1.3 (T2.2)
- .env.example has 7 new workflow tunables (T3.1)
Behavioral layer: 30/30 runnable offline tests PASS
- 3 pre-existing fast-tests: 10 + 9 + 3 = 22/22 (live_pipeline
11 deferred-runtime per v1.0/v1.1 precedent)
- 6 new v1.1 hard-fail tests: 15 + 3 + 2 + 7 + 3 = 30/30
- 1 deferred-runtime test (test_papra_link_parity): expected
fail per v1.0/v1.1 precedent; ci-pr.yml step uses
continue-on-error: true
Security layer: 4/4 STRIDE categories PASS
- no cto-inboxos in source-tree (5 hits in .ciagent/ are
audit-trail records describing the migration, not
source-tree references)
- .env is git-ignored
- 8/8 execute commits have well-formed ---ci--- blocks
- no literal token values in .env.example
- ci-pr.yml YAML parses cleanly
Quality layer: 6/6 dimensions PASS
- 9/9 REQ-INB-* coverage (the 3 DEFER candidates are filed
in v1.3-candidates.md §9 for v1.4+)
- 6/6 active personas represented (devops-engineer primary
on T1.1/T1.2/T3.1; security-engineer review on T1.1/T3.1;
lead-developer primary on T2.1-T2.5; ml-engineer review on
T2.4; data-engineer + frontend-engineer are territory-not-
invoked / deactivated respectively)
- 8/8 commit discipline (all <type>(<scope>):, max 2 files)
- 9 unique file touches (no .py changes, no new tests, no
new NocoDB tables, no new shell scripts, no new deps)
- docs/requirements.md + docs/roadmap.md byte-identical to
v1.2 close (CPO-owned, not modified)
- 0 cross-project (paperclip unmodified)
v1.1 backlog closure: 2/4
- P1-1 closed by REQ-INB-36 (runbook §6 renumber)
- P1-2 closed by REQ-INB-37 (setup_gitea_secrets.sh cto-inboxos)
- P2-1 + P2-2 deferred to v1.4+ (filed in v1.3-candidates.md §9)
Verdict: PASS, confidence high. 0 P0/P1/P2 issues, 0 blockers.
The v1.3 inboxos milestone is ready for the REVIEW and AUDIT
stages.
---ci---
project: inboxos
phase: 1
milestone: v1.3
status: verify
verification:
verdict: PASS
confidence: high
layers:
structural: PASS (8/8)
behavioral: PASS (30/30 runnable)
security: PASS (4/4 STRIDE)
quality: PASS (6/6)
must_haves: 9/9
must_haves_table:
- {n: 1, status: PASS, location: docs/gitea-actions.md:95}
- {n: 2, status: PASS, location: docs/operations-runbook.md:823}
- {n: 3, status: PASS, location: docs/operations-runbook.md:285-289}
- {n: 4, status: PASS, location: docs/papra-write-policy.md:192,202}
- {n: 5, status: PASS, location: docs/extraction-contract.md:40,98,102,161}
- {n: 6, status: PASS, location: .env.example:100-144 (new Workflow tunables section)}
- {n: 7, status: PASS, location: .gitea/workflows/ci-pr.yml (6 new test steps)}
- {n: 8, status: PASS, location: docs/operations-runbook.md:479,511,561,568 (§6 monotonic)}
- {n: 9, status: PASS, location: scripts/setup_gitea_secrets.sh:4,14,18,28}
backlog_closures:
v1_1_P1_1: REQ-INB-36 (closed)
v1_1_P1_2: REQ-INB-37 (closed)
v1_1_P2_1: REQ-INB-38 (deferred to v1.4+)
v1_1_P2_2: REQ-INB-39 (deferred to v1.4+)
tests:
pre_existing_pass: 22/22
v1_1_hard_fail_pass: 30/30
deferred_runtime: 12
deferred_reason: "imapclient missing in test host (v1.0 / v1.1 accepted precedent)"
p0_issues: 0
p1_issues: 0
p2_issues: 0
blockers: []
---/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.