BuyDubai Cube, Registers & Community Pages Master Plan
Full audit, architecture, cost model, 10-turn verification proofs, and Section 10 integration with the INVRT 24 October 2026 Dalia Implementation Handoff.
Project: Buy Dubai community pages built from the data cube
Written 2026-10-24 (Asia/Dubai, UTC+4) for Anthony Booth, so another agent can check every claim. Nothing in this file was published, sent or spent. No secrets are in it.
How to read this file
- Every claim carries a label: [V] I re-checked it myself today (command shown); [R] taken from a report listed in section 4 (path given, I did not re-run it); [P] Anthony's words or a decision from the parent chat, not re-checkable from files; [?] unverified or not reproduced.
- Checklists say PRESENT / MISSING only. No scores, on purpose (Anthony's rule). The one exception is community_eval.py, which prints a 0-100 score. Treat its item table as the checklist and ignore the total.
- Where two sources disagree the doc says so. Nothing is filled in by guess.
1. Goal, in Anthony's words
Anthony, 24 Oct 2026 (quoted from the parent chat, [P]): - "You went live with lots of bad pages because you didn't act on the eval. The work now is to build that loop. By going community by community, running the workflow eval versus the gold standard and then keep going UNTIL it is good enough. We will go community by community and build using the data cube. We should already have a build py. Using BB as the model to match; list the pages (e.g. insights) and the sections and the images and the components. Prove to me you understand the real goal." - "Ensure the analysis step and the build step come from the data cube." / "check that it uses data library metrics" / "if there are any gaps flag them" / "either the page has the data or the image or it doesn't" / "I don't want you inventing." - "Prove to me the data cube will be accurate, comprehensive, usable, expandable and is coherent. There should be a metrics register, a master list of communities, of buildings, that have all been linked and cleaned." - Latest ask: "write this project down; goals, what good looks like, what you have done so far, what the next steps are. List everything so another agent can check."
Plain restatement
1. Stop publishing thin Buy Dubai community pages.
2. Build community by community from the data cube, not from code that computes numbers itself.
3. After each build run the workflow eval against the gold standard. Business Bay (BB) is the model. The JVC benchmark is buydubai.io/jvc/ plus buydubai.io/jvc/insights/.
4. Loop until it passes. A gate sits before anything goes live.
5. Every number comes from the cube through the metrics library (factlib.py: FL.get / FL.series).
6. Master registers exist for metrics, communities and buildings, linked and cleaned.
Standing rules (Anthony)
- Plain present/missing checklists, not scores.
- Never invent. Prove with evidence ("Don't tell me you finished. Show me.").
- No spend without his approval. Current spend on this work: USD 0.
- Never publish a community page before a section-by-section check against JVC. Do not bypass the pipeline's review step.
- No proxies to get past site blocks. Never print keys. Never log in. Do not edit Studio's credentials.md. Draft messages only, never send.
- Start every reply to Anthony with "Anthony,".
2. What good looks like (acceptance checklists)
Mark each line PRESENT or MISSING after running the check. "Today" is what I found on 24 Oct 2026.
2.1 A finished community page set (per community)
Reference list comes from bb-inventory.md (BB) and eval-baseline.md (rubric). Test = community_eval.py <slug> item, plus the curl in section 8.
| # | Item | Test | Today: town-square (live) | Today: business-bay (live) |
|---|---|---|---|---|
| P1 | Hub loads, HTTP 200 | curl -s -o /dev/null -w "%{http_code}" https://buydubai.io/<slug>/ |
PRESENT [V] | PRESENT [V] |
| P2 | No placeholder text ("not shown yet", "Building NNN", TODO, lorem, NaN, undefined, empty table) | curl -s .../<slug>/ \| grep -c "not shown yet" must be 0; eval H2/Q1 |
MISSING: 1 hit [V] | PRESENT (0) [V] |
| P3 | Community key in facts/communities.yaml under the same slug as the page |
eval D5/H3 | MISSING live (staged copy has it) [V] | PRESENT [R] |
| P4 | Cube facts on the hub (data-fact-id count) at least 70% of BB's 17 |
eval D2 | MISSING: 0 [V] | PRESENT: 17 [V] |
| P5 | 4 hero key-figure tiles from the cube (buildings, sales 12m, AED/sq ft + like-for-like, yield) | eval D3 | MISSING: 1 tile [R] | PRESENT [R] |
| P6 | Price chart (SVG), prices section real, top-buildings table with real names | eval D1/D4 | MISSING [R] | PRESENT [R] |
| P7 | All 12 BB sections present in BB order, each at least 50% of BB words | eval C1/C2 | PRESENT / PRESENT [R] | PRESENT [R] |
| P8 | Tables where BB has tables (schools table carries KHDA) | eval C3 | MISSING (where, getting-around, coming) [R] | PRESENT [R] |
| P9 | FAQ count at least 80% of BB's 20 | eval C4 | MISSING: 12 [R] | PRESENT: 20 [R] |
| P10 | 3 real hero photos with alt text | eval C6 | MISSING: 0 [R] | PRESENT: 3 [R] |
| P11 | Sub-page tree: studio, 1-4 bed, towers, budget, yield, traffic, parking, hot, collections, commercial (19 in BB) | eval P1 | MISSING: 5 of 19 (live) [R] | PRESENT: 19/19 [R] |
| P12 | Building pages live as a share of buildings (BB 137 of 356 = 38%) | eval P2 | MISSING: 16 of 82 [R] | PRESENT [R] |
| P13 | /insights/ page, substantial, cube-driven (JVC reference) |
eval P3; curl .../<slug>/insights/ = 200 |
MISSING: 404 [V] | exempt (BB has none; 404 [V]) |
| P14 | Tab strip at least 70% of BB's 11 | eval P4 | MISSING live (6) [R] | PRESENT [R] |
| P15 | Not more than 5 "could not confirm" sentences | eval Q1 | MISSING: 22 [R] | PRESENT [R] |
| P16 | One live slug per community (no duplicates) | eval Q2/H4 | PRESENT for town-square [R] | PRESENT [R] |
| P17 | Valid JSON-LD (BreadcrumbList, FAQPage, Place), internal links 200, alt text, GA4 + Dalia + search + lead scripts, title/meta/h1/canonical, sitemap lists hub and sub-pages | eval Q3-Q8 | mixed [R] | PRESENT [R] |
| P18 | Page has a facts-manifest and lint_pages.py checks it |
grep -c 'id="facts-manifest"' |
MISSING live [R]; staged hub has 1 [V] | PRESENT [R] |
| P19 | Section-by-section check against JVC done and written down before going live | a saved checklist file | MISSING (no such file exists) [?] | n/a |
Gold standard counts (BB, fetched 24 Oct 2026, bb-inventory.md) [R]: hub 7,852 words, 13 sections, 20 FAQ, 10 tables, 3 hero photos, 5 key figures (the eval counts 4), 11 tabs, 31 sub-pages, 137 live building pages of 356.
JVC (/jvc/ plus /jvc/insights/) [R]: 37 sub-pages, 20 images, 21 tables, 24 FAQ; insights has 18 sections, 15 chart SVGs, 26 tables, 10 FAQ, 9,109 words.
2.2 The cube
Spec: /workspace/cube-spec.md. Proof: /workspace/cube-proof.md.
| # | Item | Test | Today |
|---|---|---|---|
| C1 | Cube numbers match raw data | cubeproof/p*.py |
PRESENT [R]: 24/24 sales months, 69/69 areas, 33/33 lease months, 14/14 live-page numbers, 20/20 random lookups |
| C2 | Build is reproducible (same inputs give same bytes/rows) | build twice, diff | MISSING [R]: 13,547 rows differ; 972 rows have NULL fact_id |
| C3 | Cube is fresh | cube_meta.json built and sales_as_of |
MISSING [V]: built 2026-10-23T00:55, sales_as_of 2026-10-22 (parquet mtime 24 Oct 02:03) |
| C4 | Nightly passes | facts/nightly.sh log |
MISSING [R]: failed 21 Oct (cube tests, number lint) and 24 Oct (lint_pages: discovery-gardens, jvt); last_good restored |
| C5 | No unsuppressed NULL values or thin cells | query in section 8 | MISSING [R]: 10,005 NULL-value rows unsuppressed, 423 NULL-n, 2,422 lr_* rows with n < 10 |
| C6 | Every metric name is in metrics.yaml |
validator check 1 | MISSING in metrics.yaml (25 of 192 registered) [R]; PRESENT in the staged metrics.csv (192/192) [R] |
| C7 | One off-plan rule, one yield rule in live code | grep | MISSING [R]: 3 off-plan rules, 3 yield rules; decision taken (section 3), code not yet changed |
| C8 | Master assignment shown (imputed vs given) | column or metric | MISSING [R]: up to 95.6% imputed (DIP Second), invisible |
| C9 | Link rates high enough to trust | section 5 | MISSING [R] (sales to tower 75.5% did not reproduce) |
| C10 | OSM link per building | register column | MISSING [R]: JVC only, 556 of 5,390 PF pages |
| C11 | Metrics register, community master, building master | registers/ |
PRESENT in staging only [R][V], not in the live repo |
2.3 Registers (staged at Studio ~/ClaudeWork/cube-stage/registers/out, copy /workspace/registers/)
| # | Item | Test | Today |
|---|---|---|---|
| R1 | metrics.csv covers every cube metric, no extras, unique names |
validator section 1 | PRESENT: 192/192 [R] |
| R2 | communities.csv ids unique; no alias maps to two communities |
validator section 2 | PRESENT: 186 communities, 431 aliases, 0 collisions [R] |
| R3 | buildings.csv ids minted once, append-only ledger |
validator section 3, ledger/*.csv |
PRESENT: 6,166 rows, BL000001-BL006166 [R] |
| R4 | Only exact/normalised-name matches become aliases; fuzzy goes to review_queue.csv |
validator | PRESENT [R] |
| R5 | Every page directory resolves to a community | page_dir_unresolved.csv |
MISSING: 6 unresolved (al-barsha, al-jaddaf, dubai-production-city, dubai-silicon-oasis, palm-jebel-ali, ras-al-khor) [V] |
| R6 | Human review of building matches | reviewed column |
MISSING: 3 of 6,166 rows reviewed; 1,128 items in the review queue [R] |
| R7 | LLM-derived proposals checked by a human | jev_project_map |
MISSING: 13 accepted proposals are LLM-derived, unreviewed [R] |
| R8 | Registers wired into the cube build and into lint | build_cube.py, lint_pages.py |
MISSING: staging only [R] |
2.4 The gate (lint and eval)
| # | Item | Test | Today |
|---|---|---|---|
| G1 | A page without a manifest fails lint | lint_pages.py |
MISSING [R]: it does if not m: continue, so such pages are skipped |
| G2 | community_eval.py runs before publish and blocks on fail |
grep in release scripts | MISSING [V]: no .sh in areas/, pilot_bb/ or facts/ mentions community_eval. areas/io_release.sh and areas/publish_area.sh exist. |
| G3 | Page contract file facts/page_contracts.yaml and lint checks L1-L13 |
file exists | MISSING (planned, spec section 8) [R] |
| G4 | Review step in the pipeline cannot be skipped | areas/STATUS.md shows review |
PRESENT as a status; enforcement not verified [?] |
| G5 | Eval pass line tested against pages Anthony called good or bad | note in eval | MISSING [R]: the 85 line is uncalibrated |
2.5 The insights page (JVC reference)
Source: /workspace/insights-cube-map.md [R].
| # | Item | Today |
|---|---|---|
| I1 | Insights numbers read from the cube via the library | MISSING: the JVC insights analysis and page builder use zero library metrics |
| I2 | Insights page carries a facts-manifest and data-fact-id tags |
MISSING [V]: live /jvc/insights/ has 0 data-fact-id and 0 facts-manifest |
| I3 | Generic insights_cube.py <slug> and insights_page.py <slug> |
MISSING (design only) |
| I4 | Live insights numbers equal cube numbers | MISSING [R]: psf 1,483 vs 1,478.3; completed/off-plan 1,296 / 1,578 vs 1,272.6 / 1,565.4; change +3.6% vs +3.2% raw (+4.8% mix-adjusted); off-plan share 68.4% vs 69.8%; 12-month sales 12,495 vs 10,988; 2-bed yield 7.33% vs 6.90% |
3. Decisions made
3.1 Anthony's 10 cube decisions (message t303u, 24 Oct 2026) [P]
Saved into the spec by an earlier agent, but cube-spec.md as I read it still words them as recommendations. Treat Anthony's message as the authority and confirm wording.
1. Off-plan: use the cube rule (one rule everywhere).
2. Yield: signed-lease gross yield (yield_gross) is the headline. yield_ask is a separate, labelled name.
3. Community = DLD master project, always. A "site" is a published union of masters from communities.yaml. dld_area is for comparison only.
4. Keep the 10 / 30 / 10 suppression thresholds.
5. Period pages (week, month, quarter) come from the cube.
6. Show owner gain only at towers with 10 or more resale pairs.
7. Agents and developers: "leave figure but correct" (keep the figure, fix how it is computed).
8. Keep the score (the existing building-page score).
9. LTV: find the rules (done, 3.2).
10. Value-a-unit tool reads cube figures and drops raw rows.
Later: t304u "go" (build slice one), t305u (prove the cube; registers), t306u "yes" (registers first), t307u (write this down).
3.2 LTV and buying-cost rules (research 24 Oct 2026, /workspace/ltv-rules.md, sources in /workspace/evidence/ltv/) [R]
Official (Central Bank Circular 31/2013 as amended by Resolution 31/2/2020, in force since 8 Apr 2020):
- First home, price up to AED 5m: 85% UAE national, 80% expat resident. Over AED 5m: 75% and 70%.
- Second home or investment: 65% national, 60% expat.
- Off-plan: 50% for everyone. Takes priority over the other rows.
- Max term 25 years. Debt burden cap 50%. Max loan 7x income (expat), 8x (national).
- Non-residents: the Central Bank sets no figure. Banks decide (ADCB up to 50%; Mashreq pages say 50 / 60 / 65). Show as bank policy.
- Use "up to and including AED 5m" for the first tier (the resolution wording).
- DLD sale registration 4% (official). Trustee AED 4,000 + VAT at 500k and over, AED 2,000 + VAT under. Mortgage registration 0.25%. Agent 2% + 5% VAT is custom, not law.
Not verified (flagged, do not state as fact): AED 580 DLD admin and AED 290 mortgage add-on (secondary sources only; a trustee receipt would settle it); off-plan Oqood fee for a buyer; 5% VAT not fetched from the tax authority; a "Circular 03/2026" claim from one site is unverified and contradicts the rulebook.
Code today (pilot_bb/costs.py, bd-costs.js): LTV is a free dropdown defaulting to 80% at any price, lacks the national 75% and 65% rows, and applies the 2% agent fee to off-plan. Not changed.
3.3 Registers rules [R]
Exact or normalised-name match only becomes an alias. Fuzzy (ratio 0.88 and above) goes to the review queue only. "PF name equals DLD project" is accepted only when the community also matches. Conflicts follow rules C0, C1, C2a, C2b or "unresolved" (conflict_resolution.csv, 105 groups).
4. What has been done (timeline and artifacts)
All times 24 Oct 2026 Asia/Dubai unless stated.
4.1 Timeline
- The failure [P] Five thin community pages went live: discovery-gardens, dubai-silicon-oasis, jvt, town-square, motor-city. They had been judged 7-8 out of 10 on load, SEO and counts only. Anthony: "all day today you have failed". They are still live today [V]: HTTP 200 for all five; four print "not shown yet" (discovery-gardens, dubai-silicon-oasis, jvt, town-square); motor-city does not and has 27 cube facts.
- The eval was built [R]
areas/community_eval.py(tracked on Studio and MacPro; copy/workspace/eval/community_eval.py). Baseline/workspace/eval-baseline.md. Scores: business-bay 100.0, jvc 94.4, discovery-gardens 39.2, dubai-silicon-oasis 41.5, jvt 38.9, town-square 44.7, motor-city 72.8. Pass line 85. BB = 100 by construction (baseline derived from BB). JVC is the only independent check. Weights and the 85 line are my adaptations, uncalibrated. - Town Square staged build, first pass [R] In staging only (never live), built before the cube-stage work. Eval 44.7 to 80.5 (
/workspace/town-square-staged/eval-before.md,eval-final.md, 11 phone-width screenshots). Data group 3.5 to 35.0 of 35. Still failing in that final run: 0 hero photos, 14/19 sub-pages (missing traffic, hot, commercial; stubs mixed-use, parking), 22 "could not confirm", no insights page, GA4/lead scripts missing on the preview host, 12 FAQ, three sections without tables, building pages not scored. Where it was built: Studio~/ClaudeWork/ts-stage/(evalsite). I did not re-run this one. - Costing [R]
/workspace/ts-costing.md(nothing run, nothing spent). Findings: free routes exist for nearly every gap and would put Town Square near 91 on paper with no photos. Hero photos (C6, +4) are the only gap with no clean free source.GOOGLE_PAID_OFFfile is in force (Anthony, 12 Oct 2026 21:23): needs his written OK before any Google call. Paid-route list prices: traffic per building $8.20 for Town Square and $80.50 for all five (zone version about $4 for five, free inside Google's monthly cap); hero photos about $0.11 reduced, about $0.8 as coded for Town Square; guide regeneration about $4.5 per community.accept.pypasses 0 of 82 Town Square staged buildings (developer 25, handover year 0, pictures 0, facts 0, words 54, sales 62). No approval file exists for these five communities. Caps: $10 per community, $25 per day. - Insights audit [R]
/workspace/insights-cube-map.md: JVC insights uses no library metrics; maps Q1-Q15 to cube needs; lists numeric differences (section 2.5). - BB and JVC inventory [R][V partly]
/workspace/bb-inventory.mdwith live files in/workspace/bbwork/live/(118 pages, all 200, fetched 24 Oct). Root causes of thin pages:community_facts.pyprints "not shown yet" when the slug is not incommunities.yaml(about line 79); hero photos need paid Google runs; guide text needsplace_guide.py(paid, Rule 17). - Non-cube calcs audit [R]
/workspace/non-cube-calcs.md(Studio HEAD 01ce8f8d snapshot in/workspace/audit/studio/). BUY groups B1-B28, RENT groups R1-R18. buydubai.io: 1,469 of 17,359 pages have a manifest; 15,890 do not (agents 8,362; week 2,748; month 1,969; quarter 1,165; developer 916). rentdubai.io: 2,408 of 2,411 have one (missing: /rent-index/, /jvc/, /dso/). - Cube spec [R]
/workspace/cube-spec.md: dimensions, 15 source tables, metric catalogue, constants registry, 10 decisions, client-side tools plan, page contracts, lint L1-L13, phases P0-P11 (about 8-12 weeks total; smallest useful slice about one week). - Cube proof [R]
/workspace/cube-proof.mdwith scripts/workspace/cubeproof/p1..p23.py. PASS and FAIL lists in sections 2.2 and 5. - LTV research [R]
/workspace/ltv-rules.md(section 3.2). - Registers built in staging [R][V]
/workspace/registers-report.mdis at/workspace/registers/registers-report.md. Validator: ALL CHECKS PASS plus one reported GAP (6 page dirs unresolved) [V fromvalidator_output.txt]. Counts: metrics 192; communities 186 (CM0001-CM0186); community_alias 431; buildings 6,166 (BL000001-BL006166); building_alias 16,401; review_queue 1,128; conflict_resolution 105 groups. Link rates before to after [R]: DLD projects with no entity 297 projects / 15,121 sales to 256 / 14,391. Ejari names unmatched 279 / 33,962 leases to 169 / 23,646. Leases to a DLD project 89.9% to 93.0%. Leases to a PF building page 5.8% to 63.6%. Sales to building 89.2% (not like-for-like with the old figure). - Slice one (cube-stage) stopped half way [P][R][V] Anthony said "go" (t304u). In Studio
~/ClaudeWork/cube-stage/:- Done: staged
communities.yamlwithtown-square: {masters: [TOWN SQUARE]}(live yaml unchanged [V]); staged cube rebuilt with that slice (facts/after: 9,646,465 rows, built 2026-10-24T10:40, took 968 s under load) and a control rebuild without it (facts/control: 9,610,110 rows, 44 s); Town Square hub and sub-pages rebuilt byrun_ts.shintosite-after(12 pages changed, hub has 1 manifest) [V]. - Eval, re-run by me read-only with
--local site-after --yaml cube-stage/.../communities.yaml[V]: 79.3 after; the savedreports/eval_before.mdsays 47.8 before. Data group 3.5 to 35.0 of 35. Place group 11.2 of 20 (9/19 sub-pages; this build's sub-page set is smaller than the first pass), Q6 0 (GA4 and lead script missing on the preview), 0 hero photos, no insights, 22 "could not confirm". The 79.3 is not saved inreports/(onlyeval_before.*); noeval_afterfile exists. - Not done: eval-after not saved, no loop iteration, no gate, no second community. The dry-run build for a new community stalled while Studio load was 158-204 with a second
build_cube.py(pid 31484) running [R]. Then the work switched to the registers (t305u/t306u). Studio load now is 6.96 and nobuild_cubeprocess is running [V 11:12]. - Nothing from slice one was copied to live.
- Done: staged
4.2 Artifact index (path, what it proves)
/workspace/ (box):
| Path | What it proves |
|---|---|
cube-spec.md |
Target design, decisions, phases, lint L1-L13 |
cube-proof.md, cubeproof/p1..p23.py |
What the cube gets right and wrong today, with re-runnable scripts |
registers/ (CSVs, ledger/, scripts/, validator_output.txt, registers-report.md) |
The three registers, rules, validator result, link rates |
non-cube-calcs.md |
Where calculations live outside the cube, manifest coverage |
insights-cube-map.md |
Insights page vs cube metrics |
ltv-rules.md, evidence/ltv/ |
LTV and fee rules with sources and unverified items |
ts-costing.md |
Paid vs free routes, prices, approval blockers |
eval-baseline.md, eval-baseline.json, eval/community_eval.py, eval/communities.yaml |
Eval rubric and scores for 7 communities |
town-square-staged/ (eval-before.md, eval-final.md, 11 PNG at 390px) |
Staged Town Square before/after |
bb-inventory.md, bbwork/live/ |
BB inventory, 118 live pages |
audit/studio/ |
Studio snapshot used by the non-cube audit |
Studio (ssh studio from MacPro), under ~/ClaudeWork/:
| Path | What it holds |
|---|---|
RealEstate/facts/ |
Live cube: facts.parquet, metrics.yaml, factlib.py, build_cube.py, communities.yaml, test_cube.py, lint_pages.py, nightly.sh, last_good/ |
RealEstate/areas/ |
community_eval.py, community_page.py, community_facts.py, place_guide.py, accept.py, io_release.sh, publish_area.sh, STATUS.md |
cube-stage/registers/ (out/, src/, scripts) |
Staged registers build |
cube-stage/RealEstate/facts/after, control |
Staged cube builds (after = with town-square, control = rebuild of live inputs) |
cube-stage/site-before, site-after, reports/, run_ts.sh |
Staged Town Square site and eval logs |
ts-stage/ |
Earlier Town Square staging (evalsite, referenced by run_ts.sh) |
5. Current state and known failures (numbers)
Cube [R unless marked]
- Shape: 9,600,087 rows, 18 columns, built 2026-10-23T00:55 [V]. 192 metric names (63 non-hub, 129 hub.*). metrics.yaml registers 25 plus derived yield_gross; 37 non-hub names unregistered. 20 slice types, 13 period types. Slices: 160 community, 25 site, 175 dld_area, 2,866 tower, 178 tower_group.
- Stale by 2 days. Nightly failed 21 Oct and 24 Oct, restored last_good.
- Not reproducible: two builds from identical inputs differ in 13,547 rows; 972 rows have NULL fact_id.
- 10,005 unsuppressed NULL-value rows; 423 NULL-n; 2,422 unsuppressed lr_* rows with n < 10.
- Bedroom rows are 9,922 sales short; 548 tower cells have ready + off-plan < all.
- Master assignment imputed with no marker (up to 95.6% in DIP Second).
- Key styles for sl: seven. 105 multi-slug combinations. Only 30 rbld slugs equal a tower slug.
- Three off-plan rules and three yield rules in live code.
Link rates
- Sales to community 87.9%; to tower 75.5% (did not reproduce: the comparable column gives 14.1% [R registers-report]); to site 61.6%. Villa sales to community 57.8%.
- Leases to rcm 99.6%; to a building 30.8%.
- PF buildings matched to DLD: proof says 3,508 of 4,159 (84.3%); registers run gives 3,039 of 4,141 (73.4%). Unexplained. PF counts quoted: 4,141 / 4,135 / 4,159 / 5,390.
- 297 DLD projects (15,121 sales) and 279 Ejari names (33,962 leases) were unmapped before the registers; after: 256 (14,391) and 169 (23,646).
- 2,332 PF pages have no DLD project; 1,327 have no community.
- 5 cube slices have no raw sales history (AL ARYAM, DMCC MASTER COMMUNITY, EMIRATES LIVING, EXPO CITY, GARDEN VIEW). 19 raw masters have no cube slice.
- Review queue (1,128): v2_weak 449, community_proposal_unaccepted 218, osm_shared 271, bb_page_no_pf 90, fuzzy 54, name_equal_community_unverified 34, name_equal_ambiguous 5, conflict_unresolved 7.
Pages
- Only 2,193 of 7,215 index pages (30.4%) carry cube tags.
- 15,890 of 17,359 buydubai.io pages have no manifest. lint_pages.py skips them. lint_rent.py has a 22-number whitelist.
- 5 thin pages live (section 4.1). Live now [V, curl 24 Oct]: discovery-gardens, dubai-silicon-oasis, jvt, town-square each 200 with "not shown yet" x1 and 0 cube facts; motor-city 200, 27 facts; /jumeirah-village-triangle/ 200 with 52 facts and "not shown yet" x1 (duplicate of jvt); /jumeirah-village-circle/ 404; BB 200 with 17 facts; JVC 200 with 27.
- Hybrid pages [R]: pages with some cube numbers and some hand-typed ones. Example: the JVC video section says 1,296 / 11,008 / +3.6% / "150 off-plan" while the cube says 1,273 / 10,988 / +3.5% / 121. 3,174 hand-typed tokens in 41 static files [R, unverified in detail].
- JVC insights page: no manifest, no fact tags [V].
- Eval pass line 85 is not wired to any release script [V]. Eval re-run on the box today for town-square gives 45.2 (baseline 44.7), so the live page is unchanged apart from tiny drift [V].
- Photos: none of the five live pages has a hero photo. Google paid APIs are switched off by a file on Studio.
- Registers exist only in staging; nothing reads them.
6. Open decisions for Anthony
- JVT slug:
/jvt/(page, eval slug, no cube key) vs/jumeirah-village-triangle/(cube key, 52 facts). Both are live. Which one is retired (301 to the other)? The eval hard-fails while both exist (H4). - LLM-derived project proposals: 13 accepted entries in
jev_project_mapcome from an LLM and are unreviewed. Accept, review by hand, or drop? - The five live thin pages: fix in place, or take down until they pass? Undecided. Taking down is an external, visible change and needs his go.
- Wire the eval gate into
areas/io_release.shandareas/publish_area.shso a fail blocks publish. Needs his go (it changes the release path). - Google: written OK to remove
GOOGLE_PAID_OFF, and a yes for each paid step (photos, traffic, guide patches). Free routes first is the default (costing report). - Photos for Town Square: his own images, Wikimedia (none for Town Square), or the reduced Google path after 1 Nov.
- Non-resident LTV default (50% suggested, bank policy) and whether to confirm AED 580 / AED 290 from a trustee receipt.
- Whether eval should show a checklist only (no total). The tool prints a score today.
- Pass-line calibration: run the eval on pages he approves and rejects.
- Whether the building-page bar (
accept.py) applies to every community or only to eligible ones (20 of 82 Town Square buildings can never pass sales >= 10).
7. Next steps, in order
"Approval" = needs Anthony's explicit yes before it starts. Owner is the agent that runs it unless noted.
| # | Step | Inputs | Done when | Verify | Approval |
|---|---|---|---|---|---|
| 1 | Read this file and re-run section 8 to confirm the baseline | this file, Studio access | every [R] claim you rely on is re-checked or relabelled | section 8 | No |
| 2 | Get answers to section 6 items 1-4 | Anthony | written answers in the chat | n/a | Yes (his decisions) |
| 3 | Fix cube reproducibility: find why two builds differ, fix NULL fact_id, suppress NULL-value and n < 10 lr_* rows |
build_cube.py, cube-proof.md p-scripts |
two builds give identical hashes; 0 NULL fact_id; 0 unsuppressed NULL value |
build twice, compare cube_hash and the section 8 queries |
Yes (changes the live cube build) |
| 4 | Fix the nightly: make cube tests and number lint pass, then refresh the cube | facts/nightly.sh, test_cube.py, lint_pages.py |
nightly passes; cube_meta.built less than 1 day old |
nightly log; cube_meta.json |
Yes |
| 5 | Register all 192 metric names in metrics.yaml and make a page without a manifest fail lint (L1, L10) |
metrics.csv, spec section 8 |
validator check 1 passes against the live yaml; lint fails a page with no manifest | validator; lint_pages.py on a manifest-less page |
Yes (changes live lint) |
| 6 | Move registers from staging into the live repo and make build_cube.py read them (communities first) |
cube-stage/registers/out |
communities.yaml generated from communities.csv; validator passes in place |
validator run in facts/ |
Yes |
| 7 | Resolve the register gaps: 6 unresolved page dirs; review queue by priority; explain 73.4% vs 84.3%; show imputed master assignment | review_queue.csv, page_dir_unresolved.csv |
unresolved dirs = 0 or each has a written reason; the baseline difference explained | validator; queue counts | Review decisions need Anthony |
| 8 | Write the JVC section-by-section checklist (P1-P19 style, but from the JVC page itself) and save it as the gate document | live JVC, bb-inventory.md |
file exists and lists each JVC section, table, image and component | file present; eval C1/C3 | No |
| 9 | Free Town Square gaps, still in staging: schools table with KHDA; FAQ from the cube (needs the place_guide.py checker to accept 20); cut or re-source the 22 "could not confirm"; GA4/lead scripts; traffic page from stored free-flow times; developer, year and words templates |
ts-costing.md section 7, run_ts.sh |
eval items C3, C4, Q1, Q6, P1 traffic show PRESENT | community_eval.py --local <site-after> |
No (staging, USD 0) |
| 10 | Insights for Town Square from the cube: build insights_cube.py and insights_page.py, fact-tagged and with a manifest |
insights-cube-map.md |
/insights/ staged, all numbers carry data-fact-id, lint passes |
grep data-fact-id; lint |
No (staging) |
| 11 | Photos for Town Square | Anthony's images, or Google reduced path | 3 hero photos with alt text | eval C6; pictures.json |
Yes (own images, or Google OK + Rule 17 spend gate) |
| 12 | Wire the eval gate (exit 1 on fail) into io_release.sh and publish_area.sh |
section 6 item 4 | a deliberate fail blocks publish in a dry run | run both scripts on a failing slug in a sandbox | Yes |
| 13 | Go live with Town Square only after P1-P19 are PRESENT and the JVC checklist is done | all above | checklist saved; Anthony reviewed | section 8 curl and eval on the live URL | Yes (publish) |
| 14 | Repeat per community, in this order: Discovery Gardens, Dubai Silicon Oasis, JVT/JVT slug, Motor City | step 13 pattern | each passes the checklist | per-community | Yes for each publish |
| 15 | Decide the five live pages (fix or take down) | section 6 item 3 | decision recorded and done | curl | Yes |
| 16 | Constants registry (LTV caps, DLD fees) and rule-driven LTV in the calculator | ltv-rules.md section 5 |
ltv_cap(price, nationality, first_or_second, off_plan) tested against the table |
unit test vs the 3.2 table | Yes (code change in live tools) |
| 17 | Remaining spec phases P2-P11: period pages from the cube, agents and developers pages, owner-gain at 10+ pairs, value-a-unit tool, client-side tools, manifests for the 15,890 pages | cube-spec.md section 8.3 |
each phase's own done-test | per spec | Yes per phase |
Spend: none planned. Any Google, Claude API or image call needs a written yes and the Rule 17 spend gate (cap $10 per community, $25 per day).
8. Verification checklist for another agent
Access: the box can run curl and community_eval.py. Studio is reached from the MacPro: ssh studio (connects as anthonybooth; fallback ssh [email protected]). The Studio entry is not shown as a connected machine in the app, so use the MacPro shell. Read-only commands only.
8.1 Live pages (box, no login)
for s in discovery-gardens dubai-silicon-oasis jvt jumeirah-village-triangle town-square motor-city jvc business-bay jumeirah-village-circle; do
printf "%s " $s; curl -s -o /tmp/p.html -w "%{http_code} %{size_download} " https://buydubai.io/$s/
printf "notshown=%s factids=%s\n" $(grep -c "not shown yet" /tmp/p.html) $(grep -o 'data-fact-id' /tmp/p.html | wc -l); done
curl -s -o /dev/null -w "%{http_code}\n" https://buydubai.io/town-square/insights/ # expect 404 today
curl -s https://buydubai.io/jvc/insights/ | grep -c 'facts-manifest' # expect 0 today
Expected on 24 Oct 2026: 200 with notshown=1 and factids=0 for discovery-gardens, dubai-silicon-oasis, jvt, town-square; motor-city 200, 0, 27; jumeirah-village-triangle 200, 1, 52; jvc 200, 0, 27; business-bay 200, 0, 17; jumeirah-village-circle 404. [V]
8.2 Eval
cd /workspace/eval && python3 community_eval.py town-square # live; 45.2 on 24 Oct (baseline 44.7)
python3 community_eval.py business-bay jvc # expect PASS, PASS
# on Studio: cd ~/ClaudeWork/cube-stage/RealEstate/areas && /usr/bin/python3 community_eval.py --local ~/ClaudeWork/cube-stage/site-after --yaml ../facts/communities.yaml town-square # 79.3 on 24 Oct
python3 community_eval.py --rubric # prints the rubric
Exit code 1 when the total is under 85, any group under 60%, or any hard fail H1-H4. Read the Item table, not the total.
Check it is not wired: on Studio cd ~/ClaudeWork/RealEstate && grep -l community_eval areas/*.sh pilot_bb/*.sh facts/*.sh returns nothing [V].
8.3 Cube (Studio, DuckDB; use /opt/homebrew/bin/python3.12)
cd ~/ClaudeWork/RealEstate/facts
cat cube_meta.json # built, rows 9,600,087, sales_as_of 2026-10-22
python3 -c "import duckdb;k=duckdb.connect();k.execute(\"create view F as select * from read_parquet('facts.parquet')\");
print(k.execute('select count(*) from F').fetchall());
print(k.execute('select count(distinct metric) from F').fetchall()); # 192
print(k.execute('select count(*) from F where fact_id is null').fetchall()) # 972 expected"
Further queries and the 23 proof scripts: /workspace/cubeproof/p1.py to p23.py (they read ~/ClaudeWork/RealEstate/... so run them on Studio). Each script's header query maps to a line in cube-proof.md. The live cube may be newer than the proof's by now, so small differences in counts are expected; large ones are a finding.
Reproducibility: run build_cube.py twice to a temp dir with identical inputs and compare row hashes. Do not run it on Studio while another build is running (load 158-204 stalled an earlier test). Use the staged copies in cube-stage/RealEstate/facts/after and control as examples.
8.4 Registers (Studio)
cd ~/ClaudeWork/cube-stage/registers && /opt/homebrew/bin/python3.12 validate_registers.py
Expected: row counts 192 / 186 / 431 / 6,166 / 16,401 / 1,128 / 105 / 48 / 6, every check PASS, one GAP (6 unresolved dirs). Saved output: /workspace/registers/validator_output.txt. To confirm the validator is not rigged, read validate_registers.py (16 KB) and regcommon.py.
Spot checks: pick 20 random rows of building_alias.csv with match type fuzzy or v2_weak and open the source; confirm none were auto-accepted (rule R4).
8.5 Lint and release scripts (Studio)
cd ~/ClaudeWork/RealEstate
grep -n "if not m" facts/lint_pages.py # the skip for manifest-less pages
git log --oneline -3; git status --short # autosave commits touch areas/STATUS.md
8.6 Spend and approvals (Studio)
ls ~/ClaudeWork/RealEstate/GOOGLE_PAID_OFF && cat ~/ClaudeWork/RealEstate/GOOGLE_PAID_OFF
ls ~/ClaudeWork/RealEstate/areas/approvals/ # only downtown-dubai.json (expired) per the costing report
8.7 Unverified items (do not rely on until checked)
- The 10 decisions and the five-page history rest on the parent chat, not on files.
- The 75.5% sales-to-tower and 84.3% PF-with-project baselines do not reproduce. The PF page count differs across sources (4,141 / 4,135 / 4,159 / 5,390).
- Studio HEAD (01ce8f8d at audit time, now different) vs the MacPro live checkout: not compared.
dev_metrics.jsonproducer;signed_rent.pyfact-id trace;search.js,alert.js, abudhabi, northern, payplan pages: not examined.- The 3,174 hand-typed tokens in 41 static files: counted by the audit, not spot-checked.
- Lint blind spots beyond the manifest skip.
accept.pyresult for staged Town Square (0 of 82 fully; per-test counts) is from the costing report, not re-run.claude -pwith web search as a free guide route; local Ollama quality; self-hosted OSRM: untested.- Wikimedia file licences per file; Google photo terms; Serper and fal.ai prices; TomTom/HERE free tiers: not checked.
- AED 580, AED 290, off-plan trustee fee, VAT 5% from the tax authority, any 2026 Central Bank circular: unverified.
- Whether
community_eval.py's traffic page test checks content or only presence. - Enforcement of the pipeline
reviewstatus (G4). - The "5 hero key figures" in BB vs 4 counted by the eval.
- The Studio
docs/folder did not exist (so this file was not copied there; see section 9).
9. Environment map
Machines
- Box (Linux, /workspace): holds all reports above, runs curl and community_eval.py. No Studio data, no cube parquet.
- MacPro (registered machine 0122eb1a..., label MacPro): has its own ~/ClaudeWork/RealEstate checkout (the audit notes it may differ from Studio's; not compared [?]) and is the jump host to Studio. No timeout command on MacPro; use ssh -o ConnectTimeout=8 or a Python timeout instead.
- Studio (hostname studio, user anthonybooth): ssh studio from MacPro; fallback ssh [email protected]. Holds the cube, builds and staging. Registered in the app as "studio" but shown disconnected, so use the MacPro shell. ~/ClaudeWork/cube-stage is 7.0 GB [V]. Uptime 8 days; load was 158-204 during the concurrent builds, 6.96 on 24 Oct 11:12.
- MacMini: connected; not used here.
Repos and paths
- Live repo: Studio ~/ClaudeWork/RealEstate, git tracked, with autosave commits ("[autosave studio] ...", changes to areas/STATUS.md). Tracked: areas/community_eval.py, facts/communities.yaml. Gitignored: facts/facts.parquet (.gitignore line 3 is * with exceptions) and most generated output, so edits to ignored files are not in git history and cannot be reverted with git. During this work gitignored files were edited in staging (cube builds, site output); the live communities.yaml was not changed [V].
- Staging (outside any git repo, confirmed not a git repository): Studio ~/ClaudeWork/cube-stage/. All new work goes here.
- Python: use /opt/homebrew/bin/python3.12 for builds (the staging script does); /usr/bin/python3 is used for some prune steps.
- Scripts: facts/build_cube.py, facts/nightly.sh, facts/test_cube.py, facts/lint_pages.py, facts/apply_communities.py, areas/community_page.py, areas/area_build.py, areas/publish_area.sh, areas/io_release.sh, areas/accept.py, areas/place_guide.py, areas/spend_gate.py, pilot_bb/*.
- Live sites: buydubai.io and rentdubai.io. Pipeline status per community is in areas/STATUS.md (the "review" state). Preview host for staged builds: buydubai-preview.vercel.app (the staged eval canonical points there).
Caveats
- Do not start a second build_cube.py while one runs; Studio load can stall everything. Check ps aux | grep build_cube first.
- Timings: a full cube build took about 44 s unloaded and about 968 s under load [V from build.log].
- No secrets: do not read credentials.md, .env files or key files. Do not log in anywhere. This document contains none.
- Keep GOOGLE_PAID_OFF in place unless Anthony's written OK says otherwise.
- Copy locations of this file: /workspace/PROJECT-buydubai-cube-and-community-pages.md and Studio ~/ClaudeWork/cube-stage/PROJECT-buydubai-cube-and-community-pages.md. It was not copied to ~/ClaudeWork/RealEstate/docs/ because that folder does not exist there (and the repo's .gitignore would let a new docs/*.md be tracked by the autosave, which would put it inside the live repo).
10. 2026-10-24 Execution Synthesis, Gate Proofs & INVRT Handoff Integration
This section records what was PROVEN ([V]), AGREED, and DECIDED across the 24 October 2026 10-turn audit and staging execution on Studio (~/ClaudeWork/cube-stage/ and ~/grok-mail/2026-10-24-1128-cube-dalia-check/), and integrates Mark Gullickson's ("Gully" / INVRT) 24 October 2026 delivery (BuyDubai-Dalia-Implementation-Handoff.pdf, Technical-Contracts.md, Build-Stories.md).
10.1 Anthony's Decisions Recorded (24 Oct 2026)
- Correction (
11:34 GST): "No cube was used for the current site build." Legacy builders (pilot_bb,areas,rentdubai) calculated their own numbers directly from raw files; post-hoc scripts (apply_communities.py,stage_500) stampeddata-fact-idonto2,193of13,029HTML files after the fact. Every builder must consumefactlib.pyat build time. - §6.1 JVT Slug (
11:33 GST&12:56 GST,09-grok.md): "1. the long slug, 301" / "2 NO IDEA YOU DECIDE" — Canonical community slug is/jumeirah-village-triangle/;/jvt/*(8URLs) receives a301redirect to/jumeirah-village-triangle/inregisters/out/takedown_redirects.csv. - §6.2 13 LLM Project Proposals (
11:33 GST): "2. drop and redo" — Replaced LLM-guessedjev_project_mapentries with the deterministicpf_seg1 + all-time dld_areasrule ([V]verified by Grok in04-grok.md&06-grok.md). - §6.3 5 Live Thin Pages & 3 Retired Folders (
11:33 GST,12:18 GST&12:56 GST,09-grok.md): "3. take down" / "folders retire" / "1 You DECIDE": - 3 Retired DLD-area-only folders (
al-barsha,al-jaddaf,ras-al-khor): Retired inregisters/out/page_dir_retired.csv(3rows[V],08-grok.md), removed from sitemap, and served410 Gone(12URLs inregisters/out/takedown_redirects.csv). - 3 Master-project folders (
dubai-production-city -> CM0090,dubai-silicon-oasis -> CM0150,palm-jebel-ali -> CM0143): Mapped inregisters/out/page_dir_map.csv(0rows left inpage_dir_unresolved.csv[V],08-grok.md). - 5 Community folders (
town-square,discovery-gardens,dubai-silicon-oasis,jvt,motor-city):/town-square/(14pages) now passes both gates (91.9/100eval +0lint problems[V],09-grok-gate.md) and is approved to GO LIVE (12:56 GST,09-grok.md)./jvt/*(8URLs) receives301$\rightarrow$/jumeirah-village-triangle/./motor-city/stays live (200 OK) until its full rebuild is ready because00-job.mdverified live/motor-city/hasnotshown=0, factids=27(not a broken thin page;10-grok.md§2).discovery-gardensanddubai-silicon-oasis(18URLs) receive410 Gone+ sitemap removal until rebuilt and gate-passed (registers/out/takedown_redirects.csv,38total rows:12retired410,8/jvt/301,18thin410).
- §6.4 Release Gate (
11:33 GST&12:56 GST,09-grok.md): "4. yes" / "3 GO LIVE" — Wirecommunity_eval.py >= 85(every group>= 60%) and strictlint_pages.py(exit 0over the whole community folder) intoareas/io_release.shandareas/publish_area.sh. Live pre-publish snapshot of all 8 affected live folders saved at~/ClaudeWork/cube-stage/versions/2026-10-24-live-pre-publish/([V],10-grok.md§2). - §6.5 & §6.6 Photos & Test Builds (
12:18 GST&12:56 GST,09-grok.md,10-grok.md): "Spend $10 max on photos. Delete test builds." / "4 YES": - Deleted
/tmp/cube_turn5_1..3and/tmp/cube_turn6_1..2(~377 MBfreed[V],08-grok.md). - Approved up to USD 10.00 total on Google Places photo spend via
~/ClaudeWork/cube-stage/RealEstate/pilot_bb/hero_photos_reduced.py(HARD_MAX_USD = 2.00runtime hard stop; gatedPASSby Grok in10-grok.md§1). - Billing & Free-Tier Verification (Fixes A–E from
09-grok-gate.md§2 &10-grok.md§1):- Verified against Google Places API (New) data-fields table (updated 2026-10-21,
10-grok.md§1.E):Text Search (IDs Only)(places.id) is$0.00;Place DetailswithX-Goog-FieldMask: photosis billed under Place Details Essentials (IDs Only) ($0.00for the photo resource references), whilePlace Photo Media(maxWidthPx=1200&skipHttpRedirect=true) is Places Photo SKU ($7/1,000 = $0.007/call). - Real list cost is
3 * $0.007 = $0.021/building($0.105per 5-building community;~$0.525list for all 25 buildings across 5 communities;hero_photos_reduced.pykeeps the conservative$2.00runtime hard stop). - Checked October local usage proxy across
pilot_*/state/spend.json(1,460Details calls and11,358Photo calls in Oct 2026; Google Cloud Console check optional before full 5-community run). hero_photos_reduced.pyrequires--approved-cap-usd10to temporarily lift only the stage~/ClaudeWork/cube-stage/RealEstate/GOOGLE_PAID_OFFlock (restoring it infinally), saveshero_photos.jsonatomically after each building, logs redacted HTTP failures tophoto_spend_log.jsonl, downloads.jpgbytes locally toareas/<slug>/state/hero_photos/<bslug>_<idx>.jpg(so expiringphotoUriURLs never break pages), and storesauthorAttributions(Photo: <displayName>, Google Maps).
- Verified against Google Places API (New) data-fields table (updated 2026-10-21,
- Execution Role Split (
13:20 GST): "I wouldn’t use Grok for coding.. audit code yes... although the plan I have given it, it should be able to execute that.. I would trust GLM 5.3 Flash with that plan. Then use a frontier to audit/steer." - Builder (
GLM 5.3 Flash/ Claude): Executes bounded stories (S00–S21) and community rebuilds. - Independent Frontier Auditor (
Grok): Never writes application code; audits diffs, runs deterministic test/eval/lint gates (F01–F36), and controls promotion tointegratedandlive.
10.2 What Was Built & Independently Verified by Grok ([V]) Today in Staging (USD 0.00 Spend)
Note on status terminology: In Gully's 6-rung status ladder (source-present $\rightarrow$ implemented/wired $\rightarrow$ locally tested $\rightarrow$ independently reviewed $\rightarrow$ integrated $\rightarrow$ live), [V] below means independently reviewed in staging (~/ClaudeWork/cube-stage/), not integrated into live production or live until published and verified on buydubai.io.
- Master Registers (
~/ClaudeWork/cube-stage/registers/) —PASS [V](Verified by Grok in04-grok.mdand08-grok.md): validate_registers.pyoutputsRESULT: ALL CHECKS PASS(0gaps,page_dir_unresolved.csv = 0 rows,page_dir_retired.csv = 3 rows,page_dir_map.csv = 61 rows[V],08-grok.md).verify_registry_formulas.pyoutputsALL 27 CHECKS PASSacross all55cube metric families (192(metric, slice_type)pairs[V],04-grok.md).- Pre-Turn 8 snapshot preserved at
~/ClaudeWork/cube-stage/registers/versions/2026-10-24-pre-turn8/. - Deterministic Facts Cube (
~/ClaudeWork/cube-stage/RealEstate/facts/facts.parquet+cube_meta.json) —PASS [V](Verified by Grok in06-grok.md): - Fixed all 6 cube defects (
LRbeds IS NULLcausing972NULLfact_idrows,lr_*n < 10suppression,value IS NULLsuppression on10,005rows, deterministicarg_maxLOWreason in Coverage7b,round(value, 9),sum(annual::decimal(18,2))::double moneyinRC, deterministicorder by slinrent_hubs.py, and deterministicJPMfromregisters/out/building_alias.csv+buildings.csv+communities.csvwith loud assertion). - Verified by Grok (
06-grok.md) across independent back-to-back builds:SELECT * FROM b1 EXCEPT ALL SELECT * FROM b2 = 0 rowsin both directions[V](06-grok.md)- Rows:
9,645,491(1,009,660suppressed)[V](06-grok.md) fact_id IS NULL:0[V]| duplicatefact_id:0[V]| unsuppressedvalue IS NULL:0[V]| unsuppressedlr_*withn < 10:0[V](06-grok.md)cube_hash:74bc4c527cb8716a6d41bda00b7a7e64da4b0cc1703b2b1b37179ad56e65b3a6(sha256 54e3870d585b18c9...)[V](06-grok.md)
- Staged Town Square Pilot (
~/ClaudeWork/cube-stage/site-after/town-square/) —PASS [V](Verified by Grok in09-grok-gate.md,09g_eval.txt,09g_lint_all.txt): - Rebuilt
/town-square/hub + all13sub-pages (insights,traffic,yield,budget,towers,parking,collections,studio-apartments,1-bedroom-apartments,2-bedroom-apartments,3-bedroom-apartments,3-bedroom-villas,4-bedroom-villas) against the Turn 6 cube (cube_hash 74bc4c52...). community_eval.py town-square --local ~/ClaudeWork/cube-stage/site-after:91.9 / 100 PASS [V](Data 35.0/35,Content 26.0/30,Place 15.9/20,Quality 15.0/15,41internal links checked /0broken, canonicalhttps://buydubai.io/town-square/,11/11sub-pages in sitemap,15/15JVC hub sections present including#videos,#videos-say,#due-diligence; verified in09-grok-gate.md&09g_eval.txt).lint_pages.py ~/ClaudeWork/cube-stage/site-after ~/ClaudeWork/cube-stage/RealEstate/facts/cube_meta.json:0problems across all 14 pages in/town-square/**[V](14/14pages carry a validfacts-manifestwithcube_hash 74bc4c52...; verified in09-grok-gate.md&09g_lint_all.txt).
10.3 Integration with Mark Gullickson's ("Gully" / INVRT) 24 October 2026 Implementation Handoff
Gully's 24 Oct delivery (BuyDubai-Dalia-Implementation-Handoff.pdf, Technical-Contracts.md, Build-Stories.md, copied to ~/ClaudeWork/cube-stage/) defines 9 milestones (M1–M9), 22 finite stories (S00–S21), 36 acceptance fixtures (F01–F36), and a 6-rung status ladder:
1. source-present $\rightarrow$ 2. implemented/wired $\rightarrow$ 3. locally tested $\rightarrow$ 4. independently reviewed (staging [V]) $\rightarrow$ 5. integrated $\rightarrow$ 6. live.
| Gully Milestone & Stories | Core Requirement (Technical-Contracts.md / Build-Stories.md) |
BuyDubai Cube & Register Seam | Current Rung on Gully's 6-Rung Status Ladder |
|---|---|---|---|
M1. Pilot trace (S00, F06, F35) |
Strictly read-only trace of Peninsula Three (156 site sales vs 35 DLD sales in 12m; catalogue "Completed" vs DLD "ACTIVE" at 93.1%); capture complete SQL for 7 truncated functions (onto_c.tower_card, rag_retag*, rag_search*, hybrid_search) and mart.tower_12m. |
registers/out/buildings.csv (BL001215) + facts.parquet (tower=business-bay-peninsula-peninsula-three) + mart.sql + 003*_rag*.sql read-only baseline captured (see §10.5). |
4. independently reviewed (staging [V]) for cube/registers (04-grok.md, 06-grok.md); 3. locally tested for read-only S00 SQL/Peninsula Three trace. |
M2. Identity crosswalk (S02, F01–F03) |
Keep onto_g (17 entity types, 20 directed relations); reconcile onto_c via a reversible crosswalk without TRUNCATE rebuilds (003_build.sql). |
registers/out/communities.csv (CM*), buildings.csv (BL*), building_alias.csv, page_dir_map.csv, page_dir_retired.csv provide the deterministic crosswalk. |
4. independently reviewed (staging [V]) for CSV registers (04-grok.md, 08-grok.md); 2. implemented/wired for Postgres onto_g/onto_c table join (S02 next). |
M3. Metric contract (S04–S05, F07–F11, F33) |
Versioned MetricDefinition + MetricObservation (entity_id, metric_id, definition_version, cohort_id, period_start, period_end_exclusive, dataset_revision, calculation_revision). Register the 9 onto_g.metric keys alongside the 55 cube metrics. |
registers/out/metrics.csv (55 metrics, 27/27 formula checks PASS) + cube_meta.json (cube_hash 74bc4c52...). Extend with the 9 onto_g.metric keys in S04. |
4. independently reviewed (staging [V]) for the 55 cube metrics (04-grok.md, 06-grok.md); 1. source-present for merging the 9 onto_g.metric keys into the same contract (S04–S05). |
M4. Evidence lifecycle (S01, S03, S10–S12, F04–F05, F20–F26) |
Preserve corpus_doc.doc_id; add doc_revision_id, evidence_id span locators, assertion_id four-state (present, absent, unknown, disputed), and prove 1 controlled correction + replay/lag/revocation end-to-end. |
Supabase corpus_source / corpus_doc + onto_g.doc_link + change outbox & per-projection revision acknowledgement. |
1. source-present (corpus_doc, rag_chunk, onto_g.doc_link exist; revision/outbox tables in S01, S03, S10–S12 to be built). |
M5. Shared read tools & Semantic Number Guard (S08–S09, S14, F10, F17–F19) |
Keep /api/dalia and its 10 tools; wire the 6 logical responsibilities (resolve_entities, get_entity_context, get_metrics, search_evidence, find_properties, propose_correction). Filter RAG by authorised doc_ids before top-k ranking (F17). Bind Number Guard to (entity, metric, unit, cohort, period) (F10). |
areas/io_api/dalia.js + corpus/rag/rag_svc.py + factlib.py / onto_g functions. |
2. implemented/wired (regex number guard & 10 tools exist in dalia.js; semantic (entity, metric, cohort, period) binding and pre-top-k doc_id filter in S08–S09, S14 next). |
M6. Property query (S06–S07, F12–F15) |
Parse natural-language brief into PropertyBrief (OR within areas/bedrooms, AND across must-haves); separate verified available inventory from historical DLD research candidates; never infer unit balcony from building marketing or swap Marina/JLT to BB/JVC (coverage_missing). |
shortlist / community tools backed by PropertyBrief partition. |
1. source-present (shortlist tool and pf_units / DLD tables exist; PropertyBrief partition and coverage_missing guard in S06–S07 next). |
M7. Readable views & website parity (S13, S19, F30) |
Builders (build_io.py, units_io.py, community_facts.py) and Dalia consume the same accepted revision (PublishedEntityDTO / facts.parquet). Use ApartmentComplex for towers, Apartment for verified units (numberOfBedrooms), no Offer from historical medians. |
/town-square/ (14/14 pages) passes lint_pages.py (0 errors) and community_eval.py (91.9/100 PASS). |
4. independently reviewed (staging [V]) for /town-square/ static pages + manifest (09-grok-gate.md); reaches 5. integrated when /api/dalia reads the same revision (S13), and 6. live after Turn 10 publish + live check. |
M8–M9. Paul agent mode, channels & measured rollout (S15–S18, S20–S21, F27–F32, F36) |
Paul mode first (per-client isolation, spend cap instead of 20-question consumer cap, no lead-capture handover); shadow $\rightarrow$ Paul pilot $\rightarrow$ public Dalia $\rightarrow$ wider site with rollback tested. | Builder (GLM 5.3 Flash / Claude) executes slices S00–S21; Frontier Auditor (Grok) gates each slice against F01–F36. |
1. source-present (public consumer mode & Lampline WhatsApp bridge exist; PaulModePolicy + brief export in S15–S18 next). |
10.4 Mapping of Build-Stories.md §6 Unresolved Implementation Choices (Owners & Current Status)
Every unresolved implementation choice listed in Build-Stories.md §6 now has an explicit owner, architectural decision, and current status:
| # | Build-Stories.md §6 Open Item |
Owner | Resolution & Current Status |
|---|---|---|---|
| 1 | Complete mart formulas & 7 truncated SQL bodies (mart.tower_12m, onto_c.tower_card, rag_retag, rag_retag_docs, rag_search, rag_search2, rag_search_v2, local hybrid_search) |
Builder (S00) / Grok Gate |
Resolved read-only today (§10.5): mart.tower_12m full DDL captured in RealEstate/warehouse/mart/mart.sql:166-195; rag_search (003_rag.sql:60-115), rag_search2 + wrapper (003c_alt_rag_search2.sql:25-113), and rag_retag_docs + rag_search_v2 (003c_rag_fixes.sql:19-115) captured from Studio repo. Remaining live DB function definitions (onto_c.tower_card, rag_retag, local hybrid_search) are exported via read-only pg_get_functiondef(oid) in S00. |
| 2 | Chosen RAG variant & production topology (Studio vs Supabase dubai-data Small 2 GB compute) |
Anthony / Builder (S00, S14) |
Key finding: Local rag-svc on Studio (bge-m3 fp16 MPS + bge-reranker-v2-m3) runs in ~80–250 ms, whereas Supabase dubai-data runs on Small compute (2 GB RAM) where rag_search / rag_search2 cold/warm vector+keyword scans take 1.4–2.9s (e2_explain.py). For S14 (F17), we consolidate on a single canonical SQL function (rag_search_canonical) that filters by authorised doc_ids (onto_g.doc_link / entity scope) BEFORE top-k vector/keyword ranking, cutting both cross-entity leakage and Supabase Small-compute latency; if Supabase remains the production DB for Vercel /api/dalia, M5 latency thresholds must either budget for Supabase Small compute (p95 <= 2.5s retrieval) or upgrade Supabase compute / route through pre-filtered doc_id candidate sets. |
| 3 | Authenticated client/session mapping (consumer_public vs paul_agent + client_id isolation) |
Builder (S15) / Gully |
Use server-verified session/auth header (x-dalia-mode, authenticated Paul workspace session + explicit client_id parameter) in /api/dalia; never trust client-supplied mode flags without server token verification (F27, F28). |
| 4 | Source rights, robots & retention policy (corpus_source) |
Anthony / Builder (S01) |
Tier A/B official public records (DLD, RERA, Ejari, Dubai Municipality, KHDA) and first-party BuyDubai guides are public_citable; third-party portals/Broker videos carry explicit rights_class in corpus_source (internal_assist_only vs citable_with_attribution). Google Places photos require local caching + Photo: <authorDisplayName>, Google Maps credit (09-grok-gate.md Fix E). |
| 5 | Current buydubai_dalia_counts RPC & per-mode spend config |
Builder (S15, S20) |
Keep buydubai_dalia_counts (20-question cap + lead-capture prompt) strictly for consumer_public (F29); bypass the 20-question cap in paul_agent mode and enforce a daily/monthly USD token-spend budget logged per turn (F27, F36). |
| 6 | Unit/layout/listing coverage & freshness (verified_available_inventory vs historical_research_candidates) |
Builder (S03, S06) |
Current local data holds DLD transactions, Ejari leases, and pf_units (4,135–5,390 building/unit records), NOT live real-time MLS offer feeds. Therefore find_properties (S06–S07) returns verified_available_inventory = [] unless backed by a fresh (<= 7d) verified listing feed, and places DLD/Ejari/cube matches in historical_research_candidates with explicit provenance (F12–F15). |
| 7 | Paul's first channel (Web brief console vs Lampline WhatsApp bridge) | Anthony / Gully (S16–S18) |
Web brief console + shareable client brief (S16, F31) first, reusing /api/dalia in paul_agent mode; Lampline WhatsApp bridge (S17) is wired second as a thin transport adapter over the exact same /api/dalia orchestration contract (F32). |
| 8 | Test runners & quality/performance thresholds | Builder + Grok Gate (S00–S21) |
Deterministic runners already active on Studio: validate_registers.py (0 gaps), verify_registry_formulas.py (27/27), test_cube.py, community_eval.py (>= 85/100, each group >= 60%), and lint_pages.py (0 errors against cube_meta.json). We add tests/dalia_fixtures_runner.py in S00–S21 to execute Gully's F01–F36 contract fixtures. |
10.5 Strict Read-Only Rule & Verified S00 / M1 Baseline Findings (Peninsula Three & 7 SQL Functions)
- Strict Read-Only Rule (
09-grok-gate.md§3.5): Both the Peninsula Three156 vs 35root-cause trace (M1) and the 7 SQL function exports (S00) are strictly read-only (SELECT,pg_get_functiondef,pg_get_viewdef, and local SQL file reads only — zero database writes, zeroTRUNCATE, zero schema mutations inS00). - Read-Only Peninsula Three Root-Cause Trace (
M1/F06/F35) — Proven Today on Studio: - Canonical Identity in Master Register (
registers/out/buildings.csv):building_id = BL001215,name = Peninsula Three,pf_slug = business-bay-peninsula-peninsula-three,community_id = CM0020(business-bay),dld_area = BUSINESS BAY,dld_project = PENINSULA THREE,dld_building = PENINSULA THREE,n_sales_2025_homes = 150,n_leases_2025_26 = 449.
- Why the Website Showed
156(now158TTM in Turn 6 Cube) vs35inonto_c.tower_card:- In
facts.parquet(cube_hash 74bc4c52...),deals|tower=business-bay-peninsula-peninsula-three|sub=homes|st=all|beds=all|ttm=ttmis158total TTM residential sales (137st=ready+21st=offplan, volume AED283,314,360;156at the slightly earlier static site build snapshot;132in calendar year2025allst=offplan, followed by50in2026-Q1[49ready],49in2026-Q2[47ready], and38in2026-Q3[37ready]). - By contrast,
onto_c.tower_market(35sales in 12 months,ONTOLOGY.md§"The 10 test questions" Q2) queried an un-reconciled Supabase slice before the2026dld_liveready-transfer rows andpf_unitsbuilding crosswalk were joined. - Resolution (
S04–S05,S13): Both/api/daliaand the static website builders must read from the same canonicalMetricObservation/facts.parquet(BL001215/business-bay-peninsula-peninsula-three) with explicit cohort (st=all158vsst=ready137vsst=offplan21) andperiod(ttm).
- In
- Why Catalogue Status is
"Completed"vs DLD Register"ACTIVE"(93.1%):- All
132sales in2025werest=offplan. In2026-Q1..Q3,133of137sales arest=ready(title-deed transfers) and449Ejari leases are active (ads|rbld=business-bay/peninsula-three = 136), proving physical handover occurred in early 2026. - However, DLD's
dld_projectsconstruction escrow register lags physical handover and still recordsPENINSULA THREEas"ACTIVE"at93.1%. - Resolution (
S02,F03):CanonicalProjectRecordcarries bothcatalogue_status = "Completed"(proven by137ready sales +449Ejari leases) anddld_register_status = "ACTIVE"(dld_completion_pct = 93.1) with an explicitreconciliation_notesexplanation instead of silently overwriting either source.
- All
- Read-Only Inventory of Complete SQL Definitions Located on Studio (
S00): mart.tower_12m: Complete untruncated materialized view DDL located in~/ClaudeWork/RealEstate/warehouse/mart/mart.sql(lines166–195).rag_search(hard-filter v1): Complete DDL in~/ClaudeWork/RealEstate/corpus/sql/003_rag.sql(lines60–115).rag_search2(RRFk=60+ priors, soft community/building boost) +rag_searchwrapper: Complete DDL in~/ClaudeWork/RealEstate/corpus/sql/003c_alt_rag_search2.sql(lines25–113).rag_retag_docs+rag_search_v2: Complete DDL in~/ClaudeWork/RealEstate/corpus/sql/003c_rag_fixes.sql(lines19–115).onto_g.resolve,onto_g.neighbors,onto_g.card,onto_g.near,onto_g.near_entity,onto_g.docs,onto_g.metrics: Complete DDL in~/ClaudeWork/RealEstate/ontology/grok/sql/005_tools.sql(lines1–65).- Remaining in-DB functions (
onto_c.tower_card,rag_retag, localhybrid_search): Exported read-only viaSELECT pg_get_functiondef(oid)inS00before anyM2/M5migration.