Skip to content

Package Documentation Standard

Capell package docs have two jobs: help a developer change the package safely, and help an owner understand why the package belongs in a Capell build. Keep both jobs visible.

Every package needs both a non-technical overview and a developer deep dive. They can live in one README when the package is small, but the distinction should be obvious.

ReaderWhat they need
Site owner, buyer, or operatorWhat workflow the package adds, which bundle it belongs to, which screens or public routes appear, and what operational risk it reduces.
Editor or admin userWhat they can create, review, approve, publish, inspect, recover, or hand off without custom development. Editors read docs/overview.md and, for Tier 1 and Tier 2 packages, docs/admin-guide.md.
DeveloperReal package boundaries: Actions, Data objects, providers, routes, models, settings, extension points, tests, and unsafe integration paths to avoid.

Use this split when writing examples:

For teams: Customer Portal gives signed-in customers one dashboard for support requests, preferences, and package-contributed self-service links.
For developers: Packages contribute dashboard cards through `PortalDashboardItemRegistry`; billing, document, event, and access packages keep their own domain actions.

Avoid examples that only restate the package name:

Weak: Automation Studio automates workflows.
Better: Automation Studio listens for package events such as form submissions and access approvals, then records each matched rule/action run in `automation_runs`.

Package Markdown ships to customers independently of this repository. Use installed package commands, Artisan commands in the customer’s application, or scripts and tools owned by that package. Keep monorepo wrappers, the private COMPOSER=composer.local.json overlay, repository-only Composer/npm scripts, and root test or analysis paths in contributor documentation outside packages/. A package’s own relative file takes precedence over an identically named root file; an alias may use its own conventionally named vendor tool when its Composer require-dev declares that tool. State customer application context explicitly.

Run php scripts/check-package-markdown-commands.php to check every package Markdown file without installing dependencies. This check runs in composer preflight, preflight:changed, preflight:all, test:affected, the package review manifest audit, and the dedicated Package Markdown commands CI workflow. The lightweight workflow includes Markdown-only PRs without starting the full test matrix.

The guard is a high-precision heuristic over Markdown, not a shell parser. Its accepted limits still require contributor review:

  • Subshells, substitutions and sh -c command strings are not interpreted.
  • Markdown extraction has edge cases around comments, backticks in fenced code, and multiline inline-code spans.
  • Customer-application context is inferred heuristically and can both exempt maintainer instructions and reject customer instructions when wording is ambiguous.
  • Alias traversal stays within one script manager; cross-manager alias chains and implicit npm lifecycle scripts such as prebuild are not followed.

Passing the guard does not prove every command is portable. Keep examples simple and review them in the package’s own checkout or the stated customer application.

Every package README should include these sections, in this order when practical. The current strict audit checks the heading names below.

SectionPurpose
Opening H1Title the page with the package or plugin name.
What This Plugin AddsPractical job, Capell surface, admin/editor outcome, and status such as Available or Pipeline.
Why It MattersSeparate developer impact from team/editor/operator value.
Screens And WorkflowPlanned or existing screenshots, diagrams, admin screens, frontend output, and workflow steps.
Technical ShapeProviders, config, migrations, models, resources, routes, Livewire, policies, events, jobs, views, cache.
Data ModelTables, relationships, core records, migration impact, deletion/retention, or no-schema statement.
Install ImpactAdmin navigation, permissions, public routes, database changes, config, queues, schedules, cache paths.
Common PitfallsPractical issues developers and operators should check.
TroubleshootingOptional symptom table for packages with routes, commands, jobs, schema, health checks, or external APIs.
Quick StartUp to four steps: install the package; run a declared install, setup, or migrate command when one exists; run the declared demo command when one exists; and open/verify the new surface. Omit undeclared command steps entirely.
Next StepsLinks to configuration, screenshots, ERD/schema, extension points, troubleshooting, related package docs.

README.md permits fenced PHP and shell examples. Keep fenced code out of docs/overview.md, docs/overview.admin.md and docs/admin-guide.md. Every package-owned extension contract and every manifest actions entry needs a worked PHP snippet in the README or a developer guide linked directly from it. Do not place examples in generated narrative fields or edit generated README inventories to satisfy this requirement; link the maintained developer guide.

The generator owns README structure, status, technical inventories, schema facts, install impact, screenshot plans, pitfalls, quick-start steps, and links. Each package owns the short editorial judgements in docs/readme-narrative.json:

  • whatThisPluginAdds: the package’s practical job and Capell surface.
  • surfaceOutcome: what an admin, editor, operator, or public user can actually do after install.
  • whyItMatters.developers: real Laravel boundaries and extension points.
  • whyItMatters.teams: the editorial, product, or operational outcome.

Each entry has a short text value and a non-empty evidence list. Evidence paths are relative to the package root, must point to committed files, and should name the smallest useful proof: capell.json, a provider, Action, route, migration, Filament resource, render hook, focused test, or screenshot contract. Generated README.md, generated docs/overview.md, and the narrative file itself are circular evidence and are rejected.

Do not put documentation-only fields in capell.json, and do not parse docs/overview.admin.md as the canonical developer narrative. The manifest is a runtime contract; the admin overview is deliberately written for a different reader.

Use the shared language guides before rewriting package docs:

The README is the developer and owner front door. The admin docs are the editor and operator front door, written in plain language for someone who runs the feature but does not change the code. Follow the admin voice in the Content Language Plan: real on-screen labels in bold, the user’s goal first, the nav path named, no class/table/Action names, no composer/artisan, no fenced code, and no non-ASCII punctuation.

When a package ships an admin-first overview, it adds a hand-authored fragment at docs/overview.admin.md (admin sections only, no H1, no footer). The generator wraps that fragment into docs/overview.md with the package H1 and a footer linking the admin guide and developer docs. The developer-shaped content is not lost: it stays in the generated README.md. The strict audit then validates overview.md in admin mode (H1 required; the developer headings What This Plugin Adds, Technical Shape, Data Model, Install Impact, and Quick Start are forbidden; install commands are forbidden). Keep the fragment short (about 40 to 70 lines) using plain headings such as:

SectionPurpose
What it does for youTwo to four sentences, concrete, in editor language. No class names.
Your screensThe actual admin nav items and pages, in plain words.
What you can doThree to six verbs the user performs: create, schedule, approve, preview, export.
Where to find itThe nav path, for example “Content > Articles” or “Settings > SEO”.
Good to knowOne to three gotchas in plain language, such as “Drafts stay private until published”.

Themes and behind-the-scenes packages use their own short headings (see the tier table) rather than this exact set.

A longer how-to with three parts in one document:

SectionPurpose
Using <Package> (editor how-to)Numbered tasks with real button labels, each titled by the user’s goal (“How to schedule an article for later”).
Rolling out <Package> (for owners)What to turn on first, what to add when needed (a Need to Enable table), what not to enable yet, and who does what (role to first useful screen).
Troubleshooting for editorsA What you see / What it means / What to do table in plain language.
TierTreatment
1 (full guide)Admin-first overview.md plus a full admin-guide.md (how-to, adoption, troubleshooting).
2 (operator note)Admin-first overview.md plus a lighter admin-guide.md (what you are looking at, what to do when X, settings and retention).
3 (theme template)One shared admin-first overview.md shape per theme: what this theme gives you, how to use it, what it adds, good to know. No per-theme admin-guide.md.
4 (behind-the-scenes note)Admin-first overview.md only: what it does, do I need to do anything, where it shows up. No admin-guide.md.

Write from the reader’s job:

  • For owners: explain what capability this adds in a Capell site, dashboard, workflow, or package bundle.
  • For editors: explain what becomes easier to create, approve, reuse, publish, inspect, or recover.
  • For developers: explain what extension point, Action, Data object, or provider surface saves custom code.
  • For operators: explain what gets monitored, cached, audited, migrated, protected, or debugged.

Avoid vague claims. Use specific outcomes that the code supports. For example:

WeakBetter
Adds generic content tools.Adds article, archive, and tag page types so teams can publish editorial content without custom page schemas.
Improves performance.Indexes cached model URLs and exposes admin cache widgets so operators can see stale HTML and refresh affected pages.
Integrates with Shopify.Stores site-scoped Shopify OAuth connections and syncs products into local tables for admin-side catalog search.

Check these source locations before documenting a package:

SurfaceSource of truth
Composer name and namespacepackages/<package>/composer.json
Product group, capabilities, surfacespackages/<package>/capell.json
Providerscomposer.json.extra.laravel.providers, src/Providers
Config and env varsconfig/*.php, settings classes, tests
Routesroutes/*.php
Commandssrc/Console/Commands
Actionssrc/Actions
Data objectssrc/Data
Models and tablessrc/Models, database/migrations
Settingssrc/Settings, database/settings, Filament settings schemas
Admin surfacessrc/Filament, registered CapellAdmin calls
Frontend surfacesresources/views, src/Livewire, render hooks, frontend providers
Extension pointscontracts, registries, tags, provider registration calls
Testspackages/<package>/tests, shared package tests under tests/Packages
Authored README claimspackages/<package>/docs/readme-narrative.json and its evidence paths

For every package-owned extension point, document:

FieldRequired answer
What to implementInterface, class, enum, registry payload, or Action call.
Where to register itProvider method, registry call, config key, or container tag.
When it runsBoot, admin render, public request, queue job, install command, sync command, publish flow, cache invalidation.
Safe fallbackWhat happens when no extension is registered or the dependency is not installed.
Focused testProvider/registry test, Action test, admin render test, public output safety test, cache invalidation test, or failure test.

Do not invent extension points to make docs feel complete. If the package only exposes Actions today, say that and name the Actions.

The audit discovers declared PHP interfaces recursively under src/Contracts (the same source directory used for the README contract inventory), plus every key in capell.json’s actions object. An imported interface from another package is not a package-owned contract. Non-interface extension seams still require the documentation above and human review; directory discovery is not a claim to identify every possible extension mechanism.

Put one labelled PHP fence immediately after an HTML comment identifying its target: <!-- example: contract Fully\\Qualified\\InterfaceName --> or <!-- example: action setup -->. Contract labels use the actual qualified interface name; action labels use the actual manifest key, not the class name. Two action keys pointing to the same class each need their own example.

The invocation or implemented interface must resolve to the actual qualified class, using its namespace, a direct qualified name or an import (including an alias or grouped import). Each namespace has its own import context. Mentioning the expected class alongside a different same-named class does not count. Contract recipes must implement the interface and include the implementation and registration context in the labelled fence. Show non-empty bodies for every method declared by the interface, then pass that same implementation to the owning container or registry through bind(), singleton(), scoped(), tag(), register() or add(), using its class reference or a constructed instance. An empty implementing class, an unrelated method or registration of a different implementation does not count. Action recipes must show the actual invocation with its required inputs: static run(), or app()/resolve() of the declared class followed by handle(). Use the calling convention the shipped Action supports; the audit does not make these conventions interchangeable. Do not use empty fences, symbol-only lists, comments masquerading as code or bare ellipsis stubs. Argument unpacking such as SetupAction::run(...$inputs) is valid; show where the inputs come from. A first-class callable reference alone is not an invocation.

Developer guides count only when linked directly from README.md and stored inside that package’s docs directory. Admin-voice documents, external links and another package’s examples cannot fulfil the requirement. Unknown labels and missing per-target examples fail the audit, irrespective of strict-heading mode. The audit uses the checkout’s locked PHP parser and name resolver for contract declarations and snippets, including classes that extend a base class. Invalid PHP fails with the target name and parser diagnostic. The gate checks coverage, method bodies and invocation/registration structure, not PHP execution or semantic correctness: reviewers must verify signatures, registration, inputs, fallbacks and the focused tests against current source.

Use symptom tables:

SymptomLikely causeCheckFix
Concrete failure visible to a user/operatorSpecific code/config/runtime causeExact command, table, route, cache key, log message, or config keySmallest safe fix and verification step

Include exact names when relevant: route names, command signatures, table names, cache key prefixes, queue/job names, config keys, settings keys, and logs.

  • Public Blade, cached HTML, and theme output must not expose authoring state, package internals, model ids, field paths, permissions, signed admin URLs, tokens, OAuth state, or editor selectors.
  • Public Blade views must not query the database or lazy-load relationships. Document render data loaders, Actions, Livewire components, or view components instead.
  • Package docs must not advise replacing core classes, bypassing registries, writing provider-side data mutations, or storing designed page/widget markup in seed content.
  • Core must not import optional package classes. Cross-package docs should describe events, Actions, registries, string command names, or documented package APIs.
ReviewerQuestions to answer
DeveloperAre all classes, routes, commands, config keys, env vars, tables, settings, Actions, and examples real?
Content writerDoes the README explain a concrete workflow benefit in plain language without filler or unsupported claims?
OwnerCan a buyer or site owner tell why this package matters and which package bundle it belongs to?
OperatorAre install, queue, cache, migration, external API, and rollback risks visible where relevant?
Security/cacheDoes the doc preserve public-output safety and avoid leaking admin/editor internals?
QADo local Markdown links resolve, stale package names disappear, and focused package tests pass?

Use the narrowest useful checks while editing:

Terminal window
vendor/bin/pest packages/<package>/tests --configuration=phpunit.xml

Run repository-level docs checks before finishing a broad documentation pass:

Terminal window
COMPOSER=composer.local.json composer docs:rewrite-readmes:check
COMPOSER=composer.local.json composer docs:check:strict
COMPOSER=composer.local.json composer security:surface-report:check

When a package README documents behavior covered by tests, run the package-local Pest command or the specific test file named in the README.