Skip to main content

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_rate as 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 shape plan_per_rate can'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​

  1. Append at the bottom with a stable ID (OD-N, monotonically increasing β€” IDs are never reused).
  2. Use the four-section template: What's open, Why it matters, Options, Owner / Next step.
  3. Link to the relevant ADR if one exists, or to a GitHub issue.
  4. 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.
  5. 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.

IDDecisionDateADR
CD-1Multi-tenant architecture: RLS-enforced Postgres isolation, orgId on every table, JWT-scoped queries2026-04-22ADR-000
CD-2LangGraph runtime deferred; Hermes Agent is active2026-05-12ADR-001
CD-3WhatsApp via Twilio Meta Cloud API + shared number pool per Specialist2026-04-30ADR-002
CD-4Deployment: 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-5Specialist = client-facing persona (not a user account); Expert = human employee who reviews AI drafts. Non-obvious β€” will confuse every new contributor.2026-05-01GLOSSARY.md
CD-10AI 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-02AutoreplyThreshold.tsx
CD-11Terminology: "thread" in client UI, "conversation" in API/code, "ticket" retired. Closes OD-5.2026-05-02β€”
CD-12Portal 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-16Email 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-17WhatsApp 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-02seed-whatsapp-pool.ts
CD-18Keep "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-19Role 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-20HWORK_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-21R2 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-22Expert 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-23Specialist 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-12docs/features/channels.md
CD-24Baileys 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-29Issue #1110
CD-25RAGflow 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-02ADR-024
CD-26pgvector on the existing humanwork Postgres is the document store, replacing RAGflow's bundled search.2026-06-02ADR-025
CD-27OpenRouter 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-02ADR-026
CD-28TypeORM 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-03ADR-027
CD-29Retire 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-03ADR-028
CD-30Runtime 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-07ADR-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.