# Silver Delivery — Decisions log

**Purpose:** single source of truth for resolved decisions, open questions,
and follow-ups blocking the launch. Updated as Mads / Vitor answer.
**Last updated:** 2026-05-04.

---

## ✅ Resolved decisions

### D1 · Endpoint hosting (was Q1 in job-bot-contract.md)

**Decision:** All 9 endpoints (7 base from ECO-8 + 2 coach-gated from
ECO-15/38) live in **one service: the admin server** at
`vianexu.com/clients/ecobe/admin` (`website-mirror/ecobe/admin/server.js`).

> Mads: All 9 endpoints in 1 service is fine I think.
> Vitor: all in one. we are taking about the admin, right? — yes.

**Implementation impact (WhatsApp side):** none. `lib/jobbotClient.js`
already speaks to a single `JOBBOT_BASE` URL.
**Implementation impact (admin server side):** Vitor implements all 9 in
the existing Express server. Specs live in `docs/job-bot-contract.md`.

---

### D3 · Coach identifier (was Q3)

**Decision:** Coach is identified by **email** (not numeric ID) in all
admin-side calls.

> Mads: Email is fine.
> Vitor: email.

**Implementation impact:** already wired — `coachEmail` body field on
`/admin/silver/users/:slug/trigger-dossier` and `/unlock-jobbot`. Stored
on `silver.json` as `coachTriggeredBy` / `coachUnlockedBy` /
`accessLevelChangedBy` audit fields.

---

### D4 · Dossier question set (was Q4)

**Decision (provisional):** Keep the 5 questions the bot already asks:
target roles · target geos · salary floor · languages · non-negotiables.
Optimise before launch.

> Mads: No reason to change the questions we ask right now […]
>       After getting this to work, we can optimise before launch.

**Open thread:** Vitor noted *"the idea is having the form fulfilled in
the admin website after the course is done"*. This may mean the user
fills the form on the **admin website** (browser) instead of via WhatsApp
multi-turn. **See Q-OPEN-1 below — needs clarification.**

**Implementation impact (current):** the multi-turn WhatsApp dossier
collector ships in `lib/silverCourse.js` (`startDossierCollection`,
`captureDossierAnswer`). It's gated behind the coach Trigger button so
nothing runs before the coach decides — if we move to a website form
later, the WhatsApp turn collector is dead code that can be removed in a
single commit.

---

## ❓ Open questions — need follow-up

### Q-OPEN-1 · WhatsApp turn collection vs admin-website form (was D4 thread)

**Tension between answers:**
- **Mads:** keep the WhatsApp multi-turn dossier flow; bot asks 5
  questions in the shared group.
- **Vitor:** "the idea is having the form fulfilled in the admin website
  after the course is done."

These are two different UX paths:
- **Path A (WhatsApp turns):** coach clicks "Trigger Bot" →
  WhatsApp asks 5 questions → bot writes `career-strategy.json` →
  coach reviews. *(currently implemented)*
- **Path B (Web form):** coach clicks "Trigger Bot" → user gets a
  WhatsApp link to a form on `admin.example/dossier?t=token` → user
  fills the form on web → web POSTs back to admin server → coach
  reviews. *(NOT implemented)*

**Why it matters:** Path B is more polished but adds (a) a webapp page,
(b) a token-bound URL flow, (c) a server endpoint, (d) the WhatsApp turn
collector becomes unused.

**Action needed:** Mads + Vitor align on Path A vs Path B before launch.
For first cohort I'd suggest **Path A** (already shipped, low-friction,
inside the existing group) and migrate to **Path B** post-launch if the
form completion rate is poor.

---

### Q-OPEN-2 · Server-to-server API auth (was Q2 — partial confusion)

**Mads's answer addresses the wrong layer.**

There are **two auth concerns**:

1. **Coach UI login** (already solved): coach logs into the admin
   dashboard via existing email + bcrypt password (`coaches.json`).
   Mads's "let the user log in as normal" applies here. ✅
2. **Bot ↔ admin server API auth** (the actual contract Q2): when the
   admin dashboard fires `POST /admin/silver/users/:slug/unlock-jobbot`,
   the WhatsApp orchestrator (this codebase) must verify the request is
   from the admin server, not random internet. Currently uses
   `Bearer <WHATSAPP_ORCHESTRATOR_TOKEN>` (a shared secret).

> Vitor: I didn't understand this one. → confirms ambiguity.

**Action needed:** confirm we keep **Bearer + shared secret** for the
4 coach-admin endpoints. If yes, set `WHATSAPP_ORCHESTRATOR_TOKEN` in
`ecobot/.env` (any random 32 bytes) AND in the admin server config so
both sides agree. Coach UI login stays separate (unchanged).

**Default if no decision:** keep Bearer. Code already handles it. Worst
case we rotate the token without re-auth'ing coaches.

---

### Q-OPEN-3 · Strategy storage source of truth (was Q5)

**Vitor:** "all information based on admin website. […] backup purposes
it was kept in vps where the whatsapp is located."
**Mads:** "the WhatsApp bot is pulling from the webapp backend […] no
changes are needed."

Currently: when the dossier finalises, `silverCourse.finaliseDossier()`
**writes** `mentees/{slug}/career-strategy.json` locally on the
WhatsApp box. This is then a **local mirror** — fine if the admin server
is also writing/reading the same path on its own VPS, but if they run
on different machines, the two states will drift.

**Action needed:** confirm with Vitor whether the WhatsApp side and
admin server share the `mentees/` directory (same VPS), or whether the
WhatsApp side should `POST /admin/silver/users/:slug/dossier-completed`
to push the data to the admin server (and the admin server writes the
file).

**Default if no decision:** keep the local write. Add a TODO to convert
to a POST once Vitor confirms hosting topology.

---

### Q-OPEN-4 · Job-bot webapp at `/plan?t=…` exists?

**Vitor:** "by job bot webapp you mean the bot to be installed on the
main website? if so, then yes. it exists already. right now it can be
found on viaweb.site (floating button on right bottom)"

The "bot on viaweb.site" sounds like a **chat widget**, not a
**career-plan unlock page**. The contract assumes a page at
`${JOBBOT_BASE}/plan?t=<token>` where the user lands after the coach
clicks Unlock — auto-authenticated, shows their plan + curated jobs.

**Action needed:** confirm whether:
- (a) the page exists already and accepts `?t=<token>` for auth
- (b) the page needs to be built — if so, who/when

If (b), this is a parallel work-stream. The WhatsApp side is fine —
mock mode lets us E2E test without it. The bottleneck shifts to: when
does the user actually have a real page to land on?

---

### Q-OPEN-5 · Thin pipeline warning destination (was Q7)

**Conflict:**
- Mads: nowhere for now, observe.
- Vitor: yes (didn't say where).

**Action needed:** simple pick — Slack channel, email to Mads, or a
banner on the admin dashboard?

**Default if no decision:** journey event `silver_thin_pipeline_warning`
is already emitted. No notification beyond that. Coach sees it via the
journey log on the user's detail page in the dashboard.

---

## 🛠️ Follow-ups & TODOs

### F1 · 30 Hotmart course URLs

`data/silver/lessons.json` has 30 entries with `videoUrl: "_todo:hotmart-url"`.
Replace with the real URLs once you have Hotmart admin access. Until
then, the `sendLesson()` body literally renders the string
`_todo:hotmart-url` — visible in dev, would-be-broken in prod.

**Owner:** Mads.
**Blocks:** real-cohort launch (test cohort can run with placeholders).

---

### F2 · Env vars (`ecobot/.env`)

```bash
# M1/M2 — landing Monday-ish
HOTMART_HOTTOK=<from Hotmart product settings>
HOTMART_MEMBERS_AREA_URL=<Hotmart Members Area URL for the Silver product>

# M2 / RAG
OPENAI_KEY=sk-...

# Community link in welcome / community_invite messages
ECOBE_COMMUNITY_URL=<community invite>

# M3 — coach orchestrator API
WHATSAPP_ORCHESTRATOR_TOKEN=<random base64 32 bytes — share with admin server>

# M3 — Job-bot webapp base (mock mode if unset; live mode otherwise)
JOBBOT_BASE=<https://jobbot.ecobe.com or similar — see Q-OPEN-4>
```

**Owner:** Mads.

---

### F3 · Cron wiring for weekly roles + status prompts

`lib/silverRoles.js` exports two functions that need scheduling in the
existing scheduler in `index.js`:

- `runWeeklyRolePush(scriptDir)` — Mondays 09:00 CET
- `runStatusUpdatePrompts(scriptDir)` — daily 10:00 CET

The existing `silverCourse.tickSilverUser()` cron in `index.js` runs at
08/14/18 slots — these new functions can hook into the same pattern.

**Status:** functions ship; scheduler hookup pending.
**Effort:** ~30 min.

---

### F4 · Dashboard UI in `website-mirror/ecobe/admin/server.js`

The four coach admin endpoints exist on the WhatsApp side
(`/admin/silver/pending-unlock`, `…/users/:slug`,
`…/users/:slug/trigger-dossier`, `…/users/:slug/unlock-jobbot`). What's
missing is the **UI in the admin dashboard** that calls them — the
buttons, list view, detail pane that Vitor builds.

**Per D1**, all 9 endpoints (these 4 + the 7 base contract endpoints)
live in the admin server. So Vitor implements:

1. Backend: the 7 base Job-bot endpoints (per `docs/job-bot-contract.md`).
2. Backend: proxy / forward calls to the WhatsApp orchestrator's 4
   coach-admin endpoints (or merge them into the same router).
3. Frontend: pending-unlock list view, user detail pane, Trigger Bot +
   Unlock Job-bot buttons.

**Owner:** Vitor.
**Blocks:** can't launch coach gate without UI; Mads can manually fire
the WhatsApp endpoints via curl in the meantime to validate.

---

### F5 · Public webhook URL for Hotmart (Cloudflare Tunnel etc.)

Hotmart's dashboard config needs a public HTTPS URL pointing at
`POST /webhooks/hotmart`. Options: Cloudflare Tunnel from the dev box
(named tunnel, free, stable), Render/Railway deploy, ngrok for testing.

**Owner:** Mads (decide + set up by Sunday).
**Blocks:** Monday's first real Hotmart webhook test.

---

### F6 · Grace's PT translations

`data/silver/messages.pt.json` has 26 keys all set to `_todo_grace`.
Each falls back to EN automatically until Grace fills them in. Grace
owns the final tone for PT-BR; the EN drafts are the spec to translate
from.

**Owner:** Grace.
**Blocks:** PT-locale users get EN until done (graceful degradation).

---

## 📚 Reference

- Architecture overview: project description in
  https://linear.app/ecobe/project/silver-delivery-10-day-build-3fc31086c30b
- Contract spec: `docs/job-bot-contract.md` (9 endpoints, request/response shapes)
- Schema: `lib/silverStore.js` `defaultSilverState()` — every field documented
- M1+M2 plan: `~/.claude/plans/check-the-liner-project-nested-dawn.md`
