cloudinit-bot 946b744a09 docs(ideate): v1.3 candidate ideas — codebase-drift ideation (12 candidates)
Source query: Gitea issue queue is now EMPTY (0 open issues).
The CTO has closed both INB-183 (paperclip monorepo PATH append,
rejected in v1.2) and INB-193 (the wrapper that the v1.3
research commit body had named as the v1.3 deliverable). The
v1.3 IDEATE pass is therefore driven entirely by codebase
analysis (per the user's redirect), not by the issue queue.

Tier 1 (mechanical, 8 findings → 8 REQs REQ-INB-28..35):
- T1-1 (P0): docs/gitea-actions.md:95 cto-inboxos drift
  (v1.1 P0 sweep missed this file; last touched Phase 4)
- T1-2 (P2): runbook footer "Next review" stale by 2 milestones
- T1-3 (P2): runbook §4.5 forward-pointer to v1.2 alerting
  (v1.2 never delivered alerting; pointer is stale)
- T1-4 (P2): papra-write-policy.md:192 test count 13→15
  (v1.1 added 2 tests; doc was not updated)
- T1-5 (P2): extraction-contract.md 4 stale line refs
  in pipelines/common.py
- T1-6 (P2): .env.example missing 7 workflow env vars
  (OLLAMA_API_KEY, INBOXOS_LLM_BACKEND, PAPRA_DRY_RUN,
   PAPRA_BODY_WRITE_MODULES, PAPRA_METADATA_ONLY_MODULES,
   MBOX_BUCKET, OPENCODE_NPM_VERSION)
- T1-7 (P0): 6 v1.1 offline tests not wired into ci-pr
  merge gate (papra_decide, papra_index, papra_link_parity,
  metrics, restore, rotate_secrets)
- T1-8 (XS): .gitignore opencode.json shadows tracked file
  (defer to v1.4)

Tier 2 (backend-enriched, v1.1 backlog + PIT items, 4 REQs):
- T2-1 (v1.1 P1-1): runbook §6 ordering 6.1→6.4→6.2→6.3
  → REQ-INB-36
- T2-2 (v1.1 P1-2): setup_gitea_secrets.sh cto-inboxos drift
  → REQ-INB-37
- T2-3 (v1.1 P2-1): apply_schemas.py --check papra_index test
  → REQ-INB-38 (DEFER, low value)
- T2-4 (v1.1 P2-2): test_papra_link_parity imapclient shim
  → REQ-INB-39 (DEFER, accepted-precedent disposition)

Tier 2 PIT items (3 carry-forward from v1.3 research):
- PIT-V1.3-1 (secret-scan pattern) → §10 lessons
- PIT-V1.3-2 (.ciagent/ sovereignty) → §10 lessons
- PIT-V1.3-3 (CI lint wiring) → absorbed into REQ-INB-34

Tier 2 re-verifications (3, drift-free at v1.3):
- T2-8: schemas.md ↔ apply_schemas.py column lists match
- T2-9: gemma4:e4b pin consistent across 5 prompts + .env.example
- T2-10: opencode backend dead code is intentional + documented

Tier 3 (cross-project, 0 REQ):
- CP-1: paperclip not a registered CIAgent project (carry)
- CP-2: inboxos source has 0 paperclip references (re-verify)
- CP-3: /root/workspace/paperclip/ unmodified (re-verify)

Disposition:
- 9 ACCEPT: REQ-INB-28, 29, 30, 31, 32, 33, 34, 36, 37
- 3 DEFER: REQ-INB-35, 38, 39
- 0 REJECT

Accept ratio: 9/12 (75%). No NocoDB schema impact. v1.3 is
doc-hygiene + test-wiring + env-file-drift + cto-inboxos
closure. Closes 2 v1.1 backlog items (P1-1, P1-2). Re-verifies
5 v1.0/v1.1 drift closures (model pin, schema, opencode dead
code, paperclip sovereignty).

---ci---
project: inboxos
phase: 1
milestone: v1.3
status: ideate
disposition:
  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]
  deferred: [REQ-INB-35, REQ-INB-38, REQ-INB-39]
  rejected: []
tier1:
  - id: T1-1
    finding: "docs/gitea-actions.md:95 cto-inboxos drift (v1.1 P0 sweep missed this file)"
    candidate: REQ-INB-28
    confidence: 0.90
    effort: XS
    schema_impact: no
    disposition: ACCEPT
  - id: T1-2
    finding: "runbook footer 'Next review' stale by 2 milestones"
    candidate: REQ-INB-29
    confidence: 0.95
    effort: XS
    schema_impact: no
    disposition: ACCEPT
  - id: T1-3
    finding: "runbook §4.5 forward-pointer v1.2 alerting stale"
    candidate: REQ-INB-30
    confidence: 0.80
    effort: XS
    schema_impact: no
    disposition: ACCEPT
  - id: T1-4
    finding: "papra-write-policy.md:192 test count 13→15"
    candidate: REQ-INB-31
    confidence: 0.90
    effort: XS
    schema_impact: no
    disposition: ACCEPT
  - id: T1-5
    finding: "extraction-contract.md 4 stale line refs"
    candidate: REQ-INB-32
    confidence: 0.85
    effort: S
    schema_impact: no
    disposition: ACCEPT
  - id: T1-6
    finding: ".env.example missing 7 workflow env vars"
    candidate: REQ-INB-33
    confidence: 0.80
    effort: S
    schema_impact: no
    disposition: ACCEPT
  - id: T1-7
    finding: "6 v1.1 offline tests not wired into ci-pr merge gate"
    candidate: REQ-INB-34
    confidence: 0.90
    effort: S
    schema_impact: no
    disposition: ACCEPT
    absorbs: [PIT-V1.3-3]
  - id: T1-8
    finding: ".gitignore opencode.json shadows tracked file"
    candidate: REQ-INB-35
    confidence: 0.65
    effort: XS
    schema_impact: no
    disposition: DEFER
tier2:
  - id: T2-1
    finding: "runbook §6 ordering non-monotonic (v1.1 P1-1)"
    candidate: REQ-INB-36
    confidence: 0.95
    effort: XS
    schema_impact: no
    disposition: ACCEPT
  - id: T2-2
    finding: "setup_gitea_secrets.sh cto-inboxos drift (v1.1 P1-2)"
    candidate: REQ-INB-37
    confidence: 0.90
    effort: XS
    schema_impact: no
    disposition: ACCEPT
  - id: T2-3
    finding: "apply_schemas.py --check papra_index offline test (v1.1 P2-1)"
    candidate: REQ-INB-38
    confidence: 0.85
    effort: XS
    schema_impact: no
    disposition: DEFER
    reason: "low value (runtime path covered by test_papra_index_offline.py); defer to v1.4"
  - id: T2-4
    finding: "test_papra_link_parity imapclient runtime shim (v1.1 P2-2)"
    candidate: REQ-INB-39
    confidence: 0.55
    effort: S
    schema_impact: no
    disposition: DEFER
    reason: "accepted-precedent disposition (v1.0 + v1.1 deferred-runtime); defer to v1.4"
tier2_re_verify:
  - id: T2-8
    finding: "schemas.md ↔ apply_schemas.py drift-free at column-list level"
    disposition: RE_VERIFY
  - id: T2-9
    finding: "gemma4:e4b pin consistent across 5 prompts + .env.example + opencode.json"
    disposition: RE_VERIFY
  - id: T2-10
    finding: "opencode backend dead code is intentional + documented"
    disposition: RE_VERIFY
tier3:
  - id: CP-1
    finding: "paperclip not a registered CIAgent project"
    disposition: REJECT_AS_CANDIDATE
    reason: "carry-forward from v1.2; closed by operator directive"
  - id: CP-2
    finding: "inboxos source has 0 paperclip references"
    disposition: RE_VERIFY
  - id: CP-3
    finding: "/root/workspace/paperclip/ unmodified by this scan"
    disposition: RE_VERIFY
final_candidates:
  count: 12
  accepted_count: 9
  deferred_count: 3
  rejected_count: 0
  ids: [REQ-INB-28, REQ-INB-29, REQ-INB-30, REQ-INB-31, REQ-INB-32, REQ-INB-33, REQ-INB-34, REQ-INB-35, REQ-INB-36, REQ-INB-37, REQ-INB-38, REQ-INB-39]
  accepted_ids: [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]
  deferred_ids: [REQ-INB-35, REQ-INB-38, REQ-INB-39]
schema_impact: none
cross_project: 0
---/ci---
2026-06-08 23:14:05 +00:00

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 the inboxos: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.

S
Description
InboxOS — self-hosted personal ERP/CRM pipeline. Prompts-as-code, Gitea Actions, NocoDB, Ollama, SeaweedFS, Papra.
Readme 1.8 MiB
Languages
Python 90.3%
Shell 9.7%