# Proposal to calendar integration handoff

Status: A and B are deployed and all C01–C25 checks are verified. This is the tested proposal integration state for the sequential calendar phase. The proposal executor has not launched that phase. The parent owns its continuation.

## Source and staging

The canonical implementation is `/Users/agency/Documents/Agty/kertujitok-proposal-20260927`, branch `codex/proposal-contract-20260927`. Recoverable pre-change source is commit `4a1874e28fbada69300f17d9df66133aeba61766`. The deployed implementation checkpoint is `1e48a549af22324e485e00a01f2bf745bb9d7053`. The final handoff documentation commit sits on this implementation checkpoint. Its exact SHA is recorded in the final report and session identity. The proposal replaces only the Ajánlatok destination in the existing calendar staging panel. The existing panel and calendar HTML were preserved byte for byte.

Variant A uses Worker `kertujitok-calendar-stage-20260923` at `https://calendar-stage-20260923.cfd-staging.com/panel/`. Variant B uses Worker `kertujitok-proposal-b-20260927`, at `https://kertujitok-proposal-b-20260927.clientsflowdigital.workers.dev/panel/`. Both use the agency staging account `32d573fbd8886c5c474e788d29252bef`, staging D1 `b9bf0967-95a7-43ee-8b8b-28d0af9ded41`, and staging R2 `kertujitok-calendar-images-20260923`. B accesses independent `pvb_` copies through `cloudflare/calendar-stage/namespaced-db.ts`. No production bindings or credentials are needed. MAIL_MODE remains disabled.

## Persisted lead fields

- `emlekezteto_datum`: existing one upcoming reminder per lead. Rescheduling replaces this date, not a new reminder record.
- `work_delivery_date`: separate nullable `YYYY-MM-DD` date for work/delivery. This proposal phase persists it without inventing work periods.
- `calendar_note_visible`: Boolean preference stored as 0/1. Calendar rendering is deferred.
- `message`: original enquiry, editable separately from `belso_jegyzet` (own note). Keep their meanings and values separate.
- Existing business/contact fields remain `full_name`, `phone`, `email`, `city`, `main_service`, `other_services`, `area_size`, `status`.
- Proposal property is derived from the one-to-one `proposal_v2.lead_id` relation. No proposal means Create; existing proposal opens that same record. IDs, versions and document history are server-owned.

The integrated sidebar is `cloudflare/proposal-v2/sidebar.js` + `sidebar.css`. Its PATCH sends only edited fields with the lead version. A conflict preserves typing and offers an explicit field merge. Do not write a full stale lead record from calendar. Preserve reminder, work date, visibility preference, both notes and proposal linkage independently. Broader sidebar rollout and calendar layout belong to the next phase.

## Proposal invariants

One ongoing proposal per lead, no project/property hierarchy, invoices or automatic messages. Rows keep quantity, labor unit price, row-level transport once, and material unit price. Settlement stages keep labor and “Anyag és szállítás”, requested money and dated payments separately. First final export captures the original rows/rates/totals. Earlier PDF bytes stay immutable. Do not treat status, PDF export or requested money as sent/paid evidence.

## Recovery and scope

Use `recovery.md` and the private backed-up SQL/Worker artifact. Additive schema can remain when restoring the previous staging Worker version. Do not reset databases or remove user work. Calendar continuation must preserve proposal source, tests, routes, both isolated namespaces, stored documents and this review board. No GAJA/GHL, production release, new security layer or client messages. Shared spending ceiling is USD20 across both phases including preparation; current execution external spend is USD0 and USD1 preparation reserve remains in `overnight/budget.json`.

The final verification and known limits are recorded below and on the stable evidence board.


## API and component reuse

The browser API base is `/panel/api/proposal-v2`. `GET /` returns proposals with calculated totals and active leads with a derived `proposalId`. `POST /` takes a stable `requestId` plus either `leadId` or `customer: {name,email,phone,address}`. Reuse the same request ID when retrying. The durable contact/request receipt survives a failure before proposal linkage. Do not generate a fresh request ID merely because a response was lost.

`GET /:proposalId` returns `{proposal,lead}`. `PUT /:proposalId` takes `{version,proposal}` and returns 409 with the latest server copy on a stale version. The client retains local edits and explicitly merges them onto current server-owned identity, version, baseline and history. `GET /leads/:leadId` and `PATCH /leads/:leadId` return `{lead,proposalId}`. A PATCH takes `{version,lead}` with only the changed allowed fields. These lead saves maintain the original CRM reminder/event meanings and never enqueue email.

`POST /:proposalId/documents` takes `{version,requestId,layout,draft,revision,pdfBase64}`. Successful exports return the immutable document URL and SHA-256. Downloads are `GET /:proposalId/documents/:documentId`. The four tables are `proposal_v2`, `proposal_v2_create_requests`, `proposal_v2_documents` and `proposal_v2_document_chunks`, with `pvb_` equivalents in B. PDF chunks and metadata are immutable. Run base migrations first, followed by `cloudflare/proposal-v2-migrations/0001_proposal_v2.sql`; do not rerun its ALTER statements on an already migrated database. `scripts/proposal_stage_sql_namespace.mjs` transforms the corresponding B migration.

`LeadSidebar` is exported by `sidebar.js`. Construct it with the sidebar root and `{api,onProposal,onSaved,storageKey}`. The API adapter uses the routes above. `onProposal(lead,proposalId)` opens the existing deep link or creates the single first proposal. `onSaved(lead)` updates the surrounding view without overwriting unrelated proposal fields. The component retains dirty drafts by lead in session storage and provides `open(id)`, `close()` and `hasDirty`. Reuse its field whitelist and its separate original-enquiry/own-note fields. Keep a distinct storage key per application surface and variant. The proposal page deep link is `/panel/proposals/?proposal=<id>`. The canonical trailing slash is intentional.

## Build and continuation checks

Run commands from the exact proposal worktree. `python3 scripts/build_proposal_stage.py` assembles new proposal assets over the retained, verified synthetic staging build. It does not regenerate or redesign marketing pages. A clean new checkout needs the final verified public-assets archive or `--base-dist` pointing to the preserved calendar staging assets. It excludes the historical internal handover from the public build while retaining that source and its backup.

Deploy A with `/usr/local/bin/node node_modules/wrangler/bin/wrangler.js deploy --config cloudflare/calendar-stage/wrangler.toml` and B with the same command using `wrangler-b.toml`. Use the existing agency account credential through the process environment, never the production client credential. `scripts/check_proposal_routes.py`, `scripts/check_proposal_deployed_api.py` and `scripts/check_proposal_scenarios.mjs` provide deployed route, persistence and PDF checks. The scenario harness creates synthetic records, so use a unique run ID and preserve its manifest.

Before calendar changes, create a recovery point from the final commit and current staging data. After calendar changes, repeat the proposal routes, API, both PDF layouts, sidebar field/conflict checks and reminder smoke test. Prove the exact deployed version and public assets, not just a local build. Keep the two variant stores independent and all existing exported PDF bytes intact.


## Verified deployed build

A serves version `f09f3726-67f5-493d-b509-4a8b9709842f`. B serves `1b76707c-ce32-43cb-a7db-8a71c7d1873b`. Both pass 14 anonymous route/source-byte checks and the combined live API harness passes 101 assertions. All C01–C25 criteria passed. The unchanged legacy panel clips its Lead hozzáadása tab at 390 px, with the full drawer working. The proposal-specific sidebar collapse was corrected and locally checked before deployment.


## Complete verification command set

The tested runtime here is Node22.17.0, Python3.12.8, Poppler26.04.0, Wrangler4.137.0 and Playwright1.55.0. Use the lockfile for application dependencies. The local browser regression accepts `PLAYWRIGHT_PATH` and uses the installed macOS Google Chrome executable. Current reusable Playwright installation is `/tmp/kertujitok-mobile-qa-20260924/node_modules/playwright`. On another host, install that exact package version in a task-owned dependency directory and set the environment variable to its absolute module path. These local tests create disposable synthetic SQLite/browser fixtures and generated evidence files.

From the exact proposal source worktree:

```sh
node --experimental-strip-types --test cloudflare/test/proposal-v2-model.test.mjs cloudflare/test/proposal-v2-pdf.test.mjs
python3 cloudflare/test/proposal-v2-pdf-render.py
./node_modules/.bin/esbuild cloudflare/test/proposal-v2-api.test.ts --bundle --platform=node --format=esm --loader:.html=text --loader:.png=binary --outfile=.tmp/proposal-tests/api.mjs
node --test .tmp/proposal-tests/api.mjs
node --experimental-strip-types --test cloudflare/test/proposal-v2-api-d1.test.mjs cloudflare/test/proposal-v2-namespace.test.mjs
python3 cloudflare/test/proposal-v2-namespace-migrations.test.py
PLAYWRIGHT_PATH=/tmp/kertujitok-mobile-qa-20260924/node_modules/playwright node cloudflare/test/proposal-v2-ui.test.mjs
python3 scripts/build_proposal_stage.py
node node_modules/wrangler/bin/wrangler.js deploy --dry-run --config cloudflare/calendar-stage/wrangler.toml --outdir .tmp/proposal-dry-a
node node_modules/wrangler/bin/wrangler.js deploy --dry-run --config cloudflare/calendar-stage/wrangler-b.toml --outdir .tmp/proposal-dry-b
```

The broader existing repository command `npm test` was rerun against implementation commit `1e48a549af22324e485e00a01f2bf745bb9d7053` and passed: 34 Node test entries, 5 bundled worker tests and 1 Python build test. Its receipt is `evidence/deployment/full-repository-test.json`, with the complete log retained. This includes the proposal browser regression and the existing repository regression checks.

The final deployed checks below write only unique synthetic proposal fixtures. Use a fresh `--run` value for each PDF scenario run. They do not send mail. Production is never a target.

```sh
python3 scripts/check_proposal_routes.py --base https://calendar-stage-20260923.cfd-staging.com --variant A --output evidence/calendar-continuation/routes-A.json
python3 scripts/check_proposal_routes.py --base https://kertujitok-proposal-b-20260927.clientsflowdigital.workers.dev --variant B --output evidence/calendar-continuation/routes-B.json
python3 scripts/check_proposal_deployed_api.py --a https://calendar-stage-20260923.cfd-staging.com --b https://kertujitok-proposal-b-20260927.clientsflowdigital.workers.dev --output evidence/calendar-continuation/api.json
node scripts/check_proposal_scenarios.mjs --phase deployed --a https://calendar-stage-20260923.cfd-staging.com --b https://kertujitok-proposal-b-20260927.clientsflowdigital.workers.dev --run calendar-continuation-UNIQUE --output evidence/calendar-continuation/pdf-scenarios
```

The contact-commit recovery harness is `scripts/check_proposal_fault_recovery.py`. It needs the private existing staging provider adapter through `--provider-helper .tmp/proposal-ops/provider.py`, the same exact A/B origins, and an `--output` path. It adds a temporary trigger matching only its own synthetic request, confirms one committed contact before linkage, removes the trigger in `finally`, then verifies concurrent retries and zero outbox. Both variants passed 24 assertions during this release. It must never be repointed to production.

API and PDF scripts do not replace browser acceptance. After calendar integration, repeat actual deployed hover/focus/pin/Escape, preview timing, both-layout pre-click preview/download SHA linkage, nonmodal 390 px simultaneous sidebar/sheet edits, dirty switching and conflict recovery. Preserve the exported history and all scenario evidence. Fresh section screenshots and independent blind descriptions remain the required visual acceptance method.


## Restore the exact build assets in a fresh checkout

The private archive contains a top-level `dist/` directory with 305 verified files. Its SHA-256 is `fedc14f3b1c101c1b472fd2b04419fda8b48335415cbd297a45069b735059fe2` and size is 28,352,648 bytes. Preserve the source archive. From a fresh task-owned checkout, use:

```sh
mkdir -p .tmp/proposal-assets-restore
tar -xzf /Users/agency/.codex/worktrees/ccd4/KERTÚJÍTÓK/.tmp/proposal-lll-20260927/private/proposal-final-public-assets.tar.gz -C .tmp/proposal-assets-restore
python3 scripts/build_proposal_stage.py --base-dist .tmp/proposal-assets-restore/dist
```

The builder overlays the committed proposal source into `dist/panel/proposals` and retains the verified surrounding site assets. It excludes the historical internal handover from the public build. Inspect the new `.tmp/proposal-assets.json` and run the documented dry-run deployments before any continuation deployment. This is a static-build restore, not a database reset.

## Final acceptance and retained limits

All 25 contract criteria passed on the same frozen A/B builds. The combined deployed API/PDF harness passed 16 scenarios, 920 assertions, 44 PDFs, 128 rendered pages and 32 served/source asset hash comparisons. The actual browser journeys separately passed eight scenarios per variant and 43 pre-click preview/download byte matches, all rehashed independently by the proposal orchestrator. The parent also verified interleaved A/B proposal/contact saves and another exact 25,000 Ft transport settlement export. Fresh postdeployment section QA retained 135 desktop and 138 mobile screenshots. Eighty primary acceptance screenshots have fresh independent Luna High descriptions and parent comparisons.

The final provider audit confirms all original staging rows preserved, all new lead names classified synthetic, empty outboxes, one proposal per lead, valid lead links, complete durable create requests, unchanged historical PDF metadata, all ten expected trigger definitions, exact deployed versions/bindings and unchanged production settings/deployments. Its first trigger allowlist omitted the two existing lead timestamp triggers. Comparison against the pre-change SQL and frozen migrations resolved that audit issue without a database change.

An injected offline PDF-module failure in A required reload to restore preview after save recovery. Typed edits survived. Baseline typing-to-preview checks passed for quantity, price, description and unit in both variants. Separate layout/export refreshes are outside that timing claim. Preserve this recovery behavior during calendar integration. The old 390 px shell tab-strip clipping remains, with full navigation available. Initial desktop menu no-op observations were independently reconciled by visible open-drawer button clicks reaching fully loaded proposal lists in both unchanged builds.

The final public evidence is indexed at https://review.clientsflow.hu/kertujitok-proposal-contract-2026-09-27-v1/. The canonical private run is `/Users/agency/.codex/worktrees/ccd4/KERTÚJÍTÓK/.tmp/proposal-lll-20260927`. Its `evidence/acceptance.json`, `evidence/final-acceptance-integrity.json`, `evidence/deployment/final-audit.json` and `evidence/blind/index.md` connect each verified criterion to the behavioral and visual proof. Private backups remain outside Git and public hosting. No client communication, production change or calendar executor launch occurred.

Final documentation commit: `7a6320141798e26bcdfca14c5fe6d9501fff3836`, pushed and verified on the private branch. It changes documentation only. The deployed application remains implementation commit `1e48a549af22324e485e00a01f2bf745bb9d7053`.
