Active task: apply the user’s clarification: GitHub is the private source repository intended to feed the website; local files are the backup. This entry supersedes the earlier backup terminology below. The application milestone remains unchanged. Website hosting connection and Supabase configuration remain deferred.
Updated AGENTS.md, README and GITHUB_SYNC to distinguish website-source synchronization from local recovery copies. Added ignored .local-backups/, preserving dated source archives, standalone Git bundles and checksum manifests on this computer. The ACTIVE hourly automation was updated in place with the same ID/schedule: verify a local snapshot before a pull and before a push; push only completed, reviewed work with applicable verification evidence. Unfinished/unverified edits stay local. Existing Git history and private visibility are preserved.
git archive --format=tar.gz --output=.local-backups/2026-10-22-8f5398e9c929-source.tar.gz 8f5398e9c929542529be838d7a4f51cccb38b525: created the pre-change source copy. A Python archive check compared all 161 files, byte for byte, with the committed tree; passed.git bundle create .local-backups/2026-10-22-8f5398e9c929-history.bundle --branches and git bundle verify .local-backups/2026-10-22-8f5398e9c929-history.bundle: passed. Internal Codex refs were excluded.8f5398e9c929542529be838d7a4f51cccb38b525, recovered working tree was clean, and git fsck --full passed. The live checkout was not replaced.git check-ignore .local-backups/: confirmed exclusion from Git. git diff --check: passed for the policy/documentation changes.sync-contact-collector-to-github, retaining its hourly interval and chat destination.First unattended execution, a real incoming remote change and website hosting/deployment linkage remain unverified. Local copies cover committed project files/history; uncommitted edits remain in the working folder, and secrets, ignored runtime files, Supabase records and private media are outside the archives. Same-disk copies do not protect against loss of the computer/disk. Application checks were not rerun because application code, migrations and dependencies were unchanged.
Website hosting connection, Supabase/cloud configuration, data/media backup, separate-device backup and founder-journal backfill remain separately scoped work. No automatic continuation to an application milestone. Review the first unattended local-backup/sync run.
Active task: source/documentation backup and safe automatic Git synchronization. The application milestone remains the owner-profile/adaptive-quiz scope documented below. User explicitly authorized automatic pushes/pulls, confirmed the repository is private, and deferred Supabase configuration.
Created and pushed the first source/history checkpoint, 3f0072562767b2d43540f0da754dea85262ef39d (160 files), through the existing GitHub Desktop sign-in. Retained origin, main, origin/main and local pull.ff=only. Created the ACTIVE hourly scheduled check Sync Contact Collector to GitHub (sync-contact-collector-to-github) and viewed its registered automation card. Added standing sync authorization to AGENTS.md, linked setup from README, and documented the safe hourly procedure in GITHUB_SYNC. No application code, migrations, credentials or saved data were changed by this task.
Initial candidate and staged-content scans found no known secret-pattern matches; git check-ignore .env.local node_modules/ .next/ test-results/ playwright-report/ confirmed those five exclusions. GitHub Desktop reported push complete; git rev-parse HEAD and git rev-parse refs/remotes/origin/main matched the first checkpoint. git status --short --branch was clean after the initial push. git diff --check passed for the sync documentation. Full command evidence and authentication limitations are in GITHUB_SYNC.md.
Unattended scheduled execution and an actual incoming remote change have not yet been exercised. Command-line Git has no HTTPS login and the GitHub plugin returns 404; GitHub Desktop is the verified transport. Application tests/build/browser checks were not rerun for this Git/documentation-only change; earlier results below retain their original scope.
Supabase/cloud configuration and database/media backup are deferred by the user. Founder-journal backfill, deployment and subsequent application milestones remain outside this sync task. Monitor the first scheduled sync for any access or concurrency issue.
Active scope: the latest approved owner-profile/onboarding/quiz prompt and supplied blue-button design reference, built on the completed Settings, plain-English, expanded-contact-profile and Contacts-folder work. Local production preview: http://127.0.0.1:3002, build .next-owner-quiz. Existing uncommitted files, credentials and saved libraries were preserved. No commit, push, deployment, live paid call or external integration was activated.
All data is synthetic and account-isolated; AI regression tests use isolated fake providers only.
| Command/check | Result |
|---|---|
npx supabase migration up --local |
Applied 018–019 without database reset |
npm test |
111 passed, including 25 new quiz checks |
npm run typecheck |
Passed |
npm run lint |
Passed, no warnings |
CONTACT_RECALL_DIST_DIR=.next-owner-quiz npm run build |
Passed; production routes generated |
npm run test:owner |
10 passed, real local Auth/RLS/RPC/storage |
npm run test:integration |
21 passed |
npm run test:profile |
7 passed |
npm run test:enrichment |
22 passed, including final standalone confirmation |
npm run test:folders |
5 passed |
PLAYWRIGHT_BASE_URL=http://127.0.0.1:3002 npm run test:e2e |
31 passed in Chromium |
PLAYWRIGHT_BASE_URL=http://127.0.0.1:3002 npx playwright test tests/browser/owner-quiz.spec.ts |
3 passed after extending every binary/three-choice button to text, top-padding and bottom-padding activation checks |
PLAYWRIGHT_BASE_URL=http://127.0.0.1:3002 npx playwright test tests/browser/owner-quiz.spec.ts -g 'approved answer rectangles' |
1 passed after adding single-person recognition and two-step Prev. recovery to the complete hit-area checks |
WebKit: PLAYWRIGHT_BROWSER=webkit PLAYWRIGHT_BASE_URL=http://127.0.0.1:3002 npx playwright test tests/browser/owner-quiz.spec.ts --output=/tmp/collector-owner-webkit-results |
Could not run: installed WebKit protocol mismatch (Unknown setting: PushAPIEnabled) during page setup; remaining checks stopped |
npm run benchmark |
Completed with synthetic 8/2,000-contact pools; pre-ranked 2,000-contact question selection median 22.65 ms, p95 33.95 ms on this machine |
Initial browser runs identified outdated menu/question selectors and a test needing explicit snapshot refresh. The photo retry test now drops a successful browser response after a real upload, preserving binary file data. One enrichment run concurrent with other suites failed; the standalone rerun passed all 22. All 65 backend checks passed in the final sequential run. Run worker/queue suites sequentially because they share local infrastructure. These observations are not evidence of live provider operation.
Visual evidence inspected: test-results/adaptive-common-1440.png, adaptive-thirds-1440.png, adaptive-dark-compact-390.png, owner-profile-1440.png and owner-profile-390.png. These use fictional records/photos and are ignored test artifacts; each browser invocation regenerates its screenshots and clears earlier default output. The supplied reference was available and used for the teal question bar, dark borders, rounded rectangles and spacing.
The requested scope is implemented. Desktop and narrow dark/compact screenshots were inspected, and the full Chromium suite passes. No automatic continuation to another milestone. Public signup/OAuth, contact-only exports/account deletion infrastructure, notifications/events, live providers and hosted rollout remain separately authorized future work. Existing invitation and account-security behavior is unchanged.
Active scope: the approved Contacts folder view, creation and membership workflows, on top of the existing Settings, plain-English and expanded-profile changes. Preview: http://127.0.0.1:3002, production build .next-folders. Older preview ports, existing uncommitted work, credentials and saved libraries were preserved. No commits, pushes, resets, deployment, live paid calls or external service activation.
components/contacts-folders.tsx: expandable full-path group tree, direct-membership markers, breadcrumbs, scoped search, subgroup tiles, deduplicated totals/rows, Include subgroups, and selected-person details. Existing notes, source-linked cues, Edit contact and See all details remain accessible. Desktop uses three sections; narrow screens stack them. Shared theme/compact settings and visible keyboard selection/focus apply.components/folder-actions.tsx: explicit root/subgroup creation, multi-person Add contacts → Add to group, independent Choose groups checkboxes, Save/Cancel, retained failure drafts and confirmed refresh before success. Existing rule-selected and inherited combined memberships keep their established behavior with explanations.lib/domain/folders.ts provides scope/path/explanation helpers; group counts and search documents evaluate the hierarchy once per person with existing rule semantics. Stable paginated loading covers all owned records. Other group pickers/editor labels now distinguish full paths.app/api/groups/route.ts and migrations 016–017 add validated, atomic, owner-scoped, idempotent additions. Same-name siblings remain forbidden while identical names in different branches are allowed. Existing profile capacity is enforced before any additions, preventing an uneditable contact. No original records are rewritten by these migrations.All fixtures are fictional and account-isolated. AI regression tests use fake providers only.
| Command/check | Result |
|---|---|
npx supabase migration up --local |
Applied 016, then 017 successfully; no reset or data-rewriting migration statements. |
npm test |
86/86 passed, eight files, 401 ms. Includes paths, overlap deduplication, independent parent/child membership, search scope and equivalence with existing smart-group predicates. |
npm run test:folders |
5/5 passed, real local backend, 1,069.49 ms. Covers sibling naming, cycles, cross-owner reads/writes, atomic/idempotent additions, unchanged sources, revisions, identical-email non-merging, smart-group restrictions and capacity. |
npm run test:integration |
21/21 passed, real local authorization/organization regressions, 1,500.95 ms. |
npm run test:profile |
7/7 passed, local backend profile/source/review/revision regressions, 1,149.80 ms. |
npm run test:enrichment |
22/22 passed, local fake-provider and Settings enforcement regressions, 2,252.21 ms. |
npm run typecheck |
Passed, exit 0. |
npm run lint |
Passed, exit 0. |
APP_ORIGIN=http://127.0.0.1:3002 CONTACT_RECALL_DIST_DIR=.next-folders npm run build |
Passed, exit 0; homepage and ten API routes. |
PLAYWRIGHT_BASE_URL=http://127.0.0.1:3002 npm run test:e2e |
Chromium 28/28 passed, 48.6 seconds against the final production build. |
npx prettier --check 'app/**/*.{ts,tsx,css}' 'components/**/*.{ts,tsx}' 'lib/**/*.ts' 'tests/**/*.ts' |
Passed. |
| Visual checks | Desktop 1440×900, narrow 390 px and dark/compact folder captures inspected. No horizontal overflow in automated narrow checks; keyboard focus and row/parent selection are tested. |
The four new browser scenarios cover the requested 15 folder cases plus same-origin/account privacy and a 501-contact/501-subgroup fixture that crosses the existing 500-row paging boundary. The folder total is 501, not 502, when one person has overlapping child memberships. The last page remains searchable and direct-only filtering leaves totals intact. Existing browser suites exercise imports, cue sources, pending photos, profile edits, explicit combining/undo, Settings, recognition, failed loading and focus recovery.
Intermediate runs found obsolete grid/filter selectors, ambiguous breadcrumb/navigation accessible names, and a keyboard-focus check that had switched to pointer input. Those were corrected without weakening privacy/data assertions. Review also corrected the row explanation for a rule-based descendant and made narrow-screen heading focus avoid the adjacent photo. The first full suite passed 25/28; the next passed 27/28 with one remaining old renamed-group selector. The focused corrected group test then passed. Final results are above.
After final suite cleanup, no temporary folder/browser test accounts remained.
Screenshots are generated in ignored test-results/: folders-desktop.png, folders-narrow.png, folders-dark.png. They contain synthetic fixtures only. The source snapshot taken before editing is outside the repository at /tmp/collector-before-folders.tar.gz; it is not a database backup or substitute for production backup procedures.
Drag-and-drop, new deletion workflows, persistence of folder navigation preferences, larger-scale architecture, integrations and future product milestones remain outside scope. Other installations must apply migrations through 017 and rebuild without resetting saved data. Next task: user review of the local Contacts folder view; no automatic continuation to another milestone.
Active scope: the approved contact-profile redesign, on top of Settings and plain-English updates. Local preview: http://127.0.0.1:3002, production build .next-profile. Earlier preview ports, existing credentials, uncommitted work and saved contact data were preserved. No commits, pushes, deployment, messages, live paid calls or external service activation.
All fixtures are fictional and account-isolated. AI tests use isolated fake adapters; no live provider was invoked.
| Command/check | Result |
|---|---|
node --env-file=.env.local --import tsx tests/profile-migration.ts seed, migration 013, then ... verify |
Passed original-row/column hashes and counts across 13 tables, including a rich old-schema fixture; isolated fixture and temporary snapshot removed. |
npx supabase migration up --local |
Applied 013, 014 and 015 successfully without resets. |
npm test |
80/80 passed, seven files; final unit run 304 ms. |
npm run test:profile |
7/7 passed, local backend, 1,563.93 ms. Includes every approved choice, source preservation, direct-RPC validation, ownership/revisions and combine/undo. |
npm run test:integration |
21/21 passed, local authorization/organization regression suite, 1,431 ms. |
npm run test:enrichment |
22/22 passed, local fake-provider regression suite, 2,111.31 ms. Includes completed-job contact deletion and Settings enforcement. |
npm run typecheck |
Passed, exit 0. |
npm run lint |
Passed, exit 0. |
APP_ORIGIN=http://127.0.0.1:3002 CONTACT_RECALL_DIST_DIR=.next-profile npm run build |
Passed, exit 0; homepage and ten API routes. |
PLAYWRIGHT_BASE_URL=http://127.0.0.1:3002 npm run test:e2e |
Chromium 24/24 passed, 34.9 seconds against the final production build. |
| Formatting / rendered inspection | Prettier check passed for app/components/lib/tests. Inspected final desktop, 390 px narrow, and dark compact screenshots; no horizontal overflow. |
Intermediate verification caught dropdown/textarea test-label assumptions, an affiliation-order test assumption, an older accessory-quiz expectation, a cascade-deletion foreign-key failure, blank partial-date display, and Escape propagating from the nested editor into its parent profile. The code defects were corrected with regression coverage. No privacy, preservation or review-state assertions were removed to obtain passing results. Synthetic accounts left by the initial failed cleanup were explicitly removed by their exact test identifiers after the deletion fix.
Screenshots: test-results/profile-desktop.png, test-results/profile-narrow.png, test-results/profile-dark-compact.png (synthetic fixtures; generated by the profile browser test). The existing database remained at its pre-change aggregate counts after isolated test cleanup; original-column hash verification, not aggregate counts alone, established migration preservation.
COLLECTOR_EXPECTED_BEHAVIOR.md were present. The explicit written profile requirements and existing specification governed this implementation.No new export, account-deletion UI, skills/attachments service, cover-image service, notifications, event system, OAuth/sync, social scraping, messaging or audio identification. Apply migrations through 015 and rebuild on another installation before using the new fields. The next task is user review of the local profile; no automatic continuation to another milestone.
Active scope: the approved interface wording request on top of Settings and milestone 4. The current production preview is http://127.0.0.1:3002, served from .next-plain-english. Only the prior preview on port 3002 was restarted; older ports were left alone. Existing uncommitted files, credentials, database structure and saved content were preserved. No commit, push, deployment, paid request or external integration was performed.
lib/ui/text.ts. lib/ui/errors.ts, the API client and server boundaries turn schema, browser and service failures into safe next steps. Known import errors are distinguished from unexpected exceptions. Failed/uncertain responses do not falsely claim a save succeeded or failed. Internal codes and network error causes remain available for debugging; no extra content logging was added.unsure session list supports the requested distinction without changing API contracts or stored contact facts. Uncertainty/skip/missing-detail choices do not add match evidence, remain restorable, and survive scope widening. Ranking and paid-processing gates are unchanged.docs/PLAIN_ENGLISH.md as the ongoing writing policy.All tests used fictional local accounts. AI tests use isolated test adapters/mocked transport; no live provider was called.
| Command/check | Final result |
|---|---|
npm test |
71/71 passed, six files, final run 304 ms. Includes separate uncertainty states, recovery/ranking, safe error text, and lost/unreadable responses. |
npm run test:integration |
21/21 passed against the local backend; account isolation, organization, import and combination integrity. |
npm run test:enrichment |
21/21 passed against the local backend; permission/budget gates, source retention, review states and isolated worker behavior. |
npm run typecheck |
Passed, exit 0. |
npm run lint |
Passed, exit 0. |
APP_ORIGIN=http://127.0.0.1:3002 CONTACT_RECALL_DIST_DIR=.next-plain-english npm run build |
Passed, exit 0; production homepage and ten API routes. |
PLAYWRIGHT_BASE_URL=http://127.0.0.1:3002 npm run test:e2e |
Chromium 21/21 passed, 30.2 seconds. Includes persistence/reload, two-account privacy, Settings failures, original notes, pending photos, combines/undo, imports, keyboard use and recovery. |
npx prettier --check 'app/**/*.{ts,tsx,css}' 'components/**/*.{ts,tsx}' 'lib/**/*.ts' 'tests/**/*.ts' |
Passed after formatting the updated test selectors. |
| Visual inspection | Inspected final desktop contact editor and Review details, plus Find someone and Settings at desktop/narrow widths. Tests assert no horizontal overflow at 390 px. Screenshots are local synthetic fixtures; the Review details error example is explicitly mocked/labeled as a test. |
Initial/intermediate runs caught obsolete test text, ambiguous selectors and a display-label/value mismatch in the workplace status selector. These were corrected; the final browser suite checks the saved former value both before saving and after reopening. A subsequent unit assertion was updated for the final duplicate-context explanation. No assertions about privacy, review gates or saved content were removed to obtain a passing result.
docs/COLLECTOR_MASTER.md was absent from the repository and its handoff copy. Existing root requirements and design/acceptance documents were used, with the current user wording request taking precedence.No database migration or new dependency is needed for this wording change. Rebuild/restart the app using the existing environment. Settings migration 012 remains the existing prerequisite. Notifications, synchronization, export, later milestones and live paid-service validation remain deferred. No Git history changes were made; the existing origin still points to the requested MonkMonay repository.
Next remaining task: user review of the local preview and any requested wording adjustments. No automatic continuation to another milestone.
Active scope: dedicated Settings tab only, on top of the existing milestone-4 application. Existing uncommitted project files and architecture were preserved. No deployment, commit/push, provider activation, paid call, reset or external integration was performed.
components/settings.tsx, lib/domain/preferences.ts, lib/server/preferences.ts, app/api/settings/route.ts: typed shared values, strict partial writes, authenticated/beta/same-origin checks, RLS, save feedback and last-confirmed UI on failed writes. Per-field patches serialize with the existing policy lock and preserve unrelated settings. No owner ID accepted from the client.202610220012_settings.sql applied locally. Existing enrichment policy/budget values are reused, not copied or reset. General preferences are read alongside library data. Existing accounts retain Light/comfortable/tips/suggestions behavior; new paid permission defaults off.enrichment_policies. Disabling automatic processing blocks new automatic queueing and dispatch. Explicit selected processing remains possible under master permission and scope/budget approval. Disabling the master cancels queued work and closes budgets. Saved notes/results/review states remain intact. Server provider restrictions still apply.| Preference/control | New location and status | Migration/default |
|---|---|---|
| Theme: Light/Dark/System | Settings → Appearance; functional shared tokens and live system-media changes | New; Light preserves prior behavior |
| Compact layout | Settings → Appearance; functional navigation, list, card and review spacing | New; off |
| Show explanatory tips | Settings → Appearance; functional optional Improve explainer | New; on; essential warnings/errors/review labels stay visible |
| Automatic memory cues | Settings → Automation & AI; functional server-enforced auto_notes |
Moved from Improve; existing value retained, otherwise true behind permission/mode/budget gates |
| Automatic photo analysis | Settings → Automation & AI; functional server-enforced auto_photos |
Moved from Improve; existing value retained, otherwise true behind permission/mode/budget gates |
| Automatic group suggestions | Settings → Automation & AI; functional deterministic suggestions in Organize groups | New switch, prior behavior on; rules/manual groups unaffected |
| Duplicate suggestions | Settings → Automation & AI; functional proactive comparisons in Improve | New switch, prior behavior on; manual comparison, decisions, merge approval/undo preserved |
| Paid AI processing permission | Settings → Automation & AI; functional existing account policy | Relabeled/moved from Improve; existing value retained, new accounts off; enabling requires explicit budget consent |
| Automatic budget approval and usage ledger | Settings → Account & Data (approval form in Automation & AI) | Moved from Improve; existing budgets retained |
| Account details | Settings → Account & Data; authenticated email | Existing identity reused; sign-out stays in header |
| Contacts grid/list; Identify scope and answers; contact manual/assisted mode; priorities; group pins/rules; import selections; cue pins; selected bulk budget; individual review/merge decisions | Contextual controls retained in Contacts/Identify/Improve and dialogs | No new persistence or relocation; these are view/workflow/record-specific, not global preferences |
npx supabase migration up --local: applied only migration 012, no reset. Initial sandbox attempt could not write CLI telemetry; escalated local retry passed.npm run typecheck: passed.npm run lint: passed.npm test: 67 passing unit tests across five files, including strict settings validation and safe defaults.npm run test:enrichment: 21 passing real-local-database tests, including new preference isolation/validation, preservation of existing policies, concurrent unrelated patches, stopped automatic enqueue/dispatch, disabled master/bulk enforcement and retained notes/cues. Providers are explicitly labeled test adapters, not live AI.npm run test:integration: 21 passing authorization/organization tests including two-account storage/data isolation, merge approval/reversal, provenance and unknown/pending data. Initial sandbox test attempt could not open tsx IPC; authorized local retry passed.APP_ORIGIN=http://127.0.0.1:3002 CONTACT_RECALL_DIST_DIR=.next-settings npm run build: production build passed, including /api/settings. Next added its generated Settings build type paths to tsconfig.PLAYWRIGHT_BASE_URL=http://127.0.0.1:3002 npm run test:e2e: 20/20 Chromium browser tests passed against the final production build (29.5 seconds). Includes navigation, server-confirmed save, reload, system-theme changes, failed-save retention, keyboard switch operation, account switching, suggestion suppression/manual management and all 18 existing workflow regressions. Inspected full-page screenshots at 1440px and 390px (test-results/settings-dark.png, test-results/settings-mobile.png); no horizontal overflow. Final preview started with APP_ORIGIN=http://127.0.0.1:3002 CONTACT_RECALL_DIST_DIR=.next-settings npm run start -- --port 3002.Notifications & Reminders: follow-up master permission, browser/email delivery and post-event debrief prompts. Integrations: Google/Microsoft Contacts automatic two-way sync, connected accounts and upcoming-event suggestions. Account & Data: export and unsupported account-management actions. These have no functioning infrastructure in the current app and no misleading controls were added. The intended categories are recorded in PRODUCT_SPEC §18.
Next task: user review of Settings and any requested adjustments. No automatic progression to another milestone. Setup: apply migration 012 and rebuild/restart as documented in README. Local Settings preview uses http://127.0.0.1:3002; older app ports were left alone.
Current scope/date: milestone 4 only, October 22, 2026. Implementation, fixes and available local validation are complete. Chromium and Firefox pass; WebKit is blocked by an installed-engine protocol incompatibility, and the dependency audit is blocked by the registry. No Prepare, practice, Follow-ups, scraping, discovery or synchronization was added. The historical milestone-1/2/3 evidence follows this current record.
Read the pasted milestone-4 request, AGENTS, full PRODUCT_SPEC, TECHNICAL_DESIGN, MILESTONES, ACCEPTANCE_TESTS, BUILD_STATUS and prompt 04. Inspected actual source, migrations, backend and browser behavior. Git still has no tracked project baseline; no reset/discard/commit was performed.
The initial process inventory identified PID 19053 on port 3000 (.next, build Yo88ldF7ia4zGAm8S3eKn) and PID 22405 on 3001 (.next-milestone3, build ZXa8_ZVr0ems0t5gReiog). The older port-3000 process was left running. Only the identified port-3001 preview was replaced with the milestone-4 production build in .next-milestone4. Open http://127.0.0.1:3001. Final listener: PID 25237, build Sgz6GebP-EH4FywGpncdY, ready in 146 ms. lsof -nP -iTCP:3000 -sTCP:LISTEN, lsof -nP -iTCP:3001 -sTCP:LISTEN and cat .next-milestone4/BUILD_ID confirmed those processes/build; PID 19053 still serves port 3000.
Preserved migrations 001–010, existing accounts/data and .env.local. Applied only additive 202610220011_deleted_enrichment_sources.sql. No database reset, real-data test target, real invitation, deployment, paid resource, model request or provider credential change occurred. Remote processing remains disabled and no provider key is present. Temporary account cleanup is scoped to fictional accounts created by the tests.
| Requirement/gap | Actual behavior and evidence |
|---|---|
| C09/C10 field/phrase search | Fixed negative-only Contacts/Improve search: neutral unknowns remain visible rather than being removed by a positive-score-only filter. Added explicit glasses/facial-hair/hair-color/length phrases, spelling variants, negative current affiliation and “no longer works at”. Only confirmed appearance is used; unknown/pending remain neutral. No inferred industry or open-ended language understanding. |
| C05–C08/C14/C15/C20 recovery | Typed structured clues prevent redundant questions. Skipped/suppressed fields can be restored independently without clearing other clues, scope or rejected batches. Existing deterministic ties, full-pool question selection, six-card batch rejection, unseen candidates and mistaken-recollection recovery remain tested. |
| B09 source-deletion retention | Reproduced a generated unedited summary surviving its note deletion in stale history. Migration 011 removes unedited/unpinned generated cues referencing that deleted note and removes photo-associated jobs/caches on photo deletion. Old worker completion cannot resurrect deleted work. Edited/pinned copies remain clearly outdated and show deleted-source text instead of dead links. A read-only audit found 0 orphan uncurated cues / 0 orphan photo caches in existing data; no historical content cleanup was needed. |
| B06 keyboard/focus | Tabbing through an unchanged aliases input incorrectly marked the editor dirty and triggered Discard unsaved changes. No-op patches now preserve a clean draft. Browser coverage keeps focus inside the panel, dismisses it from the close control, restores the opener and retains filters/list state. The 2,000-contact test additionally verifies deep scroll preservation. Native date controls can consume Escape before the panel. |
| Desktop contrast/error states | Darkened muted text and used the established accent for visible keyboard focus. Added Retry opening library after an initial temporary backend failure. Tested accessible textbox names, loading, retry, empty/unknown, provider-disabled and pending/review states; no redesign or future tabs. This is bounded accessibility coverage, not a complete audit. |
| Large-library responsiveness | Improve repeatedly rescanned the library for each contact/field. Shared counts preserve the existing reasons and remove that repeated work. The initial 2,000-contact single-sample measurement was 3,245.49 ms; after change 7.99 ms, with warmed results below. Contacts cards now mount when that view is active, avoiding thousands of hidden cards in Identify. No speculative indexing service was added. |
| Interruption/concurrency | Browser import test lets the server commit, drops the response and retries the same batch: one record remains. Simultaneous contact edits yield one 200 and one 409, preserving the winner. Existing recovery, duplicate-job prevention, old-lease rejection, cancellation and concurrent budget tests pass against real PostgreSQL with labeled test adapters. |
| A01–A08/D/E privacy and integrity | Real Auth/Postgres/Storage suites retain cross-account denial, same-owner relations, uninvited/revoked checks, imports/groups/history, private photo paths, merge/undo reconciliation, pending facts and source links. Additional HTTP checks reject over-size/over-count imports and disguised HTML photos, render malicious-looking notes literally, and verify private no-store caching. |
Environment: macOS 14.4, Apple M2 arm64, Node 22.17.0, npm 10.9.2, Next 16.4.2, local Supabase PostgreSQL 17, Playwright 1.64.1. All contact fixtures are fictional. Tests use labeled fake providers or exercise the real adapter through a mocked HTTP transport; no live provider requests occur.
| Status | Exact command/check | Observed result |
|---|---|---|
| Passed | npx supabase migration up --local |
Applied additive 011 to preserved local database; no reset. |
| Passed | npm test |
65/65, 0 failures; final domain run 319 ms. Includes 24 milestone-1, 28 milestone-2, eight milestone-3 and five milestone-4 cases. |
| Passed | npm run test:integration |
21/21, 0 failed/skipped; 1,386.77 ms on preserved local stack with isolated accounts. |
| Passed | npm run test:enrichment |
19/19, 0 failed/skipped; 1,877.85 ms. Includes new source-deletion/cache regression and prior review, budget, recovery, isolation and merge/undo cases. |
| Passed | PLAYWRIGHT_BASE_URL=http://127.0.0.1:3001 npm run test:pagination |
1/1, 0 failed/skipped; 814.18 ms total, all 1,005 owned review decisions returned. |
| Passed | npm run typecheck; npm run lint |
Exit 0. |
| Passed | npx prettier --check 'app/**/*.{ts,tsx,css}' 'components/**/*.{ts,tsx}' 'lib/**/*.ts' 'scripts/**/*.ts' 'tests/**/*.ts' '*.{ts,mjs,json}' |
Exit 0; all matched source/test/config files formatted. |
| Passed | APP_ORIGIN=http://127.0.0.1:3001 CONTACT_RECALL_DIST_DIR=.next-milestone4 npm run build |
Exit 0, homepage + nine dynamic API routes. |
| Passed | APP_ORIGIN=http://127.0.0.1:3001 CONTACT_RECALL_DIST_DIR=.next-milestone4 PLAYWRIGHT_BASE_URL=http://127.0.0.1:3001 npm run test:e2e |
Chromium 18/18, 0 failed/skipped, 25.5 seconds. Four milestone-4, three milestone-3, seven milestone-2 and four milestone-1 workflows, with actual backend persistence. |
| Passed | PLAYWRIGHT_BROWSER=firefox APP_ORIGIN=http://127.0.0.1:3001 CONTACT_RECALL_DIST_DIR=.next-milestone4 PLAYWRIGHT_BASE_URL=http://127.0.0.1:3001 npm run test:e2e -- --output test-results/firefox |
Firefox 18/18, 0 failed/skipped, 34.3 seconds, same workflows. Firefox 157.0, Playwright build 1555. |
| Blocked — engine failure | PLAYWRIGHT_BROWSER=webkit APP_ORIGIN=http://127.0.0.1:3001 CONTACT_RECALL_DIST_DIR=.next-milestone4 PLAYWRIGHT_BASE_URL=http://127.0.0.1:3001 npm run test:e2e -- --output test-results/webkit |
0 passed, 1 failed, 1 interrupted, 16 unexecuted, exit 130 after stopping only the identified runner. browserContext.newPage: Protocol error (Page.overrideSetting): Unknown setting: PushAPIEnabled occurs before application interaction; the first case timed out at 60 seconds and the second repeated the same error. Installed frozen macOS-14 WebKit build 2251 is incompatible with this Playwright page initialization. No WebKit/Safari app compatibility claim. |
| Passed | PLAYWRIGHT_BASE_URL=http://127.0.0.1:3001 npm run test:scale |
2,000 actual persisted contacts, four 500-row API imports, complete library retrieval, unseen target recognition, list/panel/focus/deep-scroll checks and zero page errors; measurements below. Created/deleted only its own fictional account. |
| Passed | npm run benchmark |
Five warmups, 30 measured samples per case, 8/2,000 deterministic contacts and 500-row near-limit CSV; results below. |
| Passed | Client-bundle actual-secret-value scan (command below) | Final build: 29 files, 0 matches for configured server secret values, no values printed; remote enabled false, provider key present false. A bounded check, not an exhaustive secret audit. |
| Passed | Read-only orphan audit (SQL below) | Counted generated unedited/unpinned cues whose source note is absent, and cached photo results whose source photo is absent: 0 / 0 before and after regressions. No rows mutated. |
| Passed | Final account/HTTP check (command below) | 0 temporary browser/enrichment/authorization/organization/pagination/scale/signup accounts remain. Both homepages 200, both unauthenticated library APIs 401, enrichment on 3001 401. |
| Passed | Screenshot review + browser desktop assertions | Chromium/Firefox screenshots at 1440 × 900 and 1280 × 800: Identify, recognition card, Contacts, expanded profile, photo review and source-note history inspected. Visible controls, source labels/links and no horizontal overflow are covered; no complete accessibility claim. |
| Passed | rg -n 'console\.|fetch\(|https?://' app lib scripts/enrichment-worker.ts; docker ps --format '' --filter 'name=supabase_' |
Source review found only sanitized worker messages and the explicit provider transport, no source-content logging or profile URL fetch. Runtime inventory shows only the original Contact_Collector stack; temporary validation containers stopped. This does not audit hosted infrastructure logs. |
| Blocked by registry response | npm audit --json --fetch-timeout=15000 --fetch-retries=0 |
Exit 1, npm registry audit endpoint 400 Bad Request with retirement notice. No fresh advisory result obtained. Earlier unbounded attempt was stopped after stalling. Historical zero production / five high development-only advisories remain historical, not a new clearance. |
These print aggregate counts/statuses only and do not mutate contacts, accounts or storage. The secret scan compares values in memory without printing them.
node --env-file=.env.local --import tsx --input-type=module <<'JS'
import { localAdmin } from './scripts/local-client.ts';
const admin = localAdmin();
let remaining = 0;
for (let page = 1; ; page++) {
const { data, error } = await admin.auth.admin.listUsers({ page, perPage: 100 });
if (error) throw error;
remaining += data.users.filter(u => /^(browser-|enrichment-|authorization-|organization-|pagination-|scale-|signup-)/.test(u.email || '')).length;
if (data.users.length < 100) break;
}
console.log({ remainingTemporaryAccounts: remaining });
if (remaining) process.exitCode = 1;
for (const [port, path, expected] of [[3000, '/', 200], [3000, '/api/library', 401], [3001, '/', 200], [3001, '/api/library', 401], [3001, '/api/enrichment', 401]]) {
const response = await fetch(`http://127.0.0.1:${port}${path}`, { redirect: 'manual' });
console.log({ port, path, status: response.status, expected });
if (response.status !== expected) process.exitCode = 1;
}
JS
node --env-file=.env.local --input-type=module <<'JS'
import { readdir, readFile } from 'node:fs/promises';
const root = '.next-milestone4/static';
const paths = await readdir(root, { recursive: true, withFileTypes: true });
const secrets = ['SUPABASE_SERVICE_ROLE_KEY', 'OPENAI_API_KEY'].map(k => process.env[k]).filter(Boolean);
let scanned = 0, matches = 0;
for (const entry of paths) {
if (!entry.isFile()) continue;
const body = await readFile(`${entry.parentPath}/${entry.name}`, 'utf8');
scanned++;
if (secrets.some(value => body.includes(value))) matches++;
}
console.log(JSON.stringify({ scanned, matches, remoteEnabled: process.env.ENRICHMENT_REMOTE_ENABLED === 'true', providerKeyPresent: Boolean(process.env.OPENAI_API_KEY) }));
if (matches) process.exitCode = 1;
JS
docker exec supabase_db_Contact_Collector psql -U postgres -d postgres -At -c "select 'orphan_uncurated_cues='||count(*) from public.memory_cues c where c.origin='generated' and not c.edited and not c.pinned and exists(select 1 from jsonb_array_elements(c.source_notes) r where not exists(select 1 from public.notes n where n.owner_id=c.owner_id and n.contact_id=c.contact_id and n.id::text=r->>'id')); select 'orphan_photo_caches='||count(*) from public.enrichment_jobs j where j.kind='photo' and j.photo_result is not null and not exists(select 1 from public.photos p where p.owner_id=j.owner_id and p.contact_id=j.contact_id and p.id::text=j.input->'photo'->>'id');"
Created /var/folders/lk/jsxzndkj381fq9yy8d8rqgm80000gn/T/contact-recall-m4-gryxzzbo with the package manifest/lockfile and source. Copied Supabase config with project ID Contact_Collector_m4_validation and ports 55320–55329 instead of 54320–54329; no existing environment/volume was copied or overwritten. Commands were run in that disposable directory unless a workdir flag is shown:
npm ci --offline --no-audit --no-fund
# From the original repository, with logs captured privately (CLI output can contain local keys):
node_modules/.bin/supabase start --workdir /var/folders/lk/jsxzndkj381fq9yy8d8rqgm80000gn/T/contact-recall-m4-gryxzzbo
# Copied migration 011 when added, then:
node_modules/.bin/supabase migration up --local --workdir /var/folders/lk/jsxzndkj381fq9yy8d8rqgm80000gn/T/contact-recall-m4-gryxzzbo
# In the disposable directory:
node --import tsx scripts/local-env.ts
LOCAL_TEST_SUPABASE_PORT=55321 npm run test:integration
LOCAL_TEST_SUPABASE_PORT=55321 npm run test:enrichment
LOCAL_TEST_SUPABASE_PORT=55321 npm run seed:local
npm run build
APP_ORIGIN=http://127.0.0.1:3002 npm run start -- --port 3002
# After smoke verification, stopped only that temporary app, then from the original repo:
node_modules/.bin/supabase stop --workdir /var/folders/lk/jsxzndkj381fq9yy8d8rqgm80000gn/T/contact-recall-m4-gryxzzbo
Passed: clean npm installation of 419 packages from the existing cache (13 seconds); fresh schema replay 001–010 and additive 011; environment generation without credential output; 21/21 real backend regressions (1,587.82 ms) and 19/19 enrichment tests (2,614.48 ms); fictional seed; production build; Chromium authenticated UI + eight seeded records on port 3002/backend 55321. Temporary app and only the named validation containers were stopped; local backup/volumes were retained by Supabase. The original stack stayed running. The clean build was repeated with the editor fix; the final small color-token adjustment is verified on the main final build.
This resolves the previously unexecuted local clean-install/migration replay path on this machine. It does not establish a new OS/Docker installation, uncached network package installation or current Safari. Installing Firefox/WebKit succeeded with npx playwright install firefox webkit; Playwright explicitly warns that macOS 14.4 receives frozen WebKit. npm also warns that the pinned ESLint 9 release is no longer supported; no unreviewed major dependency upgrade was performed.
Pure benchmark: Apple M2/macOS 14.4/Node 22.17.0, deterministic repeated-name/alias/affiliation/unknown/pending fixtures, five warmups and 30 samples, no network/provider. Question timing uses a pre-ranked full pool. These measure this fixture and environment, not real-world recognition accuracy.
| Operation | 8 contacts median / p95 ms | 2,000 contacts median / p95 ms |
|---|---|---|
| Historical sentence ranking | 0.06 / 0.07 | 9.59 / 10.60 |
| Alias + conservative typo | 0.09 / 0.13 | 16.05 / 16.48 |
| Negative appearance ranking | 0.03 / 0.07 | 5.04 / 6.87 |
| Next useful question | 0.04 / 0.05 | 3.53 / 3.63 |
| Improve reasons/order | 0.02 / 0.04 | 3.60 / 3.81 |
UTF-8 decode + CSV parse/validation: 500 rows / 2,087,399 bytes, median 186.32 ms, p95 233.61 ms, max 235.70 ms. Actual API import in the scale check: four 500-row, ~40,862-byte files took 1,932.41 / 2,078.15 / 2,321.62 / 2,987.98 ms. Library HTTP retrieval took 2,150.22 ms; Identify initial readiness 2,366.29 ms; typing the previously unseen final contact to a visible result 25.15 ms. Panel close preserved 183,471 px scroll and opener focus, with 0 page errors. A small focused browser diagnostic overlapped part of the import run; no dedicated performance lab/load isolation is claimed. The warmed pure measurements were separate. Full initial load is not subsecond; do not turn query timings into a blanket responsiveness claim.
EPERM); rerunning the same authorized local checks outside that network restriction succeeded.Unknown setting: PushAPIEnabled. Stopped the identified Playwright runner PID 25869 with kill -INT 25869; no app/backend process was stopped. Its failure traces remain in test-results/webkit/. The 16 remaining cases are unexecuted, not passing or application failures.EPERM); authorized local execution completed setup/migration checks. No automatic approval rejection remains.Use BETA_READINESS.md for the external-tester decision and README.md for supported imports/providers. Normal local setup remains Node/npm + Docker/Supabase; no AI credential is needed for Contacts/Improve/Identify.
Assessment: no unresolved critical privacy, isolation, merge/undo or data-loss defect was found in the completed checks after the source-deletion fix. This is bounded local evidence. External invitations still require separate authorization and the dependency, hosting, provisioning, retention/restore and accessibility/browser prerequisites in BETA_READINESS; the current audit and WebKit gaps remain open.
If the current preview stops:
npm run db:start
npx supabase migration up --local
APP_ORIGIN=http://127.0.0.1:3001 CONTACT_RECALL_DIST_DIR=.next-milestone4 npm run build
APP_ORIGIN=http://127.0.0.1:3001 CONTACT_RECALL_DIST_DIR=.next-milestone4 npm run start -- --port 3001
Do not start a second app on an occupied port or reset the working library. Existing fictional demo login remains alex@recall.test / Local-Recall-Only-2026!; automation does not use that library.
Preserved unverified items: actual live-provider semantic accuracy, uncertainty/refusal behavior and billing; local vision; fresh OS/uncached npm/Docker installation; current Safari/full assistive-technology audit; long idle expiry; exhaustive request/failure races beyond the tested concurrency/interruption cases; closed-dialog import resume (a new batch is still explicit); larger/heavier/deeper libraries beyond measured fixtures; hosted retention, backups/restore, full account erasure, operational provisioning and external deployment. Current dependency audit requires a successful retry/triage before external release. Source deletion clears uncurated derivations; independently retained raw import/entry provenance and explicitly edited/pinned cue copies remain documented user content. Local Docker backups/volumes are operator-managed. Contact deletion removes storage before rows, so interruption may require retry; no hosted erasure guarantee is claimed.
Deferred: all later milestones, deployment, real invitations and live provider use. Stop after milestone 4 and this readiness report.
The following text and earlier records are preserved evidence for those tasks. Their then-current wording and counts are superseded by the milestone-4 record above.
Current date: October 22, 2026 (America/New_York). Current scope: milestone 3 only — controlled enrichment and source-linked memory cues. Implementation and local verification are complete as recorded below. Milestone 4 and later features were not started. The previous milestone-2 audit/evidence follows unchanged as historical context; it was not repeated.
Read AGENTS, PRODUCT_SPEC, TECHNICAL_DESIGN, MILESTONES, ACCEPTANCE_TESTS, the existing build record and prompt 03. Extended the existing Next/React/TypeScript/Supabase implementation and npm lockfile. Preserved data and migrations 001–005; applied only additive migrations 006–010. No reset, reseed, Git discard, deployment, paid resource, invitation or live model request occurred. .env.local was not modified; safe inspection reported no OpenAI key and remote processing disabled. No external contact/photo/note transmission was authorized or performed.
The original production server on http://127.0.0.1:3000 was not restarted. A separate production build/directory serves milestone 3 at http://127.0.0.1:3001 against the preserved local database. Only that new preview process was restarted when verifying fixes.
| Requirement / acceptance coverage | Implemented and verified behavior |
|---|---|
| Save first; B02/B10 | Existing contact save remains transactional. Source/mode changes write durable outbox rows; workers can see only committed contacts. No model call runs in a save handler. Missing provider configuration leaves contacts/editing/recognition working. Existing contacts/imports default to manual; Fully manual never automatically queues work. |
| Durable jobs; E05/E08/E09 | Owned input hashes, three-attempt limit, two-minute lease, pending/processing/completed/failed/blocked/cancelled/stale states, explicit cancellation/retry, source invalidation and old-token rejection. Undispatched crashes recover; ambiguous dispatched outcomes stop for explicit retry rather than silently repeating a potentially charged request. |
| E01–E04 appearance | Auto-queue appropriate new assisted photos only under enabled account policy. Show exact private source photo; approve/correct/reject individual fields or Skip. Pending/rejected/stale values are excluded from confirmed observations. Revision checks stop stale browser approval from overwriting a newer edit. Optional skin tone is always uncertain, retained in review history and not a new quiz field. Unclear/multiple-person/no-person outputs require clarification or a new single-person crop/photo; no subject is guessed. |
| B07/B08/B09/C16 cues and sources | Generated cues immediately appear as Unreviewed summary, linked to exact original note IDs/versions. Manual cues, excerpts, editing, pins, review, dismissal and More cues work. Corrections append a new note version; original bodies remain unchanged and labeled historical. Deletion invalidates output and scrubs deleted note text from durable inputs. Stale edited/pinned cues remain explicitly outdated and leave current recognition; independent manual cues remain. Source links focus inside the active profile. |
| E06/E07 paid consent and concurrency | Account assistance is opt-in; selected bulk scope requires explicit approval and a hard budget. Worker reservation/dispatch/completion accounting is transactional. Unknown outcomes retain their conservative allowance; known token usage releases unused allowance. Concurrent workers cannot reserve beyond the cap. No local model or automatic paid fallback exists. |
| Reuse and prioritization | Unchanged per-contact inputs deduplicate; completed identical-photo results can be reused across contacts only within the same account/provider/model and cite each new owned source. Concurrent identical photos do not dispatch together. Priority uses saved contact/group choices (including nested smart rules), same-name difficulty and missing confirmed facts, not prestige. Group priority is captured at job creation; changing priority alone never triggers processing. Sufficiently described contacts are not automatically reanalyzed. |
| Genuine adapter; E10–E12 | Configurable OpenAI Responses adapter, supplied image bytes, strict output schema/vocabulary/source-reference validation, no tools, no remote profile fetch, bounded input/response and timeout. Credentials remain server-only. Tests inject labeled fake adapters or a mocked HTTP transport; production configuration cannot select a fake adapter. |
| A01–A08 isolation | New tables have RLS and ordinary-user read-only grants. Guarded user RPCs check authenticated active membership/ownership; worker RPCs are service-role-only and recheck consent, owner, source, budget and lease. Private photo download paths are independently checked. Forged IDs/writes, foreign scope, unapproved accounts and revoked access are tested. |
| Milestone-1/2 preservation | Deterministic search/quiz and profile reads remain independent of providers; original notes and affiliation/group/relationship history remain. Cues do not enter ranking/quiz facts. Existing approved merge/undo remains intact; derived edits participate in reconciliation. New processing on active merged profiles is paused until explicit undo. |
New code is concentrated in lib/enrichment/, scripts/enrichment-worker.ts, /api/enrichment, cue/review components and additive database functions. The shared contactSelect query now has a direct owner/contact relationship for photo suggestions, covered by an actual nested-query regression. Imported raw provenance and the documented CSV/vCard subset are unchanged.
Environment: the existing macOS arm64 workspace, Node 22.17.0, npm 10.9.2, Next 16.4.2, local Supabase PostgreSQL 17/Auth/private Storage, Playwright 1.64.1/Chromium. No dependencies or provider credentials were installed. Tests created isolated fictional accounts and used synthetic photos/notes. The browser helper labels synthetic job output TEST ADAPTER and is not selectable by the production worker.
| Status | Exact command/check | Observed result |
|---|---|---|
| Passed | npx supabase migration up --local |
Applied 006, then the additive recovery/reuse/relationship refinements 007–009 and group-priority integration 010 successfully. No earlier migration changed and no database reset. |
| Passed | npm test |
60/60, 0 failures: 24 milestone-1, 28 milestone-2 and eight milestone-3 unit/domain/transport tests. Final run 289 ms. Includes sensitive/invalid output, mocked real-adapter requests, oversized response rejection, source references and exclusion of cues/superseded notes from identification. |
| Passed | npm run test:enrichment |
18/18, 0 failed/skipped, final run 2,482.04 ms. Covers durable automatic/manual saves, exact library query, revoked access, review/correction/rejection, source deletion/correction, stale worker/browser results, protected pins/edits, concurrent budget enforcement, lease recovery, explicit ambiguous-outcome retry, timeout/retry bound, exact accounting, private cache reuse, selected bulk scope, nested/smart group priority parity and merge/undo reconciliation. |
| Passed | npm run test:integration |
21/21, 0 failed/skipped, 1,578.07 ms after all database changes. Existing milestone-1/2 persistence, real authorization/storage, import/group/hierarchy, approved merge/undo/rollback and provenance regressions. |
| Passed | PLAYWRIGHT_BASE_URL=http://127.0.0.1:3001 npm run test:pagination |
1/1, 0 failed/skipped, 728.22 ms total; all 1,005 owned review decisions returned through the milestone-3 API. |
| Passed | npm run typecheck |
Exit 0 (tsc --noEmit) on final types/test code. |
| Passed | npm run lint |
Exit 0, no remaining warnings/errors. The isolated generated build directory is ignored. |
| Passed | npx prettier --check 'app/**/*.{ts,tsx,css}' 'components/**/*.{ts,tsx}' 'lib/**/*.ts' 'scripts/**/*.ts' 'tests/**/*.ts' '*.{ts,mjs,json}' |
Exit 0 on final matched source/test/config files. |
| Passed | APP_ORIGIN=http://127.0.0.1:3001 CONTACT_RECALL_DIST_DIR=.next-milestone3 npm run build |
Exit 0; isolated webpack production build with homepage and nine dynamic API routes, including enrichment. Does not replace the running port-3000 build. |
| Passed | APP_ORIGIN=http://127.0.0.1:3001 CONTACT_RECALL_DIST_DIR=.next-milestone3 PLAYWRIGHT_BASE_URL=http://127.0.0.1:3001 npm run test:e2e |
14/14, 29.9 seconds: three new milestone-3 workflows, seven milestone-2 workflows and four milestone-1 workflows. All use real local persistence and isolated accounts; no external browser requests in the enriched recognition flow. Final source-focus regression included; .last-run.json reports passed, no failed tests. |
| Passed — expected unavailable result | npm run enrichment:worker -- --once |
Exit 1, with Enrichment unavailable: remote authorization, credentials, model and conservative price/token bounds are required. No requests made. This verifies disabled/missing configuration; it is not a live-provider test. |
| Passed | Screenshot inspection | test-results/enrichment-review-1440.png, generated-cue-1440.png, cue-source-history-1440.png: source-photo decisions, exact Unreviewed summary label/source notes, manual pin and outdated original/history states inspected. Existing desktop/keyboard/overflow browser assertions also passed; this is not a full accessibility audit. |
| Passed | rg -l -e OPENAI_API_KEY -e SUPABASE_SERVICE_ROLE_KEY -e api.openai.com/v1/responses .next-milestone3/static |
Exit 1, no matches in the final client bundles. This bounded scan supports server/client separation; it is not an exhaustive secret audit. |
| Passed | Read-only cleanup/HTTP command below | Exit 0; 0 temporary browser/enrichment accounts remain. Both local homepages return 200, both unauthenticated library routes return 401, and milestone-3 enrichment returns 401 without authentication. |
| Passed | APP_ORIGIN=http://127.0.0.1:3001 CONTACT_RECALL_DIST_DIR=.next-milestone3 npm run start -- --port 3001 |
Final production build running at http://127.0.0.1:3001, ready in 128 ms. The prior port-3000 process remains running. |
Read-only cleanup/HTTP check (no contact/account mutations):
node --env-file=.env.local --import tsx --input-type=module <<'JS'
import { localAdmin } from './scripts/local-client.ts';
const admin = localAdmin();
let remaining = 0;
for (let page = 1; ; page++) {
const { data, error } = await admin.auth.admin.listUsers({ page, perPage: 100 });
if (error) throw error;
remaining += data.users.filter(u => /^(browser-|enrichment-)/.test(u.email || '')).length;
if (data.users.length < 100) break;
}
console.log(`Temporary browser/enrichment accounts remaining: ${remaining}`);
let healthy = remaining === 0;
for (const port of [3000, 3001]) {
const base = `http://127.0.0.1:${port}`;
const home = await fetch(base);
const library = await fetch(`${base}/api/library`);
console.log(`Port ${port}: homepage HTTP ${home.status}; unauthenticated library HTTP ${library.status}`);
healthy &&= home.status === 200 && library.status === 401;
}
const enrichment = await fetch('http://127.0.0.1:3001/api/enrichment');
console.log(`Unauthenticated enrichment HTTP ${enrichment.status}`);
if (!healthy || enrichment.status !== 401) process.exitCode = 1;
JS
The final integration review also connected existing group priorities to enrichment ordering and queued-job priority. Migration 010 is additive; a backend test compares nested/manual/smart-ancestor scheduling against the existing shared group predicate and checks private helper access. No milestone-2 functionality was rebuilt.
PGRST200). Five tests failed, one was interrupted and eight did not run. Migration 009 adds the owner-scoped FK and the new exact-query backend test prevents recurrence. The old port-3000 build was unaffected. The interrupted run left three fictional accounts; cleanup removed only account identifiers found in that run’s traces, not pre-existing libraries. The browser fixture now handles SIGINT/SIGTERM during its active wait as well as normal disconnect.SKIP LOCKED can leave it pending until the next normal poll. No budget overrun occurred. The test now includes that next cycle and retains the exact charge/cap assertions; that rerun passed 17/17, followed by the final expanded 18/18 suite.ProcessEnv.NODE_ENV type; accepting a partial environment fixed it. Initial lint found an unused test import and a let that should be const; both fixed. Formatting warnings in the added tests/photo route and Next-reformatted tsconfig were corrected. Final checks pass without disabling rules.EPERM). The new worker script now uses Node’s --import tsx loader; the command reaches the expected configuration-disabled exit without network access. Local database/browser checks ran through authorized local execution. No automatic approval rejection remains outstanding.Blocked now: none for the requested local implementation/verification.
Open the milestone-3 application at http://127.0.0.1:3001. Docker/local Supabase remains on port 54321. Existing fictional demo login: alex@recall.test / Local-Recall-Only-2026!; Blair’s separate library is also available. These accounts were not automation targets. The prior port-3000 process remains available on its earlier build.
If the milestone-3 preview stops, keep Docker running and use:
npm run db:start
npx supabase migration up --local
APP_ORIGIN=http://127.0.0.1:3001 CONTACT_RECALL_DIST_DIR=.next-milestone3 npm run build
APP_ORIGIN=http://127.0.0.1:3001 CONTACT_RECALL_DIST_DIR=.next-milestone3 npm run start -- --port 3001
Do not start a second process on an occupied port. No reset, reseed or new credentials are needed for ordinary use. Normal fresh setup still defaults to port 3000/.next; README contains both paths and unchanged supported import limits.
No provider had previously been selected. The reversible default is a configurable OpenAI Responses adapter, disabled by default, with no hardcoded model. Before any separately authorized live test, securely set OPENAI_API_KEY, OPENAI_ENRICHMENT_MODEL, ENRICHMENT_REMOTE_ENABLED=true, verified ENRICHMENT_INPUT_USD_PER_MILLION, ENRICHMENT_OUTPUT_USD_PER_MILLION and ENRICHMENT_PHOTO_TOKEN_BOUND. The operator must verify the chosen model supports images/strict structured output and the conservative cost bounds. Approve only a fictional account’s scope and budget in Improve, then explicitly start npm run enrichment:worker. No local model/fallback is claimed. See README’s configuration table and linked official request-format documentation.
Operational bounds: 32,000 serialized input bytes, 100,000 response bytes, 1,200 output tokens, three attempts per unchanged job, 45-second request timeout, two-minute lease and 30-second retry delay for an explicit retryable rejection. A missing/unknown price or image-token bound is unavailable, not unlimited permission. Each approved automatic/bulk budget is capped at $100 and backed by atomic micro-USD reservation accounting. Unknown provider outcomes retain the full allowance; explicit retry may duplicate an externally processed request. Reported allowance is not a provider invoice. Actual billing correctness depends on verified model pricing/token assumptions; live billing was not exercised.
Only source-linked approved appearance observations feed the existing quiz vocabulary. Optional uncertain skin-tone review history does not add a quiz question. Unreviewed/generated cues never become separate facts. Large note collections fail visibly rather than silently dropping text. Pins and edits are preserved; source-linked obsolete cues remain labeled outdated, while their original note versions remain accessible unless explicitly deleted. Deleting a note does not erase separately retained import/raw-entry provenance or a user’s separately edited cue. Merged profiles keep existing derived history but require explicit undo before new processing; older merge fingerprints may request reconciliation after the additive schema upgrade.
Unexecuted/unverified, preserved rather than waived: live provider accuracy/uncertainty preservation, refusal behavior on actual photos, model availability, actual token charges and billing; local vision feasibility/accuracy; fresh-machine npm/Docker/Chromium installation; full new-database migration replay; Firefox/Safari; full assistive-technology audit; long idle session expiry; exhaustive network failures and multi-process stress beyond the tested concurrent worker cases; maximum-size import responsiveness, closed-dialog import recovery and 2,000-contact performance; hosted backups/retention/deployment/external-beta readiness. No fresh dependency security audit was run; earlier evidence recorded zero production advisories and five high development-only lint-chain advisories. These historical counts are not a new audit. Prompt/schema/source validation does not prove semantic truth; generated summaries remain Unreviewed until the user reviews them.
Deferred: milestone 4 hardening/performance/release operations and all external profile discovery, scraping, Prepare, memory practice, Follow-ups/reminders, synchronization and later work. Stop after milestone 3; no automatic continuation.
The following audit, results and limitations are retained. Its active-scope/date wording describes the earlier task; the milestone-3 record above is current.
Date: October 22, 2026 (America/New_York). Active scope: milestone-2 requirements audit, completion and stabilization. Status: the audit found required functionality missing and reproduced an unsafe merge size case, so this task completed/fixed milestone 2 only. The checks below pass with no known remaining blocking defect in this bounded implementation. Milestone 3 was not started. This is local milestone software, not the completed external beta.
Read AGENTS, PRODUCT_SPEC, TECHNICAL_DESIGN, MILESTONES, ACCEPTANCE_TESTS, the existing BUILD_STATUS and prompts 02/03. Compared requirements with the actual domain modules, components, API authorization, migrations and tests. Reused the preceding milestone-1 verification as evidence, then ran regressions on the changed application. Test counts alone did not establish completion: the earlier suite did not cover manually selected comparisons, alias/profile-link evidence, searchable group browsing or oversized affiliation merges.
| Requirement | Actual implementation and evidence | Outcome |
|---|---|---|
| CSV/vCard persistence, mapping, bounds, provenance, per-row errors | Existing import parser, preview UI, /api/imports and import_contact transaction retained. Domain/backend/browser tests cover mapped CSV, malformed rows, documented vCard subset, raw source, same-batch retry, repeated explicit uploads and remote PHOTO text without fetching. |
Retained and regression-tested; supported subset below/README. |
| Quick-add and manual workflow | Existing quickAdd extraction and contact editor preserve raw/unparsed input, permit editable extraction and retain the draft when switching to Fully manual. |
Retained; domain and browser regression passed. |
| Hybrid onboarding and priorities | Actual empty-state UI offers Import contacts and Add a person; Contacts provides Import/Add with equal prominence. Existing optional post-import contact/group priorities include Skip and do not start analysis. | Retained; Skip and saved priorities exercised in browser/backend tests. |
| Smart/manual groups, nested subgroups and consistent filters | Existing shared predicates enforce current/former/unknown rules, smart ancestor intersections, navigational manual rollups and any/all scopes. Cycle/foreign-parent guards, pins, suggestions, priority, rename/reparent and manually curated memberships retained. | Existing domain/backend/browser regressions passed. |
| Meaningful groups prominent; smaller categories searchable (PRODUCT_SPEC §8) | Previously every group was rendered without a search control. Added shared compact browsing: pins, then membership count/name; eight default groups plus additional pinned/selected entries; full-path search and Browse all in Contacts, Identify and Organize groups. Searching preserves selected scopes. | Completed; pure predicate tests and persisted browser rename/nested-path/empty-scope checks passed. |
| Manually choose records to compare (PRODUCT_SPEC §10.3) | Previously only suggested pairs could open comparison. Added Compare any two contacts using existing side-by-side review and approval/undo. Unsuggested pairs support Keep separate and Review later; deferred pairs remain visible after reload. Self/foreign comparisons are rejected by the API. | Completed; domain and browser checks passed. |
| Duplicate evidence and retained decisions | Added saved aliases and normalized HTTP(S) profile links; recorded meeting/affiliation context is shown alongside identity evidence. A shared employer/meeting alone is not an automatic identity suggestion. New review signatures track these fields; existing signatures remain supported without rewriting decisions. | Completed; deterministic matching, changed-evidence reopening, URL differences, legacy suppression and private larger signatures tested. |
| Approved transaction, originals/history, undo and later edits | Existing logical merges, source/child IDs, private photo paths, conflict choices, reconciliation and rollback retained. Reproduced a defect: two contacts with 16 affiliations could merge into a profile rejected by the editor’s 30-affiliation save bound. Added a transactional guard and matching UI/API explanation, with no truncation. | Fixed; rejection leaves both revisions/affiliation sets unchanged and no merge log. Existing authorized/unauthorized merge, rollback, undo and reconciliation tests passed. |
| Milestone-1 privacy and recognition | Persisted Contacts → Improve → Identify, original notes, historical affiliations, private media, actual saved search/quiz facts, unknown/pending handling, clue revision/Skip, None of these match and account switching remain unchanged. | Four browser workflows plus 24 domain and seven backend tests passed. Ordinary recognition remains deterministic with no provider code or AI requests. |
The only new database migration is 202610220005_comparison_evidence.sql, applied locally with npx supabase migration up --local. It extends the private review-signature size bound and replaces the merge function with the same ownership/revision/conflict checks plus the affiliation limit. Migrations 001–004 are unchanged. Existing contacts, original notes, review decisions, media and merge history were not rewritten, deleted or reset. Existing oversized merges, if any, are not silently altered; the existing explicit Undo/reconciliation flow remains available.
The repository still has untracked project files, so Git has no tracked baseline for these files. No Git reset/discard, seed rerun, database reset, unrelated-file removal, dependency change, deployment, real invitation or paid request occurred. Backend/browser fixtures used isolated fictional accounts; pre-existing libraries were not test targets.
Environment: existing macOS arm64 workspace, Node 22.17.0/npm 10.9.2, local Supabase PostgreSQL 17/Auth/Storage, Next 16.4.2, Playwright 1.64.1/Chromium. Results below supersede historical counts later in this file.
| Status | Exact command/check | Result |
|---|---|---|
| Passed | npx supabase migration up --local |
Applied additive migration 005 successfully to the existing local database; no reset. |
| Passed | npm test |
52/52, 24 milestone-1 and 28 milestone-2 domain tests; 0 failed. Final run after deferred-manual-review changes reported 485 ms. |
| Passed | npm run test:integration |
21/21, 0 failed/skipped; real local backend; 2,253.53 ms. Includes new affiliation-limit rollback and expanded/private review-signature tests. |
| Passed | npm run test:pagination |
1/1, 0 failed/skipped; HTTP API retained all 1,005 review decisions; 823.40 ms. Library pagination code was unchanged by subsequent UI/domain fixes. |
| Passed | npm run typecheck |
Exit 0 on final code (tsc --noEmit). |
| Passed | npm run lint |
Exit 0 on final code. |
| Passed | npx prettier --check 'app/**/*.{ts,tsx,css}' 'components/**/*.{ts,tsx}' 'lib/**/*.ts' 'scripts/**/*.ts' 'tests/**/*.ts' '*.{ts,mjs,json}' |
Exit 0; all matched source/test/config files formatted. |
| Passed | npm run build |
Exit 0 on final code; webpack production build, homepage and eight dynamic API routes. |
| Passed | npm run test:e2e |
11/11 Chromium workflows: four milestone-1 and seven milestone-2; no skipped tests. Final test-results/.last-run.json reports passed and an empty failedTests list. |
| Passed | Visual review of current screenshots | Inspected test-results/manual-comparison-1440.png and searchable-groups-1440.png; readable side-by-side originals, visible full subgroup paths/selected filters, no observed blocking layout issue. Existing browser keyboard/overflow/regression assertions also pass. |
| Passed | Read-only local account/HTTP check below | Exit 0: Temporary browser accounts remaining: 0; Homepage HTTP 200; unauthenticated library HTTP 401. |
| Passed | npm run start |
Final production build is serving at http://127.0.0.1:3000. |
node --env-file=.env.local --import tsx --input-type=module <<'JS'
import { localAdmin } from './scripts/local-client.ts';
const admin = localAdmin();
let remaining = 0;
for (let page = 1; ; page++) {
const { data, error } = await admin.auth.admin.listUsers({ page, perPage: 100 });
if (error) throw error;
remaining += data.users.filter(u => u.email?.startsWith('browser-')).length;
if (data.users.length < 100) break;
}
console.log(`Temporary browser accounts remaining: ${remaining}`);
const home = await fetch('http://127.0.0.1:3000');
const library = await fetch('http://127.0.0.1:3000/api/library');
console.log(`Homepage HTTP ${home.status}; unauthenticated library HTTP ${library.status}`);
if (remaining || home.status !== 200 || library.status !== 401) process.exitCode = 1;
JS
Failed checks, then resolved:
node --env-file=.env.local --import tsx --test --test-name-pattern='merge rejects an affiliation union' tests/organization.integration.test.ts failed (0 passed/1 failed): the oversized merge incorrectly succeeded. After migration 005, this regression passes in the full 21-test backend suite.npm run test:e2e run passed all nine pre-existing workflows and failed the two new tests. One exact-label locator could not locate the manual contact selector; explicit accessible labels fixed that. The compact-group fixture placed a nested path early in the sort while expecting it hidden; its parent was corrected to test a genuinely hidden subgroup. The final complete run passed all 11, including a newly added unsuggested Review later/reload assertion.npm run start ran before its build finished and reported a missing production build; starting after the completed final build resolved it.ps) was denied. This did not block verification: the browser result artifact, screenshots, direct typecheck and authenticated-backend/HTTP checks supplied the evidence above. No automatic approval rejection remains outstanding.Blocked now: none for this local milestone-2 completion pass.
Unexecuted/unverified, retained from earlier verification: fresh-machine npm ci/Docker/Chromium installation; full migration replay on a new database (005 upgrade on this existing database did pass); Firefox/Safari; complete assistive-technology audit; long idle session expiry; exhaustive network-failure and parallel-request races; maximum-size imports, multi-process concurrency and 2,000-contact performance; hosted backups/retention/deployment/external-beta readiness. A fresh dependency security audit was not run; earlier evidence recorded zero production advisories and five high development-only lint-chain advisories. The historical counts are not a new audit. No live AI provider was configured or tested; milestone 3 and provider behavior remain unimplemented/unverified, not simulated as completed enrichment.
Open the running app: http://127.0.0.1:3000. The existing local backend is on port 54321 and .env.local was preserved. Existing fictional demo login: alex@recall.test / Local-Recall-Only-2026!; Blair has a separate library. Automation provisions its own accounts and never uses these demos.
If stopped, keep Docker running and use npm run db:start, npx supabase migration up --local, npm run build, then npm run start. Stop an older app process before starting a second server. No reset, reseed or new environment variables are required for this workspace. Fresh-machine steps, supported imports and test commands are in README.md.
Supported imports remain UTF-8 CSV with headers and mapped fields / vCard 3.0–4.0 subset, at most 2 MiB / 500 records / 60,000 characters per raw record. Imported photos, legacy encodings, XLSX, provider extensions and inferred groups/appearance are unsupported; raw unsupported properties remain provenance. No remote URLs are fetched. Same-dialog retries are idempotent; reopening starts a new batch, with no durable import-resume dashboard. See README’s exact field/encoding/image table; maximum-size responsiveness and closed-dialog interruption recovery remain unverified.
Group suggestions use saved affiliation support, not invented biography. Smart rules remain an explicit organization/kind/timeframe allowlist. Compact ordering by pins/count/name and eight default entries is a reversible UI default; memberships are unchanged. Duplicate comparisons use context as supporting evidence only. New Keep separate decisions track normalized names/aliases/methods/context; legacy decisions retain their original name/email/phone suppression basis. Review later does not block imports or recognition. Merges retain the existing 30-method/alias bounds and now enforce 30 combined affiliations, with one active merge per contact. Undo explicitly reconciles later edits and may retain shared methods on both restored records.
Deferred: milestone 3 durable enrichment, provider configuration/budgets, confirmed photo suggestions and source-linked Unreviewed summary cues; milestone 4 hardening/performance and all later events/reminders/synchronization. The user’s conditional boundary selected milestone 2 because this audit found gaps. Stop here; no milestone-3 implementation in this task.
The following record is preserved as earlier evidence. Its scope, conclusions and test counts describe that earlier pass; the requirements audit and fresh results above supersede its statement that no additional milestone-2 requirement was missing.
Inspected the actual source, all four migrations, tests, manifest/lockfile, API authorization and current Git state, in addition to AGENTS, both execution prompts, PRODUCT_SPEC and the milestone/technical/acceptance/status documents. The repository still has untracked project files and no tracked diff to use as an implementation baseline. No Git reset, discard, migration application/rollback, database reset, seed rerun, or mutation of pre-existing libraries was performed during this pass. No application feature, dependency, environment setting or migration changed.
tests/browser/fixtures.ts and account-worker.ts provision unique temporary accounts and seed fictional fixtures without touching demo libraries. Both browser suites now use those accounts. The milestone-1 suite additionally compares the entire saved contact after signing out, switching accounts and signing back in, and exercises saved Yes/No/unknown facts through answer revision, sentence clues, clue removal and Skip. It asserts no browser requests during those search/quiz operations and no resulting library mutation. README and this status document were updated. No application-code repair was needed.Environment: existing macOS arm64 workspace; Node 22.17.0; npm 10.9.2; Docker/local Supabase PostgreSQL 17; Next 16.4.2; Playwright 1.64.1/Chromium. Tests used temporary fictional accounts. Cleanup was scoped to account IDs created by the tests; pre-existing accounts/data were not test targets.
| Status | Exact command/check | Result in this pass |
|---|---|---|
| Passed | npm test |
47/47: 24 milestone-1 and 23 milestone-2 domain tests, 0 failures; 231 ms reported. |
| Passed | npm run test:integration |
19/19, 0 failed/skipped; real local Auth/Postgres/Storage, 1,578 ms. Covers isolation, forged IDs, active/revoked beta access, private media, persistence, original notes, simultaneous affiliations, imports, hierarchy, merge/undo/rollback and priorities. |
| Passed | npm run test:pagination |
1/1, 0 failed/skipped; HTTP API returned all 1,005 retained review decisions, 1,253 ms. |
| Passed | npm run typecheck |
Exit 0 on the final test/helper code. |
| Passed | npm run lint |
Exit 0, no warnings on final code. |
| Passed | npx prettier --check 'app/**/*.{ts,tsx,css}' 'components/**/*.{ts,tsx}' 'lib/**/*.ts' 'scripts/**/*.ts' 'tests/**/*.ts' '*.{ts,mjs,json}' |
Exit 0; all matched files formatted. |
| Passed | npm run build |
Exit 0 on final code; webpack production build, homepage and eight dynamic API routes. |
| Passed | npm run test:e2e |
9/9, 0 failed/skipped; 14.9 seconds. Four milestone-1 workflows and all five existing milestone-2 workflows, using real local persistence and isolated accounts. |
| Passed | Screenshot review | Current Contacts and Improve at 1280×800, recognition card/original notes and group organization at 1440×900 inspected; readable layout and no observed blocking visual defect. Automated keyboard opening/Escape/focus return, list/filter preservation, empty-state recovery and viewport overflow checks passed. |
| Passed | Read-only temporary-account audit (command below) | Temporary browser accounts remaining: 0, exit 0, including after the interrupted intermediate run. |
| Passed | npm run start; curl --silent --show-error --output /dev/null --write-out 'HTTP %{http_code}\n' http://127.0.0.1:3000 |
Production server runs locally; homepage HTTP 200. |
| Passed | curl --silent --show-error --output /dev/null --write-out 'Unauthenticated library HTTP %{http_code}\n' http://127.0.0.1:3000/api/library |
HTTP 401 after the final production-server restart. |
Backend authorization is exercised using actual ordinary user credentials, not mocked UI filtering. The browser flow creates a contact with a private photo and original note, reloads, saves appearance/meeting/former-employer details through Improve, finds the saved record by a historical sentence, opens original notes, returns with clues intact, reloads again, switches accounts and signs back in with all saved fields unchanged. Foreign contact/photo requests return 404. Unknown facts rank between positive and contradictory recorded facts and remain reachable after clue changes. Existing tests cover pending exclusion, Skip/suppression, uniform/sparse questions, incorrect clues, scope widening and rejection of only the displayed six candidates. Private APIs use no-store; ordinary search/quiz has no AI integration or server query path. Runtime import and recognition checks also assert zero external browser requests.
Retained browser screenshots are in ignored test-results/: contacts-1280.png, improve-1280.png, identify-1280.png, identify-1440.png, recognition-card-1440.png, profile-expanded-1440.png, import-preview-1440.png, groups-1440.png, duplicate-comparison-1440.png, undo-reconciliation-1440.png. They contain fictional test data only. They are evidence of the scoped workflows, not a full accessibility audit.
use mistaken for a React hook). Both were fixed; final lint/type checks pass without rule suppression.npm run test:integration failed with listen EPERM creating the tsx IPC socket. The authorized local retry passed all 19 tests. A sandboxed localhost curl returned HTTP 000/connection failure; the authorized local check returned 200. No automatic approval rejection remains outstanding.npm run test:e2e attempt failed during collection with Node’s Unexpected module status 3; moving SDK loading into the worker still failed all nine fixture setups. Account provisioning was moved into a normal Node/tsx child process, retaining the existing local-backend guard and authentication.npm ci/Docker/Chromium installation; new-database migration replay or upgrade rehearsal (deliberately did not reset or alter this database); Firefox/Safari; full assistive-technology audit; long idle session expiry; exhaustive network-failure/parallel-request races; maximum-import responsiveness, multi-process concurrency and 2,000-contact performance. Existing tests do cover revoked access and stale-save conflicts. A fresh dependency security audit was not run; prior evidence recorded zero production advisories and five high development-only lint-chain advisories. Hosted retention/backups, deployment and external-beta readiness remain unverified. None of these checks is represented as passed here.Read-only cleanup audit, with no credentials or account details printed:
node --env-file=.env.local --import tsx --input-type=module <<'JS'
import { localAdmin } from './scripts/local-client.ts';
const admin = localAdmin();
let remaining = 0;
for (let page = 1; ; page++) {
const { data, error } = await admin.auth.admin.listUsers({ page, perPage: 100 });
if (error) throw error;
remaining += data.users.filter(u => u.email?.startsWith('browser-')).length;
if (data.users.length < 100) break;
}
console.log(`Temporary browser accounts remaining: ${remaining}`);
if (remaining) process.exitCode = 1;
JS
Actual address: http://127.0.0.1:3000 (local production server; Docker backend on port 54321). The current workspace is already configured; .env.local was preserved and no seed was rerun. For manual exploration use the existing fictional alex@recall.test / Local-Recall-Only-2026! account, or Blair’s separate library as documented in README. Browser automation now creates its own accounts and does not use these demo logins.
If the app has been stopped, keep Docker running and run npm run db:start, then npm run build and npm run start from the repository. Do not start a second server on the same port. No reset, reseed or new credentials are needed for this configured workspace. Fresh-machine instructions and optional seed remain in README.md.
No additional required milestone-2 blocker was identified by this pass. The existing bounded implementation remains: UTF-8 CSV/vCard 3.0/4.0 only; 2 MiB/500 records; no imported image decoding, remote photo fetches, legacy encodings, XLSX or inferred groups/appearance. Unsupported fields remain in raw provenance. Same-dialog retries are idempotent; closing/reopening an interrupted import starts a new batch, with no durable resume dashboard. Groups use allowlisted organization/kind/timeframe rules; merges permit one active merge per contact and retain the 30-method/alias limit. Reconciliation preserves later edits and can leave shared methods on both restored records. See README for exact supported fields and bounds.
Still unverified for milestone 2: interrupted-import recovery across a closed dialog, maximum-size imports, production concurrency and broader browser behavior. Those items were not developed during this pass. No provider calls, paid resources, real invitations or deployment occurred. Milestones 3–4 and later work remain deferred; stop here and await a separate instruction before feature development resumes.
The sections below preserve the earlier implementation evidence; their counts are historical. The fresh results and current scope above take precedence.
Read the root instructions, START_HERE, PRODUCT_SPEC, milestone/acceptance/technical planning documents, prior BUILD_STATUS and prompt 02. Inspected the working application, migrations, manifest, tests and uncommitted repository. Retained Next.js/React/TypeScript, npm lockfile, local Supabase, private photo handling and deterministic recognition core. No reset, rebuild from scratch, deployment, invitation or paid provider call occurred.
Before extension: npm test 24/24, npm run typecheck, npm run lint, npm run test:integration 7/7, and npm run test:e2e 3/3 passed. No unresolved milestone-1 blocking defect was found in inspection or those checks. Original evidence and limitations are archived in MILESTONE_1_VERIFICATION.md. Existing development-only lint dependency advisories are not production runtime findings; no dependency changes were made in milestone 2.
| Area | Main files |
|---|---|
| UI | components/{workspace,contact-editor,organization,import-contacts,duplicate-review}.tsx, existing primitives/styles |
| Pure logic | lib/imports/parse.ts, lib/domain/{groups,duplicates,model,identify,improve}.ts |
| API | app/api/{imports,groups,duplicates,priorities,contacts,library}/route.ts, lib/server/{api,library}.ts, lib/client-api.ts |
| Database | 202610220003_organization.sql, 202610220004_import_retries_and_priorities.sql in supabase/migrations/ |
| Tests | tests/organization.test.ts, tests/organization.integration.test.ts, tests/browser/organization.spec.ts, tests/review-pagination.ts, CSV/vCard fixtures; original test suites retained |
Migration 003 adds hierarchy/rules, priorities, raw sources, affiliation dates/change history, relationships, duplicate decisions, import receipts and merge/undo/delete functions. It extends contact saving while keeping owned RLS and same-owner composite FKs. Guarded merge/undo/delete functions use SECURITY DEFINER only after active-beta and explicit ownership checks; ordinary application requests still use the authenticated client, never the service key. Changes serialize per owner and merge/undo fingerprints include original rows and children. Migration 004 binds retries to their exact input and adds atomic revision-aware priorities. Both applied to the existing local database without resetting records.
Environment retained from milestone 1: macOS arm64; Node 22.17.0; npm 10.9.2; local Docker/Supabase PostgreSQL 17; Next 16.4.2; React 19.3.1; TypeScript 6.0.3; Vitest 5.0.5; Playwright 1.64.1 / Chromium. No dependencies added or upgraded.
| Exact command | Actual result |
|---|---|
npx supabase migration up --local |
Migrations 003 and 004 applied successfully. Sandbox initially blocked the CLI settings-directory write; the authorized local retry passed. No db:reset. |
npm test |
47 passed, 0 failed: 24 milestone-1 regressions and 23 milestone-2 cases. Network-free pure tests. |
npm run test:integration |
19 passed, 0 failed, 0 skipped: original 7 plus 12 milestone-2 tests against real local Auth/Postgres/Storage. Final integration run: 1.50 seconds. |
npm run test:pagination |
1 passed, 0 failed: actual HTTP library API returned all 1,005 retained decisions beyond the default backend row cap, using a temporary account removed afterward. Requires the local app running. |
npm run typecheck |
Passed, exit 0 on the final code. |
npm run lint |
Passed, exit 0, no warnings. |
npx prettier --check 'app/**/*.{ts,tsx,css}' 'components/**/*.{ts,tsx}' 'lib/**/*.ts' 'scripts/**/*.ts' 'tests/**/*.ts' '*.{ts,mjs,json}' |
Passed, all matched source/config/test files use Prettier style. |
npm run build |
Passed, exit 0, webpack production build; homepage plus eight dynamic API routes. |
npm run test:e2e |
8 passed, 0 failed, 0 skipped: 3 milestone-1 regressions and 5 milestone-2 browser workflows against the final production build and real local backend; final run 12.0 seconds. |
Implementation-time failures were resolved rather than waived: the new scope-mode default initially changed an old reducer expectation; adding the explicit default restored the original regression. Real integration tests exposed an ambiguous PostgREST relationship embedding after adding saved-contact targets; naming the source-contact FK fixed the library query. Initial lint found two unescaped apostrophes, corrected. The browser group test needed its actual accessible combobox role instead of an exact label-text selector; the corrected quick-add/group flow passed. Tests also prompted input-bound retry receipts and atomic priority revision updates. Final review fixed inherited manual memberships being copied onto the primary during an unrelated merged-profile edit; inherited controls now explain that undo is needed, and regression tests verify the memberships remain on their original record. vCard markers are recognized only as complete lines, so literal marker text inside notes is preserved. Formatting was rerun after the final added browser test. The review-history loader also pages decisions and active merge operations, so the backend row cap cannot silently lose Keep separate decisions; the extra real API test verified 1,005 retained decisions.
Coverage includes D01–D13: overlapping/descendant deduplication, any/all scope agreement, cycles/ownership, smart ancestor intersections, current→former changes without manual-membership loss, valid/invalid/repeated/sparse imports, Skip, size/encoding/unsupported-photo checks, identical-email suggestions, transactional merge rollback, pending/source/photo/history preservation, undo before/after later edits, Keep separate suppression and explicit reimport behavior. Real-backend tests additionally cover forged relationships, unavailable merge targets, revoked access, private media after merge, receipt ownership, permanent-delete cleanup and priority scope rollback. This evidence does not claim all of milestone 4’s release validation.
Browser coverage includes actual mapped CSV and vCard imports, per-record errors, unsupported/oversized/invalid-UTF-8 uploads, raw-source inspection after reload, Skip and saved priorities, repeated upload creating separate records, raw quick-add/manual draft preservation, group creation/nesting/pinning, any/all Contacts/Identify agreement, duplicate comparison, Keep separate across reload, changed-evidence review, approved merge, later-note edits and reconciliation. New API routes reject unauthenticated, cross-origin and unapproved mutations; an imported profile remains unavailable to the other account. The monitored import/vCard and milestone-1 workflows asserted zero external browser requests; monitored workflows asserted zero runtime errors. Tests clean their synthetic contacts/groups and temporary accounts.
Screenshots in ignored test-results/: import-preview-1440.png, groups-1440.png, duplicate-comparison-1440.png, undo-reconciliation-1440.png, and the existing recognition/workspace screenshots. Import preview, group organization, comparison, reconciliation and Contacts desktop captures were visually inspected. Long group labels prompted a small control-size fix so checkboxes do not shrink; reconciliation bullets were restored for readability. Existing 1280×800 / 1440×900 recognition regressions pass. Full assistive-technology validation remains unverified.
Full instructions and precise CSV/vCard limitations are in README.md. Existing workspace upgrade:
npx supabase migration up --local
npm run build
npm run start
Fresh local setup:
npm ci
npm run db:start
npx supabase migration up --local
npx tsx scripts/local-env.ts
npm run seed:local
npm run dev
The final production build is currently running at http://127.0.0.1:3000. Use that hostname consistently. Docker must be running; local backend port is 54321. Existing .env.local remains ignored and unchanged. No additional environment settings or credentials. The seed is opt-in, fictional and preserves nonempty libraries. Public local test credentials remain documented in README. Never reset a library to apply these migrations.
Imports: UTF-8 only; 2 MiB; 500 records; 60,000-character raw record; CSV 50 columns/100-character headers; bounded field/method/note limits. No XLSX, delimiter guessing, legacy vCard 2.1/encodings, automatic photo import, remote photo fetching, group/appearance/date inference, or external sync. Unknown/unsupported fields remain in raw source and are reported. Invalid/unselected records are not stored. Imports are transactional per record, not all-or-nothing across the file; successful rows remain usable if another fails. Same-dialog retries are idempotent for exact input; uploading again starts a new batch and creates separate records. No resumable batch dashboard.
Groups use one parent and allowlisted organization/kind/timeframe rules; no arbitrary boolean expression/SQL editor. New ancestry is bounded to 32 nodes. Manual memberships are retained independently of smart predicates. A merged record must be undone before another merge; combined methods/aliases keep existing 30-entry limits and oversized merges roll back without truncation. Inherited manual memberships remain on the original retained record; undo before editing them independently. Safe reconciliation can intentionally leave shared methods on both restored contacts. Photo deletion precedes DB deletion; a subsequent DB failure may leave removed photos unavailable until retry, as in milestone 1. Local backups/volumes remain operator-controlled.
No paid resources, deployments, invitations, provider credentials or external integrations were created. No implementation prerequisite is currently blocked.
Unverified: fresh-machine installation from zero after milestone 2; Safari/Firefox and full assistive-technology audit; crash/network-disconnect recovery across a closed importer; multi-process production concurrency/load; maximum-size import responsiveness and 2,000-contact performance targets; hosted storage/retention/backup erasure and external-beta readiness. Browser and integration tests use real local persistence with synthetic accounts, not real address books. Dependency advisories were not re-audited in this milestone because the lockfile did not change; milestone 1 recorded zero production vulnerabilities and five high development-only lint-chain findings.
The roadmap places milestone 3 next, but it is not authorized by this verification task: durable controlled enrichment, budgets/review and source-linked summaries. It has not been started. Milestone 4 still owns broader performance/accessibility/beta hardening. Events, practice, debriefs, follow-ups, notifications and provider synchronization remain later milestones.