Rebuild around the real mechanism: bypass Faraid/probate entirely

Product direction clarified: Faraid/probate court adjudication is what takes
~2 years. Assets already moved out of the estate before death (via completed
Hibah, dedicated Waqf, or a legally-direct nomination channel) are never in
that queue — there's nothing for a court to adjudicate. The app's job is to
maximize what's covered by those instruments and make the death-triggered
payout fast for whatever is covered.

New:
- nonprobate.js: coverage model — classifies each Asset Registry entry by
  fast-path channel (hibah/waqf/nomination) vs exposed (faraid/probate).
- CoverageDashboard.svelte: the single number that matters — % of estate
  that bypasses Faraid entirely, with per-asset next-step suggestions.
- NominationRegistry.svelte: third fast-path channel for assets that can't
  be fully gifted/waqf'd — EPF (EPF Act 1991 s.51), Takaful/insurance
  (Insurance Act 1996 s.166), bank death-mandate, trust/nominee holding.
- DeathTrigger.svelte: the execution layer. Multi-attestor threshold +
  death certificate reference fires the trigger; generates a ready-to-file
  execution packet per covered asset, pre-filled from Hibah/Waqf/Nomination
  records. Explicitly scoped: this app cannot itself move money or transfer
  title, but removes missing-paperwork/ambiguity as a source of delay so
  institutions can act in days instead of stacking behind a probate queue.

Changed:
- HibahTracker/FamilyWaqfDesignator: now link to a specific Asset Registry
  entry so coverage can be computed; fast-path explanation added to both.
- WassiyahGenerator: explicit warning that a wassiyah does NOT bypass
  probate — it's a post-death instrument, only appropriate for the
  discretionary one-third, not a fast-track vehicle. Users were previously
  not told this distinction.
- FaraidCalculator: reframed as informational-only, showing what applies to
  whatever remains uncovered — not itself a mechanism.

e2e-fastpath.cjs: new Playwright suite covering the full mechanism
end-to-end (coverage 0%->100%, nomination, multi-attestor trigger threshold,
execution packet generation) — 16/16 passing. Original e2e-uat.cjs suite
re-verified at 32/32 with no regressions.
This commit is contained in:
wmj
2026-08-13 17:21:44 +08:00
parent 3badc5caaa
commit ad2b3c48a3
10 changed files with 625 additions and 13 deletions
+15 -9
View File
@@ -4,6 +4,9 @@
import WassiyahGenerator from './lib/WassiyahGenerator.svelte';
import HibahTracker from './lib/HibahTracker.svelte';
import FamilyWaqfDesignator from './lib/FamilyWaqfDesignator.svelte';
import NominationRegistry from './lib/NominationRegistry.svelte';
import CoverageDashboard from './lib/CoverageDashboard.svelte';
import DeathTrigger from './lib/DeathTrigger.svelte';
import DigitalClaims from './lib/horizon2/DigitalClaims.svelte';
import { exportAll, deleteAll } from './lib/storage.js';
import { lang, setLang } from './lib/i18n.js';
@@ -11,8 +14,8 @@
let currentLang = $state('en');
lang.subscribe(v => currentLang = v);
const tabs = ['Faraid', 'Assets', 'Wassiyah', 'Hibah', 'Family Waqf', 'Claims (H2)', 'Settings'];
const icons = ['📊', '📁', '📜', '🎁', '⛲', '🔗', '⚙️'];
const tabs = ['Coverage', 'Faraid', 'Assets', 'Wassiyah', 'Hibah', 'Family Waqf', 'Nominate', 'Trigger', 'Claims (H2)', 'Settings'];
const icons = ['🎯', '📊', '📁', '📜', '🎁', '⛲', '📇', '⚡', '🔗', '⚙️'];
let activeTab = $state(0);
function handleKeydown(e) {
@@ -59,13 +62,16 @@
</nav>
<main>
{#if activeTab === 0}<FaraidCalculator />
{:else if activeTab === 1}<AssetRegistry />
{:else if activeTab === 2}<WassiyahGenerator />
{:else if activeTab === 3}<HibahTracker />
{:else if activeTab === 4}<FamilyWaqfDesignator />
{:else if activeTab === 5}<DigitalClaims />
{:else if activeTab === 6}
{#if activeTab === 0}<CoverageDashboard />
{:else if activeTab === 1}<FaraidCalculator />
{:else if activeTab === 2}<AssetRegistry />
{:else if activeTab === 3}<WassiyahGenerator />
{:else if activeTab === 4}<HibahTracker />
{:else if activeTab === 5}<FamilyWaqfDesignator />
{:else if activeTab === 6}<NominationRegistry />
{:else if activeTab === 7}<DeathTrigger />
{:else if activeTab === 8}<DigitalClaims />
{:else if activeTab === 9}
<div class="module">
<h2>Settings</h2>
<p class="sub">Your data is stored locally on this device. Nothing is sent to a third party.</p>