Document Lifecycle — Improvement & Growth Plan
Package: capell-app/document-lifecycle · Kind: package · Tier: premium · Product group: Capell Operations · Bundle: operations · Status: Draft
1. Snapshot
Section titled “1. Snapshot”Document Lifecycle is an admin/database package that turns “controlled documents” (terms, privacy, policy, operational docs) into evidence-grade records: a registry (Document), immutable versioned publications with SHA-256 content hashes (DocumentPublication), and acceptance records that extend the existing legal_acceptances table (DocumentAcceptance). Behaviour lives entirely in Actions (RegisterDocumentAction, PublishDocumentAction, PublishDocumentFromPublishingRevisionAction, ResolveLatestDocumentPublicationAction, ComputeDocumentContentHashAction, RecordDocumentAcceptanceAction, plus status/health helpers); the admin surface (DocumentResource + two read-only relation managers) can register documents, publish manual versions, archive/restore records, and record authenticated-admin acceptances. A PublishingRevision::created listener auto-publishes when a registered document’s underlying model is published in Publishing Studio. Requires capell-app/core, capell-app/admin, capell-app/publishing-studio; supports capell-app/customer-portal via a portal self-service feed (DocumentLifecyclePortalSelfServiceItemProvider). Current marketplace summary: “Version-pinned acceptance evidence for controlled documents in Capell: register documents, publish hashed versions from admin or Publishing Studio, and record who accepted which version and when.” Screenshots: capell.json now declares the extension card plus 4 committed admin PNG captures from docs/screenshots.json.
2. Improvements (existing functionality)
Section titled “2. Improvements (existing functionality)”- Done/Shipped: model factories are real.
DocumentFactory,DocumentPublicationFactory, andDocumentAcceptanceFactoryexist underdatabase/factories, modelfactory()methods resolve them, and coverage exercises state helpers such asdraft,active,archived,metadata,document,fromRevision,acceptor,subject, andforPublication. —database/factories/*,tests/Feature/DocumentLifecycleFactoryTest.php - Done/Shipped: health check has a real diagnostic report.
DocumentLifecycleHealthCheck::report()delegates toBuildDocumentLifecycleHealthReportAction, which verifies the three required tables,legal_acceptancesextension columns, protected audit-table registration, morph map aliases, and the Publishing Studio revision listener. Coverage locks both the passing report and a missing-table failure. —src/Health/DocumentLifecycleHealthCheck.php,src/Actions/BuildDocumentLifecycleHealthReportAction.php,tests/Feature/DocumentLifecycleHealthReportTest.php - Done/Shipped: document status labels and badge colours are enum-driven.
DocumentStatusEnumimplements FilamentHasLabelandHasColor,DocumentResourceuses the enum for Select options and badge colours, and coverage verifies translated labels plusgray/success/warningcolours. —src/Enums/DocumentStatusEnum.php,src/Filament/Resources/Documents/DocumentResource.php,tests/Feature/DocumentLifecycleStatusActionTest.php - Done/Shipped: acceptance evidence is request-context-pure.
RecordDocumentAcceptanceActionaccepts explicitipAddressanduserAgentparameters and hashes those values independently of the live request; when callers omit them, the Action does not silently read request state. —src/Actions/RecordDocumentAcceptanceAction.php,tests/Feature/RecordDocumentAcceptanceTest.php - Done/Shipped:
legal_acceptancesrollback only reverses package-owned additions. The migration now tracks whether it added the publication/hash columns and package-named indexes, guards drops withSchema::hasIndex(...), and preserves legacy-owned columns/indexes with matching or differently named indexes. —database/migrations/2026_05_10_190868_03_extend_legal_acceptances_for_document_lifecycle.php,tests/Feature/ExtendLegalAcceptancesMigrationTest.php - Done/Shipped:
DocumentAcceptancemass-assignment is explicit. The evidence table model now declares a field-level$fillablelist instead of$guarded = [], matching the sibling models and keeping writes constrained even outside Actions. —src/Models/DocumentAcceptance.php - Index
Document.documentablefor reverse lookups used by the listener.PublishDocumentFromPublishingRevisionAction::documentForRevision()querieswhereIn('documentable_type', ...)->whereIn('documentable_id', ...). The migration indexesdocumentablevianullableMorphs(...idx)(good), but the hot path also->first()with no ordering — fine, yet confirm the composite type+id morph index is actually hit (it is, via the generated index). No change needed if verified; otherwise addindex(['documentable_type','documentable_id']). —database/migrations/2026_05_10_190868_01_create_document_lifecycle_documents_table.php:22— S - Document the auto-publish coupling. The
PublishingRevision::createdglobal listener firesPublishDocumentFromPublishingRevisionActionon every revision row app-wide and early-returns when no registeredDocumentmatches. That’s correct but invisible; one extra query per revision create. Note it indocs/overview.mdand consider a config flag to disable, for hosts that don’t use auto-publish. —src/Providers/DocumentLifecycleServiceProvider.php:146-153— S
3. Missing Features (gaps)
Section titled “3. Missing Features (gaps)”Mapped to capabilities[]: document-lifecycle, document-lifecycle-admin, document-lifecycle-acceptance, document-lifecycle-customer-portal-document-feed.
- Admin lifecycle actions (closed for the feasible Now slice). The admin now uses Action-backed create, publish-version, archive, restore, and record-authenticated-admin-acceptance flows, all gated through the existing
DocumentPolicyupdate/create checks and backed by package Actions rather than inline lifecycle writes. Void acceptance, bulk acceptance operations, and richer acceptor targeting remain part of the export/re-acceptance work below rather than this completed row. — ties todocument-lifecycle-admin. - Done/Shipped: retention / expiry policy. Controlled documents now have
review_due_atandexpires_attimestamps, the admin resource exposes both dates, and the package registers/schedulescapell:document-lifecycle:archive-expireddaily throughArchiveExpiredDocumentsActionso active expired documents automatically move to Archived. Portal reminder surfacing remains optional future UX polish. - Done/Shipped: re-acceptance enforcement and outstanding report foundation.
RequiresDocumentReAcceptanceActionanswers whether a subject is missing the latest publication/hash, and the acceptances relation manager now exports an outstanding-acceptances CSV grouped by the latest known subject acceptance. Reminder scheduling remains Later depth. - Done/Shipped: acceptance evidence CSV export.
BuildDocumentAcceptanceEvidenceCsvActionexports a document acceptance ledger as CSV, and the acceptances relation manager exposes anExport evidence CSVheader action with an optional publication/version selector. Bulk publish/archive and PDF certificates remain separate Later depth. - Done/Shipped: signed JSON acceptance certificate.
BuildDocumentAcceptanceCertificateActionbundles the acceptance, document, publication hash, acceptor/subject, timestamp, IP/user-agent hashes, legal bundle values, and metadata into a JSON payload signed with an HMAC-SHA256 signature derived from the host app key. The acceptances relation manager exposes a row-levelDownload signed JSONaction for exportable proof bundles. PDF certificates remain optional future polish rather than a remaining roadmap requirement. - Done/Shipped: public frontend surface is reconciled. The package manifest intentionally declares only
adminandconsolesurfaces; the Customer Portal self-service feed remains an authenticated supported integration, not package-owned anonymous public output. Public consent widgets can still be built by host apps using package Actions. - Done/Shipped: diagnostics health report (see §2).
DocumentLifecycleHealthCheck::report()now exposes a real install/registration report for Diagnostics consumers. - Done/Shipped: per-version diff / content snapshot. New publications now store a normalized
content_snapshotandcontent_snapshot_formatin publication metadata, including Publishing Studioafter_payloadsnapshots.BuildDocumentPublicationDiffActionexports a JSON diff against the previous publication, and the publications relation manager exposes aDownload diff JSONrow action. Existing historical publications without snapshots honestly reportsnapshot_available: falsein the export.
4. Issues / Risks
Section titled “4. Issues / Risks”- Done/Shipped: manifest frontend surface is reconciled.
surfacesis now["admin","console"],providers.frontendis empty, and overview docs state that the Customer Portal feed is authenticated self-service data rather than package-owned public frontend output. —capell.json,docs/overview.md,tests/Feature/DocumentLifecycleManifestTest.php - Done/Shipped: critical health check asserts install and wiring state.
DocumentLifecycleHealthCheck::report()returns a typed report for table, column, protected-table, morph-map, and revision-listener wiring; package tests cover success and failure cases. —src/Health/DocumentLifecycleHealthCheck.php,src/Actions/BuildDocumentLifecycleHealthReportAction.php,tests/Feature/DocumentLifecycleHealthReportTest.php - Done/Shipped: factories resolve from models.
database/factoriescontains factories for all three models and tests proveDocument::factory(),DocumentPublication::factory(), andDocumentAcceptance::factory()resolve correctly. —composer.json,database/factories/*,tests/Feature/DocumentLifecycleFactoryTest.php - Done/Shipped: off-request acceptance evidence can be explicit. Queue/console callers can pass
ipAddressanduserAgent; the Action no longer reaches into the current request behind the caller’s back. —src/Actions/RecordDocumentAcceptanceAction.php,tests/Feature/RecordDocumentAcceptanceTest.php - Done/Shipped: asymmetric migration rollback is fixed. The rollback path now guards and ownership-checks package-added columns/indexes, including legacy pre-existing table cases. —
migrations/...03_extend_legal_acceptances...php,tests/Feature/ExtendLegalAcceptancesMigrationTest.php - PHP constraint drift.
composer.jsonrequiresphp: ^8.3while the Capell baseline is PHP 8.4. Harmless (broader floor) but inconsistent with siblings; confirm intentional. —composer.json - Done/Shipped: portal feed is scoped to the current portal account. The Customer Portal self-service test creates a second acceptance for another portal account id and verifies only the current account’s document item is returned. The feed still exposes acceptance/publication ids to the owner of the record, which is acceptable for authenticated account history. —
src/Support/CustomerPortal/DocumentLifecyclePortalSelfServiceItemProvider.php,tests/Feature/CustomerPortalDocumentFeedTest.php - Done/Shipped: admin query budget is defended. The admin index now has regression coverage that renders
ListDocumentswith publication counts and asserts select queries stay withinperformance.adminQueryBudget. —capell.json(performance),tests/Feature/DocumentLifecycleAdminTest.php - Done/Shipped: locale coverage extends beyond English.
resources/lang/es/navigation.phpmirrors the package navigation/action/message keys so admin and portal strings have a second bundled locale. Portal feed and admin strings remain translation-keyed. - Test gaps (covered vs not): Covered — publish/hash/immutability (
DocumentPublicationTest), revision listener + documentable repointing, acceptance recording + config fallback and explicit IP/UA hashing (RecordDocumentAcceptanceTest), portal feed scoping + manifest (CustomerPortalDocumentFeedTest), admin visibility/forbidden/permitted/query budget (DocumentLifecycleAdminTest), schema + legacy-table adoption + protected-table/morph registration (DocumentLifecycleSchemaTest), nav + manifest contribution, health report behavior, factory usage, migration rollback, and enum labels/colours. Not covered — bulk evidence export, re-acceptance reporting, retention/expiry command execution, signed evidence JSON download, and content snapshot/diff export.
5. Marketplace & Selling
Section titled “5. Marketplace & Selling”Critique. The manifest summary and composer description are identical-ish and feature-list-flat (“registry, publication metadata, hashes, and acceptance evidence”). They state what is stored, not what the buyer gets (defensible compliance evidence). Neither mentions the actual differentiator: tamper-evident, version-pinned proof of which document version a user accepted, wired automatically to Publishing Studio. Composer description is also truncated vs the manifest (drops “for Capell CMS”).
Improved 1-sentence summary:
Tamper-evident proof of which document version was published and who accepted it — controlled documents, immutable hashed versions, and a court-ready acceptance ledger for Capell.
Improved 3–4 sentence description:
Document Lifecycle turns terms, privacy, and policy documents into auditable records: every publication is version-pinned and content-hashed, and every acceptance captures the exact version, hash, actor, and timestamp. It hooks into Publishing Studio so a published revision automatically becomes an immutable document publication — no custom audit layer required. Acceptance evidence surfaces in the admin and (with Customer Portal) in each account’s self-service history, and the tables are registered as protected so the audit trail can’t be casually deleted. Built for compliance-sensitive Capell sites that must answer “which version did this user agree to, and when?”
Screenshot/media status. Manifest declares the extension card plus 4 committed admin captures from docs/screenshots.json (index, edit, publications RM, acceptances RM). Remaining optional media depth is a video/GIF of the auto-publish-on-revision flow, which is the most compelling demo.
Pricing / tier / bundle positioning. premium in the operations bundle, Capell Operations group — appropriate; compliance/governance is a willingness-to-pay feature. Strengthen by cross-selling on dependencies: hard-requires publishing-studio (anchor the “auto-publish” story to it), supports customer-portal (the acceptance-history feed is a portal upsell). Natural Extension-Suite framing: an “Operations / Compliance” suite = Document Lifecycle + Publishing Studio + Diagnostics + Password Policy (already the README “Best Used With” set). The retention/reminder/e-signature features in §3 justify the premium tier and a possible add-on tier.
Differentiators / value props / target buyer. Differentiator: content-hash immutability + automatic version pinning from Publishing Studio + protected audit tables — most CMS “terms acceptance” is an unversioned boolean. Target buyer: ops/compliance/legal owners of regulated or contract-heavy Capell sites (memberships, healthcare, finance, B2B SaaS portals) who need defensible “who accepted what version when” evidence.
Keywords/tags (8–12): document-lifecycle, compliance, audit-trail, terms-of-service, consent-management, content-hash, versioning, legal, governance, acceptance-evidence, customer-portal, publishing.
6. Prioritized Roadmap
Section titled “6. Prioritized Roadmap”| Item | Bucket | Effort | Impact | Section ref |
|---|---|---|---|---|
Ship model factories; fix/remove database/factories autoload | Done | S | High | §2, §4 |
Real health-check report() Action + health test | Done | M | High | §2, §3, §4 |
Reconcile frontend surface: drop it or ship safe consent component | Done | S→L | High | §3, §4 |
Capture 4 admin screenshots; sync marketplace.screenshots[] to manifest | Done | S | High | §1, §5 — closed 2026-06-06: four Capell runner PNG captures are committed and promoted into marketplace media. |
Make RecordDocumentAcceptanceAction request-context-pure (ip/UA params) | Done | S | Med | §2, §4 |
Fix asymmetric legal_acceptances migration down() + rollback test | Done | S | Med | §2, §4 |
Rewrite manifest summary + composer description | Done | S | Med | §5 — 2026-06-04: replaced court-ready/immutable phrasing with shipped admin, Publishing Studio, and acceptance-evidence wording; manifest test now locks composer/marketplace copy. |
| Admin lifecycle Actions (Publish / Archive / Record acceptance buttons) | Done | M | High | §3 |
Done/Shipped: Bulk export of acceptance evidence (CSV) per document/version. Evidence: BuildDocumentAcceptanceEvidenceCsvAction, acceptances relation manager header action, and document-lifecycle-evidence-export. | Done | M | High | §3 |
Done/Shipped: Re-acceptance enforcement + outstanding-acceptances report. Evidence: RequiresDocumentReAcceptanceAction, BuildOutstandingDocumentAcceptancesCsvAction, relation manager CSV action, and document-lifecycle-reacceptance-report. | Done | M | High | §3 |
| Portal-feed cross-account isolation negative test | Done | S | Med | §4 |
DocumentStatusEnum → HasLabels + badge colours | Done | S | Med | §2, §4 |
| Retention / expiry + review-due scheduled transition | Done | M | High | §3 — 2026-06-06: added review/expiry timestamps, admin fields/table columns, ArchiveExpiredDocumentsAction, a registered command, and a daily schedule. |
| Signed acceptance certificate (PDF/JSON proof bundle) | Done | L | High | §3 — 2026-06-06: shipped HMAC-signed JSON proof bundles through BuildDocumentAcceptanceCertificateAction and a row-level admin download action. PDF certificate rendering remains optional polish. |
| Per-version content snapshot + diff view | Done | L | Med | §3 — 2026-06-06: new publications store normalized snapshots in metadata, and admins can download JSON diffs between publication versions. |
Locale coverage beyond en; admin query-budget regression test | Done | S | Low | §4 — 2026-06-06: added bundled Spanish translations and a manifest-budgeted admin index query regression test. |
Row movement in the 2026-06-04 follow-up: Admin lifecycle Actions (Publish / Archive / Record acceptance buttons) and Fix asymmetric legal_acceptances migration down() + rollback test moved to Done. The remaining screenshot captures, exports, re-acceptance, retention/expiry, signed evidence, and content-diff rows stay outside the closed Now slice.