feat+docs: public ticket system plan + dead UI cleanup#31
Open
feat+docs: public ticket system plan + dead UI cleanup#31
Conversation
9-phase TDD plan covering: - Contact entity + email-based guest dedupe - Public widget submission with email/name - WorkflowEngine executor wiring (routing goes live) - Outbound email (Message-ID threading) - Inbound email webhook (Postmark first) - Guest identity policy (unassigned / guest_user / prompt_signup) - Macros (repurposes dead Automations UI) Plan file: docs/superpowers/plans/2026-04-23-public-ticket-system.md
Phase 0 tasks 0.1-0.3 done on escalated-nestjs feat/public-ticket-system: - d401a83: install mail deps - 4ff1310: type mail/inbound/guestPolicy options - (latest): test factories Audit correction: Macro entity + service + controllers already exist in escalated-nestjs. Phase 7 scope reduced to insert_canned_reply + frontend-only work.
Phase 1 tasks 1.1-1.5 done on escalated-nestjs feat/public-ticket-system: - eab03f2: Contact entity with unique email index - 7258712: register Contact with TypeORM - 56e75fe: add nullable contactId to Ticket - 3574928: ContactService with dedupe + promoteToUser 138 tests passing (+11 new).
Public widget endpoint now works end-to-end for anonymous submitters: - Accepts email+name (or legacy requesterId) - Resolves/creates Contact record, dedupe by email - Applies guest policy (unassigned/guest_user/prompt_signup) - Rate-limited at 10 submissions/hour/email - Emits SIGNUP_INVITE event under prompt_signup (listener in Phase 4) Tests: 152 passing (+23 in Phase 2, +34 in the series).
Routing rules now fire on ticket.created, ticket.updated, ticket.assigned, ticket.status_changed, reply.created. Workflows evaluate conditions, dispatch actions, and log each evaluation. Actions shipped: change_priority, add_tag, remove_tag, change_status, set_department, assign_agent, add_note. Actions deferred (Phase 3b, to be scheduled): send_webhook, add_follower, delay, assign_round_robin, send_notification, set_type. Tests: 180 passing (+28 in Phase 3).
Outbound email infrastructure shipped: - message-id helpers for RFC 5322 threading - EmailService with 3 transactional templates (ticket_created, reply_posted, signup_invite) - EmailListener wired to TICKET_CREATED / TICKET_REPLY_CREATED / SIGNUP_INVITE - Conditional MailerModule registration (no-op without options.mail) - Errors caught per-event (never block ticket creation) Tests: 205 passing (+25 in Phase 4).
Inbound webhook endpoint accepts Postmark payloads, parses them, routes via priority order (In-Reply-To → signed Reply-To → subject ref → new ticket), and persists an audit row. Tests: 228 passing (+23 in Phase 5 — entity 3, parser 5, router 8, controller 3, guard 4).
Admin/Automations/ pages had no backend counterpart (audit finding in Phase 0). Workflows is the canonical admin automation system; Macros handles agent canned-action bundles. Removing the dead UI prevents 404 from the sidebar link. Files removed: - src/pages/Admin/Automations/Form.vue - src/pages/Admin/Automations/Index.vue Nav entries removed: - EscalatedLayout.vue sidebar item - AdminDashboard.stories.js item Phase 9 cleanup of the public ticket system plan.
2 tasks
Public ticket system plan is now complete end-to-end: Phase 0 — foundation (deps, config, factories) Phase 1 — Contact entity + service Phase 2 — public widget submission Phase 3 — Workflow executor + listener (routing is live) Phase 4 — outbound email with threading Phase 5 — inbound email webhook Phase 6 — runtime guest policy override via SettingsService Phase 7 — insert_canned_reply action + dead Automations UI removal Phase 8 — frontend already aligned (widget, Guest/Create, Macros UI) Phase 9 — docs (CHANGELOG, README, migration notes) Total: 232 tests passing (up from 100). 0 lint errors.
Surveys the 11 host-framework implementations to document which have guest/inbound support today, which design pattern they use (inline fields vs Contact entity), and a convergence path. Summary: 6 frameworks have Pattern A (inline guest fields on Ticket), NestJS now has Pattern B (separate Contact entity + FK), 5 frameworks have no public ticketing at all. Recommendation: ship NestJS PR as-is; defer convergence of the inline-field frameworks; backlog the 5 not-yet-implemented. Addresses the 'work on all frameworks' scope question from the Ralph loop — the honest answer was a status + recommendations doc since 11 parallel reimplementations aren't tractable in this PR.
All 6 Pattern A → B convergence PRs now open: - escalated-nestjs#17 (reference) - escalated-laravel#67 - escalated-rails#41 - escalated-django#38 - escalated-adonis#47 - escalated-dotnet#17 - escalated-wordpress#27 Remaining: Symfony / Filament / Phoenix / Go / Spring (greenfield).
Every framework in the ecosystem now has a PR for Pattern B: NestJS #17 (reference), Laravel #67, Rails #41, Django #38, Adonis #47, .NET #17, WordPress #27, Symfony #26, Go #26, Phoenix #29, Spring #20 (greenfield). Filament inherits via the Laravel package. Tracks follow-up work per framework (guest controller migration, outbound threading, workflow executor wiring, inline column deprecation) as backlog — reference impl is NestJS.
Every framework in the Escalated ecosystem now has Pattern B wired: Contact entity + FK + guest submission paths writing via findOrCreateByEmail. Repeat guest submissions dedupe across all frameworks. One caveat tracked in follow-up backlog: escalated-go ships Contact dedupe but doesn't set ticket.contact_id yet (SELECT scan projection is a SQL-churn follow-up). Follow-up work (per framework, not blocking this rollout): - go: Ticket contact_id projection through SELECT scans - all except NestJS: Message-ID threading, Workflow executor wiring - all: drop inline guest_* columns after dual-read cycle - spring: inbound email webhook (greenfield — no prior guest support)
- .NET: string constants (DecideAction doesn't return an enum) - Spring: TicketPriority import path + ContactRepository ctor arg + Mockito - Django: ruff format - Symfony: dedicated ticket.priority_changed dispatch (matches NestJS event model)
Each framework (.NET, WordPress, Phoenix, Spring) now has a 3-PR stack: executor → runner → listener. 12 new PRs total. The chain is functionally complete end-to-end (event → listener → runner → engine+executor → WorkflowLog). Three of the four executor PRs have CI green; runners/listeners are stacked and won't run CI until their base merges.
Each framework now has a pure-function MessageIdUtil (or language- appropriate equivalent) with identical 4-method API covering RFC 5322 Message-ID generation/parsing + signed Reply-To (HMAC-SHA256/8 prefix) for inbound ticket-identity routing. ~150 tests total across the ecosystem. All CI green except Phoenix (no repo CI configured).
…er 56-63) Every outbound ticket notification now emits canonical RFC 5322 Message-IDs + signed Reply-To in every framework, stacked on the MessageIdUtil util PRs. 20 email-related PRs total (10 util + 10 wire-up).
… frameworks (iter 64-67) Laravel #70, Rails #45, Django #42, Adonis #50, WordPress #33. Each has a 5-priority resolution chain (In-Reply-To → References → signed Reply-To → subject → legacy). Forged signatures rejected with timing-safe comparison.
Documents the outbound Message-ID format changes per-framework so host maintainers know what to expect when upgrading. The legacy InboundEmail.message_id lookup (strategy #5 in every inbound verify PR) covers the gap for replies threaded off pre-migration emails.
… frameworks (iter 71-75) .NET #23, Spring #26, Go #29, Phoenix #35, Symfony #30. Ticket-identity routing is now complete across all 10 host frameworks: every outbound carries canonical Message-IDs + signed Reply-To, and every framework has the resolution chain to route inbound mail back to the right ticket. Follow-ups per greenfield framework: provider parsers + webhook controllers + full orchestration service.
… stacks (iter 76-80) Every framework in the ecosystem can now receive inbound webhook mail and route it to the right ticket. Postmark is the reference parser; additional providers register via the framework-native discovery pattern (DI / @component / @TaggedIterator / etc.).
…1-83) Host maintainers can now point either Postmark or Mailgun at /escalated/webhook/email/inbound without writing any custom adapter code. 10 Mailgun PRs total (5 frameworks × 2 PRs — parser + test).
…ters (iter 92-94)
…field frameworks (iter 99-102)
…ld frameworks (iter 103-106)
…eworks (iter 108-111)
…rameworks (iter 114-116)
The rollout-status doc records WHAT shipped; this companion doc
records the ORDER PRs should merge in so stacked chains don't
conflict. Covers:
- Per-greenfield-framework chain tables (5 frameworks × ~12 PRs)
with merge-top-to-bottom rows keyed by layer.
- Recommended 5-step sequence per framework (Contact first,
then Workflow triplet, then MessageIdUtil chain, then
inbound chain, then observability).
- Legacy host-app PRs (Laravel/Rails/Django/Adonis/WordPress/
Symfony) with Contact / Workflow / MessageIdUtil / email
wire-up / inbound-verify links.
- Guest-policy admin UI per-framework PRs.
- Shared frontend, docs, and NestJS reference PRs.
- "What to do when you pick this up" — a 4-step procedural
guide for whoever drives the merge train.
As of iter 116: 110 open PRs across 14 repos, all feature work
complete, awaiting review. This guide exists so the merge sequence
is in one place for future human or agent to work from.
Adds the iter 118-121 NestJS reference catch-up to the rollout-status doc and extends the merge-order guide with the new 6-PR NestJS chain (Mailgun + SES + AttachmentDownloader + parser-equivalence). The NestJS reference is no longer behind the greenfield ports on inbound features — all 14 framework implementations (reference + 5 greenfield + 6 legacy host-app + 2 frontend/docs) now share the same inbound pipeline architecture.
Automations is NOT dead — it's a time-based admin rules engine that
exists in the backend for 7/11 host plugins (Laravel, Rails, Django,
Adonis, WordPress, .NET, Spring). The prior "no backend" audit was
only accurate for the NestJS reference, which hasn't ported
AutomationRunner yet.
Correct taxonomy:
Admin tools (both rules engines)
- Workflows = event-driven (fire on ticket.created, reply.created, …)
- Automations = time-based (cron scans open tickets matching
hours_since_created / hours_since_updated / etc.
conditions; executes actions on matches)
Agent tools
- Macros = manual, one-click action bundles applied to a specific
ticket by a human
The two admin engines answer different questions and neither subsumes
the other (silence doesn't emit events, so Workflows can't auto-close
stale tickets). Restoring the Automations UI + sidebar link so hosts
that have the backend keep working. Follow-ups port the backend to
NestJS / Symfony / Phoenix / Go.
This reverts commit 0b3cc25.
mpge
added a commit
to escalated-dev/escalated-developer-context
that referenced
this pull request
Apr 25, 2026
Adds three new top-level surfaces:
- glossary.md — one-liners for Escalated-specific terms, with the most
attention paid to ambiguous ones (Workflow / Automation / Macro,
Requester / Contact / guest_*, Message-ID / signed Reply-To).
- domain-model/ — canonical explainers for core concepts:
workflows-automations-macros.md (the three-surface taxonomy)
ticketing-model.md (Ticket / Reply / Contact / SLA)
guest-policy.md (the 3 modes)
email-threading.md (outbound headers + inbound 5-priority chain)
- decisions/ — ADR-style records, dated, append-only:
2026-04-23-public-ticket-system.md
2026-04-24-admin-agent-tool-split.md
The decision-driving event was the Workflows/Automations/Macros confusion
in the public-ticket-system rollout (escalated-dev/escalated#31), where
an audit incorrectly framed Automations as 'dead UI' because the NestJS
reference doesn't have an AutomationRunner — but 7 of 11 host plugins
ship a working Automation backend. The 2026-04-24 ADR locks the correct
taxonomy.
Updates README.md to surface the new sections and adds a 'when to read
which' decision tree.
The original plan framed Phase 7 as 'repurpose dead Admin/Automations into Macros.' That framing was wrong — Automations is a separate admin tool (time-based, cron-driven) with a working backend in 7 of 11 host plugins. The audit that called the UI dead only observed the NestJS reference, which doesn't have an AutomationRunner yet. The corrected three-surface taxonomy: - Workflows = admin, event-driven (existing) - Automations = admin, time-based (cron; backend in 7/11 hosts) - Macros = agent, manual click (Phase 7 ships its admin UI) All three co-exist permanently. None is being removed or renamed. Locked in ADR escalated-developer-context/decisions/2026-04-24-admin-agent-tool-split.md. Implementation revert: d5fdb58 (this branch).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implementation plan for a public ticketing system (public form + inbound email + admin routing rules + outbound email threading + configurable guest identity policy).
File: `docs/superpowers/plans/2026-04-23-public-ticket-system.md` (~1600 lines, 9 phases)
The companion code PR lives at escalated-dev/escalated-nestjs#17.
What the plan covers
Test plan
Being executed iteratively via Ralph Loop.