← Back to docs home

Process

The live source of truth for how agents progress the Listing Lens business. Backed by process/prompt_process.tsx in the repo root.

Agent process

28/33 tasks done across 5 phases. 3 task(s) are blocked on a human action, tracked as 7 distinct blocker(s) below.

Listing Lens is at the "concept / market validation" stage (see STRATEGY.md).
The goal is a profitable, growing business — not a finished app. Every
agent session should move that forward by a bounded, honest increment.

Each session:
0a. Before doing a full from-scratch re-verification pass, call
   recentlyConfirmedWithinHours(3) from blockers.ts. If it returns true,
   every blocker was already re-checked within the last 3 hours by some
   other session — don't repeat that work. Instead do a *cheap* check only
   (leads-log.ts/listingAuditLog/loggedInterviewResults still empty? any
   new merged PRs since the last progress-log.ts entry, via git log
   origin/main? nextActionableTasks() still empty?) and if nothing is
   different, stop there: no branch, no commit, no PR. The hourly/frequent
   cron will hit this repo again regardless, and a "still blocked, nothing
   changed" branch adds a CI run and a stale ref for zero new information
   (see blockers.ts's 2026-07-13 doc comment — three near-duplicate
   recheck branches in one day is what this guards against). If the 3-hour
   window has elapsed, or the cheap check finds something new, proceed to
   a full pass below.
0b. Check blockers.ts — it's the single source of truth for what's
   blocked on a human right now (README.md and this file's dashboard both
   read from it, so it should never need updating in more than one place).
   If your session's only finding is "still blocked, nothing changed,"
   bump the relevant entries' lastConfirmed/lastConfirmedAt/confirmedByCount
   there and do not also write a new progress-log.ts paragraph restating
   the same blockers a 20th time (that's the one exception to step 5
   below). If you resolve a blocker (a human did the thing) or find a
   genuinely new blocker, edit blockers.ts for real and that DOES warrant
   a normal progress-log entry.
1. Read roadmap.ts, decisions-log.ts, progress-log.ts (or the rendered
   /process page on the docs site).
2. Call nextActionableTasks() mentally: pick ONE not-started task owned by
   "agent" or "both" that you can actually complete this session — prefer
   the earliest incomplete phase over later ones. Some tasks declare
   gatedOn: [taskId, ...] (e.g. p3-app-scope, gated on interviews + first
   deliveries producing real validation data) — nextActionableTasks()
   already excludes these until every gating task is "done", so if a task
   still looks not_started but never appears in nextActionableTasks(),
   check its gatedOn before assuming it's a bug.
2a. Claim it immediately: as your FIRST commit this session (before doing
   the rest of the work), flip that task's status to "in_progress" in
   roadmap.ts and push/merge that tiny change to main by itself. Hourly
   agent sessions poll roadmap.ts for not_started tasks, and two sessions
   can start within minutes of each other (this happened for real on
   2026-07-12: PR #13 and #14 both independently answered the same
   STRATEGY.md open question and #13 had to be closed unmerged). A cheap,
   early "claimed" commit is the whole fix — nextActionableTasks() already
   skips non-"not_started" tasks, so a concurrent session that fetches
   fresh will see it and pick something else instead of duplicating work.
3. Do NOT attempt tasks owned by "human" (real interviews, taking payment,
   shooting real listings, dashboard clicks that need Duncan's account).
   Instead, prepare everything an agent can: scripts, checklists, copy,
   pricing pages, outreach templates, scoring rubrics, code.
4. If you truly cannot make product/business progress this session (e.g.
   everything actionable is human-blocked), it is fine to spend the
   session improving the process itself: tightening the roadmap, fixing
   stale statuses, improving docs, or paying down tech debt in research/
   or docs-site/ — but say so explicitly in the progress log entry rather
   than padding it with busywork.
5. Log what you did: update roadmap.ts task status, append a
   decisions-log.ts entry for any real decision, and always append one
   progress-log.ts entry.
6. Check other branches (git ls-remote origin) before starting, since
   other agents may be working the same repo concurrently — prefer
   additive changes over ones likely to conflict with in-flight PRs.
7. Build docs-site and research before committing (npm run build in each
   that you touched); run npm test in pipeline/ if you touched it. Push to
   your assigned branch, open a PR, and merge it once it's green — this
   repo's convention is short-lived branches, not long-running ones.
   .github/workflows/ci.yml now runs these same checks automatically on
   every push/PR, but it fires after you push — still verify locally
   first so you're not relying on CI to catch something you could have
   caught before opening the PR.
8. Before merging your own PR, check for other open PRs touching the same
   files (process/*.ts, docs-site nav, etc.) and merge/rebase against the
   latest main first — process/progress-log.ts especially tends to
   conflict since every session appends to the same array tail. Resolve
   by keeping both entries in date order, never by dropping one.

Guardrails (do not violate without explicit human sign-off in this
session's actual conversation):
- No real payments, no real customer outreach sent on Duncan's behalf,
  no changes to production Cloudflare Access / DNS without confirmation.
- No iOS app build-out yet (see the phase-3 scope decision).
- No spend of real money (ad spend, contractor payment, paid tools).

Human blockers

The single source of truth for what's blocked on a human right now — see process/blockers.ts. Sorted oldest-confirmed first, so the top row is the one that's gone longest without anyone re-checking it.

Book and shoot one real concierge listing

confirmed 12x · last 2026-07-18 · since 2026-06-13

Use process/outreach-templates.ts + process/booking-link.ts to book one real listing, shoot it with process/capture-checklist.ts, run pipeline/ (enhance + microsite), and deliver it using process/delivery-message.ts's ready-to-send handoff message (printed automatically by `npm run microsite`).

Unblocks: p2-first-deliveries, p3-app-scope

Run the Phase 1 interviews

confirmed 12x · last 2026-07-18 · since 2026-06-13

Conduct 10 agent + 5 STR host interviews using process/interview-script.ts and log results.

Unblocks: p1-run-interviews, p3-app-scope

Score 20 Zillow listings against the audit rubric

confirmed 12x · last 2026-07-18 · since 2026-06-13

Pick a target metro, browse Zillow, and score 20 listings (mediocre vs excellent) using process/listing-audit-rubric.ts.

Unblocks: p1-listing-audit

Flip on the Cloudflare Access policy for the docs site

confirmed 13x · last 2026-07-18 · since 2026-06-15

In the Cloudflare dashboard: Workers & Pages -> listing-lens-docs -> Settings -> Enable access policy, restrict to duncan.andrew.haywood@gmail.com, then merge PR #4 (cursor/fix-pages-access-4a7e).

Unblocks: p0-docs-site

Delete ~37 confirmed-stale, fully-merged branches

confirmed 13x · last 2026-07-18 · since 2026-07-12

From github.com (or a token with delete permission) delete the branches confirmed strictly merged/superseded (37 non-main branches per `git ls-remote --heads origin` as of 2026-07-18T00:20Z, up 1 from the last session's count; only cursor/fix-pages-access-4a7e is live, as PR #4). The one new branch since the last full audit, cursor/setup-dev-environment-2314, has zero unmerged commits against main (`git log origin/main..origin/cursor/setup-dev-environment-2314` is empty) — no re-audit of the other 36 needed, consistent with the prior session's note that only newly-appeared branches need checking once the full set has been exhaustively audited. Confirmed a proxy-level restriction, not a repo-permissions issue: the GitHub REST `DELETE /git/refs/heads/...` endpoint returns a distinct 403 ('Write access to this GitHub API path is not permitted through this proxy'), different from a plain permissions 403 — no agent session behind this proxy can ever delete a ref, regardless of GITHUB_TOKEN scope. This session's GitHub MCP tool surface (create_branch, delete_file, create_or_update_file, etc.) also has no branch-delete method — a second, independent path to the same dead end. No unique unmerged business value found anywhere in the 37 — this blocker is purely about GitHub-side cleanup hygiene, not lost work.

Confirm/set the real payment handle prospects will pay

confirmed 7x · last 2026-07-18 · since 2026-07-13

process/payment-collection.ts (built 2026-07-13) defaults to requesting payment via Zelle (primary) / PayPal goods & services (fallback) sent to duncan.andrew.haywood@gmail.com (the same known real address already used for booking-link.ts and Cloudflare Access). Confirm that email actually receives payments on at least one of those apps (or link it), or swap in a different real handle in payment-collection.ts if not. Quick (a few minutes), independent of booking a shoot — worth doing before the first delivery, not blocking on it.

Unblocks: p2-first-deliveries

Reconsider the hourly-or-tighter agent cron cadence now that everything is human-blocked

confirmed 6x · last 2026-07-18 · since 2026-07-13

Duncan to decide (and update the schedule in whatever's calling this repo's agent sessions) whether to slow the cadence. As of 2026-07-18, nextActionableTasks() has been empty for well over a month straight (first flagged 2026-07-13, unchanged since) — every remaining roadmap task is gated on one of the five human actions above, none of which change hour to hour. At least three separate cron triggers hit this repo's agent process on sub-daily schedules (two hourly on local-site-studio, one hourly here); the recentlyConfirmedWithinHours() throttle (merged as PR #35) now stops most of the resulting branch/PR/CI churn, but doesn't reduce token spend on the sessions that still spin up hourly (or, per this session, roughly every 4 hours) and read the whole process/ tree before concluding there's nothing to do. A daily (or twice-daily) cadence would very likely catch a resolved human blocker just as fast in practice, since resolving any of them (an interview, a shoot, a dashboard click) is not itself an hourly-cadence event.

Roadmap

Phase 0 — Repo & docs scaffold

Pre-week 1In progress

Bootstrap the repo, market research site, private docs site, and this process system.

  • Next.js market research report (agent)

    research/ builds and exports statically.

    Done
  • Private docs site on Cloudflare Pages + Access (both)

    docs-site deploys and Cloudflare Access blocks anonymous visitors.

    Blocked: Site deploys fine, but Cloudflare Access isn't actually enforced on *.pages.dev yet — PR #4 (cursor/fix-pages-access-4a7e) needs Duncan to flip the Access policy on in the Cloudflare dashboard before this is really done.

    Blocked
  • Typed TS/TSX process-tracking system (agent)

    process/prompt_process.tsx exists, is rendered on the docs site, and this roadmap reflects real status.

    Done
  • CI workflow runs tests/builds on every push and PR (agent)

    .github/workflows/ci.yml runs `npm test` + `tsc --noEmit` in pipeline/ and `npm run build` in docs-site/ and research/ on every push and pull_request, independent of the existing deploy-docs.yml (which only builds docs-site on push to main).

    Done
  • Patch next.js off the vulnerable 14.1.0 pin in docs-site/ and research/ (agent)

    next bumped to 14.2.35 (latest 14.x patch) in both packages, npm run build verified clean in both. See decisions-log.ts for why the full next@16 major-version migration (needed to clear every advisory npm audit flags) was deliberately deferred rather than attempted in the same session.

    Done
  • Fix stale docs-site /docs/strategy content and the Host-pack pricing drift it exposed (agent)

    docs-site/src/lib/docs.ts reads STRATEGY.md and docs/REPLICATION_PROMPT.md directly from their canonical repo-root paths (no more hand-synced duplicates in docs-site/content/); STRATEGY.md and research/src/lib/data.ts both say Host pack is $19-79/listing; docs-site `npm run build` and research `npm run build` both pass. See decisions-log.ts.

    Done

Phase 1 — Problem interviews

Week 1-2In progress

Validate the pain and willingness to pay before building anything else.

  • Agent + STR host interview script (agent)

    A structured, typed interview script + scoring rubric exists in process/ and/or docs-site.

    Done
  • Run 10 agent + 5 STR host interviews (human)

    15 interviews logged with pay-willingness scored per the rubric.

    Blocked: Needs a human to conduct live interviews. Agents can prep scripts/outreach copy but cannot talk to real agents or hosts.

    Blocked
  • Collect 20 Zillow listings in one metro (mediocre vs excellent) (human)

    20 listings collected and tagged mediocre/excellent with notes.

    Blocked: Requires picking a target metro and manual collection from live listings. The scoring instrument is ready (see p1-listing-audit-rubric) — a human just needs to browse Zillow in a chosen metro and score what they find.

    Blocked
  • Build an objective scoring rubric for the Zillow listing audit (agent)

    A typed rubric (6 concrete 1-5 dimensions with worst/best guidance) and a computeOverallScore()/auditSummary() scoring function exist in process/listing-audit-rubric.ts, rendered on the docs site, with an empty (no fabricated data) listingAuditLog ready for real entries. See process/listing-audit-rubric.ts.

    Done
  • Answer STRATEGY.md's open 'MLS compliance by board' question (agent)

    Documented, sourced compliance rules exist in process/mls-compliance.ts, rendered on the docs site, with STRATEGY.md's open question checked off. See process/mls-compliance.ts.

    Done
  • Answer STRATEGY.md's open 'on-device vs cloud for virtual staging' question (agent)

    Documented, sourced comparison exists in process/virtual-staging-tech.ts with an explicit decision and a stated revisit condition, rendered on the docs site, with STRATEGY.md's open question checked off. See process/virtual-staging-tech.ts.

    Done

Phase 2 — Concierge MVP

Week 3-4In progress

Deliver MLS-ready photo packs by hand (checklist + manual AI pipeline) before writing an app, to test willingness to pay for $19/listing.

  • Guided shoot checklist (room-by-room) (agent)

    A typed checklist (rooms, shots, brackets) exists and renders on the docs site. See process/capture-checklist.ts.

    Done
  • Scale checklist shot count to match the 15-25 photo promise (agent)

    process/capture-checklist.ts's secondary bedroom/bathroom rooms scale per additional unit instead of a fixed single entry, pipeline/src/coverage.ts + cli.ts accept real --beds/--baths, and the typical-home default (totalShotCount() with no args) falls inside the promised 15-25 range. Verified: typical default is now 19.

    Done
  • $19/listing concierge offer one-pager (agent)

    A shareable one-pager (site page or PDF-able doc) states offer, price, turnaround. See process/pricing-one-pager.tsx, rendered at docs-site's /pricing page.

    Done
  • Cold outreach templates for mid-market agents (agent)

    Email/DM/in-person templates plus a lightweight booking intake exist in process/. See process/outreach-templates.ts.

    Done
  • Automated photo enhancement pipeline (CLI) (agent)

    A typed CLI exists at pipeline/ (npm run enhance -- --in <raw> --out <delivery> --listing "<address>"), batch-processes a folder of photos into renamed MLS-ready JPEGs, cross-checks output count against process/capture-checklist.ts's expected shot count, and has a passing self-test (npm test) with no real listing photos required. See pipeline/src/enhance.ts, cli.ts, coverage.ts, enhance.test.ts.

    Done
  • Audit capture-checklist.ts for a shooter with zero photography background (agent)

    process/capture-checklist.ts exports preShootPrep (a once-per-shoot prep list) and BRACKET_GUIDANCE (plain-language capture instructions per bracket type, including the coverage.ts gotcha that brackets must be merged down to one file per shot before running pipeline/enhance), both rendered on the docs site's capture checklist section.

    Done
  • Deliver 5 concierge packs, collect payment (human)

    5 paid concierge deliveries logged with turnaround time and customer feedback.

    Blocked: Requires a human to shoot/edit and take payment; not agent-executable yet. The edit step is now faster thanks to pipeline/ (p2-ai-pipeline) — shoot with the checklist, run `npm run enhance` in pipeline/, deliver the output folder.

    Blocked
  • Typed leads/bookings funnel tracker (agent)

    process/leads-log.ts exists with a typed Lead/LeadStage schema, starts empty (no fabricated data), and renders a funnel-count view on the docs site. See process/leads-log.ts.

    Done
  • Replace the outreach templates' placeholder booking link with a real one (agent)

    process/booking-link.ts exists with a real mailto: link (BOOKING_LINK) addressed to Duncan and pre-filled with bookingIntakeFields' questions; outreach-templates.ts's renderTemplateBody() substitutes it into the placeholder; pricing-one-pager.tsx renders an actual 'Book now' link; docs-site's /pricing page shows both with the real link substituted. See process/booking-link.ts.

    Done
  • Property microsite generator (the 'Brand' step) (agent)

    A CLI (`npm run microsite -- --dir <delivery-dir> --listing "<address>" --agent-name "<name>" ...`) generates a single self-contained index.html gallery microsite from an enhance.ts delivery folder, with agent name/phone/email/brokerage/logo/headshot fields, without ever modifying the underlying MLS-bound photo files (per the mls-compliance.ts branding guardrail). Has a passing self-test (npm test) with no real listing photos, logos, or headshots required. See pipeline/src/microsite.ts, cli-microsite.ts, microsite.test.ts.

    Done
  • CLI to append a real lead to leads-log.ts without hand-editing TypeScript (agent)

    `npm run log-lead -- --audience ... --contact ... --channel ... [--dry-run]` in pipeline/ validates required fields against the Lead/LeadStage/OutreachTemplate types and appends a correctly-formatted entry to process/leads-log.ts; has a passing self-test (npm test) that never touches the real file. See pipeline/src/log-lead.ts.

    Done
  • Define how the $19 concierge payment actually gets collected (agent)

    process/payment-collection.ts exists with a sourced comparison of zero-setup payment methods (no new business infrastructure, no real spend), an explicit decision, and ready-to-use instructions addressed to Duncan's known real inbox; pricing-one-pager.tsx's FAQ and howItWorks reference it; leads-log.ts's Lead type gains optional paymentMethod/paymentReference fields so a real 'paid' entry can record how; pipeline/src/log-lead.ts's CLI accepts them. Rendered on the docs site.

    Done
  • Ready-to-send delivery handoff message (photos + microsite + payment ask) (agent)

    process/delivery-message.ts exists with deliveryMessageText(), combining listing address, photo count, an optional microsite mention, payment-collection.ts's paymentInstructionsText(), and the existing free-redo guarantee into one ready-to-paste message; pipeline/src/cli-microsite.ts prints it automatically after a successful microsite build; has a passing self-test (npm test) with no real listing photos required. See process/delivery-message.ts, pipeline/src/cli-microsite.ts, delivery-message.test.ts.

    Done
  • Post-delivery follow-up message: testimonial + referral ask (agent)

    process/referral-followup.ts exists with followUpMessageText() (a 3-7-days-after-delivery check-in that asks for a concrete testimonial and offers a zero-cash-spend discount-based referral incentive), a TESTIMONIAL_PROMPT for turning a vague reply into a usable quote, and REFERRAL_INCENTIVE defined as future-delivery discounts (not cash) so it doesn't violate the no-real-spend guardrail; has a passing self-test (npm test) with no real listing/customer data required. See process/referral-followup.ts, pipeline/src/referral-followup.test.ts.

    Done
  • Audit outreach/delivery/referral copy for drifted customer promises (agent)

    process/delivery-message.ts's DeliveryMessageInput gains an optional isRepeatClient flag (default false, preserving today's exact output) that swaps the guarantee/signoff wording for repeat clients instead of always claiming 'this first delivery'; delivery-message.test.ts covers both branches. All other cross-file promises (price $19, 1-business-day turnaround, free-redo guarantee scope, payment instructions) re-checked and found consistent. See process/delivery-message.ts, pipeline/src/delivery-message.test.ts.

    Done
  • Fix stale photo-count claim left behind in mls-compliance.ts by the checklist-scaling fix (agent)

    process/mls-compliance.ts's photo-count-guidance implication now states the real current default (19 shots typical; 16-22 range) instead of the pre-scaling 9-11 figure. Repo-wide grep for the old '9-11' figure confirms every remaining mention is correctly past-tense (describing what the checklist used to specify, not a claim about its current behavior).

    Done
  • Correct the '~30 minute' capture-time promise in outreach/pricing copy to match the scaled checklist (agent)

    process/pricing-one-pager.tsx and process/outreach-templates.ts quote '~35-40 minutes' instead of '~30 minutes' for the guided capture step; process/capture-checklist.ts's preShootPrep doc comment updated to match. STRATEGY.md's own looser 'in 30 minutes' tagline is deliberately left unchanged (aspirational whole-workflow positioning, not a literal capture-time SLA) — same treatment as the 'under 2 hours' delivery target vs. the actual '1 business day' customer promise.

    Done
  • Make the on-site capture-time promise computed, not a second hand-typed number (agent)

    process/capture-checklist.ts exports onSiteTimeRange(additionalBeds?, additionalBaths?), which rounds totalTargetMinutes() outward to the nearest 5 minutes on each end; process/pricing-one-pager.tsx now imports and computes its two on-site-time copy lines from it directly instead of a hardcoded string, so that surface can no longer drift on its own. process/outreach-templates.ts's 3 template bodies are plain string literals (not template functions) and still need a human/agent to eyeball-sync against onSiteTimeRange()'s output — documented in a doc comment there pointing back at the source of truth. See process/capture-checklist.ts, process/pricing-one-pager.tsx, decisions-log.ts.

    Done
  • Throttle redundant hourly 'still blocked, nothing changed' recheck branches (agent)

    process/blockers.ts gains lastConfirmedAt (real timestamp) on every entry and an exported recentlyConfirmedWithinHours(hours) helper; prompt_process.tsx's AGENT_PROCESS_INSTRUCTIONS step 0 tells a session to call it before doing a full re-verification pass and to skip the branch/commit/PR entirely if nothing changed and the window hasn't elapsed. See process/blockers.ts and decisions-log.ts.

    Done

Phase 3 — Smoke-test app

Month 2Not started

TestFlight with 20 users; measure time-to-publish, NPS, willingness to pay.

  • Define v1 app scope (photos-only vs photos+3D lite) (both)

    A decision is logged in decisions-log.ts with rationale.

    Gated on: p1-run-interviews, p2-first-deliveries (not all done yet — not agent-actionable until they are)

    Not started

Phase 4 — Pick the wedge

Month 3Not started

Select the audience where 40%+ would pay $49/mo; land 1 brokerage pilot.

  • Answer STRATEGY.md's open 'brokerage sales cycle vs agent PLG' question (agent)

    A sourced comparison of brokerage-led (top-down) vs agent-led (bottom-up/PLG) go-to-market motions exists in process/gtm-motion.ts with an explicit provisional decision and revisit condition, rendered on the docs site, with STRATEGY.md's open question checked off. See process/gtm-motion.ts.

    Done

Capture checklist

Room-by-room guided shoot checklist for the Phase 2 concierge MVP, written for someone with zero photography background. Target total time: 38.5 minutes.

Before you start (once per shoot)

  • Check your phone has 2+ GB free storage and 50%+ battery (or a charger on hand)

    A ~20-shot shoot with HDR brackets can be several hundred MB; running out mid-shoot means a second trip.

  • Turn on every light in the home, including closets and under-cabinet lighting

    Buyers read a well-lit home as larger and better maintained; mixed on/off lighting looks unfinished in photos.

  • Open all blinds/curtains and clean visible windows

    Natural light plus a clean window reads better than closed blinds, and avoids streaky glare in the shot.

  • Clear counters, floors, and visible surfaces of personal items, trash, and pet bowls/toys

    MLS photos should show the space, not the seller's belongings — this also matches the no-clutter reminders repeated per-room below.

  • Move cars out of the driveway/street in front of the house and coil up hoses/cords in the yard

    The exterior front-elevation shot is usually the single most-viewed photo on a listing; a car or hose in frame is the #1 preventable re-shoot cause.

  • Turn off ceiling fans

    A spinning fan blurs into a smear in most phone exposures, including HDR brackets.

  • Close all toilet lids and hide personal bathroom items

    Same declutter logic as above, called out separately because it is the most commonly forgotten single item.

  • Set your phone camera to its highest resolution and turn ON grid lines (for level horizons/vertical lines)

    Higher resolution gives pipeline/'s enhancement step more to work with; grid lines make it easy to keep verticals straight without a tripod, which several shots below call for.

Exterior / curb appeal

~7 min
  • Front elevation, straight-on (3-exposure HDR)

    Shoot from the sidewalk/street at standing height, phone vertical-locked. Wait for even light (avoid harsh midday shadow on the facade if possible).

    Turn on your phone's built-in HDR/Smart HDR mode (iPhone: Camera app > HDR, usually automatic on recent models; Android: Camera app > More/Settings > HDR) and take the shot once — the phone captures and merges 3 exposures (dark/normal/bright) automatically into one photo. You do not need a tripod or manual bracketing app for this. Confirm only ONE merged photo lands in your camera roll for this shot before moving on.

  • Front elevation, 3/4 angle (3-exposure HDR)

    Step to one side to show depth and the side yard/driveway.

    Turn on your phone's built-in HDR/Smart HDR mode (iPhone: Camera app > HDR, usually automatic on recent models; Android: Camera app > More/Settings > HDR) and take the shot once — the phone captures and merges 3 exposures (dark/normal/bright) automatically into one photo. You do not need a tripod or manual bracketing app for this. Confirm only ONE merged photo lands in your camera roll for this shot before moving on.

  • Backyard / outdoor living (3-exposure HDR)

    Capture the full usable outdoor space; include patio, deck, or pool if present.

    Turn on your phone's built-in HDR/Smart HDR mode (iPhone: Camera app > HDR, usually automatic on recent models; Android: Camera app > More/Settings > HDR) and take the shot once — the phone captures and merges 3 exposures (dark/normal/bright) automatically into one photo. You do not need a tripod or manual bracketing app for this. Confirm only ONE merged photo lands in your camera roll for this shot before moving on.

  • Front door / entry detail (Single exposure)

    Close-up on the entry: door, house number, and any porch/landscaping detail.

    One normal photo. No special steps — just frame the shot per the instructions and tap to capture.

Living / great room

~6 min
  • Wide corner shot (5-exposure HDR)

    Shoot from the room's furthest corner at roughly waist height to maximize perceived size; keep vertical lines straight (vertical lock on).

    Same as 3-exposure HDR (turn on your phone's HDR mode and shoot once) — used for the room's hardest-to-balance shot (bright windows + dark interior), where you want the phone to lean on more merged exposure range. If your phone's HDR only offers a fixed 3-shot merge, that's fine; just make sure HDR is on and don't skip this shot. Still exactly ONE final photo per shot.

  • Opposite-corner alternate angle (3-exposure HDR)

    Second angle for MLS variety and to show any feature (fireplace, view, built-ins).

    Turn on your phone's built-in HDR/Smart HDR mode (iPhone: Camera app > HDR, usually automatic on recent models; Android: Camera app > More/Settings > HDR) and take the shot once — the phone captures and merges 3 exposures (dark/normal/bright) automatically into one photo. You do not need a tripod or manual bracketing app for this. Confirm only ONE merged photo lands in your camera roll for this shot before moving on.

  • Feature detail (Single exposure)

    Close on the room's best feature: fireplace, view, built-ins, or architectural detail.

    One normal photo. No special steps — just frame the shot per the instructions and tap to capture.

Dining area

~3 min
  • Wide shot of the dining space (3-exposure HDR)

    Shoot to show the table fits the room and connects visually to the kitchen/living area.

    Turn on your phone's built-in HDR/Smart HDR mode (iPhone: Camera app > HDR, usually automatic on recent models; Android: Camera app > More/Settings > HDR) and take the shot once — the phone captures and merges 3 exposures (dark/normal/bright) automatically into one photo. You do not need a tripod or manual bracketing app for this. Confirm only ONE merged photo lands in your camera roll for this shot before moving on.

Kitchen

~6 min
  • Wide shot showing counters + appliances (5-exposure HDR)

    Clear counters of clutter first. Shoot from the entry point toward the far wall.

    Same as 3-exposure HDR (turn on your phone's HDR mode and shoot once) — used for the room's hardest-to-balance shot (bright windows + dark interior), where you want the phone to lean on more merged exposure range. If your phone's HDR only offers a fixed 3-shot merge, that's fine; just make sure HDR is on and don't skip this shot. Still exactly ONE final photo per shot.

  • Island / eating area detail (3-exposure HDR)

    Only if the kitchen has an island or breakfast bar worth featuring.

    Turn on your phone's built-in HDR/Smart HDR mode (iPhone: Camera app > HDR, usually automatic on recent models; Android: Camera app > More/Settings > HDR) and take the shot once — the phone captures and merges 3 exposures (dark/normal/bright) automatically into one photo. You do not need a tripod or manual bracketing app for this. Confirm only ONE merged photo lands in your camera roll for this shot before moving on.

  • Appliance / finish detail (Single exposure)

    Close-up on a notable finish or appliance (range, counters, backsplash) buyers search for.

    One normal photo. No special steps — just frame the shot per the instructions and tap to capture.

Primary bedroom

~4 min
  • Wide shot from doorway or corner (3-exposure HDR)

    Make the bed, clear nightstands. Shoot to show the full footprint of the room.

    Turn on your phone's built-in HDR/Smart HDR mode (iPhone: Camera app > HDR, usually automatic on recent models; Android: Camera app > More/Settings > HDR) and take the shot once — the phone captures and merges 3 exposures (dark/normal/bright) automatically into one photo. You do not need a tripod or manual bracketing app for this. Confirm only ONE merged photo lands in your camera roll for this shot before moving on.

  • Closet / storage detail (Single exposure)

    Only if the closet is walk-in or a genuine selling point; tidy visible contents first.

    One normal photo. No special steps — just frame the shot per the instructions and tap to capture.

Primary bathroom

~3 min
  • Wide shot (3-exposure HDR)

    Close toilet lid, clear counters, wipe mirror/fixtures. Shoot from the doorway.

    Turn on your phone's built-in HDR/Smart HDR mode (iPhone: Camera app > HDR, usually automatic on recent models; Android: Camera app > More/Settings > HDR) and take the shot once — the phone captures and merges 3 exposures (dark/normal/bright) automatically into one photo. You do not need a tripod or manual bracketing app for this. Confirm only ONE merged photo lands in your camera roll for this shot before moving on.

  • Vanity / tub-shower detail (Single exposure)

    Close on the vanity or tub/shower if it's a double vanity, soaking tub, or walk-in shower.

    One normal photo. No special steps — just frame the shot per the instructions and tap to capture.

Secondary bedroom

~2 min
  • Wide shot from doorway or corner (3-exposure HDR)

    Same staging rules as the primary bedroom; repeat once per additional bedroom.

    Turn on your phone's built-in HDR/Smart HDR mode (iPhone: Camera app > HDR, usually automatic on recent models; Android: Camera app > More/Settings > HDR) and take the shot once — the phone captures and merges 3 exposures (dark/normal/bright) automatically into one photo. You do not need a tripod or manual bracketing app for this. Confirm only ONE merged photo lands in your camera roll for this shot before moving on.

Secondary bathroom

~1.5 min
  • Wide shot (3-exposure HDR)

    Same staging rules as the primary bathroom; repeat once per additional bathroom.

    Turn on your phone's built-in HDR/Smart HDR mode (iPhone: Camera app > HDR, usually automatic on recent models; Android: Camera app > More/Settings > HDR) and take the shot once — the phone captures and merges 3 exposures (dark/normal/bright) automatically into one photo. You do not need a tripod or manual bracketing app for this. Confirm only ONE merged photo lands in your camera roll for this shot before moving on.

Laundry / office / garage (if present)

~2 min
  • Wide shot of the flex space (Single exposure)

    Only shoot spaces that are finished/usable and add selling value (laundry, home office, garage).

    One normal photo. No special steps — just frame the shot per the instructions and tap to capture.

Wrap-up

~2 min
  • Review all shots on-site before leaving (Single exposure)

    Check for level horizons, no mirror reflections of the photographer, no pets/people in frame, before departing — re-shoots after leaving cost the capture-time target.

    One normal photo. No special steps — just frame the shot per the instructions and tap to capture.

MLS compliance notes

Desk research answering STRATEGY.md's open "MLS compliance by board" question. Not legal advice or board-verified — see process/mls-compliance.ts for sources and caveats.

No agent/brokerage branding on MLS-submitted photos

Virtually all MLS boards prohibit contact info, agent/brokerage names, logos, watermarks, phone numbers, or URLs anywhere in an MLS-submitted photo or virtual tour. Violations are typically an automatic, no-notice fine to the broker ($500-$5,000 per violation range reported across boards).

The pipeline/ enhancement CLI must never burn in a watermark, logo, or contact info on delivery photos — it currently doesn't (enhance.ts only auto-orients, normalizes, sharpens, resizes, re-encodes). STRATEGY.md's step 5 'Brand' (agent headshot, logo, property microsite) must stay scoped to marketing collateral around the listing (a microsite, a flyer, social posts) and never touch the actual MLS-uploaded image files.

Virtually staged / digitally altered photos need disclosure

As of 2026, a majority of states require disclosing digitally altered listing photos (added furniture, AI room redesigns, object/damage removal, sky replacement). Strict-disclosure states (e.g. California, effective Jan 1 2026 under AB 723; also New York, Texas) require a visible per-image label/watermark ('Virtually Staged') plus the original unaltered photo shown alongside it. Moderate-disclosure states require only a listing-remarks note, not a per-image label.

The current enhance.ts pipeline does NOT do virtual staging, sky replacement, or object removal, so no disclosure obligation applies to it today — this is exactly why decisions-log.ts scoped those to a future human/cloud 'Polish' step rather than the automated CLI. If/when that Polish step is built, it must (a) default to the strictest behavior — visible 'Virtually Staged' caption on the altered image and retain the original unaltered photo in the delivery — regardless of state, since Listing Lens can't verify the listing's state/board at pipeline-run time, and (b) never silently replace the original.

Photo count best practice

Most boards set only a low required minimum (e.g. CRMLS: 1 photo minimum), but engagement data favors far more: under ~9 photos measurably hurts engagement, and 22-27+ is the commonly cited best-performing range on major portals.

process/capture-checklist.ts's totalShotCount() now scales per bed/bath (typical-home default: 19 shots; a 5-bed/3-bath listing: 22; a 0-extra-bed/bath listing: 16) — already inside the promised 15-25 range and close to, though still shy of, the 22-27+ best-performing range this rule cites. Not a compliance problem (boards don't require more), but still a quality/competitiveness gap worth revisiting once real deliveries produce feedback on whether prospects want more shots per room for the same $19 flat price.

Listing quality audit rubric

Objective scoring instrument for the Phase 1 Zillow listing audit (p1-listing-audit). Still needs a human to browse a chosen metro and score real listings — see process/listing-audit-rubric.ts. No listings scored yet.

Lighting & exposure

1 = blown-out windows or underlit rooms, mixed harsh shadows. 5 = balanced natural + ambient light, no blown highlights or crushed shadows.

Composition & angle

1 = crooked verticals, doorway/corner framing that hides the room. 5 = level verticals, corner-of-room angle that shows the space and a clear sightline.

Staging & declutter

1 = visible clutter, personal items, trash cans, cars in driveway shots. 5 = tidy, depersonalized, styled surfaces (or clean vacant rooms).

Photo count & room coverage

1 = under 9 photos or missing whole rooms (see process/mls-compliance.ts's photo-count-guidance rule). 5 = 15-25+ photos covering every livable room plus exterior/curb appeal.

Resolution & sharpness

1 = visibly blurry, low-res, or compression artifacts at normal zoom. 5 = sharp focus throughout, no visible motion blur or over-sharpening halos.

Color & white balance

1 = strong color cast (orange indoor-light tint, blue-grey underexposure). 5 = neutral, true-to-life color throughout the set.

Virtual staging tech (Polish step)

Desk research answering STRATEGY.md's open "on-device vs cloud for virtual staging" question. Not yet built — see process/virtual-staging-tech.ts for sources and the revisit condition.

Decision: cloud api

When the Polish step's virtual staging feature is actually built, call a pay-per-image cloud API rather than running on-device Core ML inference. Reasoning mirrors industry practice at Listing Lens's current volume: desk research on both real-estate-specific tools and general mobile-ML architecture agrees that under roughly 1,000 images/month, a pay-per-image API or low-tier subscription beats standing up dedicated (on-device or custom) infrastructure, and on-device only starts to pay for itself at high volume inside an app that already exists. Listing Lens has zero real deliveries yet (p2-first-deliveries is still human-blocked) and no iOS app (phase 3 not started), so cloud-per-call is the only option that adds zero fixed cost and doesn't require building app/ML infrastructure ahead of proven demand.

Revisit when: Revisit once (a) an iOS app exists (phase 3+) and (b) real monthly virtual-staging volume is high enough (desk research puts the crossover around 1,000-15,000 images/month) that a per-image API cost materially cuts into margin. Until then, keep Polish/virtual-staging entirely out of the automated pipeline/ CLI, consistent with the existing MLS-compliance guardrail that any virtual-staging feature defaults to the strictest disclosure behavior (visible caption + retain original) regardless of vendor.

Pay-per-image cloud virtual-staging API (e.g. Virtual Staging AI, Apply Design, REimagineHome)

cloud api

Roughly $1-2/image at low volume via a dedicated API (Virtual Staging AI quotes ~$1/image); consumer subscription tools run $0.25-2/image at low monthly caps. No fixed monthly cost below a chosen plan; scales linearly with real usage.

Single-digit seconds to ~30 seconds per image, no local compute or model download required.

Zero fixed cost until a listing actually needs staging — matches the 'no spend of real money' guardrail (agents don't pre-pay anything; a per-listing API call would only ever be triggered by, and priced into, a real paid delivery). No iOS app or on-device ML infra needed, so it doesn't pull forward the phase-3 app-scope decision. Downside: ongoing per-image marginal cost (unlike the free local Enhance step), and a live network dependency during delivery.

On-device Stable-Diffusion-class inpainting via Core ML (Apple Neural Engine)

on device

No per-image API cost once shipped, but real fixed engineering cost: converting/hosting model weights (multi-GB), an iOS app to run them in, and ongoing model maintenance.

~8-15 seconds/image on iPhone 15 Pro-class hardware for Stable-Diffusion-class models; slower or infeasible on older/non-Pro devices. Inpainting (needed to furnish a specific empty room without altering the rest of the photo) is supported by advanced on-device tooling (e.g. Draw Things) but is a meaningfully harder integration than basic image generation.

Only pays off at real scale and only inside an actual iOS app — both explicitly out of scope for phase 1-2 per STRATEGY.md's scope guard ('do not build the iOS app in phase 1') and every prior session's decision not to start p3-app-scope early. Also gates quality/availability by device tier (Pro-class iPhones only), which a $19 concierge offer aimed at 'good enough, fast, cheap' shouldn't take on this early.

Go-to-market motion (brokerage vs agent PLG)

Desk research answering STRATEGY.md's open "brokerage sales cycle vs agent PLG" question. A provisional, sourced sequencing decision, not a real brokerage sales conversation — see process/gtm-motion.ts for sources and the revisit condition.

Decision: hybrid sequenced

Lead with agent-led PLG now — it's what the concierge MVP (checklist, pipeline, outreach templates, booking link) is already built for, matches STRATEGY.md's stated primary beachhead (mid-market agents, not brokerages), and 2026 real-estate-specific reporting shows brokerage-purchased tools risk near-zero individual-agent adoption even after a contract closes, which is the opposite of the fast, direct payment/usage signal phase 2 needs. Treat a brokerage-led motion as a Month-3 expansion step that comes after agent-level proof exists (paying concierge customers, ideally a self-serve or team referral pattern emerging), not a parallel or earlier motion — this doesn't change STRATEGY.md's existing 90-day plan, it just answers 'why agents first' with sourced reasoning instead of leaving the open-questions box unchecked.

Revisit when: Revisit once phase 2 (p2-first-deliveries) produces at least a few real paying customers, or once an inbound brokerage inquiry happens organically (e.g. a satisfied agent's team lead asks about a group rate) — at that point, evaluate whether the existing Team tier ($249/mo, 10 seats) needs an actual sales motion (a one-pager, a demo, a point of contact for procurement questions) built out, informed by real agent-level usage data rather than desk research alone.

Top-down brokerage/team sales motion (sell into marketing ops, IT, brokers)

brokerage led

General B2B SaaS benchmarks put mid-market ($15k-$50k ACV) deals at 30-60 days and multi-stakeholder deals average 6.8 stakeholders as of 2025-2026, with SOC2/vendor-risk review adding 2-4 weeks; a brokerage-wide seat deal (STRATEGY.md's $249/mo Team tier, 10 seats) would likely sit in or above that band, not the SMB under-$15k 14-30 day band.

Higher per-deal (Team tier is 3x the Agent Pro tier and covers 10 seats at once), but only if the brokerage actually rolls it out to agents after signing.

Real-estate-specific 2026 reporting (Inman, May 2026) found brokerages already buying AI/tech licenses and announcing them internally, then adoption stalling within about 90 days because purchasing a tool and getting individual agents to actually use it are different problems; agents kept defaulting to their own preferred external tools even after a brokerage-wide purchase. That is exactly the failure mode a brokerage-led motion risks here: Listing Lens could close a Team-tier contract and still see near-zero real usage, with no fast, direct signal (an agent replying to outreach, booking, or paying $19) the way a PLG motion produces.

Bottom-up agent product-led growth (concierge -> self-serve -> expand)

agent plg

PLG-motion B2B SaaS companies convert meaningfully faster than sales-led ones because the buyer experiences the product before any negotiation; median free-to-paid conversion across PLG models runs roughly 9% (higher, ~12%, for freemium), and this maps directly onto Listing Lens's actual current motion — a $19 concierge delivery is closer to a paid trial than a sales cycle.

Lower per-deal initially ($19/listing or a $29-79/mo individual-agent tier vs. a brokerage contract), but OpenView's 2024 SaaS benchmarks found hybrid PLG+SaaS-led companies hit net-revenue-retention targets at a materially higher rate (67% vs 58% for pure PLG) and PLG companies overall post a ~70% better Rule-of-40 score than sales-led peers — the expansion path (agent PLG first, brokerage-assisted upsell once individual usage is proven) outperforms either pure motion alone.

Matches STRATEGY.md's existing primary-beachhead thesis (mid-market agents, not brokerages) and the concierge-MVP plan already in motion (checklist, pipeline, outreach templates, booking link) without requiring any new sales-cycle infrastructure (no deck, no procurement process, no brokerage IT/security review) that phase 1-2 was never resourced to build. Downside: doesn't by itself produce the Month-3 'land 1 brokerage pilot' goal — that still needs an explicit brokerage-facing motion eventually, just sequenced after, not instead of, agent-level proof.

Payment collection (p2-payment-collection)

How a concierge customer actually pays the $19 — a zero-setup, zero-new-infrastructure answer to the same class of silent gap p2-booking-link found for the booking step. See process/payment-collection.ts for sourced comparisons and the human step (confirming the apps below deliver to the listed inbox) that's still tracked in blockers.ts.

Decision: zelle primary, paypal fallback

Lead with Zelle (zero fee, fastest, bank-to-bank) since it's the cheapest real option and Duncan's bank almost certainly supports it like the large majority of US banks do; offer PayPal (goods & services) as the fallback for any prospect whose bank doesn't participate in Zelle or who simply prefers PayPal's buyer protection on a first purchase from a new service. Venmo is documented as a distant third option (see paymentMethodOptions) but not offered by default since no handle is confirmed for it yet. Both primary options route to the same already-known real inbox, duncan.andrew.haywood@gmail.com, that outreach-templates.ts and booking-link.ts already use — no new business infrastructure (merchant account, invoicing tool, payment processor integration) needed to take the first paid concierge delivery.

Revisit when: Revisit once volume or price moves past what ad hoc peer-to-peer transfers comfortably support — e.g. multiple deliveries a week, a move to the Agent Pro monthly subscription STRATEGY.md describes, or a prospect who needs a real invoice/receipt for bookkeeping. At that point a real merchant processor (Stripe Payment Links is the standout zero-monthly-fee option — pay-per-transaction, hosted checkout page, real receipts) is the natural next step, not before; premature Stripe onboarding would violate the 'no spend of real money / no paid tools' guardrail for no benefit at $19/listing, one-off volume.

Zelle (bank-to-bank transfer)

None beyond enrolling the receiving bank account/email with Zelle, a one-time step already inside most US banking apps. $0 — Zelle does not charge sender or receiver fees; funds move bank-to-bank, typically within minutes.

Prospect sends $19 via their bank's Zelle feature (or the standalone Zelle app) to duncan.andrew.haywood@gmail.com, with the listing address in the memo/note field.

No fee and fastest settlement of the three, but both sender and receiver need a US bank that participates in Zelle (most major banks do, but not all, and it's not usable by anyone without a US bank account) and it moves money before any dispute process exists — fine for a $19 concierge fee paid after delivery, riskier at higher price points later.

PayPal (paypal.me link or direct send)

None if a personal PayPal account already exists (same email works); a paypal.me/username vanity link is free and optional. $0 if the prospect sends as "friends & family"; if sent as "goods & services" (buyer protection on), PayPal's standard US rate is roughly 2.9% + $0.30, which is about $0.85 on a $19 payment.

Prospect sends $19 to duncan.andrew.haywood@gmail.com via PayPal, noting the listing address; goods & services is the honest choice for a paid service (adds the small fee above) since friends & family is meant for payments with no purchase attached.

Works for prospects without a US bank/Zelle (PayPal balance, card, or linked bank all work), and goods & services gives the customer real buyer protection on their first $19 purchase from a brand-new concierge service, which is worth more at this trust-building stage than the ~$0.85 fee. Slower than Zelle (can take a day or two to bank-transfer out) and requires both parties to have or create a PayPal account.

Venmo

None if a personal Venmo account already exists. $0 for a standard balance/bank-funded personal payment marked as goods & services still carries Venmo's ~1.9% + $0.10 seller fee (about $0.46 on $19); card-funded payments add a further ~3% sender fee.

Prospect sends $19 to @[Duncan's Venmo handle] (not yet claimed/confirmed — see blockers.ts) noting the listing address, toggled to goods & services.

Lowest goods-and-services fee of the three card/wallet options and a younger-skewing user base that may already have it installed, but unlike Zelle/PayPal there is no existing, confirmed handle tied to duncan.andrew.haywood@gmail.com in this repo yet, and it shares PayPal's US-only, both-sides-need-an-account limitation without a durable link like paypal.me. Ranked third: only worth setting up if a real prospect specifically asks for it.

Referral & follow-up (p2-referral-followup)

The growth step past the first delivery: a ready-to-send check-in that asks for a testimonial and a referral once an agent has actually seen the photos work. See process/referral-followup.ts. Timing: 3-7 days after delivery, once the listing has had time to go live and get real showing traffic — sending it same-day as delivery makes both asks premature.

Incentive: discount, not cash

Both sides get a discount off a future paid delivery, not a cash payout — no new spend, only foregone margin on revenue that wouldn't exist without the referral.

Referrer: $10 off their next $19 listing (so $9) once the referred agent's first listing is paid

Referee: $5 off their first listing ($14 instead of $19)

Leads & bookings funnel

No real outreach has been sent yet — this fills in once a human uses process/outreach-templates.ts against real prospects (see process/leads-log.ts).

identified: 0
contacted: 0
responded: 0
booked: 0
shot complete: 0
delivered: 0
paid: 0
declined: 0

Decisions log

Throttle full blocker re-verification passes to once per ~3 hours instead of every session

2026-07-13 · claude/blocker-throttle-0713

process/blockers.ts's HumanBlocker entries gain a lastConfirmedAt timestamp (in addition to the existing date-only lastConfirmed) and an exported recentlyConfirmedWithinHours(hours) helper. prompt_process.tsx's step 0 now tells a session to check that first: if every blocker was confirmed within the last 3 hours, do a cheap check only (empty logs still empty? any new merges? nextActionableTasks() still empty?) and skip creating a branch/commit/PR if nothing changed, rather than repeating a full re-verification pass and pushing another 'still blocked, nothing changed' branch. Also logged a new, different kind of blocker — cron-cadence-too-frequent — flagging for Duncan that the sub-daily cron cadence itself may be worth loosening now that the business has been fully human-blocked for multiple days running.

This session found three same-day branches (claude/blocker-recheck-0713a/b/c) that each independently re-verified the identical unchanged set of ~5 human blockers and pushed a throwaway branch to record it — real cost (a branch, a commit, a CI run) for zero new information, driven by at least three separate hourly-or-tighter cron triggers hitting this repo and its sibling local-site-studio concurrently while nextActionableTasks() has been empty for days. Date-only lastConfirmed couldn't distinguish a recheck 5 minutes old from one 20 hours old on the same calendar day, so nothing in the data itself could tell a session it was about to duplicate very recent work. A 3-hour window was chosen as a compromise: short enough that a human resolving a blocker gets picked up within a few cron ticks, long enough to eliminate the same-hour pile-up actually observed today. This is a process/tooling decision, not a product one — no roadmap task depended on it, but prompt_process.tsx explicitly permits spending a session on process improvement when nothing product-facing is agent-actionable, which was the case this session (nextActionableTasks() empty, all logs still empty).

Make the on-site capture-time promise computed, to stop it drifting (and being independently re-fixed) again

2026-07-13 · claude/dazzling-allen-wrkyk8

process/capture-checklist.ts gains onSiteTimeRange(), which rounds totalTargetMinutes() outward to the nearest 5 minutes on each end (default home: '35-40 minutes'). process/pricing-one-pager.tsx now computes its two on-site-time copy lines from this function instead of a hardcoded string. process/outreach-templates.ts's 3 template bodies stay hand-typed (they're plain string literals, not template functions) but gained a doc comment pointing at onSiteTimeRange() as the source of truth to check by eye.

This session opened a PR (#34) that duplicated PR #33's fix for the exact same '~30 minutes' -> '~35-40 minutes' promise-drift bug, merged roughly a minute apart by two independent sessions auditing the same gap at the same time; #34 hit a real merge conflict against #33's already-landed fix. Both sessions independently computed the same correct number by hand from totalTargetMinutes(), which confirms the number was right, but the near-simultaneous duplicate is itself evidence the underlying design was fragile: a promise with no code connecting it to the value that's supposed to back it will keep drifting (and keep getting rediscovered/refixed from scratch) every time capture-checklist.ts's shot list changes. Closing the loop for at least the computed surface (pricing-one-pager.tsx) removes that specific recurrence risk permanently; outreach-templates.ts's static prose is left as a documented manual-sync point since making it a template function was judged more churn than the actual risk (3 short strings, unlikely to change again soon) currently justifies.

Correct the customer-facing '~30 minute' capture-time promise to match the scaled checklist

2026-07-13 · claude/mls-photo-count-drift-fix-0713

process/pricing-one-pager.tsx (2 spots) and process/outreach-templates.ts (3 spots, across the agent cold-email, in-person script, and brokerage-intro templates) now quote '~35-40 minutes' for the guided on-site capture step instead of '~30 minutes'. STRATEGY.md's own tagline/positioning ('in 30 minutes') is deliberately left as-is — same precedent as the 'under 2 hours' STRATEGY.md delivery target being left loose while decisions-log.ts's actual customer-facing turnaround promise was independently tightened to '1 business day'.

process/capture-checklist.ts's totalTargetMinutes() (scaled by p2-capture-checklist-scaling on 2026-07-12 to hit the promised 15-25 photo range) now computes 33-47.5 minutes depending on bed/bath count, 38.5 for the typical-home default — every one of those is already above the '~30 minutes' quoted verbatim in the actual outreach copy a human will read to a real prospect. This is the same silent gap p2-promise-consistency-audit found in delivery-message.ts's guarantee wording: one session's fix (scaling the checklist for photo-count accuracy) created a new drift in a claim living in a different file, and nobody had re-checked outreach-templates.ts's specific time promise since. Also updated capture-checklist.ts's own preShootPrep doc comment, which still cited the old '30-minute-fix' figure.

Fix stale photo-count claim left in mls-compliance.ts by the checklist-scaling fix

2026-07-13 · claude/mls-photo-count-drift-fix-0713

process/mls-compliance.ts's photo-count-guidance rule now states the real current capture-checklist.ts default (19 shots for a typical home; 16-22 depending on bed/bath count) instead of the pre-scaling '~9-11 shots' figure that p2-capture-checklist-scaling (2026-07-12) made obsolete but never updated here.

mls-compliance.ts is the one file whose entire job is to state accurate, sourced facts about MLS photo rules, and it's rendered live on the docs site — a stale number there is worse than the same staleness elsewhere, since a reader would reasonably treat it as current. This is the same class of drift p0-docs-content-drift-fix and p2-promise-consistency-audit already found and fixed in other files (a change in one file not propagating to a related claim in another); found by doing the exact cross-file numeric audit (turnaround times, photo counts, fee percentages) the prior session's progress-log entry suggested as the next check, since nextActionableTasks() was empty and leads-log.ts/loggedInterviewResults/listingAuditLog remain empty.

Make the delivery guarantee wording depend on repeat-client status instead of always claiming 'first delivery'

2026-07-13 · claude/promise-consistency-audit-0713

process/delivery-message.ts's deliveryMessageText() gained an optional isRepeatClient input (default false). First-delivery output is byte-for-byte unchanged ('the redo is free on this first delivery' / 'Thanks for trying Listing Lens!'); when isRepeatClient is true it instead says 'same free-redo guarantee as always' / 'Thanks again for using Listing Lens!'. Also re-checked pricing-one-pager.tsx, outreach-templates.ts, payment-collection.ts, and referral-followup.ts against each other for price ($19), turnaround (1 business day), and payment-instructions drift — all consistent, no further changes needed there.

outreach-templates.ts, delivery-message.ts, payment-collection.ts, and referral-followup.ts were each written by a different session, one funnel step at a time, with no session ever cross-reading all of them for a promise that drifts between surfaces. deliveryMessageText() unconditionally told every recipient this was their first delivery, which is wrong on its face once referral-followup.ts's own incentive loop produces a repeat or referred booking — a minor inconsistency today (zero real deliveries so far) but exactly the kind of silent gap this repo's progress-log keeps finding after the fact. Cheap to fix now, before any real customer sees the message.

Fix stale Host-pack pricing copy and eliminate the docs-site content duplication that let it drift

2026-07-13 · claude/dazzling-allen-mf5cdg

STRATEGY.md's own Business model table and research/'s public site both said Host pack (Airbnb/STR) pricing was a flat '$19/listing', contradicting the actual pricing decision this repo logged on day one ($19-79/listing, scaling with unit size — see the 2026-06-13 'Pricing anchors' entry above) and contradicting every other place that figure appears (process/interview-script.ts, process/outreach-templates.ts's str-host-dm template). Fixed both to read '$19-79/listing'. Separately, the private docs site's /docs/strategy page was rendering docs-site/content/strategy.md, a manually hand-copied duplicate of root STRATEGY.md that had silently fallen behind — it still showed 3 open questions (MLS compliance, virtual staging tech, brokerage-vs-PLG GTM) as unanswered checkboxes weeks after STRATEGY.md itself had checked them off. Rather than just re-syncing the copy (which would drift again), changed docs-site/src/lib/docs.ts to read STRATEGY.md and docs/REPLICATION_PROMPT.md directly from their canonical repo-root locations at build time, and deleted the now-unused docs-site/content/strategy.md and docs-site/content/replication.md duplicates entirely.

Same root cause blockers.ts fixed for human-blocker facts (three independently hand-maintained copies drift apart quietly until something forces a diff), just discovered in a different pair of files. A manually-synced copy is a standing liability the moment the canonical source changes and nobody remembers to re-copy it — the fix that actually prevents recurrence is removing the duplicate, not re-syncing it once more. docs-site/content/deployment.md has no canonical source elsewhere in the repo, so it was left as a real file, just resolved via the same repo-root-relative path for consistency.

Add a post-delivery testimonial + referral follow-up, with a discount (not cash) incentive

2026-07-13 · claude/dazzling-allen-l25h2i

Added process/referral-followup.ts's followUpMessageText(): a message sent 3-7 days after delivery (once the agent has seen real showing traffic) that asks for a concrete, usable testimonial quote and offers a referral to another agent. The incentive (REFERRAL_INCENTIVE) is a discount off a *future* paid delivery for both sides ($10 off for the referrer, $5 off for the referee), not a cash payout, so it costs nothing until a referral actually converts and doesn't violate the 'no spend of real money' guardrail.

The funnel had messages for getting booked, paying, and the delivery handoff, but nothing for the moment that actually grows the business past a single $19 transaction. STRATEGY.md's target customer has 4-12 listings/month and mid-market agents talk to each other; with ad spend and paid tools both ruled out by the guardrails, word-of-mouth from a satisfied first customer is the cheapest credible growth channel available, and this repo had never built the ask for it.

Combine the delivery handoff and payment ask into one ready-to-send message

2026-07-13 · claude/dazzling-allen-rb6omk

Added process/delivery-message.ts's deliveryMessageText(), which composes the listing address, photo count, an optional microsite mention, payment-collection.ts's paymentInstructionsText(), and the existing free-redo guarantee into a single ready-to-paste message. Wired it into pipeline/src/cli-microsite.ts so it prints automatically right after a successful microsite build — the exact moment a real delivery is ready to hand over.

Same class of silent gap as booking-link.ts (no way to book) and payment-collection.ts (no way to pay), just one step later in the funnel: p2-first-deliveries' doneWhen says 'deliver it' and STRATEGY.md's 5-step workflow ends at 'Brand' (the microsite), but nothing ever defined what a human actually sends at handoff. Without this, every real delivery would require improvising a message from scratch and re-deriving the payment ask by hand each time, risking the same kind of inconsistency (or omission) that let the booking-link and payment-collection gaps go unnoticed for weeks. Printing it straight from the microsite CLI (rather than a separate step to remember) means the first real delivery gets the message for free, not as a thing to look up.

Collect the $19 concierge payment via Zelle (primary) / PayPal goods & services (fallback)

2026-07-13 · real-estate-photography-continuous-improvement-2

process/payment-collection.ts defines how a concierge customer actually pays: lead with Zelle (zero fee, fastest, bank-to-bank) to duncan.andrew.haywood@gmail.com, with PayPal goods & services (~$0.85 fee on $19, buyer protection) as the fallback for prospects whose bank doesn't support Zelle or who prefer PayPal. Venmo is documented as a distant third option but not offered by default since no handle is confirmed for it. pricing-one-pager.tsx's howItWorks step 4 and a new FAQ entry now state this; leads-log.ts's Lead type gained optional paymentMethod/paymentReference fields and pipeline/src/log-lead.ts's CLI validates and accepts them (paymentReference only valid with --stage paid).

This was the same class of silent gap p2-booking-link found for the booking step: pricing-one-pager.tsx has said 'Book your first listing for $19' and leads-log.ts has tracked a 'paid' stage since early sessions, but nothing anywhere ever said HOW a prospect pays — no method, no instructions, no FAQ mention. A prospect who read the offer, replied to book, and got their photos delivered had no stated way to actually pay. Zelle-primary/PayPal-fallback was chosen over Venmo (no confirmed handle yet, higher effective fee) and over standing up a real merchant processor like Stripe (real setup cost and a paid tool, which the guardrails explicitly rule out before there's proven demand) — both route to the same already-known real inbox already used by outreach-templates.ts and booking-link.ts, so zero new business infrastructure is needed to take the first paid delivery. Confirming the inbox actually receives funds via these apps remains a human step, tracked separately in blockers.ts's payment-handle-setup entry — this decision defines the process, not the confirmation.

Patch docs-site/research off next@14.1.0 to next@14.2.35 (same major); defer the full next@16 migration

2026-07-13 · real-estate-photography-continuous-improvement-2

Bumped `next` from 14.1.0 to 14.2.35 in both docs-site/ and research/ (patch-only within the same major version, no App Router / config API changes needed). Verified `npm run build` still passes clean in both. Deliberately did NOT go further to next@16.x, which is the only version `npm audit` reports as clearing every currently-known advisory for this package (npm's audit range for `next` was reported as a single span from 9.3.4-canary.0 up to 16.3.0-canary.5, i.e. it considers all of 14.x and 15.x still partially vulnerable to at least one advisory in that combined set) — that's a major-version App Router migration with real breaking-change risk, not a same-session patch bump, and this repo has no automated visual/e2e regression coverage for docs-site or research to catch a silent breakage before a human notices.

`npm install` was printing `next@14.1.0: This version has a security vulnerability` on every session that happened to touch docs-site or research, and `npm audit` on 14.1.0 listed 14 next.js advisories (several critical: DoS via Image Optimizer, HTTP request smuggling in rewrites, middleware authorization bypass, RSC cache poisoning) that no prior session had noticed or acted on — it was pure incidental noise in install logs until this session actually read it. The patch bump closes the specific, already-fixed-in-14.x-line vulnerabilities at zero behavioral risk (build output is byte-for-byte the same route structure, verified). Doing the full 14->16 jump unilaterally in an hourly automated session — especially right when p0-docs-site is about to go from Access-blocked-but-live to actually protected once PR #4 merges — is exactly the kind of change that needs a human able to click through the deployed site afterward, not an agent's local `npm run build` passing as the only signal. Left as a flagged, scoped follow-up rather than either ignoring it (prior status quo) or attempting it blind.

Consolidate human-blocker tracking into one file (process/blockers.ts)

2026-07-13 · claude/dazzling-allen-k4o1nz

Added process/blockers.ts as the single source of truth for what's blocked on a human right now, replacing three separately hand-maintained copies of the same ~5 facts (README.md prose, roadmap.ts blockedReason fields, and a fresh progress-log.ts paragraph almost every session). A session that re-checks and finds nothing changed now bumps a blocker's lastConfirmed/confirmedByCount field instead of writing a new progress-log entry; README.md and prompt_process.tsx's dashboard both read from blockers.ts instead of maintaining their own prose list.

By this date, roughly two dozen agent sessions in a row had found every agent-doable roadmap task already done and spent part of the session re-describing the same 5 human-blocked items in prose, each written independently. This is real, observable waste (progress-log.ts had grown to ~300 lines with much of it repeating 'still blocked, nothing changed') and a real drift risk (three independently hand-written copies of the same facts can silently disagree — README's blocker list and roadmap.ts's blockedReason text were already independently worded even though they describe the same underlying facts). This doesn't resolve any blocker or invent agent-doable work where none exists; it's the kind of process-improvement session prompt_process.tsx's step 4 explicitly allows when nothing else is actionable, aimed specifically at a failure mode this repo's own history demonstrates.

Lead with agent-led PLG; sequence brokerage sales as a later expansion step

2026-07-12 · real-estate-photography-continuous-improvement-2

Added process/gtm-motion.ts, closing STRATEGY.md's last unaddressed open question (brokerage sales cycle vs agent PLG; the other, v1 app scope, is correctly deferred via p3-app-scope's gatedOn). Desk research (general B2B SaaS sales-cycle benchmarks, PLG-vs-sales-led literature, and 2026 real-estate-specific AI-tool-adoption reporting) supports leading with bottom-up agent PLG now and treating a brokerage-led sales motion (STRATEGY.md's $249/mo Team tier) as a Month-3 expansion step that comes after real paying agent customers exist, not a parallel or earlier motion.

This doesn't change STRATEGY.md's existing 90-day plan (agent interviews and concierge MVP first, brokerage pilot in Month 3) — it answers the open question with sourced reasoning instead of leaving the checkbox unticked. The strongest single piece of evidence: 2026 real-estate-specific reporting (Inman, May 2026) found brokerages already buying AI/tech licenses and announcing them internally, then individual-agent adoption stalling within about 90 days, with agents defaulting back to their own preferred tools even after a brokerage-wide purchase. A brokerage-led motion here risks closing a Team-tier contract with near-zero real usage and no fast signal; an agent-led PLG motion (a $19 concierge delivery is functionally a paid trial) produces a direct pay/no-pay signal per prospect, which is what phase 2 actually needs. General B2B SaaS data reinforces this: PLG motions convert faster and post better Rule-of-40 scores than pure sales-led motions, and hybrid PLG-then-sales-assist outperforms either motion alone once real usage exists to sell into.

Property microsite is a self-contained static HTML file, not a hosted page

2026-07-12 · claude/dazzling-allen-v3kn1n

Added pipeline/src/microsite.ts + cli-microsite.ts: reads an already-enhanced delivery folder and writes one index.html gallery page (agent name/logo/headshot/contact, plus the delivered photos) alongside the existing photo files. Deliberately a static file the agent hosts/forwards however they like (email attachment, drag into any free static host, open locally) rather than a new deployed service — same reasoning as the booking-link decision: no new domain/hosting/Cloudflare Access decision, zero infra cost, fully agent-buildable today.

STRATEGY.md's 5-step workflow (Guide -> Enhance -> Polish -> Deliver -> Brand) had 4 of 5 steps built or explicitly deferred (Polish/virtual-staging), but 'Brand' — agent headshot, logo, and property microsite — had never been started; every session since the repo's first commit focused on outreach/pricing/tracking prep, not on finishing the product itself. This closes that gap without opening a new scope debate: it never touches the underlying MLS-bound photo files (enforces the same no-branding-on-MLS-photos guardrail from process/mls-compliance.ts by construction, since it only ever writes one new HTML file next to them, never overwrites them), and needs no real agent logo/headshot to exist yet (both fields are optional, never fabricated).

Booking link is a mailto:, not a hosted landing page

2026-07-12 · claude/dazzling-allen-ucfuio

Added process/booking-link.ts: a mailto: link addressed to Duncan's real inbox, pre-filled with outreach-templates.ts's bookingIntakeFields as fill-in-the-blank prompts. This replaces the literal '[Phone / booking link]' placeholder in the cold-email/brokerage-intro templates and pricing-one-pager.tsx's callToAction (which told prospects to 'scan the QR code' — no QR code has ever existed). Considered and deferred: a hosted public booking/landing page. research/ builds locally but is deployed nowhere; docs-site is deployed but access-gated to Duncan's email only, so it can't host a public booking page without either removing Access (a real Cloudflare Access production change, guardrailed against without confirmation) or standing up a second Cloudflare Pages project (a new domain/infra decision, plus doesn't fit here as a same-session code diff).

This has been a real, silent gap for weeks: every prior session built prep material (checklist, pricing, outreach copy, pipeline, lead intake) on the assumption a prospect could eventually book, but nothing ever made that step possible — the templates and one-pager pointed at things that didn't exist. A mailto: link costs nothing, needs no new infrastructure, needs no domain/hosting decision, and doesn't touch the guardrailed Cloudflare Access policy, so it's fully agent-buildable within existing constraints. A hosted form (Formspree/Tally/a public Pages project) would convert somewhat better but is a bigger, separable decision (which service, whether it's worth a second public deploy before any revenue exists) that should wait for Duncan's input rather than an agent silently standing up new infrastructure.

Formalize p3-app-scope's sequencing as a code-enforced gate, not a repeated note

2026-07-12 · real-estate-photography-continuous-improvement

Added an optional gatedOn: string[] field to RoadmapTask (process/types.ts) and an isGateSatisfied() check in nextActionableTasks() (process/roadmap.ts). p3-app-scope is now gatedOn: ['p1-run-interviews', 'p2-first-deliveries'], so it no longer appears in nextActionableTasks() output until both produce real validation data, even though its own status stays 'not_started'. The docs-site dashboard renders a 'Gated on: ...' note on any task whose gate isn't satisfied yet.

Every session since p3-app-scope was added (7+ in a row) has independently re-derived and repeated the same instruction in progress-log.ts: 'do not start p3-app-scope yet, it's sequenced after interview + concierge validation data exists.' That reasoning lived only in prose, scattered across nextUp entries, with no code enforcement — a single session skimming only roadmap.ts + prompt_process.tsx (not the full progress-log history) could plausibly have started it, since nextActionableTasks() was structurally including it as an owner:'both'/not_started task. This is exactly the kind of process gap AGENT_PROCESS_INSTRUCTIONS step 4 asks a session to fix when there's no direct product-doable gap left: turning a repeatedly-restated tribal rule into an enforced, greppable precondition.

Zillow listing audit gets a fixed 6-dimension scoring rubric before any real listings are collected

2026-07-12 · claude/dazzling-allen-j509d4

Added process/listing-audit-rubric.ts: 6 concrete 1-5 dimensions (lighting/exposure, composition/angle, staging/declutter, photo count/coverage, resolution/sharpness, color/white balance), each with explicit worst-vs-best guidance, plus computeOverallScore() (rescales the dimension average to STRATEGY.md's 1-10 'blind quality rating' scale) and auditSummary(). The rubric ships with an empty listingAuditLog — no fabricated listings.

p1-listing-audit has stayed human-blocked since the repo's first commit, but the blocker was never just 'needs a human to browse Zillow' — nothing defined what 'mediocre' vs 'excellent' meant, or how 20 scored listings become the single 1-10 number the day-90 success bar (>= 7/10) depends on. An undefined rubric risks inconsistent scoring across listings or a rescore later. This mirrors interview-script.ts's existing scoreWillingnessToPay() pattern: agents can't run the human step, but they can make the instrument objective and ready ahead of time so the human's only remaining job is picking a metro and looking at listings.

Close duplicate PR #13 rather than merge overlapping virtual-staging research

2026-07-12 · claude/checklist-novice-audit

PR #13 (branch claude/dazzling-allen-vstaging-2937) and PR #14 (branch claude/dazzling-allen-q7uwlq) independently answered STRATEGY.md's same 'on-device vs cloud for virtual staging' open question within about 10 minutes of each other, reaching the same recommendation (low-cost pay-per-image cloud API, not on-device, deferred until real volume/an app exists). #14 merged first; #13 was closed unmerged with a comment pointing to #14, rather than merged as a second, overlapping process/virtual-staging-*.ts file. AGENT_PROCESS_INSTRUCTIONS (prompt_process.tsx) gained a new step 2a — claim a task by committing its in_progress status to main as the first action of a session — to reduce how often this race recurs.

Two independent research passes agreeing is a mild positive signal for the decision itself, but landing both artifacts would leave two near-duplicate files and two duplicate roadmap tasks with no added value, which is worse for future-agent clarity than picking one. This is exactly the scenario prompt_process.tsx step 8 (check other open PRs before merging) exists to catch, and it worked as designed here — the fix is making the race less likely to happen at all, not changing the merge-conflict-resolution step itself.

Future virtual-staging Polish step will call a cloud API, not on-device Core ML

2026-07-12 · claude/dazzling-allen-q7uwlq

When the Polish step's AI virtual-staging feature is eventually built, it will call a pay-per-image cloud API (e.g. a Virtual Staging AI-style vendor, roughly $1-2/image) rather than running Stable-Diffusion-class inpainting on-device via Core ML. See process/virtual-staging-tech.ts for the full comparison and sources.

Desk research on both real-estate-specific virtual-staging vendors and general mobile-ML architecture agrees the crossover point where on-device/custom infrastructure beats a pay-per-call API is roughly 1,000-15,000+ images/month — Listing Lens has zero real deliveries yet (p2-first-deliveries is still human-blocked) and no iOS app (phase 3 not started). A cloud API adds zero fixed cost until a real paid listing needs it and requires no new app/ML infrastructure, keeping this decision consistent with the existing scope guard against building the iOS app or over-engineering infrastructure in phase 1-2. On-device Core ML diffusion is technically feasible on iPhone 15 Pro-class hardware (~8-15s/image per Apple's own research) but would also gate quality/availability by device tier, which a $19 'good enough, fast, cheap' concierge offer shouldn't take on this early. Revisit once an app exists and real monthly staging volume approaches the crossover range.

Lead intake stays a validated append-only CLI, not a form/backend

2026-07-12 · claude/dazzling-allen-y9fqb5

process/leads-log.ts entries can now be added via `npm run log-lead` in pipeline/ (pipeline/src/log-lead.ts) instead of only by hand-editing the TypeScript array. The tool requires every field as explicit input (no defaults, no fabricated content), validates audience/channel/stage against the existing typed enums, supports --dry-run to preview before writing, and is covered by a self-test that only operates on an in-memory copy of the file shape — it never touches the real leads-log.ts during npm test.

Two prior sessions' progress-log nextUp entries flagged the same gap: the highest-leverage remaining task (p2-first-deliveries) depends on a human logging real replies fast, and hand-editing a TypeScript array is real friction at the exact moment (right after a prospect responds) a human is least likely to open an editor. A small CLI removes that friction without adding a database, a form, or any new infrastructure — consistent with the scope guard against over-engineering backend/auth. It deliberately still requires a human to type real values in; the tool formats and validates, it does not decide what a lead's stage is.

Add CI workflow to automatically gate pipeline tests + docs-site/research builds

2026-07-12 · claude/dazzling-allen-luc0w5

Added .github/workflows/ci.yml, running on every push and pull_request: pipeline/'s `npm test` + `tsc --noEmit`, plus `npm run build` in docs-site/ and research/. This is separate from the existing deploy-docs.yml, which only builds+deploys docs-site on pushes to main.

prompt_process.tsx step 7 has always told each agent session to manually run these checks before pushing, but with multiple agent branches active concurrently (git ls-remote showed 4 claude/* and 4 cursor/* branches at once this session), a manual-only gate is easy to skip and a regression from one branch could reach main undetected. Automating it costs nothing (agent-buildable, no real spend, uses GitHub's free Actions minutes) and directly helps cross-agent cooperation by catching breakage before merge instead of relying on the next session noticing.

Capture checklist scales per-bed/bath instead of a fixed shot count

2026-07-12 · claude/dazzling-allen-luc0w5

process/capture-checklist.ts's secondary bedroom and secondary bathroom entries are now per-additional-unit rooms (one wide shot per extra bed, one per extra bath) instead of a single fixed 'secondary-rooms' entry. pipeline/'s coverage check and CLI take optional --beds/--baths (total counts for the listing); omitting them falls back to a typical ~3-bed/2-bath home. New fixed detail shots were also added (entry detail, living-room feature, dining area, kitchen finish detail, primary closet, primary vanity/tub, a flex space) to round out the room-by-room coverage.

process/outreach-templates.ts's cold-outreach copy already promises prospects '15-25 enhanced, MLS-ready photos', but the checklist as originally built specified only ~9-11 fixed shots regardless of home size — an unbacked promise the moment a real prospect responds. A fixed count was also structurally wrong: a studio and a 5-bed house shouldn't get the same shot list. The typical-home default now produces 19 shots (inside the promised range); a 5-bed/3-bath listing produces 22; a 0-extra-bed/bath listing produces 16 — all still land in or near the 15-25 range mls-compliance.ts cites as the competitive best-performing count on major portals.

MLS photos stay unbranded; virtual staging (when built) defaults to the strictest disclosure

2026-07-11 · claude/dazzling-allen-7i8o70

Two guardrails going forward, based on public MLS-board rules research (process/mls-compliance.ts): (1) the pipeline/ enhancement CLI and any future 'Brand' step must never write agent/brokerage names, logos, watermarks, or contact info onto MLS-submitted photo files — branding stays confined to marketing collateral like a property microsite or flyer. (2) If/when a Polish-step virtual staging feature is built, it must default to the strictest disclosure behavior available (a visible 'Virtually Staged' caption on the altered image, plus keeping the original unaltered photo in the delivery) regardless of which state/board the listing is in, since the pipeline has no reliable way to detect that at run time.

Roughly three-quarters of states now require some form of virtual-staging disclosure, several (including California, effective Jan 1 2026) require a per-image label, and MLS boards fine broker accounts automatically (no notice) for branding on submitted media — real financial and compliance risk to a business that doesn't exist yet to absorb it. The current pipeline already complies (no watermarking, no branding) by construction; this decision makes that an explicit constraint instead of an accident, so a future session adding the 'Brand' or 'Polish' step doesn't regress it.

Enhancement pipeline is a local deterministic CLI, not a cloud AI API

2026-07-11 · claude/dazzling-allen-rkmj0x

The Phase 2 'manual AI pipeline' is a local Node/TypeScript CLI (pipeline/, using sharp) doing auto-orient, per-channel contrast/white-balance normalization, sharpening, resize, and MLS-ready renaming — not a call to a paid cloud vision/image API.

Every prior session described the Enhance step in prose but none built it, leaving the 1-business-day turnaround claim in the pricing one-pager unbacked by anything runnable. A local pipeline is free to run (no per-image API cost, no API key setup) and fully agent-buildable/testable without real listing photos or human sign-off on spend, matching the 'no spend of real money' guardrail in prompt_process.tsx. It intentionally does not attempt virtual staging, twilight conversion, or object removal — those stay the human/cloud 'Polish' step per STRATEGY.md's 5-step workflow, and can graduate to a paid API once concierge revenue justifies the cost.

Concierge MVP offer terms locked for outreach

2026-07-11 · claude/dazzling-allen-cpghie

The first concierge offer to prospects is: $19/listing flat, 15-25 enhanced MLS-ready photos, delivered within 1 business day, free re-edit guarantee if unhappy. Outreach stays manual/one-at-a-time (email, DM, in-person, brokerage intro) — no bulk or automated sending.

STRATEGY.md set the $19/listing validation price and 'deliver in under 2 hours' target loosely; a concrete, single offer with a guarantee is needed before any human can actually pitch a prospect. 1-business-day turnaround (vs. STRATEGY's 'under 2 hours') is the honest agent-achievable claim until a real delivery pipeline is timed end-to-end; tighten this claim once p2-first-deliveries produces real turnaround data. Manual-only outreach avoids spam/compliance risk while the offer is unvalidated.

Adopt a typed TS/TSX process system for cross-agent coordination

2026-07-11 · claude/dazzling-allen-wg8i3m

Track roadmap, decisions, and progress in process/*.ts(x) instead of ad-hoc markdown. Docs (strategy, deployment, etc.) also default to TS/TSX going forward unless there's a strong reason to use plain markdown.

Multiple agents (this session, plus 'cursor/*' branches) work this repo independently on different schedules with no shared memory beyond git history. A typed, greppable, buildable process file gives every session a fast, structured way to see what's done, what's blocked on a human, and what to do next — cheaper than re-deriving state from STRATEGY.md prose and commit logs each run. TS/TSX also lets the process render directly as a page on the private docs site instead of staying a file nobody reads.

Scope guard: no iOS app, no backend/auth build-out in phase 1

2026-06-13 · initial-scaffold

Do not build the iOS app or over-engineer backend/auth/Convex until problem interviews and a concierge MVP validate willingness to pay.

STRATEGY.md's 90-day plan is interview-first, concierge-MVP-second, app-third. Building the app before validation risks wasted engineering effort on the wrong wedge.

Pricing anchors set at $29-99/mo and $19-79/listing

2026-06-13 · initial-scaffold

Starter $29/mo, Agent Pro $79/mo, Team $249/mo (agents/brokerages); Host pack $19-79/listing (STR). Validation pricing starts lower: $19/listing concierge, $8-15/listing transactional test.

Anchored against real agent photography spend (~$150-500/listing or $300-1,200 opportunity cost) and STR payback data (photography ROI in ~1 night for 85% of hosts).

Lead with agents, not Airbnb/STR hosts

2026-06-13 · initial-scaffold

Listing Lens leads with mid-market real estate agents as the primary subscription wedge. Airbnb/STR hosts are a secondary, product-led, one-time-pack market.

Agents have recurring demand (4-12 listings/month) and a clear spend anchor (photographer invoices). Single-property STR hosts are episodic and churn-prone; multi-property managers behave like agents but are a smaller pool.

Progress log

recentlyConfirmedWithinHours(3) was true (blockers last confirmed 51 minutes earlier by PR #39) and the cheap check found nothing new (leads-log.ts/listingAuditLog/loggedInterviewResults still empty, nextActionableTasks() still empty, PR #4 still the only open PR, still draft/unchanged) — so per prompt_process.tsx step 0a, no full re-verification pass or 'still blocked' branch was warranted on its own. Instead used the session to finish work the stale-branch-cleanup blocker's own text had flagged as incomplete: every prior session had only spot-checked 5 of the ~20 dazzling-allen-* branches for unique content, extrapolating to the other ~30+ non-main branches rather than actually checking them. Audited all 36 non-main branches this time, using a better method than the diff --stat one blockers.ts previously recommended (which false-positives on every stale branch since main has moved on): `git log origin/main..origin/<branch> --oneline` to list each branch's own unmerged commits, then cross-referenced every one of those commit subjects against `git log origin/main --oneline` by hand. Result: zero branches contain business value not already on main — every unique-looking commit subject turned out to match an already-merged main commit under near-identical wording (checklist-scaling/gate-sequencing/on-device-vs-cloud/capture-time/blocker-recheck variants all included), except cursor/fix-pages-access-4a7e, which is the already-tracked live PR #4. This closes the open question of whether any of the ~35 stale-looking branches might be hiding real unmerged work.

2026-07-17 · claude/dazzling-allen-on504g
  • process/blockers.ts: stale-branch-cleanup entry rewritten from 'spot-checked 5 of the ~20' to a full audit of all 36, with the corrected branch count (36, up 1) and the better diff methodology documented so a future session doesn't need to redo the exhaustive per-branch check unless the branch count itself changes. Bumped lastConfirmed/lastConfirmedAt/confirmedByCount on all 6 blockers (all independently re-verified true this session, not just carried forward).
  • No roadmap.ts or decisions-log.ts change — no task status flipped and no product/business decision made, just process rigor (closing a known gap in evidence quality for an existing blocker).
  • Verified: docs-site/ `npm run build` (imports prompt_process.tsx, which imports blockers.ts, so this type-checks the edits) passes clean; pipeline/ and research/ untouched, not rebuilt. Checked open PRs via the GitHub MCP server (list_pull_requests) — only #4 open, unrelated files, still draft/unchanged.

Next up

  • Human: same six items as every recent session remain the highest-leverage next steps — see blockers.ts (first concierge delivery, phase-1 interviews, Zillow listing audit, Cloudflare Access dashboard toggle + merge PR #4, confirm the Zelle/PayPal payment handle actually receives funds, and reconsider the cron cadence now that this has been true for over a month straight).
  • Agent (next session): the stale-branch audit is now exhaustive as of this session — no need to re-spot-check individual branches again unless `git ls-remote --heads origin`'s branch count changes from 36, in which case only the new branch(es) need the same commit-subject cross-reference, not the full set again.

nextActionableTasks() still empty, all three real-data logs (leads-log.ts, listingAuditLog, loggedInterviewResults) still empty, PR #4 still open/draft/unchanged — no product-facing progress possible this session. Found the actual cause of the repeated-recheck waste flagged in blockers.ts's own doc comment is still happening: three same-day, near-identical 'still blocked, nothing changed' branches (claude/blocker-recheck-0713a/b/c) sitting on the remote, each independently re-verifying the same ~5 blockers. Spent the session on the process improvement prompt_process.tsx explicitly permits when nothing else is agent-actionable: added real-time throttling so future sessions can skip a full re-verification pass (and the branch/commit/PR that comes with it) when one already happened recently.

2026-07-13 · claude/blocker-throttle-0713
  • process/blockers.ts: added lastConfirmedAt (ISO timestamp) to every HumanBlocker entry alongside the existing date-only lastConfirmed, and an exported recentlyConfirmedWithinHours(hours) helper. staleFirst() now sorts by the finer-grained lastConfirmedAt. Bumped confirmedByCount on all 5 pre-existing blockers (re-verified: still all true, PR #4 still open/draft, stale-branch count still 31 non-main per `git ls-remote --heads origin`) and added a 6th, different-in-kind entry — cron-cadence-too-frequent — flagging for Duncan that the sub-daily agent cron cadence itself may be worth loosening now that the business has been fully human-blocked for multiple days straight.
  • process/prompt_process.tsx: new step 0a tells a session to call recentlyConfirmedWithinHours(3) before spending a full session re-verifying blockers already re-checked within the last 3 hours; if true and a cheap check (empty logs still empty, no new merges, nextActionableTasks() still empty) finds nothing new, stop there — no branch/commit/PR. Old step 0 renumbered to 0b, otherwise unchanged.
  • roadmap.ts: added p2-blocker-recheck-throttle (done) under phase-2. decisions-log.ts: logged the throttle mechanism and the cron-cadence flag as one decision entry.
  • Verified: pipeline/ `npm install && npm test` (all 5 self-tests) + `npx tsc --noEmit` pass (untouched by this change, run as a sanity check anyway); docs-site/ `npm run build` passes clean (imports prompt_process.tsx directly, so this is the real type-check for the new blockers.ts fields/helper and the updated instructions string); research/ untouched, not rebuilt. Checked open PRs via the GitHub REST API directly with the session's injected GITHUB_TOKEN (no gh CLI available) — only #4 open, unrelated files, still draft/unchanged.

Next up

  • Human: same five items as every recent session (first concierge delivery, phase-1 interviews, Zillow listing audit, Cloudflare Access dashboard toggle + merge PR #4, confirm the Zelle/PayPal payment handle actually receives funds) remain the highest-leverage next steps — see blockers.ts. New: consider whether the agent cron(s) hitting this repo need to run as often as they currently do (see the new cron-cadence-too-frequent blocker) — this is a call only Duncan can make since it involves schedules outside this repo.
  • Agent (next session): call recentlyConfirmedWithinHours(3) first. If true and nothing new, do the cheap check only and stop — do not push a branch just to restate this entry. If a genuine human-blocker resolution or new business-relevant finding shows up, that still always warrants a real progress-log entry regardless of the throttle window.

Independently found and fixed the exact same '~30 minutes' capture-time promise-drift bug as the concurrent claude/mls-photo-count-drift-fix-0713 session, computing the identical honest number (35-40 minutes) by hand from capture-checklist.ts's totalTargetMinutes(). Opened PR #34 with that fix; by the time CI finished, PR #33 (the other session's near-identical fix) had already merged to main, and PR #34's merge attempt hit a real conflict. Rather than force through a duplicate, reset this branch onto the new main and kept only the genuinely additive delta: instead of re-landing the same hardcoded '35-40 minutes' strings a second time, made pricing-one-pager.tsx compute that value live from a new capture-checklist.ts export (onSiteTimeRange()) instead of quoting a hand-typed number — the same class of fix each fix-then-drift cycle in this repo's history has eventually converged on (see p0-docs-content-drift-fix's 'stop hand-syncing, read the canonical source' pattern), applied here to a computed number instead of a duplicated file.

2026-07-13 · claude/dazzling-allen-wrkyk8
  • process/capture-checklist.ts: added onSiteTimeRange(additionalBeds?, additionalBaths?) — rounds totalTargetMinutes() outward to the nearest 5 minutes on each end (default home: '35-40 minutes'), documented with the near-simultaneous-duplicate-fix incident as the reason it exists.
  • process/pricing-one-pager.tsx: imports onSiteTimeRange() and computes both on-site-time copy lines from it instead of the (now on main, from PR #33) hardcoded '~35-40 minutes' string — output text unchanged, but can no longer independently drift from the checklist.
  • process/outreach-templates.ts: left PR #33's hand-fixed '35-40 minutes' text as-is (these are static string literals, not computed); added a doc comment pointing at onSiteTimeRange() as the source of truth to check by eye if the checklist changes again.
  • pipeline/src/coverage.ts: comment cleanup (dropped a stale reference to a fixed '30-minute target'), independent of the duplicate — kept.
  • roadmap.ts: added p2-capture-time-single-source (done). decisions-log.ts: logged the near-duplicate-PR incident and the follow-up fix as its own entry. blockers.ts: bumped confirmedByCount on the 4 blockers re-verified this session and corrected the stale-branch count to 31 (up from 26, `git ls-remote --heads origin`).
  • Verified: pipeline/ `npm test` (all 5 self-tests) + `npx tsc --noEmit` pass; docs-site/ `npm run build` passes clean (9/9 static pages), spot-checked the built /pricing page's HTML for '35-40 minutes' in both places; research/ untouched, not rebuilt.

Next up

  • Human: process/blockers.ts unchanged in substance — p2-first-deliveries remains the single highest-leverage next step.
  • Agent (next session): before opening a PR for a fix found via a repo-wide audit (numeric drift, promise consistency, etc.), re-fetch origin and re-grep for the same symptom immediately before pushing, not just at session start — this session's near-duplicate happened because two sessions started their audits close enough together that neither saw the other's fix land mid-session. When a duplicate is found post-hoc, prefer rebasing onto the winner and shipping only the genuinely additive delta (as here) over re-litigating which PR was 'first'. With nextActionableTasks() empty again, check leads-log.ts/loggedInterviewResults/listingAuditLog for real entries first.

Only open PR on this repo was still the pre-existing, human-blocked cursor/fix-pages-access-4a7e (#4); leads-log.ts/loggedInterviewResults/listingAuditLog all still empty, nextActionableTasks() still empty. Took up the prior two sessions' own suggestion to hunt for cross-file numeric drift (turnaround times, photo counts, fee percentages) and found two real gaps, both traceable to the same root cause: p2-capture-checklist-scaling (2026-07-12) changed capture-checklist.ts's shot counts/timing to hit the promised 15-25 photo range, but nothing cross-checked the other files that cite the checklist's old numbers by hand. (1) mls-compliance.ts's photo-count-guidance implication still said the checklist 'currently specs roughly 9-11 shots,' a fact the docs site renders live, when the real typical default is now 19. (2) More consequential: pricing-one-pager.tsx and 3 spots in outreach-templates.ts quote '~30 minutes' for the guided capture step, but totalTargetMinutes() now computes 33-47.5 minutes (38.5 typical) — an outreach-copy promise a human would actually read to a real prospect that the checklist itself can no longer honor.

2026-07-13 · claude/mls-photo-count-drift-fix-0713
  • process/mls-compliance.ts: photo-count-guidance implication updated to the real current default (19 typical, 16-22 range) instead of the stale pre-scaling '9-11' figure.
  • process/pricing-one-pager.tsx (2 spots) and process/outreach-templates.ts (3 spots: agent-cold-email, agent-in-person, brokerage-intro): '~30 minute(s)' capture-time promise corrected to '~35-40 minutes' to match capture-checklist.ts's real scaled default. STRATEGY.md's own looser 'in 30 minutes' tagline deliberately left as-is (whole-workflow positioning, not a literal capture-time SLA) — same treatment precedent already gave the 'under 2 hours' delivery target vs. the actual '1 business day' promise.
  • process/capture-checklist.ts: preShootPrep doc comment's stale '30-minute-fix' reference updated to match.
  • roadmap.ts: added p2-mls-compliance-photo-count-drift-fix and p2-capture-time-promise-drift-fix (both done). decisions-log.ts: logged both as separate entries (distinct customer-facing surfaces, same root cause).
  • Verified: pipeline/ `npm install && npm test` (all 5 self-tests) + `npx tsc --noEmit` pass; docs-site/ `npm run build` passes clean (9/9 static pages); research/ `npm run build` passes clean (4/4 static pages). Checked open PRs via the GitHub API directly (no gh CLI in this session) — only #4 open, unrelated files.

Next up

  • Human: process/blockers.ts unchanged in substance — p2-first-deliveries remains the single highest-leverage next step. payment-handle-setup still needs Duncan to confirm duncan.andrew.haywood@gmail.com actually receives Zelle/PayPal funds.
  • Agent (next session): with nextActionableTasks() empty again, check leads-log.ts/loggedInterviewResults/listingAuditLog for real entries first. The cross-file-drift-hunting approach (grep every concrete number — price, time, count, percentage — across process/*.ts, STRATEGY.md, research/, docs-site/, and diff against what the code that backs it actually computes) paid off twice in a row now (this session and the promise-consistency-audit session before it); worth repeating periodically rather than treating either audit as final, since any future change to capture-checklist.ts, payment-collection.ts, etc. can silently re-introduce the same class of gap. ~40+ non-main branches still exist (only cursor/fix-pages-access-4a7e is live); branch deletion remains proxy-blocked (see blockers.ts's stale-branch-cleanup entry) — still needs a human with real GitHub UI/token access.

Every open PR check across both this repo and local-site-studio came back clean (no open PRs on either besides the pre-existing, human-blocked cursor/fix-pages-access-4a7e), and leads-log.ts/loggedInterviewResults/listingAuditLog are all still empty — nextActionableTasks() is empty and every process/blockers.ts item genuinely still needs Duncan. Took up the prior session's own suggestion: audited outreach-templates.ts, pricing-one-pager.tsx, payment-collection.ts, delivery-message.ts, and referral-followup.ts against each other for promise drift. Price ($19), turnaround (1 business day), and payment instructions were all consistent (the latter shared via paymentInstructionsText(), so there was no way for it to drift). Found one real gap: deliveryMessageText() unconditionally told every recipient 'the redo is free on this first delivery' and signed off 'Thanks for trying Listing Lens!' regardless of whether they were actually a first-time or repeat/referred client — harmless with zero real deliveries so far, but wrong on its face the moment referral-followup.ts's own incentive loop produces a repeat booking.

2026-07-13 · claude/promise-consistency-audit-0713
  • process/delivery-message.ts: DeliveryMessageInput gains an optional isRepeatClient flag (default false); first-delivery output is byte-for-byte unchanged, repeat-client output swaps in 'same free-redo guarantee as always' / 'Thanks again for using Listing Lens!'.
  • pipeline/src/delivery-message.test.ts: added assertions for the default (first-delivery) wording and the new isRepeatClient branch.
  • roadmap.ts: added p2-promise-consistency-audit (done). decisions-log.ts: logged the decision and rationale.
  • Verified: pipeline/ `npm test` (still 6 self-tests, extended assertions) + `npx tsc --noEmit` pass; docs-site/ untouched, not rebuilt; research/ untouched, not rebuilt.

Next up

  • Human: process/blockers.ts unchanged in substance — p2-first-deliveries (book, shoot, deliver one real concierge listing) remains the single highest-leverage next step; the toolchain now correctly handles a repeat/referred 2nd delivery too, not just the first.
  • Agent (next session): with nextActionableTasks() empty again, check leads-log.ts/loggedInterviewResults/listingAuditLog for real entries first. cli-microsite.ts still always calls deliveryMessageText() with isRepeatClient left at its default (false) — once leads-log.ts has a real 'paid' entry for a given contact, wiring an actual repeat-client lookup into the CLI (instead of leaving the flag manual/unused) would be the natural next increment, but isn't worth building ahead of any real repeat customer existing.

Re-verified the human-blocked state first (nextActionableTasks() still empty, leads-log.ts/loggedInterviewResults/listingAuditLog still genuinely empty, PR #4 still the only open PR, all 27 non-main remote branches — up from ~23, including a new claude/promise-consistency-audit-0713 whose empty diff confirms a sibling session already ran the exact outreach/delivery/referral consistency audit this repo's last few nextUp notes suggested, finding nothing wrong there, matching this session's own spot-check). Rather than re-run that same audit a second time, widened it to the docs-site itself and found two real, concrete drift bugs: (1) docs-site/content/strategy.md — a hand-copied duplicate of root STRATEGY.md rendered live at the private /docs/strategy page — had silently fallen behind for weeks, still showing 3 already-answered STRATEGY.md open questions (MLS compliance, virtual staging tech, brokerage-vs-PLG GTM) as unanswered checkboxes; (2) auditing that surfaced a second, older drift underneath it: STRATEGY.md's own Business model table and research/'s public site both said Host-pack (Airbnb/STR) pricing was a flat '$19/listing', contradicting the actual '$19-79/listing, scales with size' decision this repo logged on day one (2026-06-13) and used consistently everywhere else (interview-script.ts, outreach-templates.ts's str-host-dm template).

2026-07-13 · claude/dazzling-allen-mf5cdg
  • docs-site/src/lib/docs.ts: strategy and replication doc pages now read STRATEGY.md and docs/REPLICATION_PROMPT.md directly from their canonical repo-root paths at build time instead of a manually-synced content/ duplicate — the same fix blockers.ts already applied to human-blocker facts, applied here to a different pair of files so this class of drift can't recur. Deleted the now-unused docs-site/content/strategy.md and docs-site/content/replication.md (which was, on inspection, still byte-identical to its source — only strategy.md had actually drifted). docs-site/content/deployment.md has no canonical source elsewhere, so it stays a real file, just resolved via the same repo-root-relative path for consistency.
  • STRATEGY.md + research/src/lib/data.ts: Host-pack pricing corrected from '$19/listing' to '$19-79/listing' to match the actual day-one pricing decision and every other place it's already stated correctly.
  • roadmap.ts: added p0-docs-content-drift-fix (done) under Phase 0. decisions-log.ts: logged both the docs-site dedup fix and the pricing-drift fix as one entry (same root cause, discovered together).
  • blockers.ts: bumped confirmedByCount 4->5 on all 5 long-standing blockers (re-verified each is still true) and corrected stale-branch-cleanup's count from ~23 to ~26 confirmed-stale (27 non-main branches now exist, only cursor/fix-pages-access-4a7e/PR #4 is live).
  • Verified: docs-site/ `npm run build` passes clean (9/9 static pages) and the built /docs/strategy/index.html was spot-checked to confirm its 3 answered checkboxes now render checked and the still-genuinely-open v1-scope question renders unchecked; research/ `npm run build` passes clean; pipeline/ `npm test` (all 5 self-tests) passes, untouched by this session (run anyway per step 7 since it imports process/capture-checklist.ts from the same package).

Next up

  • Human: process/blockers.ts's items are unchanged in substance — p2-first-deliveries (book, shoot, deliver one real concierge listing end-to-end using the now-complete toolchain) remains the single highest-leverage next step. payment-handle-setup still needs Duncan to confirm duncan.andrew.haywood@gmail.com actually receives Zelle/PayPal funds.
  • Agent (next session): the docs-site/content/ dedup pattern (read canonical files live instead of hand-copying) is worth applying proactively if any future session adds a new docs-site page mirroring another repo-root doc — don't reintroduce a hand-synced copy. With nextActionableTasks() still empty and leads-log.ts/loggedInterviewResults/listingAuditLog still empty, check for other cross-file numeric/pricing claims (turnaround times, photo counts, fee percentages) that might have drifted the same way pricing did, before defaulting to a blockers.ts-only re-confirmation.

Ran `git ls-remote` + a diff check against every remote claude/* branch per step 6 before doing anything else, and initially found what looked like a real problem: PR #26 (p2-delivery-message) showed `merged: false` with a `merged_at` timestamp set, and `git merge-base --is-ancestor` confirmed its commit wasn't on main yet — so cherry-picked it fresh and opened PR #28 to land it. Mid-session, that PR's merge attempt hit a conflict: a concurrent session had independently noticed the same PR #26 was stuck and merged it for real in the meantime (main's e5782b0 and af58679), making PR #28 a pure duplicate. Closed #28 unmerged and rebased onto the real main instead of re-litigating who was 'right' — the outcome (delivery-message.ts on main) was already achieved. With nextActionableTasks() empty again afterward and leads-log.ts/loggedInterviewResults/listingAuditLog still genuinely empty, found the next real, still-unaddressed gap one step past delivery-message.ts: nothing in the repo ever asked a paying customer for a testimonial or a referral, even though STRATEGY.md's own numbers (4-12 listings/month, agents who talk to each other) make word-of-mouth the cheapest growth channel available under the no-ad-spend guardrail.

2026-07-13 · claude/dazzling-allen-l25h2i
  • New process/referral-followup.ts: followUpMessageText() (a 3-7-days-post-delivery check-in asking for a concrete testimonial and offering a referral), REFERRAL_INCENTIVE (a future-delivery discount for both referrer/referee — $10/$5 off, not cash, so it doesn't violate the no-real-spend guardrail), TESTIMONIAL_PROMPT, and REFERRAL_SOURCED_NOTE_HINT (how to log a referral-sourced lead in the existing leads-log.ts notes field without adding a new LeadStage).
  • New pipeline/src/referral-followup.test.ts (added to pipeline's `npm test` script): asserts named/generic greeting branches, listing address inclusion, the testimonial ask, and that both incentive amounts are discounts (not cash).
  • process/prompt_process.tsx: new 'Referral & follow-up' dashboard section (incentive card + timing), same pattern as the existing payment-collection/gtm-motion sections.
  • roadmap.ts: added p2-referral-followup (done). decisions-log.ts: logged the decision and rationale.
  • Verified: pipeline/ `npm test` (now 6 self-tests) + `npx tsc --noEmit` pass; docs-site/ `npm run build` passes clean; research/ untouched, not rebuilt.

Next up

  • Human: process/blockers.ts's items unchanged in substance — p2-first-deliveries remains the single highest-leverage next step, now fully prepped end-to-end including a post-delivery growth loop (booking, capture, enhance, brand, deliver + payment ask, and testimonial/referral follow-up all exist as real, working tools). payment-handle-setup specifically still needs Duncan to confirm duncan.andrew.haywood@gmail.com actually receives Zelle/PayPal funds.
  • Agent (next session): before assuming a PR is stuck/lost, check whether a sibling session is mid-fix on it right now (fetch immediately before merging, not just at session start) — this session's near-miss (opening a duplicate recovery PR for work another session was already correctly merging) cost a wasted PR, not wasted product work, only because the rebase-and-drop was cheap; a more entangled duplicate could have been worse. With nextActionableTasks() empty again, check leads-log.ts/loggedInterviewResults/listingAuditLog for real entries first; if still empty, the funnel now runs booking -> capture -> enhance -> brand -> deliver -> follow-up/referral end-to-end, so the next silent gap (if one exists) is more likely in process quality/consistency than a missing funnel step — audit for drift between outreach-templates.ts, delivery-message.ts, and referral-followup.ts's tone/promises before inventing new files.

This session's own scheduling was misconfigured (a duplicate hourly trigger with no repo/branch bound to it, unlike its properly-configured sibling), so first re-attached the session to this repo, then picked up PR #26 (claude/dazzling-allen-rb6omk, p2-delivery-message) since neither the GitHub check-runs nor actions-runs REST endpoints are reachable from this session (403 'Resource not accessible by integration', same limitation prior sessions hit) and WebFetch 404s on this private repo's PR page. Verified locally instead: checked out the branch, ran pipeline `npm test` + `npx tsc --noEmit` (all pass) and docs-site `npm run build` (9/9 static pages, clean) against the actual PR content, read the diff for quality, then squash-merged.

2026-07-13 · real-estate-photography-continuous-improvement-2
  • Merged PR #26 (p2-delivery-message) to main via the GitHub REST merge endpoint (mergeable_state: clean, no review/bot comments, verified locally as above since CI status wasn't readable via API or WebFetch).
  • Re-verified nextActionableTasks() is still empty and leads-log.ts/loggedInterviewResults/listingAuditLog are still genuinely empty (no real human data has landed since the repo's first commit); PR #4 (Cloudflare Access) still open/draft/unchanged since 2026-06-15; branch deletion still hard-403s, now tested a second way (the GitHub REST `DELETE /git/refs/heads/...` endpoint directly, not just `git push --delete`) with the same result.
  • process/blockers.ts: bumped confirmedByCount 3->4 on the five blockers this session re-verified (all but payment-handle-setup, which needs a human financial confirmation this session can't check) and corrected stale-branch-cleanup's count from the earlier '~19' to the actual ~23 (24 non-main branches exist; only cursor/fix-pages-access-4a7e is live, as PR #4) — the estimate had drifted stale itself. Also documented in whatsNeeded that three-dot `git diff main...branch` false-positives as 'unmerged' after a squash merge (the branch ref still points at its original, un-squashed commit) and that direct two-argument `git diff main branch` is the reliable check; this tripped up this session's own first pass at auditing the branch list before working out why.

Next up

  • Human: process/blockers.ts's items are unchanged in substance from the prior session — p2-first-deliveries (book, shoot, deliver one real concierge listing end-to-end using the now-complete toolchain) remains the single highest-leverage next step.
  • Agent (next session): with p2-delivery-message now merged, nextActionableTasks() is empty again — check leads-log.ts/loggedInterviewResults/listingAuditLog for real entries first, same as every recent session. This session found the remaining agent-doable surface area for this idea has gotten thin (the last several sessions in a row have each found one small real gap, but they're getting rarer and smaller); if still nothing new and no genuine product/process gap turns up, a short, honest re-verification bumping blockers.ts is the right call, not inventing busywork.

Found process/payment-collection.ts already built and open as PR #25 by a concurrent session (started this session's own duplicate copy first, then caught the collision via `git ls-remote origin` per step 6, discarded the duplicate, verified PR #25's CI was green, and merged it) — the exact race step 2a exists to prevent, except here both sessions carried the work all the way to a PR before either checked, so the fix was catching it at merge time instead. With nextActionableTasks() empty afterward (every phase-0/1/2/4 agent task done, p3-app-scope correctly gated on human-blocked interviews/deliveries), audited outreach-templates.ts against the new booking/payment info per the merged session's own nextUp suggestion (found no gap — payment terms belong at delivery time, not in cold-outreach copy, which the existing templates already get right) and then found a real, still-unaddressed gap one step further down the funnel: nothing combined the finished photos + microsite + payment ask into an actual ready-to-send delivery message.

2026-07-13 · claude/dazzling-allen-rb6omk
  • New process/delivery-message.ts: deliveryMessageText(), composing listing address, photo count, an optional microsite mention, payment-collection.ts's paymentInstructionsText(), and the existing free-redo guarantee into one ready-to-paste handoff message.
  • pipeline/src/cli-microsite.ts: now imports and prints the delivery message automatically right after a successful microsite build (skipped when photoCount is 0, matching the existing no-photos warning path) — the exact moment a real delivery is ready to hand over, so there's nothing extra to remember.
  • New pipeline/src/delivery-message.test.ts (added to pipeline's `npm test` script): asserts the named/generic greeting branches, that the listing address/photo count/microsite filename are included when set, that the microsite line is omitted when unset, and that the payment instructions are present.
  • roadmap.ts: added p2-delivery-message (done). decisions-log.ts: logged the decision and why it's the same class of gap as booking-link.ts/payment-collection.ts, one step later in the funnel.
  • blockers.ts: bumped confirmedByCount 2->3 (3 for stale-branch-cleanup too) on the 4 long-standing blockers — re-verified leads-log.ts/loggedInterviewResults/listingAuditLog still empty, PR #4 still open/draft, and re-ran the branch-diff check (no working delete-branch tool on the GitHub MCP surface, confirming the prior 403 finding). Left payment-handle-setup's confirmedByCount at 1 (still genuinely only confirmed once) but updated its wording now that payment-collection.ts is built, not still in progress.
  • Verified: pipeline/ `npm test` (now 4 self-tests) + `npx tsc --noEmit` pass; docs-site/ `npm run build` passes clean; research/ untouched, not rebuilt.

Next up

  • Human: process/blockers.ts's items are unchanged in substance — p2-first-deliveries is now fully prepped end-to-end (booking, capture, enhance, brand, delivery message, and payment ask all exist as real, working tools) and remains the single highest-leverage next step. payment-handle-setup specifically still needs Duncan to confirm duncan.andrew.haywood@gmail.com actually receives Zelle/PayPal funds before the first real delivery closes the loop.
  • Agent (next session): check for concurrent branches/PRs *before* writing any code, not just before pushing — this session's near-miss (a second independent payment-collection.ts, caught only because ls-remote happened to run early) shows step 6's branch check needs to happen first, not as a pre-push formality. With nextActionableTasks() empty again, check leads-log.ts/loggedInterviewResults/listingAuditLog for real entries first; if still empty, look for the next silent 'nothing tells a human what to actually do/say' gap the way booking-link/payment-collection/delivery-message each did, rather than defaulting straight to a blockers.ts re-confirmation.

Completed p2-payment-collection, which a prior session (commit 9505fa7, ~4h earlier) had claimed in_progress but never finished — no branch or open PR had the actual work, so leads-log.ts/loggedInterviewResults/listingAuditLog were all still empty and this was the highest-leverage agent-doable task left. Closed the same class of silent gap p2-booking-link found for the booking step: pricing-one-pager.tsx has said 'Book your first listing for $19' since it was written, but nothing anywhere said HOW to actually pay.

2026-07-13 · real-estate-photography-continuous-improvement-2
  • New process/payment-collection.ts: sourced comparison of Zelle / PayPal / Venmo (setup cost, fee, how-to-pay, tradeoffs, sources) plus a decision (Zelle primary, PayPal goods & services fallback, both to duncan.andrew.haywood@gmail.com) and a revisit condition (real merchant processor once volume/price outgrows peer-to-peer transfers).
  • pricing-one-pager.tsx: howItWorks step 4 now says 'Pay & get the folder'; added a 'How do I pay?' FAQ entry rendering paymentInstructionsText().
  • leads-log.ts: Lead type gained optional paymentMethod/paymentReference fields. pipeline/src/log-lead.ts: CLI accepts --payment-method/--payment-reference, validates paymentMethod against payment-collection.ts's options and rejects paymentReference on any non-'paid' stage.
  • prompt_process.tsx: new 'Payment collection' dashboard section (decision + per-method cards), same pattern as the existing virtual-staging-tech/gtm-motion sections.
  • roadmap.ts: p2-payment-collection in_progress -> done. decisions-log.ts: logged the Zelle/PayPal decision and rationale.
  • pipeline/src/log-lead.test.ts: added assertions for invalid payment method, payment-reference-without-paid-stage rejection, and correct formatting of paid entries.
  • Verified: pipeline/ `npm test` (all 3 self-tests) + `npx tsc --noEmit` pass; docs-site/ `npm run build` passes clean (9/9 static pages including /process, /pricing).

Next up

  • Human: process/blockers.ts's 5 items unchanged by this session (still all confirmed 2026-07-13 by the prior session) — p2-first-deliveries remains the single highest-leverage next step, now that both booking (p2-booking-link) and payment (this session) have real, working instructions behind them. payment-handle-setup in blockers.ts specifically still needs a human to confirm duncan.andrew.haywood@gmail.com actually receives Zelle/PayPal payments before the first real delivery.
  • Agent (next session): with p2-payment-collection done, re-run nextActionableTasks() fresh — if it's empty again (likely, since phase 2's agent-doable tasks are now essentially all done), fall back to checking leads-log.ts/loggedInterviewResults/listingAuditLog for real entries first, then process-quality passes (this session didn't audit outreach-templates.ts or interview-script.ts against the new payment/booking info for consistency, which would be a reasonable next look).

Re-confirmed process/blockers.ts's 5 items are all still true (leads-log.ts/loggedInterviewResults/listingAuditLog still genuinely empty, PR #4 still open/draft/unchanged since 2026-06-15, branch deletion still hard-403s — re-tested live on claude/dazzling-allen-wg8i3m) and bumped confirmedByCount 1->2 on all 5 rather than writing a new prose paragraph, per blockers.ts's own convention. Also re-verified all 19 non-PR-#4 remote branches are still strictly behind/merged into main (git diff against each, up from the 12 last counted) and updated blockers.ts's stale-branch-cleanup count accordingly. Rather than stop there as an 18th+ re-confirmation session, read the npm install/audit output this session's own `npm install` runs produced (something no prior session's progress-log entry mentions doing) and found a real, agent-fixable gap: next.js had been pinned to a vulnerable 14.1.0 in both docs-site/ and research/ since the repo's first commit.

2026-07-13 · real-estate-photography-continuous-improvement-2
  • docs-site/package.json + research/package.json: next 14.1.0 -> 14.2.35 (latest 14.x patch release; no App Router/config API changes needed). Verified `npm run build` passes clean in both, with identical route/page output.
  • Deliberately did not go further to next@16.x, which is the only version that clears every advisory `npm audit` currently flags for this package (npm reports one combined vulnerable range spanning 9.3.4-canary.0 through 16.3.0-canary.5) — that's a major-version App Router migration with real breaking-change risk and no e2e/visual regression coverage in this repo to catch a silent break, not a same-session patch bump. Logged as a deliberately deferred follow-up in decisions-log.ts, not silently dropped.
  • roadmap.ts: added p0-nextjs-security-patch (done). decisions-log.ts: logged the patch-vs-major-bump reasoning and why the major bump was deferred rather than attempted blind.
  • process/blockers.ts: confirmedByCount 1->2 on all 5 entries; stale-branch-cleanup title/whatsNeeded updated from ~15 to ~19 confirmed-stale branches.
  • Verified: pipeline/ `npm test` (all 3 self-tests) + `npx tsc --noEmit` pass, untouched by this session's changes (ran anyway per step 7 since it imports process/capture-checklist.ts from the same package).

Next up

  • Human: same 5 items in process/blockers.ts — p2-first-deliveries (book+shoot a concierge listing) remains the single highest-leverage next step; Phase 1 interviews; the Zillow listing audit; PR #4's Cloudflare dashboard step; now ~19 stale branches (agent git credential still hard-403s on ref deletion, re-tested live this session).
  • Human/agent: the next@16 migration (docs-site/ + research/) is a real, scoped follow-up once someone can eyeball the deployed site after — don't attempt it as a routine hourly-session patch; treat it like p3-app-scope, worth a deliberate session with more build/deploy verification than `npm run build` passing locally.
  • Agent (next session): check leads-log.ts/loggedInterviewResults/listingAuditLog for real entries first, same as every recent session. If still empty and nextActionableTasks() is still empty, this session's approach (actually reading `npm install`/`npm audit` output instead of assuming dependencies are fine) is a reasonable place to look again — check pipeline/'s and docs-site/'s other dependencies for anything similarly newly-flagged, but don't attempt a major-version bump without a human able to verify the deployed result.

Re-verified the human-blocked state (all roadmap tasks still done/blocked-on-human as of the last session, PR #4 still open/draft, all other remote branches still confirmed strictly merged/behind main via `git diff origin/main...origin/<branch> --stat`, docs-site's /pricing flow re-checked and confirmed not actually broken — it's an internal copy-from page backed by a mailto: link by deliberate design, not a public prospect-facing URL, per the existing booking-link.ts decision). Rather than add yet another prose re-confirmation, fixed the repeated-re-confirmation problem itself: added process/blockers.ts as the one place blocker state lives, so future sessions with nothing new to report can update it in a one-line diff instead of writing a new paragraph.

2026-07-13 · claude/dazzling-allen-k4o1nz
  • Added process/blockers.ts: typed HumanBlocker[] for the 5 known human-blocked items (first concierge delivery, Phase 1 interviews, Zillow listing audit, Cloudflare Access dashboard step, stale-branch cleanup), each with whatsNeeded/unblocks/firstFlagged/lastConfirmed/confirmedByCount, plus staleFirst() to sort oldest-confirmed first.
  • process/prompt_process.tsx: imports blockers.ts, adds step 0 to AGENT_PROCESS_INSTRUCTIONS telling sessions to bump a blocker's lastConfirmed/confirmedByCount instead of writing a new progress-log paragraph when nothing changed, adds a 'Human blockers' dashboard section above Roadmap, and adds humanBlockerCount to ProcessSnapshot.
  • README.md: replaced the hand-written 5-item 'Human action needed' list with a short pointer to process/blockers.ts as the single source of truth (was independently drifting from roadmap.ts's blockedReason text — different wording, same facts, both hand-maintained).
  • AGENTS.md: added blockers.ts to the process read-order list with the same instruction.
  • decisions-log.ts: logged the consolidation decision and why (progress-log.ts had grown to ~300 lines with much of the growth being 'still blocked' restatements; three independently hand-written blocker copies is a real drift risk, not just verbosity).
  • Verified: docs-site `npm run build` passes with the new dashboard section; research untouched (doesn't import process/, not rebuilt); pipeline untouched, not rebuilt.

Next up

  • Human: process/blockers.ts is now the one place to check — same 5 items, unchanged this session.
  • Agent (next session): read process/blockers.ts first. If your finding is 'still blocked, nothing changed,' bump lastConfirmed/confirmedByCount there instead of writing a new progress-log paragraph — that's the entire point of this session's change. If a blocker's confirmedByCount climbs into the double digits with lastConfirmed dates spanning weeks and it's still unresolved, that's a real signal worth surfacing to Duncan directly (e.g. in a PR description) rather than silently re-bumping forever.

Re-confirmed the now month-long human-blocked state (leads-log.ts/loggedInterviewResults/listingAuditLog still genuinely empty, PR #4 still open/draft/unchanged since 2026-06-15, all other branches confirmed strictly merged/behind main, branch deletion still expected to 403) and, rather than add an 11th re-confirmation, closed STRATEGY.md's last unaddressed open question: brokerage sales cycle vs agent PLG. This was genuinely open (unlike v1 app scope, which is correctly gatedOn real validation data) and desk-researchable the same way mls-compliance.ts and virtual-staging-tech.ts closed the first two questions.

2026-07-12 · real-estate-photography-continuous-improvement-2
  • Added process/gtm-motion.ts: sourced comparison of brokerage-led vs agent-led PLG go-to-market motions (general B2B SaaS sales-cycle/PLG benchmarks plus 2026 real-estate-specific AI-tool-adoption reporting), with an explicit provisional decision (lead with agent PLG now, sequence brokerage sales as a Month-3 expansion step) and revisit condition.
  • process/roadmap.ts: added p4-gtm-motion (done) under the previously-empty Phase 4 task list.
  • process/prompt_process.tsx: imports and renders the new 'Go-to-market motion' dashboard section (decision + both options), same pattern as the existing virtual-staging-tech section.
  • STRATEGY.md: checked off the 'Brokerage sales cycle vs agent PLG' open question with a pointer to process/gtm-motion.ts.
  • decisions-log.ts: logged the decision and the specific evidence (Inman's May-2026 reporting on brokerage AI-tool adoption stalling within ~90 days) that drove it.
  • Verified: docs-site `npm run build` and research `npm run build` both pass with the new dashboard section; pipeline `npm test` + `npx tsc --noEmit` pass (untouched, run anyway since it imports process/capture-checklist.ts from the same package).

Next up

  • Human: same 5 items as every recent session's README banner — p2-first-deliveries (book+shoot a concierge listing) remains the single highest-leverage next step; Phase 1 interviews; the Zillow listing audit; PR #4's Cloudflare dashboard step; the ~12 stale branches.
  • Agent (next session): check leads-log.ts/loggedInterviewResults/listingAuditLog for real entries first. STRATEGY.md's only remaining open checkbox (v1 app scope) is correctly gatedOn p1-run-interviews + p2-first-deliveries via p3-app-scope — don't answer it early without that data, unlike this session's question which was genuinely desk-researchable. If nothing new and no fresh product gap is found, don't pad the session; a short re-verification is fine and honest.

This session's designated branch (claude/dazzling-allen-v3kn1n) had already been merged and its remote ref deleted before this session started, so restarted it from origin/main per the fresh-branch convention. Re-verified the now-familiar state: leads-log.ts/loggedInterviewResults/listingAuditLog are all still genuinely empty, PR #4 is still the only open PR (still draft, still blocked on Duncan's Cloudflare dashboard step), branch deletion still hard-403s (retested on claude/dazzling-allen-luc0w5, same result as every prior session), and all ~12 other agent/cursor branches are still confirmed strictly behind main (re-verified via git diff --stat). Rather than add an 11th re-confirmation session, looked for a genuine product gap and found one: STRATEGY.md's 5-step workflow (Guide -> Enhance -> Polish -> Deliver -> Brand) had 4 of 5 steps built (or, for Polish, explicitly deferred) but 'Brand' — agent headshot, logo, property microsite — had never been started by any session since the repo's first commit.

2026-07-12 · claude/dazzling-allen-v3kn1n
  • Added pipeline/src/microsite.ts: generateMicrositeForDelivery() reads an enhance.ts delivery folder's photo filenames (numeric-aware sorted, agent logo/headshot excluded) and buildMicrositeHtml() renders a single responsive, HTML-escaped gallery page (hero photo, grid, agent contact card) referencing those photos by relative filename. Never opens, modifies, or overwrites the underlying photo files — enforces the mls-compliance.ts no-branding-on-MLS-photos guardrail by construction, since it only ever adds one new HTML file next to them.
  • Added pipeline/src/cli-microsite.ts: `npm run microsite -- --dir <delivery-dir> --listing "<address>" --agent-name "<name>" [--agent-phone] [--agent-email] [--agent-brokerage] [--agent-headshot <filename>] [--agent-logo <filename>] [--tagline] [--out index.html]` writes the generated HTML into the delivery dir.
  • Added pipeline/src/microsite.test.ts: covers filename sorting/exclusion, HTML-escaping of a hostile agent name/listing address (no script/attribute injection), and zero-photos/zero-optional-fields rendering. Wired into pipeline's existing `npm test`.
  • Ran a real end-to-end smoke test (not just unit tests): generated two synthetic photos, ran the existing `enhance` CLI to produce a real delivery folder, then ran the new `microsite` CLI against that real output and confirmed the resulting index.html correctly referenced both delivered filenames and rendered the supplied agent contact info, without touching the two photo files.
  • roadmap.ts: added p2-brand-microsite (done). decisions-log.ts: logged why this is a static HTML file the agent hosts/forwards themselves rather than a new deployed service (same reasoning as the earlier booking-link decision — no new domain/hosting/Cloudflare Access decision, zero infra cost).
  • README.md: repo-structure tree and pipeline section document the new files and `npm run microsite` usage; the top-level 'Human action needed' banner's item 1 now mentions the microsite generator alongside the booking link. AGENTS.md: added a microsite usage section alongside the existing enhance/log-lead docs.
  • Verified: pipeline/ `npm test` (all 3 self-tests) and `npx tsc --noEmit` pass; docs-site `npm run build` and research `npm run build` both pass (neither touched, run per prompt_process.tsx step 7 since capture-checklist.ts/process/ types are shared).

Next up

  • Human: p2-first-deliveries is still the single highest-leverage next step — a concierge delivery can now ship as enhanced photos *and* a branded, shareable microsite in one extra command, not just a folder of renamed JPEGs.
  • Human: Phase 1 interviews (p1-run-interviews), the Zillow listing audit (p1-listing-audit), PR #4's Cloudflare dashboard step, and the ~12 stale-branch deletions remain the other real unblocks — all unchanged this session.
  • Agent (next session): once a real concierge delivery happens, get feedback on whether the microsite's fixed template (dark theme, single hero + grid) is actually what an agent wants to forward to a client, or whether it needs a lighter/on-brand theme option — don't guess further without that signal. If still nothing is human-unblocked and leads-log.ts/loggedInterviewResults/listingAuditLog are still empty, resist adding another roadmap.ts/process/*.ts tweak just to have a diff; a real product gap (the way this session found 'Brand' was never built, and p2-capture-checklist-scaling/p1-listing-audit-rubric found theirs) is worth more than another status re-confirmation.

Confirmed leads-log.ts/loggedInterviewResults/listingAuditLog are all still genuinely empty and PR #18 (the README human-action-needed banner) had already merged to main during this session's own investigation, so re-based onto it rather than duplicating it. Rather than stop at 'nothing agent-doable, re-verify and exit' for what would be the 9th consecutive session to do so, looked for the next gap between what prep material *says* and what actually works end-to-end (the way p1-listing-audit-rubric and p2-capture-checklist-scaling were found in earlier sessions) and found a real one: every outreach template and the pricing one-pager have told prospects to use a '[Phone / booking link]' or 'scan the QR code' since they were written — neither has ever existed, so a prospect who wanted to book from either surface had no way to.

2026-07-12 · claude/dazzling-allen-ucfuio
  • Added process/booking-link.ts: BOOKING_LINK, a mailto: link to Duncan's real inbox pre-filled with outreach-templates.ts's bookingIntakeFields as fill-in-the-blank prompts. Zero cost, zero new infrastructure, no Cloudflare Access/domain decision — deliberately not a hosted landing page (research/ builds but is deployed nowhere; docs-site is deployed but Access-gated to Duncan only, so it can't host a public booking page without a guardrailed Access change or a new public Pages project).
  • process/outreach-templates.ts: added renderTemplateBody(template, bookingLink) to substitute the placeholder (kept as a parameter, not an import, since booking-link.ts already imports bookingIntakeFields from this file and importing back would cycle).
  • process/pricing-one-pager.tsx: callToAction no longer references a nonexistent QR code; PricingOnePager now renders an actual 'Book now' link using BOOKING_LINK.
  • docs-site/src/app/pricing/page.tsx: outreach template previews now render with the real link substituted (renderTemplateBody(t, BOOKING_LINK)); Booking intake section now shows the raw link text for pasting into DM/in-person follow-ups that don't use the placeholder.
  • README.md: repo-structure tree lists the new file; bullet 1 of the 'Human action needed' section now notes the booking link is real, not a placeholder.
  • roadmap.ts: added p2-booking-link (done). decisions-log.ts: logged the mailto-vs-hosted-landing-page decision and why a hosted page was considered and deferred.
  • Verified: docs-site `npm run build` passes and the exported pricing/index.html contains the correctly URL-encoded mailto: link in both the one-pager button and the substituted template bodies; research `npm run build` passes (untouched); pipeline `npm test` + `npx tsc --noEmit` pass (untouched, run anyway since it imports process/capture-checklist.ts from the same package).

Next up

  • Human: p2-first-deliveries is still the single highest-leverage next step, and a prospect who replies to an outreach template or clicks 'Book now' on the one-pager now lands in a real, pre-filled email to Duncan instead of a dead end.
  • Human: Phase 1 interviews (p1-run-interviews) and the Zillow listing audit (p1-listing-audit) remain the other real unblocks; PR #4 (Cloudflare Access dashboard step) and the ~12 stale branches (agent git credential 403s on ref deletion) are unchanged.
  • Agent (next session): if a hosted public booking page ever becomes worth it (e.g. Duncan wants a shareable link instead of a mailto:, or wants to A/B test copy), that's a real infra/domain decision for a human to weigh in on first — don't stand one up unilaterally. Otherwise check leads-log.ts/loggedInterviewResults/listingAuditLog for real entries first, same as every recent session's note.

Re-confirmed the state every session for the last month has independently re-confirmed: nextActionableTasks() is empty (all agent-doable roadmap work is done; every remaining task is owner:"human" or gated on one), leads-log.ts/loggedInterviewResults/listingAuditLog are all still genuinely empty (no human action has landed yet since the repo's first commit on 2026-06-13), PR #4 is still the only open PR, and branch deletion still hard-403s (retested on claude/checklist-novice-audit, same result as the last two sessions that tried). Rather than add another process/roadmap.ts tweak on top of the 8+ already shipped this way, made the actual bottleneck impossible to miss: a human-facing summary at the top of README.md.

2026-07-12 · real-estate-photography-continuous-improvement
  • README.md: added a '⚠ Human action needed to unblock the next stage' section directly under the Status line, listing the 5 concrete pending human actions (book+shoot a concierge listing, run Phase 1 interviews, score 20 Zillow listings, flip the Cloudflare Access policy for PR #4, delete the ~12 stale branches) with pointers to the exact files/PRs involved. This is a communication fix, not new process machinery — the roadmap/gating system already correctly tracks all of this, but a human skimming the repo root previously had to open process/roadmap.ts and cross-reference progress-log.ts to see it.
  • No roadmap.ts/decisions-log.ts changes — no new agent-doable task exists and no new business decision was made this session, only a documentation change surfacing existing state.

Next up

  • Human: same 5 items as every recent session's nextUp, now also in README.md's top-level 'Human action needed' section so they don't require reading progress-log.ts to find. p2-first-deliveries is still the single highest-leverage one.
  • Human: consider whether running this and the sibling 'real estate photo app Business development progress' trigger both hourly against the same fully-human-blocked roadmap is worth the cost — neither has had agent-doable work to do for several consecutive sessions now, and that's expected to continue until one of the 5 items above happens.
  • Agent (next session): check leads-log.ts/loggedInterviewResults/listingAuditLog for real (non-empty) entries first — that's the actual signal anything changed. If still empty and nextActionableTasks() is still empty, don't invent another roadmap.ts/process/*.ts tweak just to have a diff; re-verify PR #4 and branch-deletion permissions (cheap, 30 seconds each) and stop there if nothing changed, rather than padding the session.

Verified all prior branches were already merged to main (git ls-remote + PR list: only draft PR #4 open, all 12 other claude/*/cursor/* branches confirmed diverged-by-1 squash-merge artifacts, zero unique content) and that every Phase 1-2 agent-doable roadmap task was done, with the sole not_started task (p3-app-scope) explicitly deferred by 7+ consecutive prior sessions in prose only. Per AGENT_PROCESS_INSTRUCTIONS step 4, spent the session hardening the process itself instead of padding with busywork: made that repeatedly-restated deferral an enforced precondition in code.

2026-07-12 · claude/gate-sequencing
  • process/types.ts: added optional gatedOn?: string[] to RoadmapTask — task IDs that must all be status "done" before a not_started task counts as agent-actionable.
  • process/roadmap.ts: added isGateSatisfied(task); nextActionableTasks() now excludes any not_started/agent-or-both task whose gatedOn tasks aren't all done. p3-app-scope gained gatedOn: ["p1-run-interviews", "p2-first-deliveries"], matching what every session's nextUp notes already said by hand.
  • process/prompt_process.tsx: AGENT_PROCESS_INSTRUCTIONS step 2 now explains gatedOn; ProcessDashboard's roadmap section renders an amber 'Gated on: ...' note under any task whose gate isn't satisfied yet, next to the existing blockedReason rendering.
  • decisions-log.ts: logged why (turning a rule seven sessions independently repeated in prose into something nextActionableTasks() itself enforces, so a future session skimming only roadmap.ts can't accidentally start it).
  • Verified: docs-site `npm run build` and research `npm run build` both pass with the new gate rendering; pipeline/ `npm test` + `npx tsc --noEmit` pass (types.ts change doesn't affect pipeline's own types, ran anyway since capture-checklist.ts is in the same process/ package).

Next up

  • Human: p2-first-deliveries remains the single highest-leverage next step — checklist, pricing, outreach copy, pipeline, and lead-intake CLI are all ready for a first real shoot.
  • Human: p1-listing-audit just needs a metro pick + Zillow browsing session, scored against process/listing-audit-rubric.ts.
  • Human: PR #4 (cursor/fix-pages-access-4a7e) is still open, still needs the Cloudflare Access dashboard toggle; the 12 confirmed-stale branches still need manual deletion (agent git credential gets HTTP 403 on ref deletes).
  • Agent (next session): p3-app-scope now won't appear in nextActionableTasks() until p1-run-interviews and p2-first-deliveries are both done — verify that holds once one of them actually flips to done, rather than assuming the gate works untested. If nothing is human-unblocked, the gatedOn mechanism could extend to other implicitly-sequenced tasks if any are found; otherwise re-verify mls-compliance.ts against a specific real MLS board once one is known.

Verified all prior agent branches were merged to main (git fetch + list_pull_requests: only draft PR #4 remains open, still blocked on Duncan's Cloudflare dashboard step) and that main's CI is green on its last 4 runs. Every Phase 1-2 agent-doable roadmap task was done and p3-app-scope stays correctly deferred per every prior session's reasoning, so this session closed a real gap flagged by p1-listing-audit's own blockedReason (it was blocked on 'a human' but never actually defined what to score) and did the branch-hygiene cleanup the last session ran out of retries for.

2026-07-12 · claude/dazzling-allen-j509d4
  • Added process/listing-audit-rubric.ts: a 6-dimension, 1-5-per-dimension objective scoring rubric (lighting/exposure, composition/angle, staging/declutter, photo count/coverage, resolution/sharpness, color/white balance) with concrete worst-vs-best guidance per dimension, computeOverallScore() (rescales to STRATEGY.md's 1-10 'blind quality rating' scale), and auditSummary(). Ships with an empty listingAuditLog — mirrors the leads-log.ts/interview-script.ts convention of shipping the instrument, never fabricated data.
  • prompt_process.tsx: ProcessDashboard renders a new 'Listing quality audit rubric' section showing all 6 dimensions and the (currently zero) scored-listing summary, same pattern as the MLS-compliance and virtual-staging-tech sections.
  • roadmap.ts: added p1-listing-audit-rubric (done); updated p1-listing-audit's blockedReason to point at the rubric as the now-ready scoring instrument, so the remaining blocker is purely 'human picks a metro and browses listings'.
  • decisions-log.ts: logged the rubric decision and rationale.
  • Branch hygiene: confirmed 9 claude/* and 3 cursor/* branches have zero unique content vs. current main (git diff origin/main origin/<branch> showed each branch strictly behind main, i.e. fully superseded by earlier squash-merges) and attempted to delete them — claude/checklist-novice-audit, claude/dazzling-allen-{7i8o70,cpghie,luc0w5,q7uwlq,rkmj0x,vstaging-2937,wg8i3m,y9fqb5}, cursor/docs-cloudflare-access-{4a7e,5df0}, cursor/setup-dev-environment-2314. Unlike the prior session's 'concurrency cap' theory, this session's `git push origin :<branch>` returned a hard `HTTP 403`, not a transient disconnect — this session's git credential can push commits but cannot delete refs on this repo. Left all 12 branches in place; this needs either Duncan to delete them manually (they're all confirmed safe — verified fully superseded above) or a token/permission fix, not more retries. Left cursor/fix-pages-access-4a7e alone regardless — it's still open PR #4, human-blocked on a Cloudflare dashboard step.
  • Confirmed via GitHub Actions API that .github/workflows/ci.yml has been green on all 4 of its runs on main so far — closes the 'confirm CI is actually green' follow-up two sessions back flagged and never checked.

Next up

  • Human: p2-first-deliveries remains the single highest-leverage next step — checklist, pricing, outreach copy, pipeline, and lead-intake CLI are all ready for a first real shoot.
  • Human: p1-listing-audit now just needs a metro pick + Zillow browsing session, scored against process/listing-audit-rubric.ts's 6 dimensions.
  • Human: PR #4 (cursor/fix-pages-access-4a7e) is still open, still needs the Cloudflare Access dashboard toggle.
  • Human: delete the 12 confirmed-stale branches listed above from github.com directly (Settings/branches UI or `gh api -X DELETE`) — agent sessions get HTTP 403 trying to delete refs, so stop assigning this to a future agent session.
  • Agent (next session): still do not start p3-app-scope — unchanged from every prior session's reasoning, no new validation data exists yet. Do not re-attempt branch deletion (403 is a hard permission wall, not transient). If nothing is human-unblocked, look for the next gap between a roadmap task's stated blocker and what's actually true (the way this session found p1-listing-audit's blocker was really 'no rubric exists', not just 'needs a human'), or re-verify mls-compliance.ts against a specific real MLS board once one is known.

Found that this exact session's cron tick raced a concurrent one: PR #13 (branch claude/dazzling-allen-vstaging-2937) and PR #14 (branch claude/dazzling-allen-q7uwlq) had independently answered STRATEGY.md's same virtual-staging question about 10 minutes apart. #14 was already merged to main, so closed #13 unmerged with an explanatory comment instead of landing duplicate content, per prompt_process.tsx step 8. Since every agent-doable, desk-researchable roadmap item was otherwise done or explicitly deferred (p3-app-scope), spent the rest of the session on two things per AGENT_PROCESS_INSTRUCTIONS step 4: hardening the process itself, and a prior session's own flagged-but-undone audit.

2026-07-12 · claude/checklist-novice-audit
  • prompt_process.tsx: added AGENT_PROCESS_INSTRUCTIONS step 2a — claim a not-started task by committing its in_progress status to main as the very first action of a session, before doing the rest of the work, so a concurrent hourly session sees it's taken instead of independently duplicating it. This is a direct response to the PR #13/#14 race, not a hypothetical.
  • capture-checklist.ts: added `preShootPrep` (an 8-item, once-per-shoot home/equipment prep checklist — lights, blinds, decluttering, moving cars, fans off, phone storage/battery/grid-lines) and `BRACKET_GUIDANCE` (plain-language explanation of what 'single'/'hdr_3'/'hdr_5' mean and exactly how to shoot each on an iPhone or Android camera app, including the previously-undocumented gotcha that pipeline/'s coverage check expects exactly one merged output file per shot, not one per raw bracket exposure).
  • prompt_process.tsx: ProcessDashboard's capture-checklist section now renders the pre-shoot prep list above the room-by-room checklist, and each shot shows its plain-language bracket how-to alongside the existing camera instructions.
  • roadmap.ts: added p2-checklist-novice-audit (done). decisions-log.ts: logged the PR #13/#14 duplicate-work decision and rationale, so a future session understands why virtual-staging-tech.ts (not virtual-staging-options.ts) is the file that exists.
  • Verified: docs-site `npm run build` passes with the new preShootPrep/BRACKET_GUIDANCE rendering; pipeline/ `npm test` + `npx tsc --noEmit` and research `npm run build` both still pass (neither package's own files were touched, ran them anyway since capture-checklist.ts is imported by pipeline/src/coverage.ts).
  • Attempted to delete the now-fully-merged/closed claude/dazzling-allen-* branches for hygiene (repo convention is short-lived branches) but the session's git proxy hit its concurrency cap on every delete attempt ("remote end hung up unexpectedly") — left for a future session or Duncan to clean up manually; not blocking.

Next up

  • Human: p2-first-deliveries remains the single highest-leverage next step across every session so far.
  • Human: PR #4 (cursor/fix-pages-access-4a7e) is still open, still needs the Cloudflare Access dashboard toggle.
  • Human/agent: the 8 stale claude/dazzling-allen-* branches (all already merged or closed) should get deleted next session when the git proxy isn't rate-limited, or manually by Duncan.
  • Agent (next session): still do not start p3-app-scope. Try step 2a (task-claiming commit) for real on your first agent-doable task and note in this log whether it actually prevented a collision — that's the only way to know if it's working. If nothing is human-unblocked, re-verify mls-compliance.ts against a specific real MLS board once one is known, or review outreach-templates.ts/leads-log.ts for usability once a human has tried the lead-intake flow end to end.

Verified all prior agent branches were already merged to main (git ls-remote + list_pull_requests: only draft PR #4 remains open, still blocked on Duncan's Cloudflare dashboard step). All Phase 1-2 agent-doable roadmap tasks were done and every remaining task was human-blocked or explicitly deferred (p3-app-scope), so this session took up the prior session's own suggested next candidate: STRATEGY.md's other unanswered open question, 'on-device vs cloud for virtual staging'.

2026-07-12 · claude/dazzling-allen-q7uwlq
  • Added process/virtual-staging-tech.ts: sourced desk research (Apple's own Core ML/Stable Diffusion research, Virtual Staging AI's published API pricing, and general mobile-ML cost-crossover data) comparing on-device Core ML inpainting vs a pay-per-image cloud API for the future Polish-step virtual-staging feature. Decision: use a cloud API (not on-device) until real monthly volume approaches the ~1,000-15,000 image/month crossover and an iOS app actually exists — Listing Lens has neither yet.
  • decisions-log.ts: logged the resulting decision with rationale and an explicit revisit condition, tied to p3-app-scope so a future session doesn't have to re-derive this when the app-scope question finally comes up.
  • STRATEGY.md: checked off the 'on-device vs cloud for virtual staging' open question with a pointer to the new file.
  • roadmap.ts: added p1-virtual-staging-tech (done). prompt_process.tsx: ProcessDashboard now renders a 'Virtual staging tech (Polish step)' section showing the decision and both options compared, mirroring how the MLS-compliance section already renders.
  • Verified: docs-site `npm run build` and research `npm run build` both pass with the new file wired in; pipeline/ untouched this session so no need to re-run its tests.

Next up

  • Human: p2-first-deliveries is still the single highest-leverage next step — checklist, pricing, outreach copy, and pipeline are all ready for a first real shoot.
  • Human: Phase 1 interviews (p1-run-interviews) and the Zillow listing audit (p1-listing-audit) remain the other real unblocks.
  • Agent (next session): both of STRATEGY.md's originally-open, desk-researchable questions are now answered (MLS compliance, virtual-staging tech); the only remaining open question ('v1 scope: photos only vs photos + 3D lite') is explicitly gated on p3-app-scope, which every session has correctly deferred until validation data exists. Do not start p3-app-scope yet. If still nothing is human-unblocked, look for the next gap between STRATEGY.md prose and what's actually built/runnable — e.g. STRATEGY.md's 'brokerage sales cycle vs agent PLG' open question is genuinely data-dependent (not desk-researchable) so leave it, but check whether outreach-templates.ts/leads-log.ts need any small usability fix once a human has actually tried the lead-intake flow end to end.

Verified all prior agent branches were already merged to main (git ls-remote + list_pull_requests: only draft PR #4 remains open, still blocked on Duncan's Cloudflare dashboard step, untouched by this session). Every Phase 1-2 agent-doable roadmap task was already done and every remaining task was human-blocked or explicitly deferred (p3-app-scope), so this session built the concrete tooling gap the last two sessions' progress-log entries both flagged: a lead-intake CLI.

2026-07-12 · claude/dazzling-allen-y9fqb5
  • Added pipeline/src/log-lead.ts: `npm run log-lead -- --audience <agent|brokerage|str_host> --contact "<label>" --channel <email|dm|in_person_script|sms> [--stage contacted] [--template <id>] [--notes "..."] [--date YYYY-MM-DD] [--dry-run]` validates input against leads-log.ts's/outreach-templates.ts's existing typed enums and appends a correctly-formatted entry to process/leads-log.ts, so a human can log a real reply/booking from a terminal without hand-editing TypeScript. --dry-run previews the exact diff first.
  • Added pipeline/src/log-lead.test.ts: exercises validation, id-generation (lead-001, lead-002, ...), and append formatting (including quote-escaping) against an in-memory copy of leads-log.ts's shape — never touches the real file. Wired into pipeline's existing `npm test`.
  • Verified end-to-end against the real file: ran `npm run log-lead` for real (no --dry-run), confirmed the exact expected diff landed in process/leads-log.ts, then reverted it — leads-log.ts stays genuinely empty, no fabricated entries committed.
  • pipeline/ `npm test` and `npx tsc --noEmit` both pass with the new files.
  • roadmap.ts: added p2-lead-intake-tool (done). decisions-log.ts: logged why this is a validated CLI rather than a form/backend — still requires a human to supply every real field, no new infrastructure.
  • AGENTS.md: documented the new `pipeline/src/log-lead.ts` usage alongside the existing enhance-pipeline docs.

Next up

  • Human: p2-first-deliveries is still the single highest-leverage next step, and now the first real reply can be logged in one command with `npm run log-lead` the moment it happens.
  • Human: Phase 1 interviews (p1-run-interviews) and the Zillow listing audit (p1-listing-audit) remain the other real unblocks.
  • Agent (next session): do not start p3-app-scope yet (same reasoning as every prior session). STRATEGY.md's remaining open question ('on-device vs cloud for virtual staging') is desk-researchable without customer contact, similar to how p1-mls-compliance was answered — a reasonable next candidate if nothing is human-unblocked. Otherwise keep watching PR #4 (still needs Duncan's Cloudflare dashboard step) and re-verify CI stays green as branches merge.

Confirmed all prior agent branches were already merged to main (git ls-remote + list_pull_requests: only PR #4 remains open, still blocked on Duncan's Cloudflare dashboard step, untouched by this session). All Phase 1-2 agent-doable tasks were done and every remaining roadmap task was human-blocked or explicitly deferred (p3-app-scope), so this session took up the prior session's own suggested next candidate plus one process improvement.

2026-07-12 · claude/dazzling-allen-luc0w5
  • process/capture-checklist.ts: closed the gap flagged in the last two sessions' progress-log entries — outreach-templates.ts's cold-email copy already promises '15-25 enhanced, MLS-ready photos' but the checklist only produced ~9-11 fixed shots. Made secondary bedroom/bathroom rooms scale per additional unit (perAdditionalUnit), added 7 new fixed detail shots (entry, living feature, dining, kitchen detail, primary closet, primary vanity/tub, flex space), and added totalShotCount()/updated totalTargetMinutes() to compute real totals from actual or typical bed/bath counts.
  • pipeline/src/coverage.ts + cli.ts: expectedShotCount()/checkCoverage() now accept optional additionalBeds/additionalBaths; `npm run enhance` gained --beds/--baths flags (total counts, converted to additional-beyond-primary internally) so coverage checking reflects the real listing size instead of a fixed number.
  • Verified: typical-home default (no args) now returns 19 shots (inside the promised 15-25 range); a 0-extra-bed/bath listing returns 16; a 5-bed/3-bath listing returns 22. pipeline/ `npm test` and `tsc --noEmit` pass; docs-site `npm run build` passes.
  • Added .github/workflows/ci.yml: runs pipeline/'s test+typecheck and docs-site/research builds on every push and PR, independent of deploy-docs.yml (which only builds docs-site on push to main). Automates the manual verification prompt_process.tsx step 7 has always required, which several concurrent agent branches made easy to skip.
  • decisions-log.ts: logged both the checklist-scaling decision and the CI decision. roadmap.ts: added p2-capture-checklist-scaling (done) and p0-ci (done).

Next up

  • Human: p2-first-deliveries remains the single highest-leverage next step — the checklist, pricing, outreach copy, and pipeline (now with a real --beds/--baths-aware coverage check) are all ready for a first real shoot.
  • Human: Phase 1 interviews (p1-run-interviews) and the Zillow listing audit (p1-listing-audit) remain the other real unblocks.
  • Agent (next session): once CI has run on a real PR, confirm it's green before relying on it as a merge gate. Do not start p3-app-scope yet. If still nothing is human-unblocked, good candidates are re-verifying mls-compliance.ts against a specific real MLS board once one is known, or building the booking-intake -> leads-log.ts transcription path so a human's real outreach replies are fast to log.

Confirmed all prior Phase 1-2 agent-doable work was already merged to main (this branch's own prior PR #9 included), then closed two more gaps flagged by the last session: STRATEGY.md's unanswered 'MLS compliance by board' open question, and the fact that p2-first-deliveries had no structured place to land a real lead/booking once outreach starts.

2026-07-11 · claude/dazzling-allen-7i8o70
  • Added process/mls-compliance.ts: sourced desk research on MLS photo rules — no agent/brokerage branding or watermarks on MLS-submitted photos (fineable per-board), virtual staging/digital-alteration disclosure required in most states as of 2026 (California's AB 723 strictest, effective Jan 1 2026), and photo-count best practice vs. what capture-checklist.ts currently specs.
  • decisions-log.ts: logged the resulting guardrail — pipeline/ and any future 'Brand' step must never brand MLS photo files; a future 'Polish'-step virtual staging feature must default to the strictest disclosure (visible caption + keep the original) regardless of state, since the pipeline can't detect the listing's board at run time.
  • STRATEGY.md: checked off the MLS-compliance open question with a pointer to the new file and a note that board-specific re-verification is still needed once a real listing is booked.
  • Added process/leads-log.ts: typed Lead/LeadStage schema and an intentionally empty leadsLog (agents must never fabricate lead/booking/revenue data here — only real entries from a human's actual outreach get appended).
  • prompt_process.tsx: ProcessDashboard now renders an 'MLS compliance notes' section and a 'Leads & bookings funnel' section (currently all-zero, correctly reflecting that no real outreach has happened yet).
  • roadmap.ts: added p1-mls-compliance (done) and p2-leads-tracking (done).
  • Verified: git ls-remote showed claude/dazzling-allen-{cpghie,rkmj0x} fully merged into main and claude/dazzling-allen-wg8i3m fully superseded (empty diff / strict subset) before starting, per this file's own process step 6.

Next up

  • Human: p2-first-deliveries is still the single highest-leverage next step. Once a real prospect responds, log it in process/leads-log.ts (even a one-line entry) so the funnel section on the docs site reflects reality instead of staying at zero.
  • Human: Phase 1 interviews (p1-run-interviews) and the Zillow listing audit (p1-listing-audit) remain the other real unblocks.
  • Agent (next session): do not start p3-app-scope yet (same reasoning as prior sessions — sequenced after validation data exists). If still nothing is human-unblocked, good next candidates are re-verifying process/mls-compliance.ts against a specific real MLS board once one is known, or tightening capture-checklist.ts's shot count toward the 15-25 photo range the pricing one-pager already promises.

Merged PR #7 (concierge pricing + outreach, resolving its conflict with the just-merged PR #6/#8 progress-log entries), then closed the biggest actually-agent-buildable gap left in Phase 2: the 'manual AI pipeline' from STRATEGY.md existed only as prose across every prior session, so built it as a real, tested CLI.

2026-07-11 · claude/dazzling-allen-rkmj0x
  • Merged PR #7 (claude/dazzling-allen-cpghie) into main: rebased it onto main first to resolve a progress-log.ts merge conflict against PR #6/#8, verified docs-site still builds clean, then merged.
  • New pipeline/ package (Node + TypeScript, run via tsx, no build step): src/enhance.ts (auto-orient, contrast/white-balance normalize, sharpen, resize, re-encode via sharp), src/coverage.ts (cross-checks output count against process/capture-checklist.ts's expected shot count), src/cli.ts (`npm run enhance -- --in <raw> --out <delivery> --listing "<addr>"`), src/enhance.test.ts (generates synthetic images, no real listing photos needed — `npm test` passes).
  • Verified end-to-end: `npm test` passes, `npx tsc --noEmit` is clean, and a manual CLI smoke run against synthetic photos correctly produced renamed/resized JPEGs and correctly flagged an under-count against the checklist (exit code 1).
  • roadmap.ts: added p2-ai-pipeline (done); p2-first-deliveries blockedReason updated to point at the new pipeline as the concrete unblock for the edit step.
  • decisions-log.ts: logged why the pipeline is a local deterministic CLI (sharp) instead of a paid cloud AI API — zero marginal cost, fully agent-testable, respects the 'no spend of real money' guardrail; virtual staging/twilight/object removal stay a future paid 'Polish' step once revenue justifies it.

Next up

  • Human: p2-first-deliveries is now the single highest-leverage next step — shoot a real listing with process/capture-checklist.ts, run `npm run enhance` in pipeline/ on the raw photos, and use process/outreach-templates.ts + the /pricing page to book and deliver it. This exercises the whole concierge loop end-to-end for the first time.
  • Human: Phase 1 interviews (p1-run-interviews) and the Zillow listing audit (p1-listing-audit) are still the other real unblocks; nothing agent-side is left to prep for either.
  • Agent (next session): once pipeline/ gets used on a real shoot, feed back any real timing/quality data into the turnaround claim in decisions-log.ts and pricing-one-pager.ts. Do not start p3-app-scope yet — STRATEGY.md sequences it after interview + concierge validation data exists, not before; jumping ahead would be scope creep against the phase-1-first plan. If nothing is agent-actionable, look for the next gap between STRATEGY.md prose and what's actually built/runnable, the way p2-ai-pipeline was found this session.

Merged PR #5 (the process system from a concurrent session), then completed the next agent-doable roadmap item: the concierge pricing one-pager plus outreach templates to actually get it in front of prospects.

2026-07-11 · claude/dazzling-allen-cpghie
  • Merged PR #5 (claude/dazzling-allen-wg8i3m) into main: process/ system + Phase 1-2 deliverables.
  • Added process/pricing-one-pager.tsx: typed $19/listing concierge offer (what's included, how it works, FAQ, guarantee) plus a standalone, screenshot/print-shareable PricingOnePager component (p2-pricing-page).
  • Added process/outreach-templates.ts: email/DM/in-person/brokerage-intro copy for agents, brokerages, and STR hosts, plus a minimal booking-intake field list (p2-outreach-templates, new task).
  • Wired both into docs-site's new /pricing page (docs-site/src/app/pricing/page.tsx) and added it to DocsShell's Live nav.
  • Logged the concierge offer-terms decision in decisions-log.ts, including the honest 1-business-day turnaround claim (vs. STRATEGY.md's looser 'under 2 hours') until real delivery data exists.
  • Updated roadmap.ts: p2-pricing-page done, new p2-outreach-templates task added and done.

Next up

  • Human: use process/outreach-templates.ts + the /pricing page to book and run the first concierge shoots (p2-first-deliveries) — still the highest-value human-blocked task.
  • Human: run Phase 1 interviews (p1-run-interviews) and the Zillow listing audit (p1-listing-audit) — both still blocked on real-world action.
  • Agent (next session): once any real concierge deliveries or interview results are logged, tighten the turnaround claim in decisions-log.ts and pricing-one-pager.ts with real data; otherwise, prep Phase 3 app-scope framing (p3-app-scope) or improve this process itself.

PR #5 merged, then fixed all 6 Cursor Bugbot findings it surfaced (broken Tailwind content globs, a stale deploy trigger, two status/data inconsistencies, a bad task-ID reference, and an unrendered checklist) in a follow-up PR #6, now merged.

2026-07-11 · claude/dazzling-allen-wg8i3m
  • docs-site/tailwind.config.ts: added ../process/**/*.{ts,tsx} to content so classes used only by ProcessDashboard actually get generated.
  • .github/workflows/deploy-docs.yml: added process/** to the push trigger paths so process-only edits still redeploy the dashboard.
  • process/roadmap.ts: p2-first-deliveries and p0-docs-site corrected to status "blocked" (both had a blockedReason but the wrong status); p0-docs-site now explicitly notes Access enforcement is still pending PR #4.
  • process/decisions-log.ts: fixed the scope-guard decision's relatedTaskIds to reference real task IDs instead of phase IDs.
  • process/prompt_process.tsx: ProcessDashboard now renders captureChecklist, matching p2-capture-checklist's doneWhen criterion.

Next up

  • Another agent session (branch claude/dazzling-allen-cpghie) opened PR #7 on top of this work adding process/pricing-one-pager.tsx and process/outreach-templates.ts for the $19/listing concierge offer — not this session's to merge, but worth reading once it lands since it'll touch roadmap.ts/progress-log.ts/DocsShell.tsx too.
  • Human: run the Phase 1 interviews (still the biggest unblock — everything else is prep work).
  • Human: Cloudflare Access dashboard step for PR #4.

Added the process/ system (roadmap, decisions log, progress log, interview script) so agent sessions can coordinate without re-deriving context, and shipped the Phase 1 interview script plus a Phase 2 capture checklist.

2026-07-11 · claude/dazzling-allen-wg8i3m
  • process/types.ts, roadmap.ts, decisions-log.ts, progress-log.ts, interview-script.ts, prompt_process.tsx.
  • docs-site: added /process page rendering the live roadmap/decisions/progress from process/.
  • Wrote the room-by-room guided capture checklist (process/capture-checklist.ts) for the Phase 2 concierge MVP.
  • Updated AGENTS.md and README.md to document the process convention for future agents.

Next up

  • Human: run the Phase 1 interview script with 10 agents + 5 STR hosts and log results in interview-script.ts's loggedInterviewResults.
  • Agent: draft the $19/listing concierge one-pager (p2-pricing-page) next session.
  • Watch PR #4 (cursor/fix-pages-access-4a7e) — still blocked on a human dashboard step.

Fixed Cloudflare Access enforcement for the docs site on *.pages.dev.

2026-06-15 · cursor/fix-pages-access-4a7e
  • Rewrote docs-site/scripts/setup-cloudflare-access.sh to verify status instead of applying a broken API-only Access app.
  • Added optional Pages Functions JWT middleware for defense-in-depth.
  • Opened PR #4 (draft) — still needs a one-time manual Cloudflare dashboard step from Duncan.

Next up

  • Duncan enables the Pages Access policy in the Cloudflare dashboard and merges PR #4

Bootstrapped the repo: market-research Next.js site, STRATEGY.md, and the private docs-site skeleton.

2026-06-13 · initial-scaffold
  • Added research/ Next.js static-export site with hero, audience comparison, market analysis, and business plan sections.
  • Added STRATEGY.md with verdict, GTM plan, pricing, and scope guard.
  • Added docs-site/ private Next.js site on Cloudflare Pages + Access.

Next up

  • Run problem interviews
  • Stand up a shared process-tracking system