1. Repo Structure & File Ownership
The monolith was split so parallel Claude sessions can build without overwriting each other. One feature, one file.
js/config.js, js/shared.js, index.html, css/styles.css) are edited by ONE session, in the same session as the feature needing the change. Always git pull before starting, push promptly after committing.
| Feature | Edit this file | Notes |
|---|---|---|
| Leadership Dashboard KPIs | js/dashboard.js | Loads the shared global data arrays for everyone |
| Cash flow / balance calculator | js/cashflow.js | Forecast, UC checks, what-if |
| Reconciliation engine | js/reconciliation.js | Matching, knowledge base, accuracy tracking; rate-limit pattern lives here |
| Invoices | js/invoices.js | Fetch, render, match, approve, pay |
| CFVs / rent statements | js/cfv.js | Overdue rent detection, chase actions |
| Costs / transactions / income / P&L | js/costs.js Β· js/transactions.js Β· js/income.js Β· js/pnl.js | Finance section tabs |
| Money Confidence / Wealth | js/money.js Β· js/wealth.js | Personal finance layer |
| Arrears | js/arrears.js Β· js/ar-variable.js | Rent balance per tenancy |
| Fintable monitor | js/fintable.js | Bank sync health |
| Site map & links | js/sitemap.js | Also SOP update requests |
| AI assistant panel | js/ai-assistant.js | Context blocks per tab live here |
| Skills / KPI sources / quick task | js/skills.js Β· js/skills-data.js Β· js/kpi-sources.js Β· js/quick-task.js | |
| Health bars | js/sync-bar.js | registerSyncBar engine β shared, treat like shared.js |
| Operating Systems pages | os/*/index.html | tasks, operations, strategy, systemisation, team, business-plan-builder β loaded via iframe |
| Standalone iframe pages | follow-up.html Β· compliance.html Β· ai-brain.html | Each self-contained |
| Styling | css/styles.css (consumes css/tokens.css) | Whole-app look changes: tokens.css ONLY |
| Sidebar / tab shell | index.html | Protected: OS sidebar items, OS iframe tab panels |
| Constants, table + field IDs | js/config.js | F, TABLES, INV, REC, PS, PAGE_REGISTRY, AI model IDs |
index.htmlβ shell only: sidebar, tab containers, no logic*.htmliframe pages βfollow-up,compliance,ai-brain,how-it-workssop*.htmlβ SOP per page*-supabase.html+supabase-app.htmlβ shadow-build twins (Β§5)*-shim.jsβ 5 fetch-override shims for the twins (Β§5)architecture.html/dev-blueprint.htmlβ the two mapscss/js/os/docs/scripts/tests/supabase-migration/MASTER-PLAN.mdβ THE only plan Β·STRUCTURE.mdβ file-location source of truth Β·PRODUCTISATION.mdβ de-Kevining spec
- Small fixes, single-file tweaks: push directly to
main - New features, multi-file, anything touching shared files: branch β PR (
feature/β¦orfix/β¦) - Parallel sessions can sweep uncommitted edits into other commits β commit before context-switching
- If a feature vanishes, check git history before reimplementing
- Sidebar OS menu items and their health dots
- OS iframe tab panels:
tab-tasks,tab-operations,tab-systemisation,tab-os-strategy,tab-os-team,tab-os-bplan - PAGE_REGISTRY OS entries in
js/config.js
2. JS Internals β Globals, Helpers, Conventions
All JS loads as plain <script> tags in one global scope. No bundler, no modules. Load order: config.js β shared.js β feature files.
PATβ Airtable auth token, set by the auth flow. Never log it, never hardcode it.- Data arrays loaded by
dashboard.js:allTransactions,allTenancies,allTenants,allCosts,allCategories,allSubCategories,allBusinesses - Constants:
F(field IDs),TABLES,INV,REC,PS - AI models:
AI_MODEL_DEFAULT,AI_MODEL_LIGHTβ feature files must use these, never a literal model ID
- Data:
airtableFetch(tableId, params)(handles pagination) Β·getFieldΒ·getNumValΒ·lookupValuesΒ·txDisplayAmountΒ·getActiveBusinesses - Status logic:
isTenancyActiveΒ·isTenancyIncomeΒ·isTenantStatusActive/FormerΒ·isCostActiveΒ·getPaymentStatusName - Security:
escHtml(str)for ALL external text into HTML Β·escJs(str) - UI:
expandableCardΒ·toggleCardΒ·switchTab(tabId)Β·showToastΒ·showConfirmΒ·openQuickTaskModal - Health:
updateSidebarHealth(tabId, status)Β· sync bars viaregisterSyncBarinjs/sync-bar.js - Robustness:
limitedApiFetch(rate limit) Β·nextRenderGen/isCurrentRender(render race guard) Β·smartRefresh+ idle auto-refresh
- Missing Number() on PATCH β Airtable number fields reject strings:
fields: { [F.amount]: Number(value) } - Partial re-render stale state β after inline edit/status/filter change call the full
renderAll(), not the single-item updater - returnFieldsByFieldId β this project uses field NAMES via
Fconstants; field-ID keys silently read undefined - CSS overflow truncation β long names need ellipsis or
word-break: break-word - localStorage quota β Safari caps at 5MB; large caches go to IndexedDB (dashboard.js pattern), localStorage is small UI state only, keys namespaced per feature (
cfv_,recon_β¦) - Linked-record filters β filter by record ID, never ARRAYJOIN display names
console.log/debuggerin production paths- Silent
catchblocks (must log or toast) - Secrets or PAT tokens hardcoded anywhere
- Hardcoded AI model IDs in feature files (a retired ID is an app-wide AI outage)
eval(),document.write(),Function()constructorinnerHTMLwith unescaped external data β useescHtml()- Hardcoded colours or fonts β design tokens only (Β§1 of CLAUDE.md design system)
3. Deploy Pipeline
The repo is the source of truth. Push to main β GitHub Pages auto-deploys in 2-3 minutes β confirm live before declaring done.
- Live URL:
https://chaichoong.github.io/leadership-dashboard/ git push origin mainβ Pages build β poll the deploy monitor script in the background until live- Never say "done" after pushing β the task completes when the deploy is confirmed live (watch stale browser cache; hard reload)
- Rollback = identify the revert commit BEFORE deploying anything risky
PAGE_REGISTRYinjs/config.jstrackspageVer/sopVerper page.github/workflows/auto-bump-pagever.ymlbumpspageVerautomatically when a page's source file hits main- Fileβpage mapping lives in
scripts/pre-commit-action.pyβ update it when adding pages - Local hooks:
scripts/pre-commit(version bump) andscripts/pre-push(test gate) β symlink into.git/hooks/ sopVeris bumped manually when the SOP catches up with the page
monthly-valuations.ymlβ property valuations run, 1st of the month (needsPROXY_SERVICE_TOKENsecret)sync-ai-brain.ymlβ publishes the AI Brain feedsync-transactions.ymlβ transaction syncscripts/sync-master-plan.pyβ nightly planβAirtable sync: ticks MASTER-PLAN from Airtable completions, pushes approved plan tasks to Airtable
4. Testing & Quality Gates
Nothing ships unverified. Kevin never debugs β the session that builds it proves it works.
tests/sync-invariants/β 11 spec files (33 tests): active-business filter, auth token handling, badge count sync, cascade sync, CFV detection, no-crash-on-load, and more- Run:
npm run test:sync - A local pre-push gate (not committed to the repo) blocks failing pushes to main
tests/shared.test.jsβ shared helper coverage- The
/test-gapsskill generates Vitest tests for untested paths in changed files: data writes and business logic first, trivial getters skipped
/build-feature and /fix automatically:
- Self-audit β
/verify(browser proof) β simplify pass β/test-gapsβ independent review gate (blocking) β security review when auth/data/money β/pre-deploychecklist β deploy β confirm live β/testagainst production
5. Supabase Migration β the Shadow Build
Airtable stays the system of record until per-module cutover. A parallel Supabase/Vercel build runs alongside via twins and shims. Spec: docs/supabase-schema-spec.md.
supabase-app.htmlβ the shell for the migrated app (deploys to Vercel)*-supabase.htmltwins mirror live pages: index, accounts, pnl, money, wealth, ai-brain, follow-up, skills*-shim.js(5:dashboard,wealth,ai-brain,comms,skills) overridewindow.fetchand translate Airtable REST calls into Supabase queries β so the original feature JS runs unmodified- Shims load NON-deferred, before feature code, and can chain (a page can load several)
- Shims await the Supabase session before the first query (fixes blank-data-on-load)
- In repo:
0015_systemisationΒ·0016_app_settingsΒ·0017_objectives_strategyΒ·0018_ai_brainΒ·0019_wealth - 0001β0014 applied to the project but pending commit to the repo (MASTER-PLAN Β§4 task, Mica)
- Tenancy spine applied:
tenants,tenant_users,tenant_config,ai_usage_log - Open: RLS policy audit, tenant_id retrofit on every table, entities table (D1)
- Per-module cutover, not big-bang. A module cuts over when its twin loads real data on Vercel and passes the parity check
- Until then every write goes to Airtable; the shadow build is read-mirrored
- Replace any Airtable automation a migrating page depends on before its cutover
- Target: launch on Vercel + Supabase by early August 2026
6. Docs & Specs Registry
Which document governs what. If it is not listed here, check STRUCTURE.md before creating a new one.
| Document | Governs | Rule |
|---|---|---|
MASTER-PLAN.md | THE plan: every OD roadmap, launch task, priority | Never create another plan doc. Amendments via Β§13 holding pen, dated + sourced in the Changelog |
STRUCTURE.md | Where every file lives: repo, AI context layer, Drive | Read before creating any file; update in the same commit if you add a new location |
CLAUDE.md | Session rules: stack, workflow, quality gate, design system, concurrency | The contract every session works under |
PRODUCTISATION.md | De-Kevining: D1-D9 decisions, tenant model | Β§5 holds the decided outcomes |
docs/supabase-schema-spec.md | Target Postgres schema, RLS approach | Migration source of truth |
docs/agent-runtime-spec.md | How agents execute (engine live since 3 Jul 2026) | |
docs/agentic-extraction-spec.md | Loom β SOP β agent pipeline (the core product engine) | |
docs/business-blueprint-spec.md | Client-facing Blueprint tab ("% of work run by AI") | v1 = view over existing data |
docs/client-profile-questionnaire.md | Onboarding interview that writes tenant_config | |
docs/airtable-cleanse-2026-07-07.md | Record of the 7 Jul task cleanse | Historical |
7. AI Layer
Everything AI-facing: model routing, prompts, skills, memory. The north star: AI agents execute up to 90% of repeatable operational work.
AI_MODEL_DEFAULTβ main model for in-app AI featuresAI_MODEL_LIGHTβ cheap model for light tasks- Feature files import these constants. A hardcoded model ID that later retires takes down every AI feature at once β this happened; do not repeat it
- Requests route through the claude-proxy worker; per-tenant AI keys (D9) are in build
js/prompts/boardroom-mentor.jsβ the single source of truth for every AI wizard's voice. All wizards import it; never fork the tone per featurejs/ai-assistant.jsβ the chat panel; per-tab context blocks (e.g.ctx.compliancePage) tell the AI what the user is looking at. New features that hold data Kevin might ask about add a context block here
build-featureβ end-to-end build pipeline (BILD β plan β build β audit β review β deploy)fixβ same pipeline for bugs and amendmentsauditΒ·verifyΒ·testΒ·test-gapsΒ·pre-deployΒ·health-bar
~/.claude/skills/.
- Claude Code project memory β persistent facts across sessions (MEMORY.md index + one file per fact)
- AI Brain vault β Google Drive
00 AI Context: founder profile, priorities, meetings, transcripts. Nightly compound engine links and reviews it - The AI Brain page (
ai-brain.html) reads a published Airtable feed β sensitive items are link-only pointers, never copied - Agent engine: live testing since 3 Jul 2026; agents come from the Systemisation pipeline (Loom β SOP β readiness β shadow β live)