OPEN_DECISIONS.md β Pending Architectural / Product Calls
Tracks decisions discussed but not finalized β questions that need E's input or where both paths are valid and we need to pick one. Each entry: what is open, why it matters, options on the table, and owner / next step. When a decision lands, write the canonical ADR (
docs/decisions/NNN-*.md) or add a row to the Closed Decisions table at the bottom, then delete the entry from this file. Do not leave stale "resolved" entries here β that's what the ADR / CD table is for.
Last updated: 2026-07-09 (closed OD-13 golden-set format β folded into ADR-035 Β§7, now Accepted; the light "job + context + rubric" unit was adopted and seeded by P4.1. Entry deleted per the fold-and-delete convention β ADR-035 is the canonical record).
OD-12 β Client read-only confidence-threshold displayβ
What's open. The Confidence Threshold is operator-configured (CD-10 β AM/SuperAdmin only; clients never see the numeric value). Open sub-question: should clients see a read-only humanized summary of the current setting (e.g. "Your account is currently set to 'Always Reviewed by Expert' β contact your AM to change."), so they understand the safety posture of their account without being given the lever?
Why it matters. Some client orgs ask "are you AI-only or human-reviewed?" before signing. A read-only surface answers the question without leaking the implementation. Risk: clients may treat the label as a control they can negotiate down, which is fine if the AM is the gatekeeper but adds friction.
Options.
- A. No client-facing surface. Clients learn the posture from the AM during onboarding. Status quo. β current
- B. Read-only humanized label in client settings ("Always Reviewed by Expert" / "Reviewed by Expert on most replies" / etc.) tied to the numeric threshold. No control β the only action is "contact your AM".
- C. Read-only label + one-click "Request change" button that opens a ticket for the AM.
Owner. Design + product (E).
Next step. Pick A vs. B vs. C. Referenced from docs/design/VIEWS_BY_ROLE.md.
OD-10 β Lago pricing: premium plan_overrides license vs. free plan_per_rateβ
What's open. End-to-end testing against the live dev Lago revealed that plan_overrides β the per-subscription price-override mechanism the provider-leveraged billing design relied on β is a Lago premium-licensed feature (createSubscription with plan_overrides β 403 feature_unavailable on the free self-hosted edition; the identical call without it succeeds). We shipped a dual-strategy adapter (LAGO_PRICING_STRATEGY: plan_per_rate default/free | plan_overrides premium) so billing works on free Lago today, but the underlying call stands: do we accept plan_per_rate as the operating model, or buy a Lago premium license to keep the original override design? Detailed brief: 2026-05-29-lago-pricing-strategy.md. PR #1105.
Why it matters. It picks the billing data model. plan_per_rate creates one Lago plan per distinct rate (specialist_<currency>_<cents>, reused across subscriptions) and a rate change becomes a native upgrade/downgrade; plan_overrides keeps one shared plan + per-sub override but costs a recurring license. Consolidation (one invoice/org, one line/Specialist), proration (mid-period assign/unassign + rate-change), and arrears are all preserved either way β the choice is cost vs. the slightly cleaner override model, not feature parity. The flag is a deploy-time choice, not a hot toggle on live subscriptions, so switching strategies once subscriptions exist is a deliberate migration (see brief's "Change over time").
Options.
- A. Accept
plan_per_rateas the operating model. $0, works on free Lago now, default already ships it. Plan sprawl bounded by distinct rates (rates cluster into tiers). β proposed - B. Buy a Lago premium license and set
LAGO_PRICING_STRATEGY=plan_overrides. Keeps the one-generic-plan design; recurring cost; existing per-rate subs convert lazily on next rate-change (or via a one-off backfill). - C. Stay dual-strategy indefinitely β ship
plan_per_rate, revisit premium only if a concrete need appears (e.g. a pricing shapeplan_per_ratecan't express). Effectively "A now, B reserved".
Owner. Billing + product (E) β license-spend call. Next step. Decide A vs. B (C is the safe default in the meantime). Sub-questions tracked in the brief: dedicated vs. shared Lago org for prod; webhook HMAC scheme confirmation; multi-currency. When this lands, promote to a numbered ADR and delete this entry.
How to add an entryβ
- Append at the bottom with a stable ID (
OD-N, monotonically increasing β IDs are never reused). - Use the four-section template: What's open, Why it matters, Options, Owner / Next step.
- Link to the relevant ADR if one exists, or to a GitHub issue.
- When the decision lands, either write the canonical ADR (
docs/decisions/NNN-*.md) and reference it from a new Closed Decisions row below, or just add a CD row if no ADR is warranted. Then delete the entry from this file. - Do not leave stale "resolved" entries here β the CD table and the ADR (if any) are the canonical historical record.
OD-14 β First golden scenarios (which JTBDs to seed)β
What's open. Which 1β3 jobs-to-be-done become the first golden scenarios? See ADR-035 Β§9.
Why it matters. The first scenarios prove the sandbox machinery and set initial coverage. Picking high-value, high-error cases makes the eval discriminating (good vs bad policies actually diverge on it) rather than trivially passing β which is the whole point of a regression set.
Options. (Selection strategy, not fixed alternatives.)
- A. Data-driven β pick the clusters where the agent measurably struggles today (high real-traffic
editRatio, #3323). - B. Coverage-driven β span distinct JTBD types (e.g. one domain-knowledge case, one high-stakes/escalation case, one clarify-first case).
- C. Hybrid β 1 data-driven + 1β2 coverage. β proposed
Candidates derive from docs/user-stories.md (JTBDs), docs/design/USER_FLOWS.md (flows), and Slack threads.
Owner. Technical & production managers + analysts (Alex O, Alex D, Tom, Paul). Next step. Nominate 1β3 JTBDs. When chosen, record them in ADR-035 / the implementation plan and delete this entry.
OD-15 β ICP expansion into general marketing opsβ
What's open. The marketing-tooling issue batch (#3699 analytics tooling, #3701 channel access RBAC, and siblings) collectively implies an expansion from crypto/compliance verticals into general marketing ops β a domain with a weaker guard-rail posture (content drafting and analytics vs. KYC/AML compliance). This expansion should be an explicit strategy call, not something the platform backs into via tooling issues.
Why it matters. The platform's compliance-first reputation and guard-rail density (always-HITL, confidence calibration, risk grading) are tuned for high-stakes verticals. Marketing ops has different error economics: a slightly imperfect social post is not a regulatory violation. If we expand without acknowledging this, we either (a) over-engineer guard-rails that slow marketing workflows, or (b) silently relax guard-rails in a way that erodes the compliance posture for existing verticals. Either outcome is worse than making the call explicitly.
Options.
- A. Marketing ops is a first-class vertical with its own guard-rail profile (lower default threshold, lighter HITL). Requires per-vertical configuration surface.
- B. Marketing ops uses the same guard-rail stack as compliance verticals. Clients/AMs tune thresholds per-engagement. No platform-level vertical distinction.
- C. Marketing ops is deferred until a product strategy document explicitly greenlights the ICP expansion, guard-rail profile, and pricing implications.
Owner. Product strategy (E / Franky). Next step. Explicit strategy call before committing engineering effort to marketing-channel connectors beyond the architecture decision (ADR-036). When decided, fold into a product strategy doc or numbered ADR and delete this entry.
Closed Decisionsβ
Decisions that have been made and documented as ADRs. Listed here for historical context. Full rationale in the linked ADR where one exists.
| ID | Decision | Date | ADR |
|---|---|---|---|
| CD-1 | Multi-tenant architecture: RLS-enforced Postgres isolation, orgId on every table, JWT-scoped queries | 2026-04-22 | ADR-000 |
| CD-2 | LangGraph runtime deferred; Hermes Agent is active | 2026-05-12 | ADR-001 |
| CD-3 | WhatsApp via Twilio Meta Cloud API + shared number pool per Specialist | 2026-04-30 | ADR-002 |
| CD-4 | Deployment: Railway (API + agent + Postgres + Redis) + Vercel (frontend). Superseded for staging/prod 2026-07-13 by ADR-038 β staging+prod are now AWS ECS Fargate + RDS Proxy; Railway remains canonical for dev only. Frontend-on-Vercel is unaffected. | 2026-04-22 (β superseded 2026-07-13) | ADR-003, ADR-038 |
| CD-5 | Specialist = client-facing persona (not a user account); Expert = human employee who reviews AI drafts. Non-obvious β will confuse every new contributor. | 2026-05-01 | GLOSSARY.md |
| CD-10 | AI Autoreply Threshold: AM+Expert only (clients never see it). Scale inverted: 0=always autoreply, 101=never. UI: Never/Sometimes/Often/Always. Closes OD-1. | 2026-05-02 | AutoreplyThreshold.tsx |
| CD-11 | Terminology: "thread" in client UI, "conversation" in API/code, "ticket" retired. Closes OD-5. | 2026-05-02 | β |
| CD-12 | Portal routing: path-based (/client/chat) is canonical. No subdomain routing (``{slug}.h.work). All docs and invite email URLs use path-based URLs. Closes OD-6. | 2026-05-07 | β |
| CD-16 | Email outbound always from Specialist alias (bob@h.work), never from Expert's personal address. No BCC. Protects persona integrity. Closes OD-3. | 2026-05-02 | β |
| CD-17 | WhatsApp inbound: From-first routing (sender β whitelist β org β conversation). Pool number used only for outbound context. Seeded from whatsapp-pool.json. Closes OD-4 (status-quo confirmed). | 2026-05-02 | seed-whatsapp-pool.ts |
| CD-18 | Keep "org"/"orgId" in code; use "client" in all user-facing copy and docs β renaming would touch 50+ files, DB migrations, API contracts. "Workspace" is retired as a user-facing term; "Expert workspace" remains correct for the Expert's surface. Canonical table in GLOSSARY.md. | 2026-05-03 | β |
| CD-19 | Role boundary: SuperAdmin creates/manages Specialists and Experts. AMs manage client orgs only (assign existing Specialists, run onboarding). API guards: POST/PATCH/DELETE /admin/specialists restricted to superadmin; GET accessible to both. | 2026-05-03 | β |
| CD-20 | HWORK_DOMAIN is the canonical env var for all domain config (Specialist email, client subdomains). Backend: HWORK_DOMAIN; frontend: NEXT_PUBLIC_HWORK_DOMAIN. Staging: h852.work, production: h.work. Replaces SPECIALIST_EMAIL_DOMAIN and NEXT_PUBLIC_APP_DOMAIN. Closes OD-8. | 2026-05-04 | β |
| CD-21 | R2 file storage: planned only. Spec at docs/design/R2_FILE_STORAGE_SPEC.md. No implementation. All references in docs must be marked "Planned β not yet implemented". Closes OD-9. | 2026-05-04 | β |
| CD-22 | Expert pool model removed. Expert β Org assignment was briefly direct via org_experts, superseded 2026-05-22 by ADR-007 β Expert β data access is now governed by expert_access (dual-scope: org or org Γ specialist). Pool-based routing (ExpertPool, PoolMembership, pool_id on queue items) deleted. Reassign UI being re-introduced on expert_access. | 2026-05-03 (β superseded 2026-05-22) | ADR-007 |
| CD-23 | Specialist email long-term: each bob@h.work will be a real Google Workspace user (not a Cloudflare routing alias). Single GCP service account + domain-wide delegation gives the platform programmatic access to Gmail, Calendar, Drive, Admin SDK β no per-user OAuth or human intervention at runtime. Current Cloudflare + Resend setup is MVP; migration planned post-MVP. See docs/features/channels.md β System 2 long-term plan. | 2026-05-12 | docs/features/channels.md |
| CD-24 | Baileys WhatsApp QR-pairing path retired. Production WhatsApp is Twilio Number Pool only (see CD-3 / ADR-002). All Baileys frontend remnants (WhatsAppQrModal, /integrations/whatsapp/, connectWhatsApp/getWhatsAppQrStatus API methods, channel admin QR triggers) removed. Backend whatsapp-pairing.service.ts is Twilio Sender pairing β unrelated to Baileys β and remains. | 2026-05-29 | Issue #1110 |
| CD-25 | RAGflow retired; Haystack 2.x + Hayhooks on Railway is the retrieval backend. Supersedes ADR-004 + ADR-009. Target $440/env vs prior $2k/env-month. | 2026-06-02 | ADR-024 |
| CD-26 | pgvector on the existing humanwork Postgres is the document store, replacing RAGflow's bundled search. | 2026-06-02 | ADR-025 |
| CD-27 | OpenRouter for embed, chat, and rerank β single key, single billing line. correction_embeddings (PR #1604) now routes through OpenRouter too (#1614), so ADR-026 holds end-to-end. Closes OD-11. | 2026-06-02 | ADR-026 |
| CD-28 | TypeORM migrations only. Raw SQL files in api/src/database/migrations/ are forbidden; CI blocks them. Deployments opt in to RUN_MIGRATIONS_ON_BOOT=true. | 2026-06-03 | ADR-027 |
| CD-29 | Retire agent-runtime per-org Railway provisioner β ECS-only. railway_per_org removed from AgentRuntimeKind; supported kinds are shared_railway + ecs_per_org. Impl PR #1584 closed #1450. | 2026-06-03 | ADR-028 |
| CD-30 | Runtime is read-only toward Specialist assets (Soul/Skills/KB); improvement loop proposes, humans approve. OQ-205 Option A. Transition: correctionβKB auto-ingest was gated behind correction_auto_ingest_enabled (default OFF β corrections stayed unprocessed until the P4.2 proposal flow consumed them); flag + direct-ingest branch removed (P4.2c). Closes OQ-205 (ADR-037 accepted (2026-07-09)). | 2026-07-07 | ADR-037 |
IDs CD-6 through CD-9 and CD-13/14/15 are intentionally unused β they were ephemeral implementation-detail rows pruned on 2026-05-02 (commit
5c9d05ad) once their content had been folded into the ADRs and surrounding docs. IDs are never reused.