⤓ Download as PDF · property intake form · back to the app
One document, every part of the program:
Version August 2026 · covers the 14-sheet production build (340 tests) Live app: https://orangeoceanatlas.com (also 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 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), 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. Chips toggle overlay layers: - Lenses: Status / Expiry / Rent / Use / HVAC / Size — unit fills + legend. - 🅿 Parking — the 314 drawn stalls by zone. - 🎥 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. 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, rollover, and concentration charts; the NOI worksheet
(enter opex → NOI → cap-rate value); Collections & aging (month
collected-vs-charged and per-unit FIFO aging from the ledger). The Excel
proforma (npm run proforma) is the owner-overridable model for underwriting
conversations.

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.

Lease expirations plus instrument deadlines (JD Bank easement end, etc.) on one timeline. Derived — nothing to maintain.

See Part II. 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, the document register (rows can carry a real uploaded file — 📎 attach → the row's link becomes an "Open 📎" that always serves a fresh secure URL), and the site-imagery library.
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 (orangeoceanatlas.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.Sign in with your authorized email. You see the sheets the operator has shared (dashboard, rent roll, financials, and the Safe are typical), read-only. 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/sot-2026-07/ (authority rank 1).src/data/units.json.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 (340) · npm run build ·
deploy · grep the prod bundle for the change · smoke the live URL.
Committing is not shipping: commits do not auto-deploy.
npx vercel deploy --prod --yes --scope adams-projects-0c52918e
(CLI is logged in as orangeonyx on this machine. .vercelignore governs
uploads — the 16MB splat + 3MB mesh ride in public/.)
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 August 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://orangeoceanatlas.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).