Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions FOUNDER-TLDR.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,18 @@ coming next. Newest updates are at the top.

---

## 2026-06-14 — Swiss plans now say *when* a fee or timeline is set by your canton

**What changed.** Many Swiss steps have no single nationwide fee or processing time — the **canton** sets them (and for getting into a school, the **institution** does). Until now those steps simply showed *nothing*, which can read as "free/instant" or leave people guessing. Now each such step carries a plain note: *"Fees & processing time: set by your canton, not federally — confirm the exact amount and timing with your cantonal authorities"* (study admission says "the institution"). The "Your canton" box also now reminds you these vary by canton. Importantly, **we invented no numbers** — we only made the plan honest about where the real figures come from and who to ask. Covers 12 steps (the employer-sponsored work permit, the family-reunification routes, the retirement permit, and the study permits/sub-steps).

**Why it matters.** For legal/financial decisions, silence misleads. This sets honest expectations and points people to the right desk for the real figure — without us publishing a number we can't verify.

**What you'd notice today.** On a Swiss plan, steps like family reunification, the retirement permit and study permits now show a "fees & processing time are cantonal — confirm locally" line instead of nothing.

**What's next.** If/when we source the actual per-canton fee and timeline figures (currently paused), these notes can become specific numbers.

---

## 2026-06-14 — Swiss plan steps now link to *your* canton's office, not a list of all 26

**What changed.** You pointed out that for an unmarried partner moving to Switzerland the rules really do hinge on the canton — and they do. Until now, the steps that say "contact the migration office of the canton where your partner lives" sent everyone to one big federal list of all 26 cantonal offices, even after they'd already told us their canton. Now, if you've picked a canton we've fact-checked (Zürich, Geneva, Vaud, Zug, Basel-Stadt), those steps link **straight to that canton's own migration office** (e.g. Zürich's Migrationsamt, Geneva's OCPM). Pick any other canton and it still falls back to the federal list — so no one ever hits a dead end. This covers every "apply at your cantonal office" step: all the family-reunification routes, the unmarried-partner case, retirement, and study. It's now passed our automatic safety checks and an independent accuracy review, I've confirmed it working in a live preview (Zürich and Geneva point to their own offices; an un-sourced canton like Bern still gets the safe federal list), and it's bundled into the batch of changes waiting for your approval — **not live yet; it goes live the moment you approve that batch.**
Expand Down
4 changes: 2 additions & 2 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
>
> Legend: ✅ done · 🚧 in progress · ⏳ planned · 🔁 ongoing · 🗓️ backlog

Last updated: 2026-06-14 (Canton-aware plan-step links: the six "apply at / contact your cantonal authority of the canton where you live/study" steps (family reunification ×3, unmarried-partner concubinage, retirement, study) now deep-link the user's *selected* canton's own migration office (ZH Migrationsamt, GE OCPM, VD SPOP, ZG, BS) instead of the federal SEM directory of all 26 — falling back to that directory for not-yet-sourced cantons, so no dead ends. A `StepLinkSchema.cantonOffice` rendering hint (asserts no fact, no provenance) drives a frontend-only swap to the already-VERIFIED canton migrationOffice claim; static SSR full-guide stays federal (can't know the canton). Build/gate/`astro check`/277 tests green; fact-verifier (accurate, no overreach) + provenance-integrity verified; ADR-0021. Outbound links: the written-guide's 109 external source links now open in a new tab (target=_blank + rel=noopener noreferrer), matching the planner — readers don't lose their place. Plan UI: the desktop plan sidebar (progress / answers / journey) is now collapsible (Hide/Show), giving the task panel full width; mobile already had this. Canton dimension for Switzerland (ADR-0021): corridor-embedded `cantons` schema + provenance gate; CH-only "which canton?" intake step (searchable combobox over 26 cantons, optional) + `cn` share-param + "Your canton" plan panel with a verified SEM-directory fallback; per-canton sourced detail for ZH/GE/VD/ZG/BS in the two-agent pipeline. Earlier: Link audit: all 950 source links render-checked — 147 good, 23 Fedlex "under preparation" links flagged + the us-ch fix applied (79 occurrences → static-HTML, independently fact-verifier re-verified; re-audit now 0 suspect / 170 good). The weekly link-auditor now also checks anchor-reach — a `#article` link that loads but whose section never renders (SPA shell) goes RED on SPA domains (fedlex-class regressions); france-visas added to manual-check (403s the bot, fine for users). Live run: 0 errors / 142 URLs. Plan-personalisation correctness: "Who's joining you?" companion multi-select (partner/children/"It's just me") replaces the children yes/no; family/dependant steps gated to who is actually coming; full-guide route labels; cross-corridor plan-correctness regression suite — ADR-0020. UI/UX "Trust & Authority" design system adopted — audit fixes 1–10: AA-contrast CTAs, 44px touch targets, readable legal text, EB Garamond + Lato self-hosted fonts, brand tokens, consent-overlap fix, home/wizard trust framing, plus polish 11–13 (SVG icons, consistent focus rings, tighter line-length); ADR-0019. Earlier: 8 corridors published + intake availability dots, ADR-0018; source-snapshot cache, ADR-0017; corridor production via agent teams in two runs, ADR-0016; all-routes intake + launch waitlist, ADR-0015; launch-readiness drafts, og:image, dormant consent+analytics, ADR-0013; us-ch founder approval, ADR-0014; go-live blocked on domain + lawyer review)
Last updated: 2026-06-14 (Cantonal fee/timeline transparency: 12 us-ch steps that have no single federal fee/processing-time figure (the employer-sponsored work permit, the family-reunification routes, the retirement permit, the study permits/sub-steps) now show a user-facing "Fees & processing time" note that these are set by the canton — or, for study admission, the institution — and to confirm locally; driven by a presentation-only `localCostTimeline` task hint (no invented numbers, no new sourced claims, ADR-0007/ADR-0021), plus a "varies by canton" caveat now always shown in the "Your canton" panel. Canton-aware plan-step links: the six "apply at / contact your cantonal authority of the canton where you live/study" steps (family reunification ×3, unmarried-partner concubinage, retirement, study) now deep-link the user's *selected* canton's own migration office (ZH Migrationsamt, GE OCPM, VD SPOP, ZG, BS) instead of the federal SEM directory of all 26 — falling back to that directory for not-yet-sourced cantons, so no dead ends. A `StepLinkSchema.cantonOffice` rendering hint (asserts no fact, no provenance) drives a frontend-only swap to the already-VERIFIED canton migrationOffice claim; static SSR full-guide stays federal (can't know the canton). Build/gate/`astro check`/277 tests green; fact-verifier (accurate, no overreach) + provenance-integrity verified; ADR-0021. Outbound links: the written-guide's 109 external source links now open in a new tab (target=_blank + rel=noopener noreferrer), matching the planner — readers don't lose their place. Plan UI: the desktop plan sidebar (progress / answers / journey) is now collapsible (Hide/Show), giving the task panel full width; mobile already had this. Canton dimension for Switzerland (ADR-0021): corridor-embedded `cantons` schema + provenance gate; CH-only "which canton?" intake step (searchable combobox over 26 cantons, optional) + `cn` share-param + "Your canton" plan panel with a verified SEM-directory fallback; per-canton sourced detail for ZH/GE/VD/ZG/BS in the two-agent pipeline. Earlier: Link audit: all 950 source links render-checked — 147 good, 23 Fedlex "under preparation" links flagged + the us-ch fix applied (79 occurrences → static-HTML, independently fact-verifier re-verified; re-audit now 0 suspect / 170 good). The weekly link-auditor now also checks anchor-reach — a `#article` link that loads but whose section never renders (SPA shell) goes RED on SPA domains (fedlex-class regressions); france-visas added to manual-check (403s the bot, fine for users). Live run: 0 errors / 142 URLs. Plan-personalisation correctness: "Who's joining you?" companion multi-select (partner/children/"It's just me") replaces the children yes/no; family/dependant steps gated to who is actually coming; full-guide route labels; cross-corridor plan-correctness regression suite — ADR-0020. UI/UX "Trust & Authority" design system adopted — audit fixes 1–10: AA-contrast CTAs, 44px touch targets, readable legal text, EB Garamond + Lato self-hosted fonts, brand tokens, consent-overlap fix, home/wizard trust framing, plus polish 11–13 (SVG icons, consistent focus rings, tighter line-length); ADR-0019. Earlier: 8 corridors published + intake availability dots, ADR-0018; source-snapshot cache, ADR-0017; corridor production via agent teams in two runs, ADR-0016; all-routes intake + launch waitlist, ADR-0015; launch-readiness drafts, og:image, dormant consent+analytics, ADR-0013; us-ch founder approval, ADR-0014; go-live blocked on domain + lawyer review)

> **Current focus:** USA → Switzerland (`us-ch`) **only**, until it is perfected end-to-end.
> All other corridors are deferred (see DECISIONS.md **ADR-0006**). India → Germany (`in-de`)
Expand Down Expand Up @@ -72,7 +72,7 @@ _Founder direction: drop the visual flowchart from the UI; make the whole produc
- self-employment / starting a business
- lump-sum taxation ("forfait fiscal") / wealth-based residence
- study-adjacent stays (language stay, sabbatical, internship/trainee agreements with the US)
- [~] **Canton-aware path shaping (Switzerland)**: replace the free-text "canton or city" intake field with a canton picker, and let the chosen canton shape next steps — link the exact cantonal migration office, and (longer-term) reinstate the 5 cantonal-variable fee/timeline fields omitted under ADR-0007 as per-canton verified claims. _Content-heavy: every per-canton fact needs the full two-agent pipeline (26 cantons), so machinery first, cantons added incrementally starting with Zürich/Geneva/Vaud/Basel-Stadt/Zug._ **Done (ADR-0021):** canton picker + intake `cn` param + "Your canton" panel; and the exact cantonal migration office is now deep-linked both in that panel **and** in the plan's "contact your cantonal authority" steps (`StepLinkSchema.cantonOffice`, 2026-06-14). Sourced cantons: ZH/GE/VD/ZG/BS (others fall back to the federal SEM directory). **Still pending:** reinstate the per-canton fee/timeline claims, and expand canton coverage beyond the first five.
- [~] **Canton-aware path shaping (Switzerland)**: replace the free-text "canton or city" intake field with a canton picker, and let the chosen canton shape next steps — link the exact cantonal migration office, and (longer-term) reinstate the 5 cantonal-variable fee/timeline fields omitted under ADR-0007 as per-canton verified claims. _Content-heavy: every per-canton fact needs the full two-agent pipeline (26 cantons), so machinery first, cantons added incrementally starting with Zürich/Geneva/Vaud/Basel-Stadt/Zug._ **Done (ADR-0021):** canton picker + intake `cn` param + "Your canton" panel; and the exact cantonal migration office is now deep-linked both in that panel **and** in the plan's "contact your cantonal authority" steps (`StepLinkSchema.cantonOffice`, 2026-06-14). Sourced cantons: ZH/GE/VD/ZG/BS (others fall back to the federal SEM directory). Where no federal fee/processing-time figure exists, the plan now **notes** that these are set by the canton (or, for study admission, the institution) and to confirm locally (`localCostTimeline` hint, 2026-06-14) — transparency without invented numbers. **Still pending:** source the actual per-canton fee/timeline figures, and expand canton coverage beyond the first five.
- [ ] **EU/EFTA passport branching**: switch the path on a free-movement passport (deferred to capture-only for now — ADR-0008)
- [x] **F-08** URL state encoding (2026-06-12): plan-affecting answers encoded in the URL **fragment** (`/us/ch/#pp=us&m=work&ws=has-offer&dur=long&kids=0`) for shareable/bookmarkable profiles. Fragment, not search params, per the compliance share-link ruling — the fragment is never sent in HTTP requests, so answers can't land in server/CDN logs or analytics; free-text answers are never encoded. Restore precedence: URL > localStorage > defaults; a URL with intake params fully determines the plan (codec: `src/utils/urlState.ts`, 15 tests)
- [x] **F-08 follow-up** (2026-06-12): "Copy link to this plan" button in the plan sidebar with the compliance-required pre-share warning ("This link contains your answers…"); clipboard-API failure falls back to a URL prompt
Expand Down
19 changes: 18 additions & 1 deletion src/components/CorridorApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,10 @@ export interface TaskData {
documents: (string | TaskDocumentData)[];
timeline?: ClaimData;
cost?: ClaimData;
/** Presentation hint (ADR-0021/ADR-0007): this step's fee + processing time are set
* locally — by the canton or, for study admission, the institution — with no federal
* figure. Renders a "confirm locally" note only when no cost/timeline claim exists. */
localCostTimeline?: 'canton' | 'institution';
warning?: string;
dependsOn: string[];
appliesIf?: string;
Expand Down Expand Up @@ -1053,6 +1057,9 @@ function CantonPanel({ canton, data }: { canton: string; data: CantonData | unde
<p className="mt-0.5 text-sm text-slate-700">{note.text}</p>
</div>
))}
<p className="text-xs text-slate-500">
Exact fees, processing times and some procedures are set by {name} and vary across Switzerland — confirm them with the office above.
</p>
</div>
) : (
<div className="mt-2 space-y-1.5">
Expand Down Expand Up @@ -1241,7 +1248,7 @@ function TaskCard({
{/* Key facts: scannable grid. keyFacts come from the content pipeline;
the verified timeline/cost claims are appended automatically (the
researcher does not duplicate them in keyFacts). */}
{(task.keyFacts?.length || task.timeline || task.cost) && (
{(task.keyFacts?.length || task.timeline || task.cost || task.localCostTimeline) && (
<div>
<h3 className="font-semibold text-slate-900 mb-2">Key facts</h3>
<dl className="grid gap-2 sm:grid-cols-2">
Expand All @@ -1263,6 +1270,16 @@ function TaskCard({
<dd className="text-sm text-slate-800">{task.cost.text}</dd>
</div>
)}
{task.localCostTimeline && !task.cost && !task.timeline && (
<div className="rounded-lg bg-slate-50 border border-slate-200 px-4 py-2.5 sm:col-span-2">
<dt className="text-xs font-semibold uppercase tracking-wide text-slate-500 mb-0.5">Fees &amp; processing time</dt>
<dd className="text-sm text-slate-800">
{task.localCostTimeline === 'institution'
? 'Set by the educational institution, not at the federal level. Confirm the exact application fee and timing directly with the institution.'
: 'Set by your canton, not at the federal level — there is no single national figure, and it varies across Switzerland. Confirm the exact amount and timing with your cantonal authorities (see “Your canton”).'}
</dd>
</div>
)}
</dl>
</div>
)}
Expand Down
Loading
Loading