Files
HiveGrid-Wireframes/hivegrid-cloud/docs/feature-manifests/plugin-candidates.json
T

351 lines
19 KiB
JSON

{
"schemaVersion": "1.0",
"id": "plugin-candidates",
"title": "Plugin Candidate Feature Groups",
"classification": "plugin-candidate",
"status": "review-needed",
"owner": "core-pruning",
"description": "Feature groups with explicit extraction boundaries for future optional plugins or separately installable modules. Some are currently active in the versioned Product Core CRM SaaS showcase.",
"rationale": [
"These features are useful in some products but add product opinion, dependencies, or operational complexity beyond the neutral core.",
"A feature can be active in product-core today and still remain a plugin-candidate when the intended next boundary is optional extraction rather than deletion.",
"Extraction should happen after core dependency boundaries are verified.",
"Email Marketing remains a CRM-contact campaign plugin candidate; Email Sequences are documented separately as foundation/admin-preview work with production delivery follow-ups, and marketing-outbound is only a general handler foundation until worker bootstrap is added."
],
"codeAreas": [
"apps/web/app/modules/crm",
"apps/web/app/modules/emailMarketing",
"apps/web/app/modules/workflowEngine",
"apps/web/app/modules/knowledgeBase",
"apps/web/app/modules/portals",
"apps/web/app/modules/sites",
"apps/web/app/modules/analytics",
"apps/web/app/modules/metrics",
"apps/web/app/modules/events",
"apps/web/app/modules/ai",
"apps/web/app/modules/chatbot",
"apps/web/app/modules/helpDesk",
"apps/web/app/modules/onboarding",
"apps/web/app/modules/featureFlags",
"apps/web/app/modules/widgets",
"apps/web/app/modules/notifications",
"apps/web/app/modules/storage"
],
"dependencies": [
"core",
"auth-tenancy",
"rows-entities-runtime",
"transactional-mail",
"admin-app-shell"
],
"consumers": [],
"components": [
{
"id": "crm",
"title": "CRM",
"rationale": "Strong vertical opinion coupled to default company/contact/opportunity entities and email marketing.",
"codeAreas": [
"apps/web/app/modules/crm",
"apps/web/app/routes/admin/crm*",
"apps/web/app/routes/app.$tenant/crm*"
]
},
{
"id": "email-marketing",
"title": "Email Marketing and Newsletter",
"rationale": "Email Marketing is an extract-plugin CRM-contact campaign plugin candidate, not Core and not the Email Sequences foundation: campaigns, sender secrets, CRM-contact audience selection, tracking, and consent are product-specific. Current audiences depend on CRM contacts, so extraction must bundle CRM, declare a CRM plugin dependency, or introduce a plugin-owned audience/list abstraction while keeping transactional mail and the separate email-sequences admin-preview foundation separate.",
"codeAreas": [
"apps/web/app/modules/emailMarketing",
"apps/web/app/modules/emailMarketing/services/EmailMarketingService.ts",
"apps/web/app/modules/emailMarketing/db/emailSender.ts",
"apps/web/app/modules/emailMarketing/db/campaigns.db.server.ts",
"apps/web/app/modules/emailMarketing/db/outboundEmails.db.server.ts",
"apps/web/app/routes/admin/email-marketing*",
"apps/web/app/routes/app.$tenant/email-marketing*",
"prisma/schema/email.prisma"
]
},
{
"id": "portals-sites",
"title": "Portals and Sites",
"rationale": "D007 canonical report: docs/feature-decisions/portals-sites-decision-report.md. Action: extract-plugin. Product-platform tenant/admin surfaces with Stripe portal billing, analytics, domains, and module-owned persistence that should not remain default Boilerplate Core.",
"codeAreas": [
"apps/web/app/modules/portals",
"apps/web/app/modules/sites",
"apps/web/app/routes/admin/portals.tsx",
"apps/web/app/routes/app.$tenant/__portals/*",
"apps/web/app/routes/app.$tenant/sites/*",
"prisma/schema/portals.prisma",
"prisma/schema/sites.prisma"
]
},
{
"id": "knowledge-base",
"title": "Knowledge Base",
"rationale": "D009 canonical report: docs/feature-decisions/kb-widgets-workflow-marketing-decision-report.md. Action: extract-plugin. Support/docs product surface with public docs visibility and data boundaries, not a minimum SaaS boilerplate requirement.",
"codeAreas": [
"apps/web/app/modules/knowledgeBase",
"apps/web/app/routes/docs/*",
"apps/web/app/routes/admin/knowledge-base*",
"apps/web/app/routes/help/*",
"prisma/schema/knowledgeBase.prisma"
]
},
{
"id": "analytics-metrics",
"title": "Analytics and Metrics",
"rationale": "Product analytics UI and collection should be opt-in; minimal observability can remain separate.",
"codeAreas": [
"apps/web/app/modules/analytics",
"apps/web/app/modules/metrics",
"apps/web/app/routes/admin/analytics*",
"apps/web/app/routes/admin/metrics*",
"apps/api/src/app.ts",
"/v1/analytics/*"
]
},
{
"id": "events-webhooks-audit",
"title": "Events, Webhooks, Audit Trails",
"rationale": "Domain events may be useful for integrations, while browsers and entity webhooks can be optional.",
"codeAreas": [
"apps/web/app/modules/events",
"apps/web/app/modules/events/services/.server/EventsService.ts",
"packages/db/src/outbox.ts",
"apps/web/app/routes/admin/events*",
"apps/web/app/routes/admin/audit-trails.tsx",
"apps/api/src/app.ts",
"/v1/events/webhooks/attempts/:id"
]
},
{
"id": "ai-chatbot-prompt-mcp",
"title": "AI, Chatbot, Prompt Builder, MCP",
"rationale": "Provider cost, security, and product behavior make this a plugin surface.",
"codeAreas": [
"apps/web/app/modules/ai",
"apps/web/app/modules/chatbot",
"apps/web/app/modules/promptBuilder",
"apps/web/app/modules/mcp",
"apps/web/app/routes/admin/ai/*",
"apps/web/app/routes/admin/chat/*",
"apps/web/app/routes/app.$tenant/chat/*",
"apps/api/src/app.ts",
"/v1/ai/upload"
]
},
{
"id": "help-desk",
"title": "Help Desk",
"rationale": "Support-product surface that should couple to optional KB and ticket modules.",
"codeAreas": [
"apps/web/app/modules/helpDesk",
"apps/web/app/routes/admin/help-desk*"
]
},
{
"id": "onboarding-builder",
"title": "Onboarding Builder",
"rationale": "Useful for some SaaS products but not part of the minimum boilerplate.",
"codeAreas": [
"apps/web/app/modules/onboarding",
"apps/web/app/routes/admin/onboarding*",
"apps/web/app/routes/onboarding.$id.tsx"
]
},
{
"id": "feature-flags",
"title": "Feature Flags",
"rationale": "Advanced admin capability that should be reviewed as plugin or core only if needed by retained flows.",
"codeAreas": [
"apps/web/app/modules/featureFlags",
"apps/web/app/routes/admin/feature-flags*"
]
},
{
"id": "widgets",
"title": "Widgets",
"rationale": "Embedded widgets are product-specific and should be optional.",
"codeAreas": [
"apps/web/app/modules/widgets",
"apps/web/app/routes/app.$tenant/__widgets/*",
"apps/api/src/app.ts",
"packages/db/src/widgets.server.ts"
]
},
{
"id": "workflow-engine",
"title": "Workflow Engine",
"rationale": "D009 canonical report: docs/feature-decisions/kb-widgets-workflow-marketing-decision-report.md. Action: extract-plugin. Automation/workflow orchestration is a product-specific capability layered on core events, not a minimum SaaS boilerplate requirement.",
"codeAreas": [
"apps/web/app/modules/workflowEngine",
"apps/web/app/routes/admin/__workflows/*",
"apps/web/app/routes/app.$tenant/__workflows/*",
"apps/web/app/routes/internal/api/__workflows/*",
"prisma/schema/workflows.prisma"
]
},
{
"id": "notifications",
"title": "Notifications",
"rationale": "Notification admin surfaces are optional; product delivery uses the internal queue and database diagnostics.",
"codeAreas": [
"apps/web/app/modules/notifications",
"apps/web/app/routes/admin/notifications*",
"packages/db/src/notifications.server.ts",
"apps/worker/src/handlers.ts",
"apps/api/src/app.ts",
"/v1/notifications/stream"
]
},
{
"id": "storage-supabase",
"title": "Storage and Supabase",
"rationale": "File storage abstraction may be core, while Supabase provider integration should be optional.",
"codeAreas": [
"apps/web/app/modules/storage",
"@supabase/*"
]
}
],
"integration": {
"ownership": "hybrid",
"surfaces": {
"adminRoutes": [
"apps/web/app/routes/admin/* optional module routes"
],
"tenantRoutes": [
"apps/web/app/routes/app.$tenant/* optional module routes"
],
"publicRoutes": [
"optional public plugin routes"
],
"apiRoutes": [
"optional plugin API routes"
]
},
"configuration": {
"admin": [
"Plugin enablement, provider credentials, global policy"
],
"tenant": [
"Tenant-level plugin settings when enabled, rendered through the sandboxed PluginSettingsFrame contract with deny-by-default CSP and unexpected-navigation remounts",
"Manifest-declared plugin settings UI hosted from ui.settings.entry through PluginSettingsFrame; PluginUserConfig persists only caller-owned per-user settings behind app.plugins.use; lifecycle, grants and tenant-wide configuration remain behind app.plugins.manage.",
"Plugin capability-use audit trails remain an account audit surface and require app.settings.auditTrails.view instead of broad app.plugins.use.",
"Plugin settings HTML and per-user setting values are size-budgeted server-side; secret-like, reserved, unsafe and over-budget keys/values must be dropped or rejected before reaching iframe state or persistence.",
"Plugin settings iframe postMessage rejects and unexpected reloads create client-deduplicated, server-rate-limited plugin.settings_frame.violation audit events with redacted metadata only."
],
"environment": [
"Plugin-specific environment variables only when installed"
],
"secrets": [
"Plugin provider secrets must be admin/platform-owned unless explicitly tenant-owned."
]
},
"workers": {
"required": true,
"queues": [
"plugin-specific queues when needed",
"outbox.workflow",
"outbox.plugin-hook",
"outbox.plugin-capability-use",
"toolctrl.plugin-invoke"
],
"jobs": [
"plugin-specific jobs when needed",
"workflow-trigger fanout",
"workflow execution request drain",
"workflow definition hook fanout",
"workflow.executed hook fanout",
"plugin-hook fanout",
"plugin capability-use audit receiver",
"plugin invoke"
],
"notes": [
"Each extracted plugin must declare whether it is worker-backed and which queue group owns its jobs.",
"Email-sequence trigger/scheduler/producers are not plugin-candidate jobs in this manifest; they are tracked in docs/features/email-sequences.md as foundation/admin-preview work gated by MARKETING_SEQUENCES_ENABLED and remaining production follow-ups. Signed Mailjet bounce webhook ingestion is API-owned, not a plugin worker job.",
"The generic marketing-outbound handler foundation is documented in docs/features/marketing-funnel.md and must not be inferred as bootstrapped plugin delivery until apps/worker/src/index.ts wires a general subscriber.",
"Workflow trigger fanout producers should use the shared @toolctrl/queue outbox payload builder instead of hand-writing workflow.trigger.event envelopes.",
"Manual workflow runs publish workflow.execution.requested through the transactional outbox before worker-general starts the existing execution via the signed internal bridge or native executor; queued waitForInput continuations can resume natively when that wait boundary is the only native preflight gap.",
"Default outbox queue delivery must include receiver-level BullMQ attempts/backoff and deterministic jobId=outbox:<eventId> before marking the Postgres outbox row DONE, so transient subscriber failures are retried by the target queue and dispatcher redelivery does not create duplicate receiver jobs.",
"Plugin capability-use batches are produced by worker-owned plugin invocation and drained by worker-general through outbox.plugin-capability-use; receiver inserts must be idempotent with duplicate row ids ignored on outbox redelivery.",
"The worker-general runtime must pass through OUTBOX_DISPATCHER_ENABLED plus WORKFLOW_TRIGGER_OUTBOX_* and WORKFLOW_TRIGGER_EXECUTOR_* settings so outbox.workflow is either actively drained or fails startup in required mode; WORKFLOW_TRIGGER_OUTBOX_REQUIRED=true also requires the outbox dispatcher to start.",
"WORKFLOW_TRIGGER_EXECUTOR_MODE=native is opt-in and currently supports credential-free safe core event/manual/if/switch/doNothing/log/iterator/variable/alertUser/httpRequest paths plus tenant-scoped rowGet/email blocks and single-boundary waitForInput continuation resumes. Native and hybrid preflight must reject $credentials.*, lookup $credentials, and #with $credentials references before block execution; the worker-native executor must not hydrate stored workflow credentials. Iterator blocks execute the loopNext edge once per array item with context.iterator and then continue through loopEnd. httpRequest uses the shared outbound HTTP guard, email publishes mail.transactional.send through the transactional outbox for worker-mail delivery, while unsupported blocks and arbitrary conditional edges outside if/switch/iterator must persist an errored execution and fail loud instead of falling back to HTTP.",
"WorkflowExecution keeps query-shape indexes for tenant-scoped execution lists, workflow execution histories, and status/type filtered admin views while workflow runtime remains a plugin-candidate surface."
]
},
"externalProviders": [
"plugin-specific providers"
],
"permissions": [
"admin.plugins.*",
"app.plugins.*"
],
"auditEvents": [
"plugin.enabled",
"plugin.disabled",
"plugin.auto_disabled",
"plugin.capabilities.revoked",
"plugin.configuration.updated",
"plugin.invoke.completed",
"plugin.invoke.failed",
"plugin.invoke.host.log",
"plugin.invoke.host.audit"
],
"billingImpacts": [
"Plugins may introduce plan gates or usage meters and must declare them before implementation."
],
"uat": [
"Extraction plan defines how the plugin can be disabled without breaking core.",
"Extraction plan defines whether plugin surfaces disappear, redirect, or show unavailable states when disabled.",
"Plugin dependencies are explicit.",
"Capability grants require an explicit capability-review acknowledgement and a manifest/version/scope digest across UI and API entry points.",
"Capability grant, revoke, enable, disable, and per-user settings mutations commit atomically with their audit outbox event so plugin authorization and configuration cannot be left half-applied.",
"Settings UIs use the sandboxed iframe contract, deny frame navigation, remount after unexpected iframe loads, and persist only non-secret per-user configuration through the reviewed PluginUserConfig path.",
"Settings iframe messages use the SDK-exported plugin.settings.* protocol contract, bind request identifiers and keys, and persistent per-user settings writes are rate-limited per tenant/user/plugin.",
"Plugin manifests bound and deduplicate allowedHosts, capability names and hook entrypoints so grants and fanout remain reviewable.",
"Plugin hook payloads have SDK-level exported types matching the runtime registry; examples use canonical entity/workflow payload fields so plugin authors do not rely on stale ad-hoc shapes. Terminal workflow execution hooks include optional nullable userId for actor-aware plugins while accepting legacy payloads without userId.",
"Tenant-scoped service-path workflow definition create/update/delete and builder graph mutations emit metadata only: tenantId, workflowId, userId, status, appliesToAllTenants, update changedFields, and occurredAt; platform-scoped definitions and template-import flows remain out of scope unless routed through the reviewed workflow service path."
],
"runtime": [
"Production install, grant, and enable paths must reject capabilities without reviewed host bindings; currently only log and audit are production-available plugin sandbox host capabilities.",
"Worker plugin invocations must enforce current TenantPluginCapabilityGrant rows before loading binaries, consuming rate-limit budget, or entering the sandbox; manifest-declared capabilities alone are not sufficient runtime authorization.",
"Plugin invocation verifies stored install metadata (Plugin.wasmSha256 and Plugin.signaturePublicKey) still matches the manifest before signature verification, capability checks, or sandbox entry.",
"Plugin signatures cover the canonical manifest payload plus wasmSha256 through the shared @toolctrl/plugin-runtime signing helper so manifest capability, hook, or allowedHost changes require re-signing.",
"Plugin invocation must run inside the worker-owned sandbox boundary with memory, host-fn fuel and PLUGIN_MAX_WALL_CLOCK_MS limits; true per-instruction fuel remains a Wasmtime/runtime-upgrade concern."
]
},
"boundaries": {
"mustKeep": [
"Core dependencies these plugins rely on",
"Data migrations required by retained core features until plugin extraction is implemented"
],
"mayMoveToPlugin": [
"All listed component feature groups after dependency review",
"Future portal/site-builder plugins may declare affiliate integrations explicitly, but none are active by default."
],
"mustNotTouch": [
"Routes, navigation, chatbot code, and historical affiliate migrations unless an owning cleanup task explicitly scopes them"
]
},
"pruning": {
"phase": "1",
"action": "extract-plugin",
"notes": [
"This manifest is a planning bundle. Individual plugin manifests can split out when a candidate is actively extracted.",
"Do not remove shared runtime dependencies until consumers are audited."
]
},
"validation": {
"source": [
"docs/feature-inventory.md",
"docs/architecture-migration-status.md",
"docs/feature-decisions/crm-decision-report.md",
"docs/feature-decisions/email-marketing-decision-report.md",
"docs/feature-decisions/portals-sites-decision-report.md",
"docs/feature-decisions/kb-widgets-workflow-marketing-decision-report.md"
],
"lastReviewed": "2026-05-27"
}
}