Publishing Studio — Improvement & Growth Plan
Package: capell-app/publishing-studio · Kind: package · Tier: premium · Product group: Capell Publishing Pro · Bundle: publishing-pro · Status: Complete
1. Snapshot
Section titled “1. Snapshot”Publishing Studio is Capell’s flagship editorial-workflow package: copy-on-write workspaces (drafts) over any registered Draftable model, atomic versioned publish, rebase/stale-detection, rollback + entity restore, signed live preview links, an approval chain, a durable scheduler (publish / unpublish / embargo / review-reminder events), an editorial calendar that aggregates contributors (Blog, Campaign Studio, Newsletter, Events), and four admin pages (Workflow command centre, Activity trail, Scheduled publishing, Stale drafts) plus two Filament resources (WorkspaceResource, PreviewLinkResource) and several dashboard widgets/Livewire panels. It is large and mature: 242 src/ PHP files, 132 test files, surfaces ["admin","console"]. Core engine lives in root-namespaced classes (Publisher, Rebaser, Rollback, ReleaseWindowGuard) plus ~45 Actions; data models are Workspace, Version, PublishingRevision, PreviewLink, SchedulerEvent, SchedulerDelivery, SchedulerIcalToken, WorkspaceApproval, WorkspaceFieldComment, WorkspaceReviewAssignment over 12 migrations (workspaces, versions, preview_links, publishing_revisions, publishing_scheduler_events, approvals/comments/assignments, plus workspace_id/shadowed_by_workspace_id columns added to core + external tables). Dependencies: capell-app/{admin,core,html-cache,migration-assistant,navigation} + jfcherng/php-diff. Marketplace summary now leads with the review-first CMS value proposition, and the manifest declares the extension card plus populated editor/reviewer workflow captures. Empty workflow, preview-link, scheduled-publishing, and stale-draft captures were demoted until seeded recapture.
2. Improvements (existing functionality)
Section titled “2. Improvements (existing functionality)”Prioritized.
- Closed 2026-06-05: Co-locate publish-check + release-window config defaults inside the package. — Shipped
config/publishing-studio.php, merged it ascapell.publishing-studio, published it for console installs, and included default publish checks, release-window settings, scheduled publishing, prune schedule, and preview home-route config. —config/publishing-studio.php,src/Providers/PublishingStudioServiceProvider.php— M - Done/Shipped: Make the manifest health check actually verify something.
PublishingStudioHealthChecknow reports real diagnostics for required workflow/revision/scheduler tables and configured publish-readiness checks, with focused health coverage. —src/Health/PublishingStudioHealthCheck.php,tests/Feature/Health/PublishingStudioHealthCheckTest.php— M - Closed 2026-06-05: Fix the
load-testingcapability so it works on a real install.LoadTestPublishingStudioCommandnow resolves a package-owned fixture model fromsrc/Support/LoadTesting, so the advertised command no longer depends ontests/autoload. —src/Console/Commands/LoadTestPublishingStudioCommand.php,src/Support/LoadTesting/WorkspaceDraftableFixture.php— S - Done/Shipped:
broken-linksis clarified as internal link integrity. The legacy identifier remainsbroken-linksfor config compatibility, but the surfaced label and messages now say “Internal link integrity” and explicitly validate only internal Capell URLs againstpage_urls; external URLs are ignored until a future opt-in HTTP probe exists. —src/Checks/BrokenLinkCheck.php,resources/lang/en/workspace.php,tests/Integration/Checks/IndividualChecksTest.php— M - Done/Shipped: legacy scheduled-publish job is a durable scheduler shim.
PublishScheduledPublishingStudioJobno longer runs a separate Workspace scan-and-publish loop. It delegates toRunDueSchedulerEventsAction::run(includePublish: true), so publish, unpublish, and reminder execution all flow through claimedSchedulerEventrows andExecuteSchedulerEventAction. —src/PublishScheduledPublishingStudioJob.php,src/SchedulePublishAction.php,tests/Integration/PublishScheduledPublishingStudioJobTest.php— M - Done/Shipped: central publish-readiness data boundary.
BuildPublishReadinessActionreturnsPublishReadinessDatafrom the dry-run report, URL collisions, stale conflicts, and publish-check failures.ValidateActionnow consumes that typed boundary instead of interpretingDryRunReportdirectly, and dry runs can respect release-window bypasses for authorized callers. —src/Actions/BuildPublishReadinessAction.php,src/Data/PublishReadinessData.php,src/Publisher.php,src/Filament/Resources/PublishingStudio/Actions/ValidateAction.php,tests/Integration/PublisherDryRunTest.php— M - Closed 2026-06-05: Populate the CHANGELOG. The changelog now records marketplace value-copy/media expansion, real health diagnostics, manifest settings/table reconciliation, and the production-autoloadable load-test fixture. —
CHANGELOG.md— S
3. Missing Features (gaps)
Section titled “3. Missing Features (gaps)”Tied to capabilities[] and editorial-workflow norms.
- Field-level merge / three-way diff resolution (differentiator).
Rebasersurfaces conflicting uuids and supports only coarsekeep-mine/take-liveper record; there is no field-level merge when two editors touch different fields of the same record. Statamic/Sanity-class tools resolve at field granularity. This is the single biggest differentiator available given the diff infrastructure (WorkspaceDiffService,jfcherng/php-diff) already exists. —src/Rebaser.php,src/Services/WorkspaceDiffService.php - Scheduled-content calendar drag-to-reschedule (table stakes for “editorial calendar”). The calendar (
ContentSchedulerCalendarWidget,BuildEditorialCalendarEventsAction) aggregates and displays events but reschedule still goes throughSchedulerMetadataActionforms. Inline drag-reschedule is expected of an editorial calendar. - Approval workflow configurability beyond numeric levels.
Workspace::approve()usessettings.requiredApprovalLevels(default 2) — purely a count. Norms include named stages, per-role required approvers, and per-content-type policies.ReviewPolicyResolver/RequiredReviewerexist but the public surface is a bare integer. —src/Models/Workspace.php,src/Approvals/ReviewPolicyResolver.php - Multi-site / cross-site coordinated publish. Release Workspaces group content for one atomic publish, but
siteId()derivation inSyncWorkspaceSchedulerEventsActionpicks the first page’s site; there is no first-class “publish this release to sites A+B” concept. Given Capell is multi-site, this is a notable gap. —src/Actions/SyncWorkspaceSchedulerEventsAction.php - Notifications breadth. Only
WorkspaceStateNotificationandWorkspaceReviewReminderNotificationexist. Editorial teams expect Slack/Teams/webhook fan-out and digest summaries (“3 drafts awaiting your approval”). Could cross-sell into a notifications/automation package. - Embargo on the public read path.
embargo_untilblocks publish, but there is no evidence of a public-render guard that hides embargoed-but-scheduled content if a stale cache or preview leaks it. Worth an explicit anonymous-safety test asserting embargoed content never renders to guests. - Diff/restore for media and layout blocks.
MediaDiffServiceexists, but entity restore (Rollback/EntityRollbackAction) is row-oriented; granular per-asset restore and layout-block-level rollback would round outentity-restore.
4. Issues / Risks
Section titled “4. Issues / Risks”- Closed 2026-06-05: Publish checks may be silently no-op (highest risk). Default config now resolves to the shipped publish checks, and
PublishCheckPipelineTestasserts the configured defaults are non-empty, implementPublishCheck, resolve through the container, and return pipeline results. —src/Checks/PublishCheckPipeline.php,tests/Integration/PublishCheckPipelineTest.php - Closed 2026-06-05: Manifest ↔ code drift.
capell.jsonnow declaresPublishingStudioSettings,database.settings: true, the package-owned storage table set, the extension card, desktop/mobile hero images, and runner-backed workflow captures.load-testingnow resolves a production-autoloadable fixture model. —capell.json,src/Settings/PublishingStudioSettings.php,docs/screenshots.json,src/Support/LoadTesting/WorkspaceDraftableFixture.php - Done/Shipped: admin query budget is asserted for dashboard and calendar Actions.
AdminQueryBudgetTestreadsperformance.adminQueryBudgetfromcapell.jsonand profiles the workflow command center, site stats, workspace activity, merge history, scheduler event builder, and editorial calendar builder against the 40-query ceiling. —capell.json,tests/Feature/Performance/AdminQueryBudgetTest.php frontendRenderBudgetMs: 0vs a real frontend surface. The package does serve frontend HTTP (signed preview render path throughResolveWorkspaceContext, plus the exit-preview route), yetsurfacesomits"frontend"and the render budget is 0. Preview rendering disables cache (config(['capell-core.disable_cache' => true])) so it is inherently slower than a cached page; document/measure the preview render path rather than declaring 0. —capell.json,src/Http/Middleware/ResolveWorkspaceContext.php- Concurrency: strong, keep it tested.
Publisherrow-locks the liveVersionand re-checks for a shifted live id (throwsStaleWorkspaceException);ExecuteSchedulerEventActionclaims events vialockForUpdate+ a 15-minute stale-claim reclaim; SQLite path is documented as serialised.PublisherConcurrencyTest,PublisherEmbargoTest,PublisherReleaseWindowTest,PublishScheduledPublishingStudioJobTestcover this. Risk is low — preserve these tests on any scheduler refactor. - Closed 2026-06-05: Public-output safety explicit coverage. Preview is signed (
URL::temporarySignedRoute) with a DB-backed revocable token as the authoritative key, signedcms_workspacecookie (hash_equals),userMayResolve()re-checksviewpolicy so revoked users lose context, and workspace responses setCache-Control: private, no-store. The live frontend test now proves an anonymous request to the live URL keeps rendering published copy while hiding embargoed draft copy, workspace names, preview markers, workspace cookies, and preview-only private/no-store headers. —src/Http/Middleware/ResolveWorkspaceContext.php,src/Actions/GenerateWorkspacePreviewUrlAction.php,src/Http/Controllers/SchedulerIcalFeedController.php,tests/Feature/Page/PageWorkspaceDraftTest.php - Scheduler
siteIdheuristic.SyncWorkspaceSchedulerEventsAction::siteId()takes the first workspace-scoped page’ssite_id; for a multi-site release workspace this is ambiguous and can misattribute calendar/feed scoping. —src/Actions/SyncWorkspaceSchedulerEventsAction.php - i18n. Labels are translation-key driven (
capell-publishing-studio::*) — good. Confirm scheduler/notification human strings and exception messages (several are hard-coded English inPublisher/Rebaser, e.g. “Workspace #%d must be approved…”) are acceptable as developer-facing only, not user-facing. —src/Publisher.php,src/Rebaser.php - Test-coverage gaps. Config-default publish-check resolution, embargo/live-path safety, and
LoadTestPublishingStudioCommandproduction-style autoload coverage are now present. Multi-site release scoping remains the notable missing dedicated test. Coverage is otherwise broad.
5. Marketplace & Selling
Section titled “5. Marketplace & Selling”Critique. The current summary and composer description are accurate but read as a feature list, not a value proposition — they enumerate verbs (preview/compare/approve/schedule/publish/restore/rollback) without naming the pain (publishing directly to live, no review trail, no safe rollback). The composer description (“Editorial workflow package for Capell revisions, scheduling, approvals, and controlled publishing”) is serviceable but generic.
Improved 1-sentence summary.
Publishing Studio turns Capell into a review-first CMS: edit any content in an isolated draft workspace, get sign-off, schedule it, and publish atomically — with one-click rollback when something breaks.
Improved 3–4 sentence description.
Publishing Studio is Capell’s premium editorial workflow. Every change happens in a copy-on-write workspace that stays invisible to visitors until it’s approved and published as a single atomic, versioned release — so the live site is never half-edited. Built-in publish-readiness checks (accessibility, SEO meta, alt text, internal links, URL collisions, stale-draft and release-window guards) stop risky publishes before they ship, while signed, revocable preview links let stakeholders review the exact pending state. When a publish goes wrong, roll back to any prior version or restore individual entities in seconds.
Screenshot / media status. The manifest now exposes the extension card plus the populated editor/reviewer workflow captures. Empty workflow dashboard, preview-link, scheduled-publishing, and stale-draft captures were demoted. The contract still asks for additional route/admin captures for live preview, compare/readiness, approval history, scheduler metadata, activity history, rollback/restore, and preview banner states; a 20-30s capture of the draft-to-approve-to-schedule-to-publish-to-rollback loop would still strengthen the listing.
Pricing / tier / bundle positioning. Correctly premium in the publishing-pro bundle — this is anchor-tenant functionality, not an add-on. Position it as the headline of the Publishing Pro bundle. Natural cross-sell follows the dependency + supports graph: migration-assistant (recovery/import into workspaces), html-cache (publish-time invalidation), and the soft contributors — blog, campaign-studio, newsletter, events — which feed the editorial calendar. Bundle messaging: “buy Publishing Pro, get a governed editorial calendar across every content type you publish.”
Differentiators / value props / target buyer. Differentiators: atomic multi-model release workspaces, durable scheduler with embargo + release windows, signed revocable previews, and true version rollback + entity restore — capabilities usually reserved for Statamic/Sanity/Contentful-tier products. Target buyer: agencies and in-house teams running multi-author, compliance-sensitive, or brand-controlled sites where publishing directly to production is unacceptable.
Keywords/tags (8–12). editorial-workflow, content-approval, draft-publishing, scheduled-publishing, content-versioning, rollback, live-preview, release-windows, publish-readiness, editorial-calendar, content-governance, workspaces.
6. Prioritized Roadmap
Section titled “6. Prioritized Roadmap”| Item | Bucket | Effort | Impact | Section ref |
|---|---|---|---|---|
Ship package config with publish-check + release-window defaults (mergeConfigFrom) | Done | M | High | §2, §4 |
| Test asserting default config resolves ≥1 publish check (guard against silent no-op) | Done | S | High | §4 |
| Done/Shipped: Reconcile manifest with code: declares settings, package-owned storage tables, expanded screenshots[], and production-autoloadable load-testing fixture | Done | S | High | §4 |
| Done/Shipped: Anonymous-safety test proves guests never see workspace markers or embargoed content on live path | Done | S | High | §4 |
| Done/Shipped: Populate CHANGELOG with real release history | Done | S | Med | §2 |
| Done/Shipped: Give the manifest health check real assertions (tables and checks resolvable) | Done | M | Med | §2 |
Done/Shipped: Fix load-test fixture to a production-autoloadable model | Done | S | Med | §2, §4 |
| Consolidate legacy publish job onto durable scheduler events | Done | M | Med | §2 — closed 2026-06-08: PublishScheduledPublishingStudioJob delegates to RunDueSchedulerEventsAction::run(includePublish: true), so scheduled publishing now uses durable claimed scheduler events. |
Central BuildPublishReadinessAction returning typed Data | Done | M | Med | §2 — closed 2026-06-08: BuildPublishReadinessAction returns PublishReadinessData, and ValidateAction consumes it for failure/warning/success notifications. |
Clarify/extend broken-links (rename or add opt-in external probe) | Done | M | Med | §2, §3 — closed 2026-06-08: surfaced copy now says “Internal link integrity”; the legacy broken-links identifier remains for config compatibility, and external URLs are explicitly ignored. |
Profile dashboard/calendar Actions against adminQueryBudget: 40; assert query ceiling in tests | Done | M | Med | §4 — closed 2026-06-08: AdminQueryBudgetTest profiles workflow, dashboard, scheduler, and editorial-calendar Actions against the manifest budget. |
| Recapture populated Publishing Studio workflow, preview-link, scheduled-publishing, and stale-draft screenshots before promoting them as product media | Later | S | Med | §5 — media-only follow-up; implementation scope is complete and promoted screenshots should wait for populated runner captures. |
| Field-level three-way merge in Rebaser (flagship differentiator) | Later | L | High | §3 |
| Configurable named approval stages / per-role required approvers | Later | L | Med | §3 |
| First-class multi-site coordinated release publish + correct calendar site scoping | Later | L | Med | §3, §4 |
| Calendar drag-to-reschedule + Slack/webhook + digest notifications | Later | L | Med | §3 |
| Granular media/layout-block restore | Later | L | Low | §3 |