# WB Listora 1.0.0 — Release Plan

**Status**: DRAFT — pending sign-off
**Owner**: Varun
**Companion**: [`../wb-listora-pro/plan/1.0.0-release-plan.md`](../../wb-listora-pro/plan/1.0.0-release-plan.md)
**Punch list**: [`release-issues-and-flow-tests.md`](release-issues-and-flow-tests.md)

> **Release principle**: 1.0.0 is the product's first impression. We do not ship half-cooked. Every workstream must clear its exit criteria — no waivers, no "we'll fix in 1.0.1".

---

## 1. What 1.0.0 means

A site owner can install Free (or Free+Pro), complete the setup wizard in <10 minutes, and operate a directory with confidence — without consulting docs for routine tasks.

**The 1.0.0 promise to site owners**:
1. Every admin page has a clear purpose, no orphan classes
2. Every documented feature has an admin UI to configure it
3. Every public REST endpoint is rate-limited or capability-gated
4. Every notification has a template + opt-out path
5. Every user role (admin / moderator / subscriber / submitter / guest) has a tested flow

**The 1.0.0 promise to end users**:
1. Submission flow works in <90 seconds for a logged-in user, including geolocation
2. Search returns results in <500ms p95 on 10,000 listings
3. Detail page renders all 5 tabs without JS errors
4. Dashboard shows accurate counts + recent activity
5. Email verification doesn't bounce; expired links offer a clear "request new" path

**Out of scope for 1.0.0** (deferred to 1.1+):
- Multi-language (i18n exists, .pot file ships, but no bundled translations)
- Multisite-network admin UI (works on single sites; multisite is a 1.1 target)
- WooCommerce-managed credit packs (1.0.0 ships Pro's own gateway adapter only)
- Mobile companion app (REST endpoints support it, but no React Native shell ships)

---

## 2. Workstreams + sequencing

Five workstreams. Each has architecture decisions, exit criteria, owner, and order constraints.

| # | Workstream | Blocks | Owner |
|---|---|---|---|
| **W1** | Security & DoS hardening | release | core |
| **W2** | Moderator workflow completion | release; depends on W1 | Pro |
| **W3** | Half-built feature classes (10 in Pro) | release | Pro |
| **W4** | Customer UX gaps | release | Free + Pro |
| **W5** | Documentation + marketing | release packaging | docs |

**Critical path**: W1 → W2 → (W3, W4 in parallel) → W5 → release.

---

## 3. Architecture decisions

### ADR-001 — Public REST endpoint baseline

**Decision**: Every endpoint with `__return_true` permission must clear one of:
- (a) Read-only AND idempotent AND cached at edge (HTTP cache headers)
- (b) Behind per-IP rate limit (token bucket, configurable in settings)
- (c) Behind nonce + session token (issued from a server-rendered page)

**Rationale**: Currently 11 Free + 8 Pro endpoints are `__return_true`. Without policy, more get added over time. Need a doctrine.

**Implementation**:
- New `WBListora\Services\Rate_Limiter` — token bucket, transient-backed (`_listora_rl_<endpoint>_<ip>`).
- All public read endpoints pass through `Rate_Limiter::check()` with category-specific quotas.
- Quotas default: 60/min for `/search`, 30/min for `/search/suggest`, 10/min for `/listings/bulk`, 3/hour for `/submission/resend-verification`.
- Admin can override via `wb_listora_rate_limits` filter.

**Files**:
- New: `includes/services/class-rate-limiter.php`
- Modified: every REST controller that uses `__return_true`

**Exit**: ✓ All `__return_true` endpoints either edge-cached, rate-limited, or session-token-gated. Documented in `audit/manifest.json` security block.

### ADR-002 — Write-endpoint nonce policy

**Decision**: Every POST/PUT/DELETE endpoint requires either:
- (a) WordPress nonce (preferred, default for logged-in users)
- (b) HMAC signature (machine-to-machine, e.g., payment webhook)
- (c) CSRF-safe captcha challenge (anonymous user-driven actions)

**Rationale**: Lead form, analytics tracking, resend verification all currently take `__return_true` POST without nonce. Spam vectors.

**Implementation**:
- Free: helper `wb_listora_verify_request_nonce($request, $action)` — checks `X-WP-Nonce` header, falls back to `_wpnonce` body param.
- Pro: same helper for Pro endpoints.
- Captcha integration: existing `class-captcha.php` (reCAPTCHA v3 + Turnstile) becomes the canonical anonymous-user gate. New endpoints reuse it via `Captcha::verify($request)`.

**Exit**: ✓ Zero write endpoints accept requests without one of the three gates.

### ADR-003 — Moderator workflow (the big one)

**Current state** (verified by code trace):
- ✅ Pro registers `listora_moderator` role on activation (`Moderator::register_role()`)
- ✅ Pro has admin page at `listora-moderators`
- ✅ Pro hooks `wb_listora_listing_submitted`, `wb_listora_after_create_review`, `wb_listora_after_submit_claim` for round-robin
- ✅ Pro has REST endpoints (activate/deactivate/queue/reassign/stats)
- ✅ Free's caps system already grants moderator caps to admin+editor
- ✅ Pro filters `pre_get_posts` so moderators see only their queue in admin

**Gaps to close in 1.0.0**:

1. **Frontend moderator dashboard** (block) — currently moderators must use wp-admin. Customer-installed sites often hide wp-admin from non-admins. Need a frontend block + page so moderators work in the storefront experience.

2. **Email notifications on assignment** — round-robin assigns but doesn't email the moderator. They have to check the queue manually. Add email + in-app notification on assignment.

3. **Bulk reassign notifications** — `/moderators/reassign` works but receiving moderator gets no alert.

4. **Moderator-scoped audit log** — `audit/audit-log` REST is `manage_options` only. Moderators can't see what they themselves did. Add scoped read with `moderate_listora_listings` cap.

5. **Promote-user UI** — REST endpoints exist but the admin page must include a clean "browse subscribers → promote" UI, not just a stats table.

6. **Review pre-publish moderation toggle** — currently reviews go live immediately. Add setting `moderate_reviews_before_publish` → reviews enter queue.

**Long-term direction (1.1+, mentioned for context)**:
- Per-category moderators (Italian-restaurant moderator handles only Italian listings)
- Team queues (3 moderators round-robin within a team, multiple teams per region)
- Moderator performance reports (avg response time, accuracy)

These don't ship in 1.0.0 but the data model must not block them.

**Data model decision**:
- Use existing `listora_audit_log` table for moderator actions (already exists in Pro).
- Add columns if needed via migration `1.0.0` (no schema breaks).
- Moderator assignment stored in `wp_postmeta` (`_listora_assigned_moderator` user_id) — not a new table. Keeps queue queryable via standard WP_Query.

**Notification delivery**:
- Reuse `WBListora\Workflow\Notifications` (Free's pipeline).
- Add events: `moderator_assigned_listing`, `moderator_assigned_review`, `moderator_assigned_claim`, `moderator_queue_reassigned`.
- Each event has a template in `templates/emails/moderator/` that themes can override.

**Frontend block**:
- New block: `listora-pro/moderator-queue`
- Renders only for users with role `listora_moderator` (or admin viewing as moderator).
- Tabs: My Queue · Recent Actions · Stats.
- Auto-created page on Pro activation: "Moderator" with this block.

**Exit**:
- ✓ Moderator can log in, see frontend dashboard, work without touching wp-admin.
- ✓ Receives email when assigned an item.
- ✓ Sees own audit trail.
- ✓ Admin can promote any subscriber from a clean UI in <30 seconds.
- ✓ Browser-tested by 3 different roles (admin, moderator, subscriber-attempting-to-be-moderator-without-cap).

### ADR-004 — License fail-soft contract

**Decision**: When Pro license expires or is removed:
1. **All Pro features disable cleanly** — no fatals, no broken admin pages, no customer-visible errors.
2. **Pro data is preserved** — credit balances, audit log, saved searches, needs all remain in DB. License reactivation restores access.
3. **Admin sees a non-dismissible notice**: "Pro features paused due to expired license. [Reactivate]"
4. **Free continues working at full functionality** — no Free feature depends on Pro being active.

**Implementation**:
- `Feature_Manager::should_load($feature)` returns false if `License::is_active() !== true`.
- All Pro REST endpoints return 503 with `{code: 'wb_listora_pro_license_expired', reactivate_url: ...}` when license inactive.
- Admin pages render a "Reactivate" CTA instead of the feature UI.
- Free's calls to Pro hooks (e.g., `wb_listora_card_actions`) are no-ops because Pro features unhook themselves on license check.

**Exit**: ✓ Manual test — activate Pro → use feature → invalidate license → confirm clean degradation in admin + frontend + REST. No fatals in `debug.log`.

### ADR-005 — Admin UI pattern (settings tab vs dedicated page)

**Decision rule**:
- **Settings tab** (under `listora-settings` page) — cross-cutting config, < 6 fields, no workflow.
- **Dedicated admin page** — workflow (CRUD entities, queues, dashboards), needs > 6 fields, OR has its own table view.

**Applied**:
| Feature | Goes where | Why |
|---|---|---|
| Maps provider + API key + style | Settings tab | Cross-cutting config |
| White Label (logo, colors, hide plugins) | Dedicated page | Workflow + many fields + preview |
| Coming Soon / Visibility | Settings tab | 3 fields, simple toggle |
| SEO Pages | Dedicated page | CRUD entity (rules) |
| Pro Features toggle | Dedicated page (Pro Features) | Already exists, list of toggles |
| Coupons | Dedicated page | CRUD |
| Badges | Dedicated page | CRUD |
| Webhooks | Dedicated page | CRUD + delivery log |
| Audit Log | Dedicated page | Read-only viewer + filters |
| Moderator | Dedicated page | Workflow |
| Reverse Listings | Dedicated page | CRUD + moderation queue |

**Exit**: ✓ Every Pro feature toggle in `wb_listora_pro_features_enabled` has a corresponding location for its config (tab or page) — no orphan toggles.

---

## 4. Workstream W1 — Security & DoS hardening

**Order**: ships first; all other workstreams depend on it.

| Task | File | Source from punch list | Estimated days |
|---|---|---|---|
| Build `Rate_Limiter` service | `includes/services/class-rate-limiter.php` | F-01, F-03 | 1 |
| Apply rate limits to public read endpoints | various REST controllers | F-01, F-03, F-04 | 0.5 |
| Add nonce to anonymous POST endpoints | F-02, P-01, P-02 | 1 |
| Cap `WP_REST_Posts_Controller` per_page on `/listings` | `class-listings-controller.php` | F-04, F-35 | 0.25 |
| HMAC replay protection on payment webhook | `class-webhook-controller.php` | P-03 | 0.5 |
| Refresh manifests after security changes | n/a | — | 0.25 |

**Exit criteria**:
- ✓ All P0 security items in punch list closed
- ✓ Browser flow test: anonymous user can submit listing/review (legitimate path) but bot script burst is rate-limited
- ✓ `audit/manifest.json` security block updated with rate limits + nonce policy
- ✓ PHPStan L7 + WPCS clean

## 5. Workstream W2 — Moderator workflow

**Order**: after W1 (rate limits protect moderator endpoints too).

Per ADR-003, six gaps to close. Sequenced:

1. **Promote-user UI** in admin Moderator page (1 day)
2. **Email notifications on assignment** (0.5 day)
3. **Bulk reassign notifications** (0.5 day)
4. **Scoped audit log access** for moderators (0.5 day)
5. **Frontend moderator dashboard block** (1.5 days, biggest piece)
6. **Review pre-publish toggle** + queue routing (1 day)

**Exit criteria** (per ADR-003 Exit + below):
- ✓ Browser flow tests pass for all 5 moderator-flow rows in `release-issues-and-flow-tests.md`
- ✓ Admin can demote moderator → user falls back to subscriber, queue items reassigned via `/moderators/reassign`
- ✓ Notification preferences honor moderator opt-outs (per W4)

## 6. Workstream W3 — Half-built feature classes

**Order**: parallel with W4 after W2.

Per ADR-005, 10 features need admin pages or settings:

| Feature | Type | Days |
|---|---|---|
| White_Label | Dedicated page | 2 |
| Coming_Soon / Visibility | Settings tab | 0.5 |
| SEO_Pages | Dedicated page (CRUD) | 2 |
| Map styles + Google API key | Settings tab | 0.5 |
| Google Places API key | Settings tab | 0.25 |
| Pro Features toggle UI | Dedicated page (already exists, polish) | 1 |
| Quick_View | Decision: ship or remove | 0.25 |
| Infinite_Scroll | Settings tab | 0.5 |
| Verification queue | Dedicated page | 1.5 |
| BuddyPress opt-out toggle | Settings field | 0.25 |

**Exit criteria**:
- ✓ Zero orphan feature classes remain (every class has an observable surface)
- ✓ Every feature toggle either toggles something the admin can configure OR is removed
- ✓ Setup wizard surfaces the new admin pages so site owners discover them

## 7. Workstream W4 — Customer UX gaps

**Order**: parallel with W3.

| Item | Source | Days |
|---|---|---|
| Per-user notification preferences in dashboard | F-34, P-30 | 1.5 |
| Saved-search alert toggle (without delete) | P-28 | 0.5 |
| Comparison up to 6 listings | P-26 | 0.5 |
| Need response: "request more info" + "negotiate" | P-27 | 1 |
| Subscription credit auto-top-up | P-29 | 1.5 |
| "Open now" timezone math fix | F-36 | 0.5 |

**Exit criteria**:
- ✓ Every flow in `release-issues-and-flow-tests.md` end-user matrix passes browser test in Chrome + Safari mobile
- ✓ No double-confirmation modals on routine actions (favorites, helpful vote)

## 8. Workstream W5 — Documentation + marketing

**Order**: ships last, blocks release packaging.

| Item | Owner | Days |
|---|---|---|
| Customer docs site updated for every shipped feature | docs team | 3 |
| Developer docs (extension API, hook reference, REST API) | core | 2 |
| readme.txt + changelog.txt | core | 0.5 |
| Marketing landing page | marketing | parallel |
| Demo video (5-min walkthrough) | marketing | parallel |
| Per-feature support article in KB | support | 5 |

**Exit criteria**:
- ✓ Every public hook + filter listed in `audit/manifest.json` has a docs entry
- ✓ readme.txt valid (passes WP plugin readme validator)
- ✓ Marketing landing page reviewed by 3 site owners (informal panel)

---

## 9. Acceptance criteria — release-ready checklist

This is the gate. **All boxes must be ticked, no waivers.**

### Code quality
- [ ] PHPStan level 7 clean (Free + Pro)
- [ ] WPCS clean (no warnings)
- [ ] Plugin Check (PCP) clean
- [ ] PHPUnit suite passes 100%
- [ ] No `// TODO` / `// FIXME` in shipped code paths
- [ ] No new fired hooks without documented consumers in manifest

### Security
- [ ] Every public REST endpoint either edge-cached, rate-limited, or session-gated (ADR-001)
- [ ] Every write endpoint requires nonce / HMAC / captcha (ADR-002)
- [ ] HMAC replay protection on payment webhook (ADR-002)
- [ ] No `$wpdb` prepared-SQL with unescaped input
- [ ] All file uploads pass through `wp_handle_upload` with mime allowlist

### Functional (from punch list)
- [ ] All 38 Free tasks (F-01 to F-38) closed with browser test ✓
- [ ] All 34 Pro tasks (P-01 to P-34) closed with browser test ✓
- [ ] Phase 2.5 manifest coverage gate ≥95% per category, both plugins

### Persona flows (browser-tested)
- [ ] **Site owner**: setup wizard → directory live → first listing approved in <15 minutes
- [ ] **Moderator**: promoted by admin → email arrives → frontend dashboard → approve queue → audit visible
- [ ] **Customer (logged out)**: search → detail → contact owner via lead form (captcha) → email arrives at owner
- [ ] **Customer (logged in)**: submit listing → email verify → pending → admin approves → published → review submitted → owner replies
- [ ] **License lapsed**: Pro disabled cleanly, Free continues working, admin sees reactivate CTA

### Documentation
- [ ] readme.txt validated
- [ ] Customer docs site has page per shipped feature
- [ ] Developer docs published (hooks, REST, extension guide)
- [ ] Changelog complete

### Cross-plugin coupling (Free + Pro)
- [ ] All 12 Free hooks Pro depends on still fire correctly after security changes
- [ ] License off → Free still works, Pro disables cleanly
- [ ] License on → Pro works without modifying Free files

### Performance
- [ ] Search p95 < 500ms on 10,000 listings
- [ ] Detail page LCP < 2.5s on mid-tier mobile
- [ ] Admin list views paginate (no full-table loads)

---

## 10. Release sequencing (release notes outline)

### 1.0.0 ships with:
- Full directory plugin (Free): listings, reviews, claims, favorites, search, dashboard, submission flow, 11 blocks, 49 REST routes, WP-CLI tooling, demo seeder
- Pro extensions: credits, coupons, badges, multi-criteria reviews, photo reviews, lead forms, comparison, saved searches, analytics, audit log, outgoing webhooks, reverse listings (needs marketplace), moderator workflow, white-label, coming-soon, SEO pages, BuddyPress integration
- 100% verified browser flows for 5 personas
- Documentation site live
- Marketing landing page
- 5-minute demo video

### Post-1.0.0 (1.1 themes — separate plan):
- Multilingual bundles (Polylang / WPML companions)
- Multisite network admin
- Per-category moderators + team queues
- Mobile companion app shell
- WooCommerce-managed credit packs
- Stripe Connect for marketplace payouts

---

## 11. Risks & mitigations

| Risk | Probability | Impact | Mitigation |
|---|---|---|---|
| Moderator workflow takes longer than 5 days | Medium | High | Parallelize: frontend block + email notif by 2 different agents |
| Rate limiter breaks legit users | Low | High | Whitelist logged-in admins; warn-only mode for first week |
| License server downtime breaks fail-soft | Low | Medium | Cache last-known status for 7 days; treat unreachable as "active" not "expired" |
| Setup wizard fails on themes that override block CSS | Medium | Medium | Test on BuddyX, Astra, Neve, Twenty Twenty-Five before release |
| Customer feedback on lead form spam | High (over time) | Medium | Build first-week monitor: track lead-form submissions; tune throttle |

---

## 12. Sign-off gate

This plan is ready when:
- [ ] Architecture decisions reviewed by 1 senior eng (or self-review with docs trail)
- [ ] Workstream estimates validated (no >50% slip in week 1)
- [ ] Cross-plugin coupling watchlist re-verified
- [ ] Punch list re-cross-referenced (every P0/P1 mapped to a workstream task)

**Release commit message template**:
```
release: 1.0.0

First public release of WB Listora directory plugin.

Free: full directory functionality (listings, reviews, claims,
search, dashboard, submission, 11 blocks, REST + WP-CLI).
Pro: 28 license-gated features including moderator workflow,
credits, multi-criteria reviews, comparison, needs marketplace,
analytics, audit log.

Browser-tested across 5 personas. Coverage gate ≥95% per
manifest category. PHPStan L7 + WPCS + PCP all clean.

Plan: plan/1.0.0-release-plan.md
```
