Nội bộ — kế hoạch. Tài liệu thiết kế để duyệt, không phải bản phân phối. Bản nháp, có thể thay đổi. Một số định danh hạ tầng đã được lược bỏ. noindex.

_system / plans · Option B (đã chốt)

Management Hub — sơ đồ vận hành & kế hoạch tích hợp

Một app · một đăng nhập · gắn các bảng con. Bản nháp 2026-05-31.

Status: draft for Editor review · 2026-05-31 · author: Claude session Companion to: _system/notes/management-hub-mockup-handoff-2026-05-31.md, memory project_management-hub-v2-design. The live mockup is v2.1 at https://hmt-media-review.pages.dev/management-hub/.

This doc answers two asks:

panel it maps to.

(Option B: one app, one login, mounted sub-apps), in de-risked phases.

Phase A is untouched throughout: the hub reads and routes; every sensitive action (approve-with-dignity, watchlist escalation, thank-you send) stays in its own surface behind the login. No auto-publish; no channel-publisher cron.


1. The four areas (what is actually built)

#AreaField → outputData store (bucket)Cloud RunMaturity
2Media & StoryDrive upload → ingest → sanitise → triage → retouch → caption → QC → review → approve → Stage-1 packindex/registry.sqlite + Sandbox/posts/ (bundles bucket) + Dashboard Sheetevent-folder, ingest-watch, pipeline, decisions, status-reconcile, reviewDeployed, live (Đợt 1–4)
1Care / Beneficiaryintake drop → promote → profile + benefits ledger + watchlist; school-doc extractso-dang-ky.sqlite + Ho-so/*.md (care-db bucket)care-dashboard, care-jobsDeployed (Đợt 3)
3+4Donor / Financedonor intake → contribution / in-kind → thank-you + receipt → monthly transparency reportso-tai-chinh.sqlite (care-db bucket)finance-dashboard (factory ready), donor-input (built)Built, not deployed (Đợt 5)
Op1Communitycomments/messages → approval-gated replies → R1 hide+escalate → monthly engagement reportnone yetnone yetNot built (placeholder)

Three SQLites, split by privacy boundary, never ATTACH-joined — any cross-area read (e.g. a benefit funded by a specific donor) happens at the application layer.


2. Workflow sketch — the operating loop and where each surface lives

            FIELD                EDITORIAL              AUDIENCE            RESOURCES
   ┌────────────────────┐  ┌──────────────────┐  ┌──────────────┐  ┌──────────────────┐
   │ brief-form submit  │  │ content plan     │  │ approved post│  │ donor gives      │
   │  → event folder    │  │  (12mo + 3mo)    │  │  → FB + web  │  │  cash / in-kind  │
   │ photos to Drive    │  │  ↑ pulls from    │  │              │  │  → thank-you +   │
   │  → ingest-watch    │  │  trips, holidays,│  │ presence:    │  │    receipt       │
   │  → registry.sqlite │  │  milestones,     │  │  cadence,    │  │  → transparency  │
   │                    │  │  journey miles   │  │  80/20,      │  │    report        │
   │ pipeline:          │  │                  │  │  petal cover │  │                  │
   │  sanitise→triage→  │  │ orphan-trip flag │  │              │  │ community:       │
   │  retouch→caption→  │──┼─▶ feeds plan     │  │ comments →   │◀─┤  "where it went" │
   │  QC (gate 1)       │  │                  │  │  replies     │  │   follow-ups     │
   │  → awaiting-approval│ │ queue: approve   │  │  (Op1)       │  │                  │
   └─────────┬──────────┘  │  with dignity_ok │  └──────┬───────┘  └────────┬─────────┘
             │             └────────┬─────────┘         │                   │
   CARE (parallel, field-driven):   │                   │                   │
   intake→promote→profile+benefits  │                   │                   │
   +watchlist  ──── journey miles ──┘                   │                   │
                                                         ▼                   ▼
                                              everything closes the transparency loop

Area → hub panel map

Hub panel (sidebar)Backed byKind
🌻 Tổng quansummary counts from all four areas + today-stripnew (native hub)
🧭 Hoạt động & Chuyến điDashboard Sheet rows + brief.yaml + registry.sqlite photo countsnew read-view
🌱 Thụ hưởngapp/main.py (care-dashboard)mount existing
🗓️ Kế hoạch nội dungnew content-plan artifact + content-calendar agent + VN observancesnew surface + new data store
📝 Hàng đợi duyệtapp/review_main.py (review)mount existing
📣 Hiện diện xã hộiregistry.sqlite post table + monthly recapsnew read-view
💬 Cộng đồngplaceholder
🤝 Tài trợ & Tài chínhapp/finance_dashboard.py (finance)mount existing
ℹ️ Cách hoạt độngstaticstatic

So the hub is 3 mounts (care, review, finance) + 3 new read-views (trips, presence, overview) + 1 new surface with its own data (content plan) + 1 placeholder (community).


3. Target architecture (Option B)

One Cloud Run Service management-hub, one login, this route layout:

app/hub.py  (new parent — owns login + session middleware)
  /                     → Overview        (native)
  /trips                → Trip ledger     (native read-view)
  /plan                 → Content plan    (native; new data store)
  /presence             → Social presence (native read-view)
  /community            → placeholder     (native static)
  /login /logout        → parent-owned (care_auth session)
  /care/*    → mount  app.main:create_app(...)            auth OFF (parent gates)
  /review/*  → mount  app.review_main:create_app(...)     auth OFF (parent gates)
  /finance/* → mount  app.finance_dashboard:create_finance_app(...)

Single login. All three apps already import tools/care_auth.py and already expose factories (create_app, create_app, create_finance_app). The parent owns /login + the @app.middleware("http") session check; Starlette runs that middleware around mounted sub-apps too, so each sub-app is built with password_hash=None (auth_off) and trusts the parent gate. One secret pair (care-password-hash, care-session-key) already shared by review + care.

Two buckets, one service. This is the part the "50 lines" estimate missed:

Sandbox/posts/, _system/runs).

gcsfuse can mount both in one container at different paths (e.g. /data for bundles, /care-data for the care-db bucket); each sub-app's env points at its own path. This is a Cloud Run deploy-config change (two volume mounts), not app code. The single service account already has object-admin on both buckets.


4. The real wrinkles (honest scoping)

templates use literal href="/bundle/...", /children/..., /donors/.... Mounted at /review etc., those point back at the parent root. Fix: switch the handful of cross-page links to request.url_for(...) (Starlette includes the mount prefix) or root-relative-to-mount. This is the main effort in the mount step — a template audit per app, not a rewrite.

mounted (each lives under its prefix, e.g. /review/static), but the parent must own the canonical /login; sub-app logins become dead routes (leave or strip).

not exist yet — no plan artifact on disk, content-calendar has no board, Knowledge/Brand/facts.md is missing. Needs a small artifact (content-plan.yaml: 12-month themes + 3-month slots + source palette) and a thin reader. The self-flagging "editorial intelligence" (orphan trips, petal gaps, 80/20 drift, observance countdown) is computed, not stored.

static/. The parent gets its own template dir; sub-apps keep theirs. Shared CSS can stay one file served by the parent.

Sheet.** The Sheet read needs the service account creds the pipeline already uses; cache it (it changes slowly).

Honest effort: not 50 lines. Realistically 3–4 focused sessions: (a) parent shell + login + finance mount, (b) trip + presence read-views, (c) content-plan artifact + plan surface, (d) review + care mount with template-link audit + the two-bucket deploy. The "~50 lines" was just the finance mount+auth.


5. Phased plan (de-risked order — each phase ships something usable)

Phase 0 — decide & freeze the contract (no code). Confirm the route layout in §3 and the panel map in §2. Confirm Editor is fine with one combined public URL replacing the separate app URLs.

Phase 1 — parent shell + login + finance mount. Lowest risk: finance is the newest, simplest templates, not yet deployed, and the mount path is already designed for. Build app/hub.py (login + middleware + a static Overview reading mock numbers first), mount finance at /finance, audit finance templates for absolute links. Deploy locally; verify one login reaches finance.

Phase 2 — trip ledger + presence read-views (native, real data). No mount risk (new code). Read Dashboard Sheet + registry.sqlite for the trip ledger and the presence aggregation. Wire the Overview today-strip + tiles to these real numbers. Orphan-trip + petal-gap computed here.

Phase 3 — content-plan artifact + /plan surface. Define content-plan.yaml, a thin reader, and the editorial-intelligence computed flags. This is the genuinely new product surface; can lag if the Editor wants to ship the mounts first.

Phase 4 — mount review + care; two-bucket deploy. The heaviest: template-link audit for both, build the management-hub Dockerfile, mount both GCS buckets, move the env to per-app paths, deploy as one Cloud Run Service, retire (or alias) the separate review + care-dashboard services. Verify Phase-A approve flow end to end through the mounted review app.

Phase 5 — community. Only when Op1 is actually built; the placeholder holds the slot meanwhile.

De-risking option for Phase 4: if the template audit proves fiddly, an interim is to keep review + care as their own services but share the session cookie + secret so the single login works across them and the hub deep-links (closer to A for those two, full-B for finance + the native views). Ship that, finish the true mount later. Stated here so it is a deliberate choice, not drift.


6. Phase-A invariants (non-negotiable, carried through every phase)

their own surface behind the login (gate-8 unchanged; safeguarding.md §3a).

still private-by-login (the public Pages mockup stays mock-only, noindex).


7. Decisions — LOCKED (Editor, 2026-05-31)

read-views. Route layout per §3.

read-views → Content-plan → review/care mount. Each phase ships usable; the two-bucket mount + template audit comes last.

hand-edited YAML — Phase 3 wires the existing content-calendar agent to produce/refresh the 12-month themes + 3-month board from VN observances + trips (orphan-trip feed) + journey milestones. The agent writes the plan artifact; the hub reads it and computes the editorial-intelligence flags.

management-hub Service replaces the standalone review + care-dashboard (and absorbs the not-yet-deployed finance-dashboard) in Phase 4. One service, one URL, one login. (Cut-over is a single switch — mitigate by verifying the full Phase-A approve flow on the new service before retiring the old ones.)

Resulting build backlog

mount finance at /finance + finance template-link audit.

wire Overview today-strip/tiles to real numbers.

+ computed flags (orphan trips, petal gaps, 80/20 drift, observance countdown).

Dockerfile; two-bucket gcsfuse deploy; replace the old services after the approve-flow smoke test passes.