ghostguild-org/docs/BACKLOG.md
Jennie Robinson Faber ba84429917
Some checks failed
Test / vitest (push) Successful in 11m4s
Test / playwright (push) Failing after 9m59s
Test / visual (push) Failing after 9m20s
Test / Notify on failure (push) Successful in 2s
docs(BACKLOG): file findings from e2e expansion
A11y bug: /board contrast violations (since fixed via --text-faint).
Wave-Slack: /api/auth/member missing slackInvited (fixed), markSlackInvited
non-reactive (fixed), deprecated slackInviteStatus serialization (fixed),
spec-vs-UI wave-language mismatch.
Known gotchas: /admin/series-management Delete is a no-op for empty
series; past-deadline and sold-out events render identically.
Simplify follow-ups: STATUS_LABELS dedup completed.
E2e infrastructure gaps: other email routes still send live in dev,
no dev seeder for arbitrary member status, SSR useFetch blocks
page.route mocking, self-cancel paid registrations not e2e-tested,
visual snapshot regen process.
2026-04-30 22:26:38 +01:00

14 KiB
Raw Blame History

Ghost Guild — Open Backlog

Last consolidated: 2026-04-30. Single source of truth for every open issue across the codebase. Pulls from LAUNCH_READINESS.md, TODO.md, the post-launch backlog memory, and a fresh sweep of in-code TODO/FIXME comments.

Cutover has not happened yet. Deploy steps live separately in LAUNCH_READINESS.md.


Pre-cutover (do once)

Operational steps that have to run during cutover. Full details + env-var list in LAUNCH_READINESS.md.

  • Provision the Dokploy app, set env vars (full list in LAUNCH_READINESS.md), confirm BASE_URL exact-matches the public origin and NODE_ENV=production.
  • Add the daily Dokploy Scheduled Task that POSTs to /api/internal/reconcile-payments with X-Reconcile-Token.
  • Run node scripts/migrate-contribution-amount.cjs --apply against prod Mongo BEFORE the new code serves traffic.
  • Set NUXT_HELCIM_MONTHLY_PLAN_ID=50302 and NUXT_HELCIM_ANNUAL_PLAN_ID=50303 in Dokploy.
  • Set NUXT_RECONCILE_TOKEN to a 32+ char random string.
  • Push local main to origin/main.
  • Deploy.
  • Run node scripts/reconcile-helcim-payments.mjs --apply against prod Mongo AFTER the new code serves traffic.
  • Audit prod for pre-fix series-pass bypass registrations (registrations on pass-only series children with registeredAt < 2026-04-20 from non-pass-holders). Decide per case.
  • In Helcim dashboard: disable the default payment-confirmation email for plans 50302 + 50303 (we send our own CRA-safe version via Resend).
  • Run one real test charge and verify (a) Payment doc in Mongo and (b) exactly one CRA-compliant confirmation email.
  • Rotate HELCIM_API_TOKEN in the Helcim merchant portal and update the Dokploy env var.
  • Trigger the daily reconcile task once manually in Dokploy to confirm it's wired correctly.

Pilot smoke walks (before first wave)

Once cutover lands, before the first Slack onboarding wave goes out:

  • Pilot smoke walk for Slack-invited workflow. One admin manually clicks "Mark as Slack invited" against a real test member in production, confirms the row updates in place, and confirms the dashboard "Slack coming" note disappears for that member. Unit tests cover the pieces; nothing covers the live admin-to-member round-trip.

Bylaws-decoupling (waiting on amendment ratification)

Membership status is being decoupled from payment status. Copy + UI gates already align; behavioral changes below remain.

  • B1. server/api/members/cancel-subscription.post.js:31,48 flips status to pending_payment on cancel. Under the new bylaws, cancellation should keep status active (just zero contribution). Update the findByIdAndUpdate payload + response, the comment at line 26, and add coverage in tests/server/api/cancel-subscription.test.js.
  • B3 cancelled. pending_payment stays.
  • B4 admin "Pending Payment" → "Payment setup incomplete" shipped 2026-04-29 (59d2be2).

Known gotchas (post-launch)

  • Admin edit does not sync Helcim recurringAmount. /admin/members/[id] PUT writes contributionAmount direct to Mongo by design. Admins must PATCH Helcim manually. The admin form already shows an --ember-bordered notice (commit e756170); a real sync flow is a future enhancement.
  • Cadence switch rejected on active subscriptions. server/api/members/update-contribution.post.js:206 refuses cadence changes mid-subscription with a TODO comment pointing here. No UI toggle exists on /member/account. Adding cadence switch requires a Helcim subscription replacement flow, not a plain update.
  • S2 test fixture id/slug mismatch (local dev only). Seeded S2 series has id: 'test-s2-drop-in-allowed' but slug: 'test-s2-drop-in-allowed-series'. Doesn't affect prod — fix the seed script if anyone re-runs fixtures.
  • /admin/series-management "Delete" button doesn't actually delete. Click handler iterates events to PUT-unlink each from the series, never calls DELETE /api/admin/series/:id. For an empty series the button is a no-op; for a series with events it just orphans them. Either rename to "Unlink events" or add the actual DELETE call. Surfaced by e2e/admin-series.spec.js (delete test skipped). Flagged 2026-04-30.
  • Past-deadline events and sold-out events render identically. EventTicketPurchase.vue falls through to "Event Sold Out" panel for both tickets.available.reason === 'Registration deadline has passed' and zero-stock cases. If "Registration closed" is meant to read differently from "Sold out," add a distinct branch. Flagged 2026-04-30 (no e2e written — gated on this UX decision).

Accessibility / a11y

  • Button minimum target size. Site-wide .btn renders ~35px tall. WCAG AA 2.5.8 (24×24) passes; AAA 2.5.5 (44×44) fails. Bumping padding affects every button — design call, not a drop-in fix. Flagged 2026-04-11.
  • /board color-contrast violations (WCAG AA). .block-label ("Offering" tag) and .slack-handle use #746a58 on #e8dfc8 → 4.01:1; AA needs 4.5:1 for small text. Surfaced by e2e/a11y.spec.js (the /board route fails; test is intentionally left red until fixed). Likely a single CSS variable adjustment. Flagged 2026-04-30.

Deferred features (own session each)

  • Email automation system. Patterned after Tranzac's implementation (separate project, already built). HTML email bodies with template management and drip sequences. Deferred 2026-04-20 — ruled wasted work given the larger system is designed elsewhere. Current transactional email lives in server/utils/resend.js + inline in server/api/auth/login.post.js, server/routes/oidc/interaction/login.post.ts, server/api/admin/{members,pre-registrants}/invite.post.js. Copy dump at docs/email-copy-dump.md. See memory: project_email_automation_future.
  • Receipts for event ticket purchases (Phase 2). Phase 1 receipts only cover membership payments. Event tickets — especially guest purchases without member accounts — need a receipt flow. Likely an emailed PDF/HTML receipt at purchase time. Build target: JuneOct 2026, live Jan 2027. See memory: project_receipts.
  • Series/event waitlist. Admin can configure tickets.waitlist.enabled and maxSize; server/utils/tickets.js returns waitlistAvailable: true when full; app/components/SeriesPassPurchase.vue:341 and EventTicketPurchase.vue have stub handleJoinWaitlist that toasts "Waitlist Coming Soon." No server endpoint, no confirmation email, no event_waitlisted activity hook. Either implement end-to-end or hide the button by removing the v-if="availability?.waitlistAvailable" branches in EventSeriesTicketCard.vue:175 and EventTicketCard.vue:73.
  • ASVS Phase 4. File-upload validation pipeline, granular RBAC, credential encryption.

Wave-Slack pilot follow-ups

  • /api/auth/member doesn't return slackInvited. Dashboard's Slack-coming note is gated on memberData.slackInvited, which is always undefined client-side, so the note shows for every active member regardless of state. Real bug. Add slackInvited (and slackInvitedAt) to the auth/member response. Surfaced by wave-slack §7.2 e2e (skipped pending this fix). Flagged 2026-04-30.
  • Admin members list row mutation isn't reactive. markSlackInvited in app/pages/admin/members/index.vue does Object.assign(member, res.member) on a plain object inside a useFetch array; Vue doesn't react, so the "Mark as Slack invited" button stays visible until a manual reload. Fix: members.value[i] = { ...members.value[i], ...res.member } or splice. Detail page uses the right pattern (covered by §6.6). Surfaced by wave-slack §6.2 e2e (skipped pending this fix). Flagged 2026-04-30.
  • Deprecated slackInviteStatus field still serialized. Removed from UI but still on Member documents and the /api/admin/members payload. Project it away in the API response and run a one-shot $unset cleanup. Surfaced by wave-slack §6.7 e2e. Flagged 2026-04-30.
  • Spec vs shipped-UI mismatch on wave language. docs/specs/wave-based-slack-onboarding-tests.md §7.5 asserts "no wave/cohort/batch language" in the dashboard note, but the shipped welcome-email and dashboard copy say "monthly onboarding waves." Decide which side wins; update the other.
  • E2E coverage for e2e/wave-slack-onboarding.spec.js. 9 of 16 scaffolded tests now passing (admin Slack-invited button + non-trivial dashboard cases). 7 remain skipped pending the bugs above (7.2, 6.2), seeding gaps (7.4 — no dev endpoint to mint members of arbitrary status), Open Questions (7.8, 6.9), or spec-vs-UI conflicts (7.5, 6.7).
  • Pilot exit decision (~8 weeks post-launch). Either restore server/_archive/utils/checkSlackJoins.js + its plugin if polling is needed, or delete the archive permanently. Driven by whether the manual-invite cadence is sustainable post-pilot.
  • slack_invite_failed enum slug cleanup. Detector and alert removed in d15458b, but the slug remains in server/models/adminAlertDismissal.js enum so historical dismissal rows continue to validate. Full removal needs a one-shot cleanup of stale dismissal rows in the DB. Roll into a future schema-tidy pass.

Simplify-pass follow-ups (still open)

Items surfaced during the 2026-04-29 /simplify review. The 2026-04-30 small-wins batch shipped 3 items (STATUS_LABELS dedup, ImageUpload focus, signupBridge rename). Remaining:

  • Extract .tint-candle / .tint-ember utility classes. The color-mix(in srgb, var(--candle) 15%, transparent) + matching border pattern is now inlined as style="" in ~9 sites across EventSeriesTicketCard.vue, SeriesPassPurchase.vue, NaturalDateInput.vue, ImageUpload.vue. Promote to utility classes in app/assets/css/main.css so future tints don't keep multiplying inline styles (and so :hover / :focus variants are reachable).
  • Audit member && truthy checks in sibling ticket/subscription routes. Commit f66455e fixed server/api/events/[id]/tickets/available.get.js:115 to use hasMemberAccess(member). Same anti-pattern likely exists in adjacent routes (tickets/purchase.post.js, subscription endpoints). Guests/suspended/cancelled members would currently look like full members for any feature gated on truthiness alone.
  • STATUS_LABELS dedup — verify. The 2026-04-30 small-wins batch claimed STATUS_LABELS dedup, but e2e/admin-members.spec.js expansion found an inline copy still at app/pages/admin/members/index.vue:491 and another at app/pages/member/account.vue:420. Either the previous dedup was partial or a new copy was reintroduced — confirm and finish dedup into a shared constants module.
  • app/pages/admin/members/[id].vue status select still hand-written. Commit 441a5f5 aligned the index page's status <select> to STATUS_LABELS, but the detail page ([id].vue) still hand-codes raw status options. Refactor to drive from the same constant.

Optional / low-priority

  • Welcome-email Slack-timing mention. Currently the welcome email doesn't mention Slack timing — the dashboard carries that note. Could add a one-line "Slack invitation comes in monthly waves — there may be a short wait" if the dashboard turns out not to be enough signal.

E2e infrastructure gaps

Surfaced during the 2026-04-30 e2e expansion. None block a green suite, but each blocks specific coverage from being added.

  • Other email routes still send real emails in dev mode. The ALLOW_DEV_TEST_ENDPOINTS short-circuit was added to server/api/admin/pre-registrants/invite.post.js (which calls new Resend(...) directly), but the five wrapper functions in server/utils/resend.js (event registration, cancellation, waitlist, series pass, welcome) still dispatch live. Either add the same gate to each wrapper, or refactor the wrappers into a single sendEmail({ from, to, subject, text, html }) helper holding the gate centrally — would also dedupe ~5 near-identical try/catch blocks.
  • No dev endpoint to seed members of arbitrary status. Wave-slack §7.4 (note hidden for suspended/cancelled/guest) is gated on this. /api/dev/test-login only mints an active admin. A minimal /api/dev/members.post accepting { email, status, slackInvited, ... } would unblock many more dashboard-state e2e tests.
  • SSR useFetch blocks page.route mocking. Page-level fetches in [slug].vue files run during SSR and can't be intercepted client-side. Affects: hidden-event 404 e2e, any test that needs a mocked event payload before client hydration. Either expose a client-side fetch alternative, add a server-side test mock layer, or accept that DB seeding is required for these cases.
  • Self-cancel block on paid event registrations not e2e-tested. Requires seeding a logged-in member with a paid registration row. Out of scope for this round.
  • Visual snapshot for join — desktop is stale. 12,676px diff (2% of image) from layout drift. Regenerate via npx playwright test --update-snapshots e2e/visual/pages.spec.js once a designer eyeballs the diff.
  • E2e cross-file races on admin specs. With fullyParallel: false + workers: 4 + retries: 1, ~1 admin CRUD test still fails per full-suite run (rotates between admin-events CRUD, board page-loads, and wave-slack §6.4). Each passes 100% in isolation. Root cause: tests anchor on "first row" / "any visible button" rather than uniquely-identified data, so they race when other admin specs mutate the shared dev DB. Proper fix is per-test data isolation: each test creates its own scoped record with a Date.now() suffix and queries by that exact identifier. Out of scope for the e2e expansion.

  • project_post_launch_backlog.md — high-level digest of this file
  • project_launch_readiness.md — cutover status (NOT YET happened)
  • project_launch_flow_map.md — onboarding flow + Slack wave model
  • project_pre_registrants.md — invitation system + pre-reg lifecycle
  • project_helcim_plan_model.md — cadence-keyed plan model
  • project_contribution_amount_redesign.md — arbitrary $ amount + guidance presets
  • project_receipts.md — Phase 1 done, Phase 2 pending
  • project_email_automation_future.md — Tranzac reference for full system