⤓ Download as PDF · property intake form · back to the app
One document, every part of the program:
Version September 22, 2026 · covers the 18-sheet production build (777 tests) Live app: https://otb.cypresscommand.com (also orangeoceanatlas.com · otb-command.vercel.app) · Operator: adam@adamabdalla.com
This supersedes the July 2026 text-only edition (docs/pitch/operator-manual.md).
Every screenshot is a real capture of the running system. Tenant names and
dollar figures shown are representative sample data, not actual tenancy or
economics. Part I is orientation, Part II
is the operator's day, Part III covers each sheet in depth, Part IV is the
counterparty guides (owner / vendor / tenant / signer), Part V is the monthly
and periodic rhythms, Part VI is administration and recovery.

There are no passwords. Enter your email on the login screen; a magic link arrives by email; clicking it signs you in. Your role is resolved automatically:
| You are… | How the system knows | You land on |
|---|---|---|
| Operator | adam@adamabdalla.com | Everything |
| Owner | pre-authorized email (see §6.1) | Read-only, operator-chosen sheets |
| Vendor | email matches the vendor roster | V-1 Vendor Portal only |
| Tenant | email in the tenant-contacts list | M-1 Maintenance only, own unit |
| Anyone else | — | A "pending" holding screen; no data |
If a legitimate person lands in pending, the operator promotes them (§6.1).
The left sidebar is a sheet index, like an architect's drawing set:
The masthead carries a KPI ribbon — Occupancy · Rent/mo · Vacant · Expiring ≤12 mo · Needs attention — and each figure is a button that drills into its sheet.
The URL tracks the open sheet (#roll, #plan…) — the back button walks your
history, and links can deep-link a sheet. Boot lands on D-1 (D-0 sits above it
in drawing-set order but is a rollup, not the daily surface).

Click any unit anywhere (site plan, rent roll, 3D lens, satellite) and the drawer opens with everything about that unit: term progress, PSF economics, HVAC responsibility, notes, contacts, documents, photos, compliance rows, the Ledger panel, and the E-Sign panel. Most day-to-day work happens here.

At phone widths the sheet index becomes an off-canvas drawer behind the ☰ button, grids collapse to one column, and the unit drawer goes full-screen. Everything works; nothing is mobile-only.
A normal day touches five surfaces:
One card per property you can see: A/R outstanding (sum of positive unit balances over effective ledger entries) and open work orders, with an as-of stamp. "Open →" switches the active property. With a single property the property switcher stays hidden; at two or more it appears in the sidebar (see the onboarding manual). Derived — nothing to maintain here.

Read-only KPIs derived from live data — nothing to maintain here. Cards: occupancy/rent KPIs, parking variance (from the instruments file, not typed), Parking Occupancy (C3) with 7-day trend (48-hour freshness window; the as-of label shows date+time for non-today samples), Network (UniFi) up/down, Automation (cron) — green with last-run age; brick-red STALE past 26 hours, which matters because maintenance aging, voice leads, C3 heartbeat, UniFi, rent posting, and the owner brief all ride that one daily cron — Client Errors (24h) (renders only when the count is nonzero), the Calls KPI (recorded voice calls awaiting attention), and the Action Queue (same cards as W-1). If a card shows brick-red, it is telling you something needs attention.

The recorded plat, interactive. A View row above the chips sets a preset
— Leasing · Daily Ops · Building Systems · Common Areas · Signage · Roof ·
Site / Hardscape — and the chips re-derive from it; toggling any chip by hand
drops the preset highlight. Chips toggle overlay layers:
- Lenses: Status / Expiry / Rent / Use / HVAC / Size — unit fills + legend.
- 🅿 Parking — the 314 plat-labeled stalls by zone, plus the 10-stall Johnston
row the CAD stripes but the plat never labels (marked as such).
- ⇆ Access — ingress/egress as built: every curb cut drawn as an apron with its
throat width, two-way arrow pairs at the cuts and along the aisles, the Arnould
raised median and its one 55' opening (Driveway A is the only full-movement cut),
edge-of-pavement lines, Lot 7's open Patricia frontage. Source: the architect CAD,
satellite-confirmed (docs/site-access-inventory-2026-09.md).
- § Easements — the §3a liquor line and its notes, the church easement note,
10' utility easements, electric easement 577566, the 5×5' guy easement. Switch off
for a clean leasing exhibit.
- 🎥 Cameras — 17 mounts with view cones; click one → its live view.
✎ Adjust cams (operator-only): drag pin to move, drag the brass dot to
re-aim, double-click to reset; corrections persist.
- 🚗 Occupancy — latest measured stall states painted green/outline over
the storefront row, Lot 8 pocket, and the 149 corner.
- 📍 Pins / + Pin — drop a pin per water shutoff, meter, bench, column — and
for lease/ops designations: reserved parking, monument-sign site, common area,
loading zone, dumpster pad. Pins also appear on the satellite lens.
- Overlay → Floor plan — whole-center floor-plan raster under the unit
boxes, with an opacity slider.
Click any unit → drawer.


11 sortable columns; any bare monthly amount is TOTAL rent (base + additional). The PSF breakdown chart is the only place component economics (Base · CAM · Tax · Ins) appear — by design. ⤓ SHEET prints the owner one-pager: guaranteed one page (the layout measures itself and shrinks to fit), with expiry flags — ▲ brick ≤6 months, △ amber 6–12 months — and a legend in the stamp.

Income composition (bars + a composition donut beside the Base · CAM · Tax ·
Ins breakdown), rollover, and concentration charts; the NOI worksheet
(enter opex → NOI → cap-rate value) now opens with a revenue → NOI
waterfall (gross potential rent + vacancy are labeled ESTIMATES at the
effective PSF; expense steps are your worksheet lines only) and a cap-rate
sensitivity table ±100 bp in 25 bp steps around the entered rate (or the
flagged 8.50% appraisal reference when blank — never stored);
Collections & aging (month collected-vs-charged, collections history bars,
and per-unit FIFO aging from the ledger); Tenant health — five tiles plus
a worst-first list with OK / Watch / At-risk chips and a "How this score is
calculated" fold (score model 60/25/15; rows open the drawer; hosted only);
and the CAM reconciliation card — year picker, prior-year opex worksheet,
per-tenant caps from the lease abstracts (src/data/recovery-terms.json),
derived occupancy gross-up, and a per-row ⤓ Statement that prints the
tenant's annual reconciliation with the PSF breakdown, cap line and
methodology. Suites with unknown PSF print "Not determinable" rather than a
guess. The Excel proforma (npm run proforma) is the owner-overridable model
for underwriting conversations.

A Document coverage card sits above the matrix: per occupied suite — Lease · COI · COI expires · other documents — with filter chips and five summary counts. Evidence = a file on the suite's document records OR a matrix "On file"; a matrix Flag is a gap. Then the 11×27 matrix. Click a cell to cycle unknown → ok → flag → n/a (food-service-only fields apply only where relevant). Every flip is recorded — who, when, from→to — and ⏱ History shows the trail. You cannot corrupt this history; corrections are new flips. Edits sync live between devices — flip a cell on your phone and it flips on the desktop without a reload.

Three derived views — nothing to maintain:
- Lease expiry timeline — a four-year Gantt with a today line and bucket
cards (past end · <90 d · 90–180 d · >180 d · term unresolved). Suites whose
lease end is unresolved in the September 2026 lease review list under
"term unresolved" with their review label — BY DESIGN, never guessed.
Row click → drawer.
- Renewal pipeline — option terms transcribed from the executed leases
(src/data/renewal-options.json, reference only; every notice = 60 days).
Groups: Notice window · Deadline passed · Option available · Term
unresolved. "Open suite" → drawer.
- Instrument + governance deadlines — the JD Bank servitude end, the
insurance renewals, the premium-finance maturity, and every S-1 governance
item with a date, on the same timeline as the leases.

See Part II. A leasing pipeline strip over the deals table (inquiry →
tour → proposal → signed; voice and web leads land here on their own) sits
above the lanes. Card types include seeded facts (holdover, renewal, vacancy,
compliance flag, covenant) and live work-order cards (mr: prefixed).
Overrides (lane moves, dismissals) persist; the seed recomputes every render,
so a dismissed card returns only if the underlying fact returns.
docs/stripe-payment-links-2026-08.md,
copy on Drive). You send the link through your own channel — the app sends
nothing. When the payment settles (ACH takes days), the unit's ledger gains
an ach:<payment-intent-id> entry automatically. A payment that can't be
matched to a unit, or that fails, opens a manager thread in AI-1
(ach-unmapped: / ach-fail:) instead of landing silently.tools/stripe-payment-links.mjs for that unit
and retire the old link in the Stripe dashboard.
Property contacts (including the Property lines block — the tenant and
leasing numbers — and the insurance contacts: agent, brokers, claims lines),
the document register, and the site-imagery library. Register rows carry
counterparty · amount · effective · matures · status · risk besides the
file (📎 attach → the row's link becomes an "Open 📎" that always serves a
fresh secure URL); an expiring strip at the top lists what lapses next.
The pylon sign renders as a native SVG elevation next to the tenant
roster — hover links panel ↔ row, click → drawer; installed-but-unlisted
faces draw brick-dashed.
Opens with Property records: a Risk register (worst-first) and a
Renewal & maturity radar (soonest-first) derived from the document
register — today that is the 2026–27 insurance program as bound (property ·
BOP liability + umbrella · workers' compensation, all renewing 2027-05-15, the
premium-finance agreement maturing 2027-03-15), the loan maturity, the lender
title policy and the recorded instruments. Abstract of the bound policies:
docs/insurance-program-2026-27.md. Then the Governance block (items with
deadlines feed T-1), ⤓ Board report (quarterly, assembled from the stored
owner briefs), and the category folders (Proforma / Leases / Tax / Insurance /
Banking / Other). Upload and open as the operator; owners read-only;
vendors/tenants sealed out at the database layer. Recent access
(operator-only) shows every view, upload, and delete with who and when.

Three personas — 🏛 Concierge (property Q&A), 🤝 Leasing, 🔧 Property Manager — all grounded in the property dossier + live state. - Conversations persist and resume after reload; 🗂 History reopens any thread; + New starts fresh. - 🎙 mic input and 🔊 spoken replies (Chrome/Edge). - Numbers are guardrailed: once a calculation is involved, every figure in the reply must trace to the deterministic engines or the reply is replaced with the engines' own summary. Ask the leasing agent things like "compare retaining at $17 vs replacing at $20 with 3 months free and $10 TI, 5-yr term, 1,917 SF, 7.5% cap" — or the manager "should I spend $45K on an RTU replacement with a $120K reserve, $30K/yr contributions, installed 2008, 15-yr life?" - 📊 Briefs — the monthly owner-brief archive; Open 🔒 renders any month. - 📦 Lead SMS (leasing persona): copies a ready-to-send text with the public leasing one-pager (otb.cypresscommand.com/leasing.html) and the leasing line number. You paste and send it — the app sends nothing. - Lease packages (operator-only): ask 🤝 Leasing to assemble a proposal; it collects terms conversationally, then generates a branded DRAFT proposal (and optionally the internal owner summary) as a card with Open 🔒 and ✉ Email (pre-filled mailto — you send it; it never auto-sends). Every generated document is DRAFT-stamped, subject to legal review.
voice-lead: manager thread within hours so no lead drops.docs/a3-voice-runbook.md; call
records: docs/voice-call-records-runbook-2026-09-18.md.Five blocks, every one generated from the source of truth on the fly — a
flyer can never disagree with the rent roll, and rates are never printed:
1. Availability flyers — one printable flyer per vacant suite.
2. Center overview — stats · logo wall · site plan · availability.
3. Tenant cards — 1080-square co-marketing card per tenant.
4. Photo library — the assets bucket by suite; the operator picks the
hero image per suite and the pick persists (marketing layer).
5. 360° tour media — panoramas + hero stills uploaded to the PUBLIC
tour bucket; Publish manifest refreshes what the /tour microsite
shows. Insta360 export settings and the upload steps:
docs/marketing-b1-and-tour-2026-09-18.md.
Every output opens as a page with Print / Save PDF. The pylon sign block
shows the sign, its roster and "tenants without a face" per tenant. QR codes
for the leasing page and the tour live in public/qr/.
The cross-channel correspondence sheet. Filter by channel (note / e-mail / letter / meeting / SMS / voice / web) or unit, search, expand an entry, hand-log new correspondence, delete stale rows. Voice rows (§3.17) carry intent and urgency chips, a needs-attention dot, the summary, outcome links (work order · tour · lead · package), ▶ Play recording, the Caller/Agent turns, and ✓ Mark handled. The "Calls · 7 days" strip and the attention filter are the morning triage (Part II). Web tour requests from /tour land here too. Owners see the log read-only, including every call's summary, transcript and recording; tenants and vendors never see this sheet.
Long-running property affairs as status-filtered cards: kind (lease negotiation, claim, permit, dispute, project, general), status, deadlines, correspondence and file attachments. Operator: open a matter, edit fields inline, log meeting notes (they mirror into L-1), attach documents (they ride the documents bucket), close or reopen. Deadlines feed T-1. Owners read the cards and open attachments but never attach.
The procedures (SOP) library — categories → procedures → steps, with due-today / overdue / streak state. Browse, expand a procedure, ✓ Complete with notes and minutes (it closes the oldest open occurrence), author new content (add or edit categories, procedures, steps; deactivate), clear stale occurrences. The daily cron materializes each scheduled procedure's current occurrence and opens ONE "SOPs overdue" digest thread in AI-1 keyed by the newest lapse (a static backlog never re-fires). Owners read-only; tenants and vendors never see O-1.
otb.cypresscommand.com/tour needs no sign-in: a 360° viewer per suite when
panoramas have been published from B-1, otherwise the plat, the numbers and
Call / Text-to-tour. The Request a tour form writes a web lead (deal row +
L-1 note, capped per day), opens a manager thread in AI-1, and e-mails the
owners when mail is configured. The leasing one-pager stays at
/leasing.html.
Sign in with your authorized email (the owner list is fixed by the operator — §6.1). You see the sheets the operator has shared (dashboard, rent roll, financials, and the Safe are typical), read-only. The Comm Log, when shared, lets you listen to any recorded call and read its summary and transcript. The 📊 Briefs panel in AI-1 holds your monthly intelligence brief — every figure deterministic, archived permanently. You may ask the AI desk questions; its numbers are guardrailed the same as the operator's.
Magic-link in with the email the operator has on file. You see one sheet: your own folder (your documents + "send a file to management"), your COI status with a renewal nudge, and your assigned work orders — open each to read notes and photos, add notes, and ✓ Mark complete when done.
Magic-link in with the email your management added. You see M-1 for your unit only: file a maintenance request (describe the problem, attach photos), watch its status, and "Your account" — your current balance and recent ledger entries. Rent is paid through the payment link management sends you — one link per unit, reusable every month. Emergencies (water leak, electrical hazard, break-in, sewer): call the maintenance line rather than filing a ticket.
You'll receive a link from management. It opens a simple page — no account, no app. Review the request (and the attached document if one is linked), then sign or decline. The signature is recorded with timestamp and consent language under the federal E-SIGN Act and Louisiana UETA.
docs/lease-population-2026-09-10.md and the
suite's leaseEvidence in src/data/units.json (Tier 1 since the
September 2026 lease review; docs/sot-2026-07/ stays authoritative for
suites the review does not name). Supersession rules:
docs/sot-supersession-2026-09-11.md.src/data/units.json. Leave a lease end BLANK when
the signed documents do not resolve it — T-1 and W-1 then show "term
unresolved" instead of a guessed date. No holdovers are assumed.npm run split-seed && npm run concierge-context (the test suite fails
if you forget — deliberately).npm test → commit → deploy (§6.3) → verify live.
Never trust an imported system's dates over signed paper; store stated-rent
exceptions as exceptions — do not "fix" them to formula.npm run poster,
leasing one-pager per tools/leasing-package.py).node --check on changed modules · npm test (777) · npm run build ·
deploy · grep the prod bundle for the change · smoke the live URL.
Since 2026-09-11 every push to master deploys through
.github/workflows/deploy.yml (npm ci → npm test → vercel build/deploy
--prod); a failing test blocks the deploy. Verify by fetching the live page
and checking the assets/main-<hash>.js bundle name against the local build.
Push to master (or merge a PR) — GitHub Actions deploys production. Manual
fallback from this machine:
npx vercel deploy --prod --yes --scope adams-projects-0c52918e
(.vercelignore governs uploads — the 16MB splat + 3MB mesh ride in
public/.) Never promote a Preview deployment: previews run against the
isolated Supabase branch and the build aborts if production secrets leak into
the Preview scope.
G:\My Drive\00 OTB\.OTB-Repo-Backup, daily 03:00) bundles full git history
to Drive. Committed-only — commit anything that matters..env aside
temporarily — and restore it after. (Confidential rents do not render in
that mode — by design.)Keys live only in Vercel env — never in chat, repo, or disk. Rotations are
scripted drills: tools/rotate-secret.mjs (shared cron secret),
tools/rotate-voice-secret.mjs (voice). One-shot key needs (e.g. the
restricted Stripe key used to mint payment links) follow the drop-use-delete
drill: key to a dotfile, tool consumes it, file deleted — never in chat.
Vercel rejects env values with trailing whitespace at deploy time — the
scripts handle this; when feeding a secret to a CLI, use cmd /c "… < file"
(PowerShell pipes append a newline).
Fully hands-free: sampler (5-min ticks, watchdog task self-heals it at logon + every 5 min) → midday 12:00 + nightly 23:45 classify + upload → app surfaces. If it goes quiet 36 hours, a manager thread opens on its own. After a machine reboot the watchdog relaunches the sampler; no manual step. Known conventions: capture day-directories are UTC-keyed; frames live outside the repo.
Questions the manual doesn't answer: ask the 🏛 Concierge — it is grounded in the same governed data this manual describes.
Version September 2026 · the Phase C-1 rail, funnel-proven by the C-2 live run Operator-driven by design — there is no self-serve signup. One intake file = one property.
This is the complete procedure for bringing a new property onto the platform: what to collect, how to build the intake file, how to run it (dry, then live), how to verify the result, and what the new property's people do next. It ends with the deliberate v1 fences and the teardown procedure.
A successful run creates, in one all-or-nothing transaction:
facts list
(label/value/source rows that dashboard-class cards derive from).The moment it lands, the property appears as a card on D-0 Portfolio, and — at two or more visible properties — the property switcher appears in the sidebar for every org-wide member. Nothing else changes: rent posting and API surfaces keep serving the default property until the new one is deliberately activated.

Org (management company)
- Slug (lowercase, hyphens — permanent, choose once) and display name.
- Brand kit if available (palette / wordmark / contact block) — may start
empty {}.
Property
- Slug (permanent), display name, street address, IANA timezone
(e.g. America/Chicago).
- Facts worth surfacing: GLA, unit count, parking (with source citations —
"rent roll 2026-08", "site plan"…). Facts are display rows, not schema;
add what the owner will want to see.
Settings
- ledger_start_ym (YYYY-MM) — the first month rent charges will post.
Choose the first FULL month on the platform; historical balances are a
data-package concern, not an onboarding concern.
- renewal_horizon_days (default 180), occupancy_floor (default 0.85),
late-fee schedule (late_grace_days / late_flat / late_per_day).
- SOP answers (emergency policy, vendor rules …) — free jsonb under
settings.settings.
People
- Every email that should have access on day one, each tagged with a role:
operator (full control) or owner (read-only, operator-curated sheets).
Vendors and tenants are NOT onboarding rows — they come later through the
vendor roster and tenant-login editor inside the app.

Send the new property's manager the intake form link —
https://otb.cypresscommand.com/manual/intake-form.html (or email them the
file itself, docs/phase-c/intake-form.html — it works offline too). They
answer plain-English questions — company name, property name and address,
first billing month, late-fee schedule, who gets access — then click
⤓ Download intake file and send back the resulting
intake-<property>.json. The form checks its own answers as they go
(problems appear in plain English), auto-derives the permanent slugs from the
names, and cannot produce a file the validator would reject. It only prepares
the file — nothing is created until you run the rail in §4–5.
Anything they skip (facts, notes) you can add to the JSON afterward; their
free-text notes arrive under settings.settings.intake_notes for you to
translate into real SOP settings.
Copy the template and fill it in:
docs/phase-c/onboarding-intake-template.json → docs/phase-c/intake-<slug>.json
{
"org": {
"slug": "example-mgmt",
"name": "Example Management, LLC",
"brand": {}
},
"property": {
"slug": "example-center",
"name": "Example Shopping Center",
"address": "100 Example Blvd, Lafayette, LA 70506",
"tz": "America/Chicago",
"facts": [
{ "label": "GLA", "value": "50,000 SF", "source": "rent roll 2026-08" },
{ "label": "Parking", "value": "250 spaces", "source": "site plan" }
]
},
"settings": {
"ledger_start_ym": "2026-09",
"renewal_horizon_days": 180,
"occupancy_floor": 0.85,
"late_grace_days": 5,
"late_flat": 100,
"late_per_day": 25,
"settings": {}
},
"authorized": [
{ "email": "operator@example.com", "role": "operator" },
{ "email": "owner@example.com", "role": "owner" }
]
}
Rules the validator enforces (the tool refuses to touch the database on any
error): slug shape, timezone shape, YYYY-MM ledger start, role whitelist,
fact and authorized row shapes. Reusing an existing org slug is legitimate
(that is how a portfolio grows); reusing an existing property slug under
the same org is refused.
node tools/onboard-property.mjs docs/phase-c/intake-<slug>.json --dry-run
Validates the file and prints the full plan — org reuse vs create, the property row, settings, and authorized emails — without any database contact. Fix anything wrong and re-run until the plan reads exactly like the deal.
The live run uses the standard CRON_SECRET pull-load-delete drill (the secret is pulled from Vercel env for the moment of use — never typed into chat, never left on disk):
node tools/onboard-property.mjs docs/phase-c/intake-<slug>.json
The RPC is all-or-nothing: any failure rolls the entire intake back. A
duplicate property raises (P0001) and clobbers nothing — this is the
protection, not an error to work around.
In the app (as an org-wide member): 1. D-0 Portfolio shows the new property's card ($0 A/R, 0 work orders — correct for an empty property). 2. The property switcher now renders in the sidebar (it appears only at two or more visible properties). 3. Switch to the new property — every sheet boots empty. That is correct: a fresh property has no data package (§8).
In SQL (Supabase console), if you want belt-and-suspenders:
select p.slug, p.name, p.created_at, s.ledger_start_ym
from properties p join property_settings s on s.property_id = p.id
order by p.created_at;
select email, role from authorized_emails order by email;
Financial isolation: the rent cron posts only to the default property;
api/* still resolves the flagship. A newly onboarded property cannot create
financial side effects until it is deliberately given a data package and
activated.
Send each authorized person to the login page — they enter their email and click the magic link; their membership row and role materialize on first sign-in. Nothing to provision, no passwords to distribute.

The C-2 proof property was created and torn down cleanly with this exact pattern. Order matters (children first), and this is irreversible — read twice, run once:
-- any typed-layer/child rows first (comp_state, board_state, …), then:
delete from property_settings
where property_id = (select id from properties where slug = '<slug>');
delete from properties where slug = '<slug>';
Then verify: D-0 shows one card fewer; the switcher disappears if only one property remains; the flagship's data is untouched.
| Symptom | Meaning | Do |
|---|---|---|
| Validator errors on dry-run | Intake file shape is wrong | Fix the listed fields; nothing was sent |
P0001 duplicate raise on live run |
Property slug already exists under that org | This is the no-clobber guard. Pick a new slug, or tear down the old property first |
| New property card missing on D-0 | Viewer is not an org-wide member | Check the viewer's membership; org-wide members see all properties |
| Switcher not visible | Only one property is visible to you | Correct at one property — it renders at two or more |
| Sheets look like the flagship after switching | You are looking at bundled flagship data | Expected until the property's own data package exists (§8) |
| Someone stuck on "pending" | Email wasn't in authorized, or typo'd |
Add/fix via the sidebar access panel; they re-click a fresh magic link |
Provenance: intake contract and rail design docs/phase-c/01-onboarding-design.md;
validation seam src/lib/onboard.js; RPC migration onboarding_rpc; driver
tools/onboard-property.mjs. The rail was smoke-tested under rollback on
prod and funnel-proven live by the C-2 run (property created via the rail,
verified, then torn down).