Cover business interests via a proper continuity instrument

Same gap as land parcels: business assets were routed into the generic
trust fields (trustee/successor/beneficiaries), which don't fit — a
business needs a continuity instrument, not a trustee holding title.

New "business-continuity" channel in Nomination Registry, modelled on the
digital-waqif docs repo's own business-succession framework (Clause 9):
- Structure selector (sole proprietorship / partnership / company). Sole
  proprietorship surfaces an explicit warning: it legally dies with the
  owner, so the only fast-path options are converting to a company or
  dedicating as a waqf-owned enterprise.
- Instrument selector: partnership continuation clause, shareholder
  buy-sell agreement (the standard fast-path for company shares, often
  Takaful-funded), or a direct redirect to Family Waqf Designator for a
  waqf-owned enterprise (nothing duplicated here — reuses that module).
- Dedicated business-continuity-draft.txt export, same treatment as the
  trust deed and waqfiyya exports.
- Death Trigger packet text updated with the business-specific execution
  action (execute buy-sell / continuation clause, file Takaful claim if
  funded — a private agreement between owners, not a probate filing).

e2e-business.cjs: new suite covering exposed->warning->instrument
selection->export->100% coverage->execution packet, plus the
waqf-enterprise redirect disabling the Add button. 10/10 passing.
Re-verified e2e-uat.cjs (32/32), e2e-fastpath.cjs (16/16), and
e2e-trust.cjs (12/12) — 70/70 total, no regressions.
This commit is contained in:
wmj
2026-08-13 17:32:32 +08:00
parent ff47dc9ed2
commit 051c9d9a94
6 changed files with 235 additions and 5 deletions
+80 -4
View File
@@ -19,10 +19,22 @@
{ value: 'epf', label: 'EPF nomination', note: 'Pays the nominee directly under EPF Act 1991 s.51 — bypasses probate by law.' },
{ value: 'takaful', label: 'Takaful / insurance nomination', note: 'Nomination as trust under Insurance Act 1996 s.166 (or Takaful equivalent) — pays the named beneficiary directly.' },
{ value: 'bank-mandate', label: 'Bank death mandate / joint account', note: 'Ask the bank for a death-benefit nomination, or hold as joint account with survivorship.' },
{ value: 'trust', label: 'Pre-funded trust / nominee holding', note: 'For land or illiquid assets with no nomination bypass — requires setting up a real trust or nominee entity now, while alive.' }
{ value: 'trust', label: 'Pre-funded trust / nominee holding', note: 'For land or illiquid assets with no nomination bypass — requires setting up a real trust or nominee entity now, while alive.' },
{ value: 'business-continuity', label: 'Business continuity instrument', note: 'A business dies with its structure by default: a sole proprietorship dies with the owner, a partnership dissolves on any partner\'s death unless the agreement says otherwise, and company shares fall into probate like any other asset unless a buy-sell or shareholder agreement already routes them.' }
];
const emptyForm = () => ({ linkedAssetId: '', type: 'epf', institution: '', nomineeeName: '', referenceNumber: '', trusteeName: '', successorTrustee: '', trustBeneficiaries: '' });
const BUSINESS_STRUCTURES = [
{ value: 'sole-prop', label: 'Sole proprietorship', warning: 'A sole proprietorship legally dies with you — there is no entity left to transfer. The only fast-path options are converting it to a company with a shareholder agreement, or dedicating it as a waqf-owned enterprise now, while alive.' },
{ value: 'partnership', label: 'Partnership', warning: null },
{ value: 'company', label: 'Company (shares)', warning: null }
];
const BUSINESS_INSTRUMENTS = [
{ value: 'partnership-continuation', label: 'Partnership continuation clause', note: 'Prevents automatic dissolution on a partner\'s death — but does not by itself transfer the deceased partner\'s share; pair it with a buy-sell agreement for that.' },
{ value: 'shareholder-buy-sell', label: 'Shareholder agreement with buy-sell provision', note: 'Pre-agreed, often Takaful-funded, automatic buyout of the deceased\'s shares by co-shareholders at a pre-set valuation — the standard fast-path for company shares.' },
{ value: 'waqf-enterprise', label: 'Dedicate as waqf-owned enterprise', note: 'Use the Family Waqf Designator for this instead — a waqf-owned enterprise survives you and serves your purpose in perpetuity, same as any other waqf corpus.' }
];
const emptyForm = () => ({ linkedAssetId: '', type: 'epf', institution: '', nomineeeName: '', referenceNumber: '', trusteeName: '', successorTrustee: '', trustBeneficiaries: '', businessStructure: 'company', businessInstrument: 'shareholder-buy-sell', successorOwner: '', buySellTerms: '' });
let nominations = $state(load('nominations', []));
let form = $state(emptyForm());
@@ -31,6 +43,9 @@
if (!form.linkedAssetId) return;
if (form.type === 'trust') {
if (!form.trusteeName) return;
} else if (form.type === 'business-continuity') {
if (form.businessInstrument === 'waqf-enterprise') return; // redirect only, nothing to save here
if (!form.successorOwner) return;
} else if (!form.nomineeeName) {
return;
}
@@ -39,6 +54,37 @@
form = emptyForm();
}
function exportBusinessContinuity(n) {
const asset = assets.find(a => a.id === n.linkedAssetId);
const structureLabel = BUSINESS_STRUCTURES.find(s => s.value === n.businessStructure)?.label;
const instrumentLabel = BUSINESS_INSTRUMENTS.find(i => i.value === n.businessInstrument)?.label;
const lines = [
'BUSINESS CONTINUITY INSTRUMENT — DRAFTING AID',
`Generated: ${new Date().toISOString().slice(0, 10)}`,
`Business: ${asset ? asset.description : '(asset removed)'}`,
`Structure: ${structureLabel}`,
`Instrument: ${instrumentLabel}`,
`Successor / buyer: ${n.successorOwner}`,
`Terms: ${n.buySellTerms || '________________________'}`,
'',
n.businessInstrument === 'shareholder-buy-sell'
? 'ACTION: this shares are transferred per a pre-agreed buy-sell provision in the\nshareholder agreement, often funded by a Takaful/insurance payout timed to the\nbuyout value. Once the agreement is executed and (ideally) funded, the transfer\nat death is a private buyout between shareholders — not a probate filing.'
: 'ACTION: a partnership continuation clause prevents automatic dissolution on\ndeath, but the deceased partner\'s share itself still needs a buy-sell provision\nor a will-directed transfer to actually move — pair this with a buy-sell\nagreement for full coverage.',
'',
'ACTION REQUIRED: this document is a drafting aid only. A lawyer must draft the',
'actual shareholder/partnership agreement, and it must be signed and (for a',
'buy-sell) ideally funded by a Takaful/insurance policy while all parties are',
'alive for this to have legal effect and actually bypass probate.',
'',
'DISCLAIMER: Not a fatwa. Not legal advice. Drafting aid only.'
];
const blob = new Blob([lines.join('\n')], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url; a.download = 'business-continuity-draft.txt'; a.click();
URL.revokeObjectURL(url);
}
function exportTrustDeed(n) {
const asset = assets.find(a => a.id === n.linkedAssetId);
const lines = [
@@ -107,12 +153,37 @@
<label class="field"><span>Successor trustee</span><input type="text" bind:value={form.successorTrustee} /></label>
<label class="field"><span>Beneficiaries</span><input type="text" bind:value={form.trustBeneficiaries} placeholder="e.g. equal split among children" /></label>
</div>
{:else if form.type === 'business-continuity'}
<div class="trust-fields">
<label class="field"><span>Business structure</span>
<select bind:value={form.businessStructure}>
{#each BUSINESS_STRUCTURES as s}<option value={s.value}>{s.label}</option>{/each}
</select>
</label>
{#if BUSINESS_STRUCTURES.find(s => s.value === form.businessStructure)?.warning}
<p class="business-warning">{BUSINESS_STRUCTURES.find(s => s.value === form.businessStructure).warning}</p>
{/if}
<label class="field"><span>Continuity instrument</span>
<select bind:value={form.businessInstrument}>
{#each BUSINESS_INSTRUMENTS as i}<option value={i.value}>{i.label}</option>{/each}
</select>
</label>
<p class="trust-note">{BUSINESS_INSTRUMENTS.find(i => i.value === form.businessInstrument)?.note}</p>
{#if form.businessInstrument === 'waqf-enterprise'}
<p class="business-warning">Go to the Family Waqf Designator tab and select this business as the corpus asset — nothing to save here.</p>
{:else}
<label class="field"><span>{form.businessInstrument === 'shareholder-buy-sell' ? 'Buying co-shareholder(s)' : 'Successor partner'}</span><input type="text" bind:value={form.successorOwner} /></label>
<label class="field"><span>Terms</span><input type="text" bind:value={form.buySellTerms} placeholder="e.g. valuation method, Takaful-funded buyout" /></label>
{/if}
</div>
{:else}
<label class="field"><span>Institution</span><input type="text" bind:value={form.institution} placeholder="e.g. KWSP, Bank Islam, Takaful Ikhlas" /></label>
<label class="field"><span>Nominee name</span><input type="text" bind:value={form.nomineeeName} /></label>
<label class="field"><span>Reference / policy / account number</span><input type="text" bind:value={form.referenceNumber} /></label>
{/if}
<button class="btn-primary" onclick={addNomination}>Add {form.type === 'trust' ? 'trust setup' : 'nomination'}</button>
<button class="btn-primary" disabled={form.type === 'business-continuity' && form.businessInstrument === 'waqf-enterprise'} onclick={addNomination}>Add {form.type === 'trust' ? 'trust setup' : form.type === 'business-continuity' ? 'business continuity instrument' : 'nomination'}</button>
</div>
{#each nominations as n (n.id)}
@@ -121,16 +192,20 @@
<div class="nomination-row">
<div class="nomination-info">
<strong>{asset ? asset.description : '(asset removed)'}</strong>
<span class="muted">{channelInfo?.label}{n.type !== 'trust' ? ` · ${n.institution || '—'}` : ''}</span>
<span class="muted">{channelInfo?.label}{n.type !== 'trust' && n.type !== 'business-continuity' ? ` · ${n.institution || '—'}` : ''}</span>
{#if n.type === 'trust'}
<span class="muted">Trustee: {n.trusteeName} · Successor: {n.successorTrustee || '—'}</span>
<span class="muted">Beneficiaries: {n.trustBeneficiaries || '—'}</span>
{:else if n.type === 'business-continuity'}
<span class="muted">{BUSINESS_INSTRUMENTS.find(i => i.value === n.businessInstrument)?.label}</span>
<span class="muted">Successor: {n.successorOwner} · Terms: {n.buySellTerms || '—'}</span>
{:else}
<span class="muted">Nominee: {n.nomineeeName} · Ref: {n.referenceNumber || '—'}</span>
{/if}
</div>
<div class="row-actions">
{#if n.type === 'trust'}<button class="export-btn" onclick={() => exportTrustDeed(n)}>Export</button>{/if}
{#if n.type === 'business-continuity'}<button class="export-btn" onclick={() => exportBusinessContinuity(n)}>Export</button>{/if}
<button onclick={() => remove(n.id)}>✕</button>
</div>
</div>
@@ -161,4 +236,5 @@
.trust-note { font-size: 11.5px; color: #C9A84C; line-height: 1.5; margin-bottom: 10px; }
.row-actions { display: flex; gap: 8px; align-items: center; }
.export-btn { background: rgba(46,204,113,0.15); color: #2ECC71; border: none; border-radius: 8px; padding: 6px 10px; font-size: 11px; cursor: pointer; font-weight: 600; }
.business-warning { font-size: 11.5px; color: #EF4444; background: rgba(239,68,68,0.08); border-radius: 8px; padding: 8px 10px; margin-bottom: 12px; line-height: 1.5; }
</style>