feat/* branches, four of them duplicated work. This is the ordered plan to fold them into main — Memory first (its code is deployed but lives off-trunk), then rebase the rest onto the new main. Merge order is decided here; deploy is a separate deliberate act.Surveyed read-only against origin/main @ 44a8ffe on 2026-07-07 (git fetch + gh pr list + git merge-tree). Nothing was merged, rebased, or pushed — merging is Sam's per-PR call. This page is the map for making those calls in the right order.
The sprawl is only partly real work. Four PRs are duplicated / retired designs (#75 · #76 · #77 → folded into #80; #79 → replaced by the thin seam). Retiring them takes the count from 10 open PRs to 6 that actually need a decision. Do that on the same pass you merge Memory — otherwise the noise regrows.
memory.oll.am is already live and deployed, but its code lives on feat/oll-memory — not on main. Until it merges, main is not the source of truth for a running money-path spine, and every branch that builds on Memory (e.g. the Bible verse-reingest work, which is literally stacked on it) has nothing to rebase onto. Landing Memory makes main honest, then everything else rebases onto a main that finally contains the whole platform.
Every open PR and every origin/feat|docs/* branch, measured against origin/main @ 44a8ffe. "Diverged" = commits ahead of main. "Clean?" = does git merge-tree apply without conflict today. Verdict legend below the table.
| Branch / PR | What it does | Kind | Frozen / money? | Ahead | Clean vs main? | Verdict |
|---|---|---|---|---|---|---|
feat/oll-memoryPR #80 |
Consolidated Memory service (ingest + RAG + extract in ONE service on LlamaIndex; pgvector + RRF; gateway-routed extraction). The code behind the live memory.oll.am. | Code | YES — Memory spine | 1 | clean | Merge · 1st |
feat/bible-verse-reingestPR #81 |
nomic task-prefixes + verse-level multi-granularity retrieval + real PDF text extraction, on top of Memory. Stacked on feat/oll-memory (contains its base commit daf6d7a). |
Code | YES — Memory | 9 | clean* | Rebase→Merge |
feat/oll-extractPR #75 |
Standalone structured-extraction service. Folded into #80 as extract.py. |
Code | new svc | 1 | clean | Superseded · close |
feat/oll-ingestPR #76 |
Standalone document-ingestion service. Folded into #80. | Code | new svc | 1 | clean | Superseded · close |
feat/oll-ragPR #77 |
Standalone hybrid-retrieval service (pgvector + RRF). RRF fusion ported verbatim into #80; then superseded. | Code | new svc | 1 | clean | Superseded · close |
feat/oll-mcpPR #79 |
Heavy deployable MCP gateway (auth + entitlement-gated platform tools, own deploy). The thin seam is the chosen design (proven live this session over stdio + HTTP). | Code | new svc | 3 | clean | Superseded · close |
feat/oll-mcp-thin(no PR yet) |
Thin FastMCP adapter over live write/account endpoints + retrieval-first Memory tools — the Agent Seam v1. Both branches edit services/oll-mcp; keep this one. |
Code | seam | 3 | clean | Open PR→Rebase→Merge |
feat/oll-model-sovereignPR #78 |
Additive EU / on-device privacy-routing tier for the Model gateway (backward-compatible). Parked per #80's note. | Code | YES — Model | 1 | clean | Rebase→Merge |
fix/billing-status-selfhealPR #30 |
Core billing /status self-heals a stale Stripe customer (test→live cutover). Oldest open PR (2026-06-29). |
Code | YES — Core | 1 | CONFLICT† | Rebase→Merge |
feat/core-guest-checkout(remote) |
Core guest (no-JWT) checkout — the next-dollar unblock. Being re-landed locally on feat/core-guest-checkout-reland (not yet pushed). |
Code | YES — Core | 2 | clean | In-flight · verify |
feat/dev-composePR #82 |
One keyless dev docker-compose (Core→SQLite, model→mock) — "docker compose up just works." Green + adversarially cleared. |
Code (dev/infra) | no | 1 | clean | Merge |
feat/observability-sentry(no PR) |
Sentry wiring across services + Core. Touches many service dirs — best landed after Memory so it can cover it too. | Code | touches frozen | 1 | clean | Rebase→Merge (late) |
feat/ci-hardening(no PR) |
CI workflow hardening (.github/** + integration compose). Foundation for everything else's green gate. |
Code (CI) | no | 1 | clean | Merge (early) |
feat/landing-pages(no PR) |
Landing/marketing pages under landing/. Product-frontend surface, no backend risk. |
Code (landing) | no | 2 | clean | Rebase→Merge |
feat/postiz-distribution-layerPR #83 |
Postiz distribution layer (deploy config under deploy/ + a site page). Deploy-ready, not deployed. |
Code (deploy) + docs | no | 1 | CONFLICT† | Rebase→Merge |
* feat/bible-verse-reingest merge-tree's clean against today's main because it carries the whole Memory branch inside it — but it must be rebased onto main after Memory lands, or it will re-introduce Memory's commit as a duplicate. Treat it as stacked, not independent.
† The only two "conflicts" are in site/build-log.html and state/state.json — the hand-written timeline + generated state, not code. They vanish on rebase (regenerate state, re-add the timeline card). No source-code conflict exists in any open branch.
Of 10 open PRs, 4 are retire-on-sight (#75/#76/#77/#79). The remaining 6 + three PR-less branches are real. Zero of them conflict in code — the platform is far less tangled than the branch count suggests. The whole job is: land Memory, retire the duplicates, rebase the rest onto the new main in dependency order.
Each rung: rebase on latest main → CI green → adversarial verify (if frozen/money) → Sam per-PR go → merge. Docs-only changes skip the PR and go straight to main behind the design gate. Retire the superseded PRs on the Memory pass.
Close, don't merge: PR #75 (oll-extract), #76 (oll-ingest), #77 (oll-rag) — folded into #80. Close #79 (heavy oll-mcp) — the thin seam is chosen. Leave a one-line "superseded by #80 / thin-mcp" comment on each so the decision is auditable, then delete the branches.
Merge first, if you want a hard gate under the rest: feat/ci-hardening (CI-only). It has no product surface, so it's a clean, low-risk first merge that strengthens every subsequent branch's green check.
Result: 10 open PRs → 6 real ones. Sprawl actually shrinks before any product code moves.
PR #80 feat/oll-memory. Adds services/oll-memory/ (new dir — no code conflict with main). This is a frozen money-path spine, so:
origin/main (only state/timeline may conflict — regenerate).services.yml + the keyless integration.yml boot (mock providers, no keys).document_id→document remap, RRF fusion parity vs the retired oll-rag, and the extraction retry/422 discipline. Verify by running a real ingest→query→extract loop against a Neon oll_memory branch, not by reading.On merge: add oll-memory to deploy-all.yml as a T0 foundation service (it's a spine, same tier as Core + Model) and to the plan job's path-map — currently the workflow only knows core/model/write/foto/site. Without this, the decoupled deploy can't target Memory. (Small workflow edit; can ride in the same PR or a fast follow.)
PR #81 feat/bible-verse-reingest. It is stacked on Memory (carries daf6d7a). The instant #80 lands, rebase #81 onto the new main so its 8 own commits replay on top of the merged Memory — dropping the now-duplicated base. Then CI green → adversarial (Memory retrieval quality: nomic task-prefixes, verse-level granularity, the CiteBible locators) → Sam go → merge.
These touch no frozen money-path service — merge them in any order once rebased + green (no adversarial gate, just Sam's go where a PR exists):
feat/dev-compose (PR #82) — already green + cleared; merge.feat/landing-pages — rebase, green, merge.feat/postiz-distribution-layer (PR #83) — rebase (resolves the build-log conflict), green, merge.feat/oll-mcp-thin — open its PR, rebase, green; it's the seam, not a money path, but verify the tool wiring loop still passes both transports before merge.Money-path Core/Model changes — each needs adversarial verify + Sam's explicit go. Land last so they rebase onto a fully-converged main:
fix/billing-status-selfheal (PR #30) — Core billing; rebase (resolves state/build-log conflict), adversarial on the Stripe test→live self-heal path, Sam go, merge.feat/core-guest-checkout / -reland — the next-dollar unblock; reconcile the remote branch with the local reland first (pick one), then treat as a Core money-path merge.feat/oll-model-sovereign (PR #78) — additive Model tier; rebase, adversarial (backward-compat of the routing tier), Sam go, merge.feat/observability-sentry — cross-cutting; land after Memory so Sentry covers it too; rebase, green, merge.After Memory (or any earlier rung) lands, every remaining feat/* must be replayed onto the new main before its own merge. The concrete moves:
# 0. always start from a fresh main
git fetch origin
# 1. rebase the feature branch onto the new main
git switch feat/<branch>
git rebase origin/main
# 2a. clean replay → straight to force-push
# 2b. conflict? the ONLY conflicts here are the hand-written narrative files:
# site/build-log.html (timeline rail)
# state/state.json (generated live-state)
# resolve by taking main's version, then regenerate/re-add your card:
git checkout --theirs state/state.json && python3 scripts/state.py # regen, don't hand-merge
git checkout --theirs site/build-log.html # keep main's rail, re-add your entry after
git add state/state.json site/build-log.html
git rebase --continue
# 3. re-run the gate + CI locally before pushing
sh scripts/design-audit.sh # docs/nav/token/naming gate
sh scripts/oll-am-local.sh mock # keyless full-stack boot, if the branch touches a service
# 4. publish the rebased branch (force-with-lease is safe; plain --force is not)
git push --force-with-lease origin feat/<branch>
# 5. CI re-runs on the pushed branch → wait green → request Sam's per-PR go → merge
Across all 14 branches, no two touch the same source file. Every conflict you will see is in state/state.json or site/build-log.html — machine-generated state and the hand-written timeline. Never hand-merge those: take main's copy, then re-run scripts/state.py / re-add your one timeline card. If a rebase ever surfaces a conflict in a .py / Dockerfile / openapi.yaml, stop — that means two branches genuinely overlap and it's a design decision, not a mechanical resolve.
deploy-all.yml. Converge all the merges first; deploy once, cleanly, at the end.Coolify's own git-auto-deploy is OFF on every backend app (ON only for site), so the tag / workflow dispatch is the only path to prod. Once the merge sequence above is done and main is green:
# Option A — tag a green commit (canonical release)
git switch main && git pull
git tag v0.4.0 # that commit must be CI-green
git push origin v0.4.0 # → deploy-all.yml fires
# Option B — deliberate partial deploy
# Actions → deploy-all.yml → Run workflow
# services = all | changed | "core,model,memory"
deploy-all.yml gates and orders it for you:
services=all|changed|<list> to a concrete set (no variable toggling).memory.oll.am is already running. After #80 merges, its deploy is simply pointing the deployed Coolify app at the main-built image instead of the feat-branch build — a re-point of a live service to the now-authoritative trunk, health-gated like any T0 service. Confirm the running image's digest matches the main-tagged build after the deploy so the "source of truth = main" claim is actually true, not just asserted.
Full CD model + env matrix: CI/CD & Environments. Service topology: The Platform. Live PRs + running services: Control Room.