A schema-driven KYC / beneficial-ownership intake prototype. It replaces a static printed form with a data capture whose output is a document: a declarative rules engine, a form generated from the schema, a live requirements panel that explains every requirement by rule id, an evidence-bearing Salesforce-ready payload, and a print layout that mirrors the approved paper form.
The whole thing is one offline HTML file — vanilla HTML/CSS/JS, no framework, no npm dependencies, no CDN, no network calls at runtime. It opens by double-click and can be emailed.
Download dist/intake.html, open it in a browser, and use Demo → Load scenario to load a synthetic case (start with S1-standard, then switch Risk tier to enhanced).
src/schema.json,src/config.json,src/rules.json,src/sf_map.json— the data layer: fields, thresholds, rules and Salesforce targets. Schema, rules and config are version0.3.0;rules.jsoncontains{version, rules}. Editing these never requires an engine change.src/engine.js— pure, DOM-free functions:evaluateRules(state, config, rules),validate(state, schema),buildPayload(state, schema, sfMap),computeEffectiveOwnership(parties, holdings),deriveLane(state, evaluation, config). Deterministic: same state → same output, no clock access.src/view.js— pure view model (deriveView, Review/print projection);src/actions.js— pure state transitions (record certification/refusal, mock stamp, case serialize/parse). The browser supplies timestamps to those actions.src/ui.js,src/styles.css,src/print.css— rendering only. No rule logic in the UI.build.py— inlinessrc/*plus the scenarios and test files intodist/intake.html, with SHA-256 rulebook metadata. Python standard library only.tests/scenarios.json— synthetic scenarios S1–S6;tests/run.jsruns them in Node against the engine, and the same file runs in the browser from the footer Scenario self-test link.tests/check_view.js,tests/check_bundle.js,tests/check_handover.js— view/Review, built-bundle and handover-pack checks.tests/browser_smoke.jsis an optional Playwright check that also regenerates the sample PDFs.docs/— milestone plans and reports, the generated handover pack, sample print output, and the session prompts that produced the code.
- Every rule carries
{ id, basis, why, params, cite, cite_status }.basisis[REG](a regulatory requirement),[POLICY](a bank-policy assumption to be confirmed), or[REG]/[POLICY].citeis a supplied reference or null; its status isconfirmedorverify. R7 retains a second reference inadditional_cites. The requirements panel shows the rule id andwhy, with citations/statuses on the tag tooltip. R6 FAQ numbering, R7's additional FAQ reference and R8's paragraph citation remain markedverify. - Every schema field carries a basis (
[REG],[POLICY],[FOLKLORE]orTBD) and string/null cite. The legal-floor handover groups the whole inventory.[FOLKLORE]is the future deletion-candidate list after the paper form is inventoried; no field is assigned that classification by inference. - Anything the bank has not supplied is a
TBD_-prefixed placeholder: paper-form fields, certification wording, enhanced-tier fields, and every Salesforce object/field API name. Placeholders stay placeholders — the prototype never invents a field name, a policy or a regulatory requirement. - The public repository holds generic conventions and synthetic scenarios only. Real field lists, risk-tier definitions, certification wording, Salesforce API names and bank policy decisions belong in a private overlay of the JSON files.
- Risk tiers are
standardandenhanced. The higher-risk tier lowers the beneficial-ownership threshold (25% → 10% here) and reveals an extra information section; both come fromconfig.json, not from code. - R7 reuse is recorded in
attestation.bo_certification: confirmation, confirming name/role, verbal/written channel, browser recording time and confirmed BO-file version. Record certification captures the time and file version; Record refusal recordsconfirmed: falseandrefused_at. The old no-change boolean is removed. The record is separate from the mock stamp and implements no electronic signature. - R7 separates
[POLICY]bo_rebuild_triggersfrombo_certify_events. Rebuilds require full ownership/control collection; certify events permit reuse with a recorded confirmation, with explicit refusal returning to full collection. With a prior file and no event,always_certify_on_reusecontrols whether the record is required. Reuse retains signer/trustee CIP, trust documents and foreign-party obligations. Payload evidence records which class fired. - Lane L/H is a
[POLICY]operating-model signal. Entity owners, trust involvement, foreign parties, enhanced risk or a rebuild trigger select H; the ownership-coverage warning also selects H when its policy option is enabled. Reasons appear on the top-bar tag, in Review and in payload evidence. Lane selection changes no collection requirement or readiness decision. - The build pins the canonical
{rules, config, schema}JSON: recursively sorted object keys, retained array order, compact UTF-8 encoding, then SHA-256.intake-metacarries the hash and three versions;payload.evidence.rulebook_pincopies them and Review shows the short hash. Reconstructing the rulebook requires the exact associated source JSON. - Save/Load exchanges
{schema_version, saved_at, state}with exactly six live keys: customer, parties, holdings, documents, attestation and as_of_date. Load accepts only0.3.0, rejects0.2.0without migration, refreshes the as-of date and preserves a saved mock stamp. Case files are the only persistence. - Data is synthetic everywhere:
Synthetic …names,123-45-0000/00-0000000tax IDs,FAKE-ID-…identity documents,0 Example …addresses.
docs/handover/ contains eight generated Markdown documents (00–07): field inventory with basis/cites, rule explanations and citation statuses, a customer-type × risk-tier collection matrix, the full Salesforce map and relationship/evidence shapes, consolidated policy confirmations, implementation notes, and the legal-floor inventory. It is a prototype specification with TBD_ placeholders, not an approved bank document. Regenerate it with python3 handover.py; tests/check_handover.js regenerates and verifies it independently of build.py.
python3 build.py # writes dist/intake.html
node tests/run.js # engine scenarios S1–S6
node tests/check_view.js # view model and Review projection
node tests/check_bundle.js # the built single file
node tests/check_handover.js # regenerates and verifies docs/handover/The optional browser check needs a separately installed Playwright:
NODE_PATH=<path to a separately installed Playwright> INTAKE_BROWSER_CHANNEL=chrome node tests/browser_smoke.jsIt reports SKIP when Playwright is absent. Node and Python are development tools only; the sole runtime deliverable is dist/intake.html.
This is a prototype built with synthetic data. It is not legal, regulatory or compliance advice, it is not an approved form, and it is not affiliated with or endorsed by any bank or financial institution. Regulatory references (the FinCEN CDD rule, FinCEN Order FIN-2026-R001) are cited for context only — confirm every requirement with your own counsel and compliance function. Every [POLICY] tag marks an assumption that has not been confirmed by anyone.
MIT — see LICENSE.