Evaluated in order. Each depends on the one before it. Don't skip ahead.
This file is strategy only.
Tactical work lives in PILLAR_<n>_PLAN.md files.
Operational issues and ticket-level execution live in Clubhouse.
Can the system find the valuer's comps at all? Do they appear anywhere in the candidate pool?
docs/CURRENT_PILLAR.md for the current phase status and metrics.Do the valuer's comps land in the top 20 scored candidates?
Same input = same output every time. Deterministic scoring required.
Final price estimate within ±10% of the valuer's adopted value.
These rules govern how the pipeline operates. Every agent reads this file every session. No exceptions, no drift.
HARD RULE — FIXED RADIUS. NO EXCEPTIONS.
Search uses ONE fixed radius per run. No radius expansion, no iterative widening, no
nextRadiusStepacross radii.Rationale: Valuer Dan expands radius iteratively, but only because he hand-reviews each expansion pass with a fine-tooth comb. We can't replicate that yet. So we pick a radius sized to capture ~95% of likely comps for the subject type and stick with it.
Paginating within a single radius is allowed. Widening to the next step is prohibited.
Type-code translation (canonical → source-native codes like
HOUSE→ RPPHOUSE) is a SEPARATE concern and is allowed. It must never be labelledsearch_expansionsto avoid confusion with radius expansion.
search_parameters_master. No expansion rounds, no radius widening, no fallback searches.search_parameters_master table, keyed by property_family + density. Don't invent parameters.dateRangeMonths, plus 180 days forward (CLB-2091). Valuers use comps up to 6 months after assessment date.candidateMatchesTruth() uses 4-tier matching: (1) CoreLogic ID, (2) hardened address, (3) address+date ±30 days, (4) building-level fallback for strata.google_geocoded_addresses for normalised address before RPP resolution. Use Google's version first, fall back to raw.UNIT type is family-ambiguous. A UNIT row may be residential, commercial, industrial, or mixed-use. Never infer family from the TYPE field or from subtype strings (unit standard, house one storey). Use zoning, floor area, and subject-family context instead. See docs/DATA_MODEL.md — RPP Property Type Taxonomy. [PROMPT-INFRA-OK]valuations table, never from RPP. Use valuations.property_type + valuations.property_classification as the authoritative subject family. Never infer subject family from the RPP response (rppType, subtype string, or any derived field). Inferring from RPP re-introduces the same family-ambiguity problem as rule 12. [PROMPT-INFRA-OK]pipeline_run_id and search_run_id. No exceptions. If it touched an external API and isn't in api_call_log, it's a bug. [PROMPT-INFRA-OK]repository.js UPDATE.rpp-direct-client.js + autopilot/adapters/rpp-direct-adapter.js). It does NOT use the CoreLogic partner API (corelogic-client.js / enrichProperty / /details endpoint). The two are different services, different auth, different field shapes, different contractual basis with Cotality. Never substitute one for the other. If a task seems to need partner-API detail calls, fix the RPP-portal path first — the portal already returns the data in most cases. Calling the partner API as a "backfill" is a contractual + cost decision, not a routine fix, and needs explicit Jon approval per Rule 7. If Jon explicitly asks for the partner API address matcher, the endpoint is /search/au/matcher/address and the property ID can live at matchDetails.propertyId; /search/au/address/matcher is the wrong path. See docs/DATA_MODEL.md — Two RPP data paths.ok=false in stage_3_raw_candidates.sourceStats, the run MUST NOT reach status=review as a measurement. Vector-text / pgvector / any non-RPP fallback results carry a source ≠ rpp marker and recall scoring MUST refuse to count them toward Pillar 1. A pilot whose only candidates came from a fallback is invalid by definition. Why: 2026-04-29 Birchgrove subject completed status=review with 114 vector-text candidates from Vincentia/Cessnock/Yeppoon while RPP was rate-limited; the result looked like a normal recall failure when it was an auth blocker./opt/palermo/apps/truemarket/ is a flat copy, not a git checkout — pre-run gate verifies it matches origin/master HEAD (or document the exact commit it's on). Pilots against drifted code are invalid by definition. Why: 2026-04-29 sidecar ran 22h on stale code, masked PR #17/#18/#19 from production for hours after merge./etc/palermo/truemarket.env, the running pid's /proc/<pid>/environ MUST reflect the new values. Restart procedure is part of the env update, not a follow-up. Why: 2026-04-29 RPP_BROWSER=chrome-linux was added to env file but the sidecar process kept its 23h-old environ until manual restart.When sources disagree about the current operational state, the higher item wins. Lower items can annotate or remind, never override.
CHECKPOINT.md / .agent-handoff.md — current state in the project.git log / git blame — who-changed-what, when.This mirrors the ordered hierarchy in ~/.claude/rules/core.md. The two files MUST stay in sync on the ordering. The carve-outs below are project-specific elaboration; if they prove generally useful, copy them back to core.md to keep the doctrine unified.
Carve-outs (the hierarchy is not a single linear order for every question):
git log / git blame is authoritative, regardless of its position in the operational ranking. Memory and checkpoints can be wrong about history; git cannot.pipeline_runs, valuation_comparables, corelogic_property_cache, api_call_log, search_runs) are the recorded outcome of runs and outrank docs that describe runs.Practical rule: pick the source with the most direct claim on the question. If a doc says X but grep says it doesn't exist, grep wins. If memory says a ticket is closed but Clubhouse says it's open, Clubhouse wins. If checkpoint says PR #N is blocked but live code shows the fix landed, live code wins.