Spaces:
Building
Building
My Diaspora Nexus Backend Implementation Log
| Date/Time | Feature | Implementation Summary | Test Run ID | Result | Evidence Path | Notes |
|---|---|---|---|---|---|---|
| 2026-06-19 12:52 EAT | Project setup and health foundation | Created Laravel API project in C:\xampp\htdocs\mdn-backend, installed required packages, configured MySQL to use MDN_DB_*, added /api/v1/health and /api/v1/health/database, and verified missing-env and wrong-credential behavior. |
20260619-1248-foundation | PASS | storage/test-runs/20260619-1248-foundation/ | Live DB health passed through HTTP; no credentials written to evidence. |
| 2026-06-19 12:52 EAT | Shared API infrastructure | Added standardized ApiResponse, global request ID middleware, API exception mapping, UUID generator, transaction manager, base repository, ownership validator, and pagination validator. |
20260619-1248-foundation | PASS | storage/test-runs/20260619-1248-foundation/ | Unknown routes return standardized JSON with X-Request-Id; rollback command verified failed multi-step writes persist zero rows. |
| 2026-06-19 12:52 EAT | Audit Service foundation | Implemented AuditService, repository, DTO, event constants, health-check audit events, and php artisan mdn:audit:test append-only verification. |
20260619-1248-foundation | PASS | storage/test-runs/20260619-1248-foundation/ | Inserted safe AUDIT_TEST_EVENT; update/delete attempts were rejected by live DB immutability protection. |
| 2026-06-19 13:35 EAT | Identity and Access implementation | Implemented registration, email verification challenge creation/verification, login, JWT access tokens, hashed refresh sessions, refresh, logout, bearer auth middleware, permission middleware, /users/me, /authorization/check, and mdn:create-admin. Added redacted Step 4 API runner at tests/api/run-auth-step4.ps1. |
20260619-identity-step4 | PENDING LIVE ACCEPTANCE | storage/test-runs/20260619-identity-step4/ | PHP syntax and runner parser checks passed. Live HTTP acceptance was not run because approval was rejected for persistent writes to the live identity DB; requires explicit user approval before proceeding. |
| 2026-06-19 13:08 EAT | Identity safe HTTP checks | Ran actual HTTP checks that do not persist live identity records: invalid email, weak password, protected route without token, and missing refresh token. Hardened login so unverified/disabled login failures write login_events and audit failures before rejection. |
20260619-identity-step4-safe-http | PARTIAL PASS | storage/test-runs/20260619-identity-step4-safe-http/ | Non-mutating HTTP checks passed. Full Step 4 acceptance remains pending explicit approval for marked live identity test writes. |
| 2026-06-19 13:23 EAT | Identity live HTTP acceptance | Fixed live-schema issue by letting MariaDB generate users.email_normalized; fixed tests/api/db-helper.php parameter handling; ran live HTTP tests for register, duplicate email, invalid email, weak password, verification message creation, wrong/expired/valid verification token, unverified login denial, verified login, wrong password, refresh, protected /users/me, RBAC denial, logout, revoked refresh, disabled login, and audit signal. |
20260619-identity-step4-live6 | PASS WITH PRIVILEGED ADMIN PENDING | storage/test-runs/20260619-identity-step4-live6/ | Core auth/RBAC live acceptance passed with marked QA records and redacted evidence. SUPER_ADMIN creation/access test was skipped by default and then rejected by escalation reviewer as requiring exact privileged approval. |
| 2026-06-19 13:48 EAT | Identity privileged admin acceptance | With explicit approval, ran Step 4 live acceptance including mdn:create-admin, login as a marked QA SUPER_ADMIN, and protected admin endpoint access. |
20260619-identity-step4-admin2 | PASS | storage/test-runs/20260619-identity-step4-admin2/ | All Step 4 identity/RBAC cases passed through real HTTP calls with redacted evidence. QA audit/session/user records retained per guide. |
| 2026-06-19 14:10 EAT | Communications Service implementation and acceptance | Implemented CommunicationsService, template renderer, recipient resolver, channel router, DEV_LOG email provider, provider DTOs/interfaces, template/message/delivery repositories, communication APIs, template seed command, and refactored email verification to send through Communications Service. Seeded required MVP templates and ran live HTTP acceptance for template create/duplicate/publish, send, missing variable, unknown user, forced failure, retry, auth integration, audit, and redaction signal. |
20260619-communications-step5-live1 | PASS | storage/test-runs/20260619-communications-step5-live1/ | All Step 5 cases passed through real HTTP calls with marked QA records and redacted evidence. |
| 2026-06-19 15:18 EAT | Configuration and Secret Metadata Service | Implemented ConfigRepository, ConfigService, SecretMetadataService, config/secret metadata APIs, and non-secret default config seed command. Ran live HTTP acceptance for namespace read, unauthorized update denial, admin version update/publish, invalid value rejection, version listing, secret metadata list/create/rotate, raw secret rejection, and audit signal. |
20260619-config-step6-live1 | PASS | storage/test-runs/20260619-config-step6-live1/ | All Step 6 cases passed with marked QA records and redacted evidence; raw secret values are prohibited and not returned. |
| 2026-06-19 15:25 EAT | File Storage Service | Implemented FileService, repositories, access policy, and file APIs for metadata registration, linking, metadata read, download URL, archive, and delete. Ran live HTTP acceptance for valid metadata, unsupported type, oversized file, one/zero/multiple/missing link targets, unauthorized/authorized metadata read, controlled download URL, archive, and sensitive access audit. |
20260619-files-step7-live1 | PASS | storage/test-runs/20260619-files-step7-live1/ | All Step 7 cases passed with marked QA records and redacted evidence; API responses expose storage-key hashes rather than raw local paths. |
| 2026-06-19 15:35 EAT | Contract Service | Implemented contract template/version management, MVP renderer, generation, retrieval, acceptance, download URL, supersession, policy checks, and acceptance immutability test command. Ran live HTTP acceptance for template/version create, unpublished generation block, publish, membership contract generation, no/multiple subject validation, owner/wrong-user acceptance, idempotent acceptance, acceptance update/delete trigger rejection, authorized retrieval, and unauthorized retrieval. | 20260619-contracts-step8-live1 | PASS | storage/test-runs/20260619-contracts-step8-live1/ | All Step 8 cases passed with marked QA records and redacted evidence; rendered content stored in generation_metadata with PDF generation marked deferred. |
| 2026-06-19 15:50 EAT | Payment and Billing Service | Implemented invoice creation, MANUAL_TEST payment intents, idempotency, admin manual payment confirmation, receipts, failed/waived/refunded statuses, allocations, ledger entries, reconciliation, owner/admin access checks, and payment communications/audit. | 20260619-payments-step9-live3 | PASS | storage/test-runs/20260619-payments-step9-live3/ | All Step 9 cases passed with marked QA records and redacted evidence; no raw card data persisted. |
| 2026-06-20 10:09 EAT | Application, ISP, Review, and Vetting Services | Implemented Step 10 application workflow module with draft/update/get/status/document APIs, terms/privacy consents, submission gates, application-fee readiness, MANUAL ISP validation, review queue/assignment/start/notes, clarification request/response, vetting, and decision gates. | 20260620-applications-step10-live1 | PASS | storage/test-runs/20260620-applications-step10-live1/ | All Step 10 required API cases passed through live HTTP calls with marked QA records and redacted evidence. Approved applications do not activate memberships directly; ISP exemption did not create membership or bypass annual fee. |
| 2026-06-21 18:10 EAT | Database bootstrap in this environment (Phase 0 migration prep) | This Laravel project's existing domains (Identity through Applications, Steps 1-11 above) were originally built/tested against a database that does not exist in this environment. Reverse-engineered exact schema for all 34 referenced tables from Repository/Service code, resolved 3 real table-name collisions with live legacy PHP tables via a mdn_ connection-level table prefix (config/database.php, zero app/Domain changes), added 9 additive migrations plus append-only triggers for audit_events/contract_acceptances, added mdn:rbac:seed-defaults (no RBAC seeding existed anywhere in the repo), and found+fixed a real bug in the bootstrap itself (a botched generated-column migration left users.email_normalized permanently NULL, breaking all login). |
20260621-1800-bootstrap-step12 | PASS | storage/test-runs/20260621-1800-bootstrap-step12/ | Full live HTTP/artisan chain verified: migrate, audit immutability (both directions), payments/config/communications seeding, RBAC seeding, mdn:create-admin with real SUPER_ADMIN assignment, real JWT login, and an authenticated /users/me call -- all against the newly bootstrapped schema. This is prerequisite infrastructure for the RealEstate domain build (Phase 1 proper), not the RealEstate domain itself. |
| 2026-06-22 01:35 EAT | RealEstate domain -- Deals slice (Phase 1, first migrated domain) | Built app/Domain/RealEstate/ (Service/Repository/Policy/PartnerRepository/ReferenceGenerator/DealStage) covering Deal create, get, sequential stage-advance with due-diligence and closing-checklist gates, status changes, due-diligence task CRUD, and closing-checklist upsert -- ported from the legacy realestate_deal_repository.php state machine. Added 4 new tables (additive), 7 new routes under mdn.auth/mdn.permission:REAL_ESTATE_MANAGE, a legacy DB connection for read-only partner_id validation against the unmigrated legacy partners table, and extended mdn:rbac:seed-defaults with the new role/permission. |
20260622-0130-realestate-step13 | PASS | storage/test-runs/20260622-0130-realestate-step13/ | Verified live via manual HTTP sequence (the equivalent tests/api/run-realestate-step13.ps1 is written and syntax-checked but not executed natively -- no PHP 8.4 on this Windows host, app runs in a php:8.4-cli Docker container here). Covered: happy-path create through full lifecycle to auto-closed_won, illegal stage jump (422), due-diligence gate (422 then 200 after completion), closing-checklist gate (422 then 200 after completion), unknown partner (404), unauthenticated request (401), invalid status value (422), row-version conflict (409), and full audit-trail completeness (16 audit rows verified directly in mdn_audit_events). Real membership row obtained via the genuine Applications/Memberships HTTP chain already verified in Steps 10-11, reused via direct repository calls for speed (not re-validating those domains' own preconditions). |
| 2026-06-22 01:50 EAT | RealEstate domain -- Advisory Requests slice | Built AdvisoryService/AdvisoryRepository/AdvisoryReferenceGenerator (reusing RealEstatePolicy) for the member-facing "show interest" entry point: self-service create with ownership enforcement, admin-only status/outcome transitions, and role-scoped serialization stripping advisor_user_id/session_notes from any non-managing actor's view. Added 1 new table (additive) and 5 routes. |
20260622-0148-realestate-advisory-step14 | PASS | storage/test-runs/20260622-0148-realestate-advisory-step14/ | Verified live via manual HTTP: admin create/transition with internal fields visible, the SAME record re-fetched by the actual member via their own real login correctly omits the internal fields (proves server-side role-scoped serialization, not just admin-tooling behavior), member self-service create, ownership/permission denials (403/404), invalid-outcome (422), row-version conflict (409), both list endpoints, and full audit-trail confirmation (3 rows). |
| 2026-06-22 02:02 EAT | RealEstate domain -- Recommendations slice | Built RecommendationService/RecommendationRepository/RecommendationReferenceGenerator, and extended RealEstatePartnerRepository with the ported legacy partner-recommendable gate (verified status, not suspended/archived/terminated, active signed agreement, not on active watchlist -- all checked against live legacy tables via the read-only 'legacy' connection). Added 1 table (additive) and 5 routes; member self-service respond with illegal-double-response protection, admin-only create/expire. |
20260622-0200-realestate-recommendations-step15 | PASS | storage/test-runs/20260622-0200-realestate-recommendations-step15/ | Verified live: gate correctly rejects a known non-qualifying live legacy partner with itemized reasons, accepts a known qualifying one; member's own login accepts a recommendation (status/response fields set correctly); double-response blocked (422); admin expire with reason; member denied expire (403); full audit trail confirmed (4 rows). |
| 2026-06-22 02:08 EAT | RealEstate domain -- Introductions slice (pipeline-closing) | Built IntroductionService/IntroductionRepository/IntroductionReferenceGenerator. The hard member-consent gate is the only path that can create an introduction; on success it creates the linked Deal (reusing RealEstateRepository/DealReferenceGenerator from Step 13) in the same DB transaction and marks commission_eligible. Reuses the Recommendations slice's partner-eligibility gate. Added 1 table (additive) and 3 routes. |
20260622-0205-realestate-introductions-step16 | PASS | storage/test-runs/20260622-0205-realestate-introductions-step16/ | Found and fixed a real bug live (undefined-array-key 500 when introducing via recommendation_id only, no partner_id). After the fix: consent gate (422), not-yet-accepted recommendation blocked (422), full recommendation-driven introduction succeeds and the auto-created deal was independently fetched and confirmed (correct stage/owner/history), direct-partner introduction also succeeds, non-qualifying partner blocked (422) reusing the Step 15 gate, get/list endpoints verified, audit trail confirmed (2 rows). The full Advisory->Recommendation->Introduction->Deal pipeline is now proven live end-to-end. |
| 2026-06-22 02:18 EAT | RealEstate domain -- Commission Invoices slice | Built InvoiceService/InvoiceRepository/InvoiceReferenceGenerator -- full legacy status state machine (draft/issued/sent/partially_paid/overdue/disputed/paid/cancelled/written_off) and the "no invoice before closing" gate (deal must be at/past commission_invoice stage with closing checklist's commission_trigger_met confirmed), reusing RealEstateRepository/DealStage from Step 13. Commission-amount auto-computation from listing/pricing-policy is out of scope (no Listings/Pricing domain exists yet) -- explicitly required as input instead, flagged not hidden. Added 1 table (additive) and 5 routes. |
20260622-0215-realestate-invoices-step17 | PASS | storage/test-runs/20260622-0215-realestate-invoices-step17/ | Verified live against the real closed_won deal from Step 13: gate blocks a not-yet-closed deal (422), illegal status jump blocked (422), payment blocked while draft (422), full draft->issued->sent->partially_paid->paid lifecycle works with correct balance math and paid_at timestamping, payment blocked once paid (422) and on negative amount (422), deal-owner member access confirmed via real login, admin-only create denied to member (403), full audit trail (5 rows). |
| 2026-06-22 02:28 EAT | RealEstate domain -- Partner Vetting slice | Built VettingService/VettingRepository (checklist seed/update, decision recording) and extended RealEstatePartnerRepository with the one intentional write through the 'legacy' connection -- recordVerificationStatus(), mirroring legacy's exact behavior of flipping partners.verification_status on a vetting decision. Added 2 tables (additive) and 6 admin-only routes. |
20260622-0225-realestate-vetting-step18 | PASS | storage/test-runs/20260622-0225-realestate-vetting-step18/ | Verified live with a real before/after proof: a genuinely pending_review legacy partner was blocked from recommendation with reason partner_not_verified; after recording an approved vetting decision (confirmed live in the legacy partners table), the identical recommend call no longer returns that reason -- proving the Laravel-built feature genuinely controls the cross-domain gate, not just that a row got inserted. Also verified: reject-without-reason (422), invalid outcome (422), conditional-approval-without-conditions (422), admin-only access (403 to member), checklist row-version conflict (409), full audit trail (5 rows). |
| 2026-06-22 02:40 EAT | RealEstate domain -- Partner Agreements slice | Built AgreementService/AgreementRepository -- full legacy transition map (draft/sent/signed/active/expired/terminated/superseded/under_review), the activation-checklist-or-override gate, and auto-supersede-other-active-agreements-per-partner. Added 1 table (additive) and 5 admin-only routes. |
20260622-0238-realestate-agreements-step19 | PASS | storage/test-runs/20260622-0238-realestate-agreements-step19/ | Found and fixed a real bug live: the Recommendations gate (Step 15) only checked the legacy agreements table, so a brand-new Laravel-created agreement didn't unlock anything. Fixed recommendableFailureReasons() to check both tables. After the fix: full draft->sent->signed->active lifecycle works, illegal jump blocked (422), activation blocked without checklist/override (422), the partner vetted in Step 18 is now provably fully recommendable end-to-end (zero gate failures) -- completing a real cross-step business journey. Also verified override-activation and automatic supersede-of-prior-active-agreement live, and admin-only access (403 to member). Full audit trail (9 rows). |
| 2026-06-22 02:48 EAT | RealEstate domain -- Benefits/Gifts slice | Built BenefitService/BenefitRepository/BenefitReferenceGenerator -- full legacy transition map (promised/pending/delivered/missed/disputed/expired/cancelled/not_applicable) and the FR-BEN-010 housewarming gift calculation (1% of deal value, capped at 5000, both constants ported verbatim). Missed/disputed auto-escalation to a linked risk record (FR-BEN-006) is explicitly deferred -- Risk/Watchlist doesn't exist yet (next slice) -- flagged in code rather than forward-referencing a missing table. Added 1 table (additive) and 6 admin-only routes. |
20260622-0246-realestate-benefits-step20 | PASS | storage/test-runs/20260622-0246-realestate-benefits-step20/ | Verified live against the real closed_won deal from Step 13: housewarming calc correct (1% of real $120,000 deal_value = $1,200, under cap), ineligibility correctly blocked for a not-yet-closed deal (422), gift creation correct, invalid type (422), illegal status transition blocked (422) then a valid chain succeeds, admin-only access (403 to member), partner-scoped list, full audit trail (3 rows). |
| 2026-06-22 03:02 EAT | RealEstate domain -- Risk & Watchlist slice | Built RiskService/RiskRepository/RiskReferenceGenerator -- watchlist add/resolve and the full risk-record status transition map. Closed two loops: RealEstatePartnerRepository's watchlist gate now checks both legacy and new tables (mirroring the Step 19 Agreements fix), and Benefits' (Step 20) deferred missed/disputed auto-escalation is now wired to create a real linked risk record. Added 2 tables (additive) and 7 admin-only routes. |
20260622-0300-realestate-risk-step21 | PASS | storage/test-runs/20260622-0300-realestate-risk-step21/ | Found and fixed a real MySQL identifier-length-limit bug live (auto-generated composite index name exceeded 64 chars once prefixed) -- masked across retries by a half-succeeded prior migration attempt, diagnosed by manually isolating each table. After the fix: full watchlist add->gate-blocks->resolve->gate-clears cycle proven live (same pattern as Vetting/Agreements before it); closed-loop proof that marking a benefit "missed" auto-creates a correctly-linked risk record; risk-record transition map validated (422 on illegal jump, 200 on valid chain with resolved_at set); invalid category rejected (422); admin-only access enforced (403); full audit trail (5 rows). |
| 2026-06-22 03:08 EAT | RealEstate domain -- Reviews slice | Built ReviewService/ReviewRepository/ReviewReferenceGenerator -- BR-023 eligibility gate (closed_won deal owned by reviewing member, one review per deal), moderation workflow, and BR-025 rating-label mapping. Wired the low-rating (<=2) approved-review auto-escalation to a linked risk record directly via RiskService (no deferral needed, unlike Benefits in Step 20, since Risk now exists). Added 1 table (additive, with a proactively-shortened unique-constraint name) and 5 routes. |
20260622-0306-realestate-reviews-step22 | PASS | storage/test-runs/20260622-0306-realestate-reviews-step22/ | Verified live: ineligible-deal rejection (422), the real QA member submits a genuine 1-star review on the real closed_won deal from Step 13 (rating_label correctly "Poor Performance"), duplicate-review blocked (409), approving it auto-creates a correctly-linked risk record (verified by fetching the risk-records list), invalid moderation status (422), out-of-range rating (422), full audit trail (2 rows). |
| 2026-06-22 03:09 EAT | RealEstate domain -- Partner Documents slice (final slice) | Built DocumentService/DocumentRepository/DocumentReferenceGenerator -- reuses the existing Files domain (Step 7) for actual file storage instead of re-implementing upload handling (file_id is a plain reference to stored_files.id); full legacy status transition map and the FR-DOC-011 nearing-expiry work-queue query. Added 1 table (additive) and 6 admin-only routes. This completes all 11 RealEstate sub-resources (Steps 13-23). |
20260622-0307-realestate-documents-step23 | PASS | storage/test-runs/20260622-0307-realestate-documents-step23/ | Verified live: registered a real file via the unmodified existing Files domain, created a document referencing it (201), invalid file_id rejected (404), illegal status jump blocked (422), valid submitted->under_review->accepted chain works, nearing-expiry date-windowed query correctly returns an accepted document expiring within the window, full audit trail (6 rows). |
| 2026-06-22 03:23 EAT | Membership module -- live end-to-end re-verification in this environment | Steps 1-11 (Identity/Communications/Config/Files/Contracts/Payments/Applications/Memberships) were originally built and PASS-marked in a different environment before this repo's Docker bootstrap (Step 12) existed; Applications/Memberships had only been exercised via a tinker shortcut in Step 13, not real HTTP, in this environment. Ran the full real chain end-to-end with no shortcuts: register -> verify email -> login -> application create/consent/submit -> application fee invoice+payment -> file/document attach -> review assign/start/complete -> vetting start/outcome -> decision approve -> contract template/version/publish/generate/accept -> annual fee invoice+payment -> membership activate. | 20260622-0316-membership-live-verification-step24 | PASS | storage/test-runs/20260622-0316-membership-live-verification-step24/ | Found and fixed a real, environment-breaking bug live: POST /auth/register 500'd on the very first attempt because RecipientResolver reads users.phone_number, a column the Step 12 schema bootstrap never created (missed because that pass only scanned UserRepository's own usage, not Communications'). This meant the entire Membership module was non-functional in this environment via the real registration path until fixed (Step 12's own register/login smoke test never caught it because mdn:create-admin bypasses IdentityService::register() entirely). After the fix: full chain completed successfully -- real member created, MDN-2026-000001, status ACTIVE, activation correctly idempotent on retry, 30 real audit-trail rows spanning every domain touched. |
| 2026-06-22 07:10 EAT | Membership flow correction -- no self-registration; admin/system-driven account creation after annual fee payment | User correction: applicants never self-register; legacy creates the member's account and emails credentials only after the $100 annual fee is paid. Refactored Applications to use a per-application access token (X-Application-Access-Token) instead of a login for all pre-payment applicant actions (create/get/update/consents/submit/status/documents/clarification-responses/ISP validation), removed POST /auth/register entirely, and wired real account auto-provisioning (pre-verified user, must_change_password, generated temp password emailed) into MembershipService::activate(). |
20260622-0655-membership-corrected-flow-step25 | PASS | storage/test-runs/20260622-0655-membership-corrected-flow-step25/ | Found and fixed 2 real bugs live: an access_token_hash leak in the application API response, and application_consents.user_id being NOT NULL (blocked the first applicant-token consent call with a 500; fixed via migration 2025_06_22_001600). Full live HTTP chain verified end-to-end with a brand-new application created with zero auth, through to a real auto-provisioned member account, a working login with the emailed temporary password (must_change_password: true correctly surfaced), and confirmation that the original access token is invalidated once the real account exists. /auth/register confirmed 404. |
| 2026-06-22 12:40 EAT | Merge missing Products domain from mdn-backend remote's main branch |
User asked to check main for changes not yet in our branch and merge them in. main is a much older, pre-monorepo, pre-Step-12 snapshot with no shared git history (no merge-base) -- compared by diffing trees directly. Found main had one thing we didn't: the full Products domain (cross-cutting member product-request/entitlement/blocker layer for CARE/INVESTMENT/REAL_ESTATE/CONNECT/CONCIERGE/MARKETPLACE). Ported the 8 domain files and 14 routes verbatim. |
20260622-1230-products-domain-merge-step26 | PASS | storage/test-runs/20260622-1230-products-domain-merge-step26/ | Found the same class of gap as Step 12: the Products domain's tables were never reverse-engineered into this environment's schema (built/tested in an earlier environment). products catalogue table didn't exist at all; product_requests/product_entitlements existed but only with the narrow columns MembershipService itself needed; product_request_blockers didn't exist. Added migration 2025_06_22_001700_create_products_domain_tables.php and ported the mdn:products:seed-catalogue command. Verified live: catalogue seed/list, a real product request against the real Step-25 membership with correct auto-seeded blockers (contract/payment/approval matching CARE's flags), submit correctly stays blocked while blockers are open, blockers list, and entitlements recalculate on the empty case. |
| 2026-06-22 14:50 EAT | Calendly integration -- shared API client + real advisory-meeting booking on Applications | User provided a live Calendly personal access token and asked for real booking integration in the active Laravel app (not the legacy PHP monorepo), built as a global/shared service reusable by future integrations. Built App\Infrastructure\Calendly\CalendlyClient (stateless, no constructor deps, same tier as UuidGenerator/TransactionManager) and its first consumer, App\Domain\Applications\AdvisoryMeetingService, which creates a real single-use Calendly scheduling link per application and matches the booked/canceled webhook back via a utm_content tracking param (Calendly's scheduling-links API has no field for arbitrary metadata). |
20260622-1445-calendly-advisory-meeting-step27 | PASS | storage/test-runs/20260622-1445-calendly-advisory-meeting-step27/ | Verified against the real, live Calendly account: POST /applications/{id}/advisory-meeting made a real API call and returned a real single-use booking link that was confirmed to resolve (HTTP 200). Simulated realistic invitee.created/invitee.canceled webhook payloads (matching Calendly's documented schema) against POST /webhooks/calendly -- correctly transitioned REQUESTED -> SCHEDULED -> CANCELED with all Calendly fields populated, correctly recorded webhook_signature_verified: false (no secret configured yet -- no public URL exists to register a real subscription against), and didn't crash on unknown utm_content or unrelated event types. Full audit trail confirmed. Real signed webhook delivery from Calendly itself remains unverified until a public URL exists; the payload/signature logic is implemented against their real documented schema but not yet exercised by an actual incoming webhook. |