Skip to main content

Part 6 — Troubleshooting & Recap

Why this step exists: building the app is one thing; understanding what breaks and why is what makes you productive. This part collects the errors you are most likely to hit, how to debug them, and a recap of the whole system.

Common mistakes

AUSUS exceptions are named precisely — the class name is the diagnosis. Here are the ones a beginner meets most often.

FieldRequired

FieldRequired: helpdesk.ticket.title not in payload and no default

A create was invoked without a value for a non-nullable field that has no default. Fix: include every required field in inputs, or give the field a ->default(...) or ->nullable(). The React renderer's create modal validates required fields on the client and blocks submission before the server sees the request — see Create a ticket from the UI.

PolicyDenied

PolicyDenied: helpdesk.ticket.start

The actor does not hold the role the action requires (ticket.agent). Fix on the CLI: pass the role in Application::create(['roles' => [...]]). Fix over HTTP: send the X-Actor-Roles header. Omitting it yields a roleless actor — every protected action returns 403 with the role the action actually requires (no demo fallback).

WorkflowStateMismatch

WorkflowStateMismatch: current state 'OPEN' does not match any declared source

You invoked a transition that is not legal from the record's current state — for example resolve on an OPEN ticket. This is the workflow guard working. Fix the call order, or add the transition you intended.

BadRequest: missing required X-Tenant-ID header

A projections/* or actions/* request arrived with no X-Tenant-ID. Fix: send -H 'X-Tenant-ID: helpdesk'. The React renderer sends it for you from the AususProvider tenant prop.

TenantBoundaryViolation

The subject reference names a different tenant than the one the request runs in. Fix: the tenantId in a subject reference must match X-Tenant-ID (helpdesk throughout this tutorial).

AmbiguousWorkflowField

AmbiguousWorkflowField: entity 'helpdesk.ticket' has multiple enum fields
with a default (priority, status)

The entity has two enum fields with defaults and no ->workflow() call, so AUSUS cannot guess which one is the lifecycle. Fix: declare the workflow explicitly — ->workflow(field: 'status', initial: 'OPEN') — which this tutorial does. (If you removed that line, this is the error you would see.)

ProjectionNotFound / ActionNotFound

A 404 with one of these kinds means the FQN in the URL is wrong. FQNs are case-sensitive and fully qualified: it is helpdesk.ticket.summary, not ticket.summary or summary.

Class not found

Error: Class "Helpdesk\TicketSystem" not found

Composer's autoloader has not indexed the class. Fix: confirm the file is src/TicketSystem.php, the namespace is Helpdesk, the PSR-4 rule is in composer.json, then run composer dump-autoload.

SQLite cannot open the database

unable to open database file means the directory in the database path does not exist — SQLite creates files, not folders. Fix: use a path whose directory exists (this tutorial keeps tickets.sqlite in the project root).

Debugging tips

A short checklist for when something is wrong:

  1. Read the exception class, not just the message. WorkflowStateMismatch, PolicyDenied, ConcurrencyConflict each point at a specific layer.
  2. Isolate with the CLI. If the browser misbehaves, reproduce it in a small PHP script with $app->invoke(...). That removes HTTP and React from the picture and tells you whether the bug is in the domain.
  3. Inspect the ViewSchema directly. Before blaming the renderer, run curl -H 'X-Tenant-ID: helpdesk' …/projections/helpdesk.ticket.summary and read the JSON. If the data is wrong there, the problem is server-side.
  4. Check the graph hash. GET /api/_health returns graphHash. If it does not change after you edit the plugin, the server is running stale code — restart php -S.
  5. Look in the database. sqlite3 tickets.sqlite 'SELECT * FROM helpdesk_ticket;' shows the real rows. SELECT action_fqn, timestamp FROM kernel_audit_log; shows every action that ran — the audit trail is your event history.
  6. Watch for deprecation notices. A line containing AUSUS deprecation: on boot() means you are relying on a legacy fallback (for example an implicit workflow). It still works, but fix it before it becomes an error.
  7. Read the server terminal. php -S prints PHP errors and stack traces in the terminal where it runs — keep that window visible.

Architecture recap

You wrote one file of domain code — src/TicketSystem.php — and got a persisted, HTTP-exposed, browser-rendered application. Here is why that worked.

The layers

TicketSystem plugin ← you wrote this
│ compiled by the Compiler

MetadataGraph ← entities, fields, actions, workflows, projections
│ consumed by

Runtime / Invoker ← preflight → policy → workflow guard → effect → audit
│ reads & writes

SQLite persistence ← schema derived from the graph; + kernel_audit_log

And the outward-facing half:

HTTP Router (ausus/api-http) ← maps /projections + /actions onto the runtime
│ emits

ViewSchema JSON ← fields + actions + data
│ consumed by

@ausus/renderer-react ← draws lists, detail views, action dialogs

What happens on one action

When you clicked Start in the browser, this is the full path:

  1. The renderer POSTed /api/actions/helpdesk.ticket.start with the ticket reference and the tenant/role headers.
  2. The Router parsed it and called the Invoker.
  3. The Invoker ran its chain inside one transaction: preflight (the action exists, the subject is in-tenant) → policy (the actor holds ticket.agent) → workflow guard (the ticket is OPEN, so OPEN → IN_PROGRESS is legal) → effect (write status = IN_PROGRESS) → audit (append to kernel_audit_log).
  4. The response went back as JSON; the renderer refetched the projection and redrew the row.

Every guarantee — tenancy, authorization, the legal-transition check, the audit entry — came from the metadata you declared, not from code you wrote per route.

What you built

  • A domain plugin: 1 entity, 5 fields, 4 actions, 1 workflow, 2 projections.
  • A SQLite-backed application you ran on the CLI.
  • An HTTP API with health, projection and action routes.
  • A React UI that lists tickets and drives their lifecycle.

Remaining documentation gaps

Honest notes on what this tutorial could not show, because the capability is not in v0.1.x:

  • Authentication. v0.1.0 has no auth layer; roles are passed as headers / config. A real deployment needs an auth middleware in front of the Router.
  • Databases other than SQLite. Persistence is validated on SQLite only.
  • List filtering and pagination. Projections return all rows for the tenant; pagination.nextCursor is always null.
  • Per-transition guard policies. Authorization is per action; a guard policy attached to an individual transition is designed but not run in v0.1.0.
  • Multiple entities and cross-entity references. This tutorial uses a single entity; relations between entities are not covered.

create-project installs the monorepo instead of the starter

Symptoms

After running:

composer create-project ausus/starter myapp

the generated directory contains unexpected folders such as:

  • packages/
  • apps/
  • docs-site/
  • renderer/
  • scripts/

and Composer output contains lines similar to:

Symlinking from packages/kernel

Cause

Composer defaults to minimum-stability=stable. Without explicitly requesting the alpha channel, Composer resolves the latest stable 0.1.x release.

Resolution

Use the documented alpha installation command:

composer create-project ausus/starter myapp

composer boot returns Command "boot" is not defined

Cause

This usually means the project was scaffolded from the legacy stable starter instead of the v0.2 alpha channel.

Resolution

Delete the generated directory and reinstall using:

composer create-project ausus/starter myapp

Alpha resolution failure

Symptom:

Your requirements could not be resolved to an installable set of packages.

Problem 1
- Root composer.json requires ausus/standard-stack ^0.2@alpha
- ausus/standard-stack v0.2.0-alpha.X requires ausus/api-http ^0.2@alpha
→ does not match your minimum-stability.

Cause. Your root composer.json has minimum-stability: stable (Composer default). When ausus/standard-stack@^0.2@alpha declares a transitive ausus/api-http ^0.2@alpha, that inner alpha constraint is evaluated against YOUR root's minimum-stability. Stable rejects it.

The @alpha flag on a single root require does not propagate to transitive dependencies — this is documented Composer behaviour, not AUSUS-specific.

Fix. Declare alpha stability at the root of your composer.json:

{
"minimum-stability": "alpha",
"prefer-stable": true
}

Or via CLI:

composer config minimum-stability alpha
composer config prefer-stable true
composer update

If you are using composer create-project, request the alpha channel explicitly:

composer create-project ausus/starter myapp

The flag is required for every v0.2.x alpha install until the first stable AUSUS release.

See Alpha installation requirements for the full explanation.

Packagist propagation delay

Symptom. A new ausus/* version was just tagged on GitHub but composer require ausus/standard-stack:^0.2@alpha still pulls the previous version.

Cause. GitHub → Packagist webhook propagation typically takes 1–3 minutes (occasionally up to 15 min on slow days). Composer's local cache may also serve a stale package list.

Fix.

composer clear-cache
composer require ausus/standard-stack:^0.2@alpha --no-cache

If after 15 min the new version still doesn't appear, click the manual Update button on the package's Packagist page (https://packagist.org/packages/ausus/<name>).

npm dist-tag confusion

Symptom. npm install @ausus/renderer-react returns a version that doesn't match what's documented as the current alpha.

Cause. npm dist-tags. AUSUS publishes pre-releases to @next and also promotes @latest to the current pre-release as long as no stable v1+ exists.

Fix. Be explicit:

# Default (gets @latest = current alpha until a stable v1+ exists)
npm install @ausus/renderer-react

# Always-current pre-release stream
npm install @ausus/renderer-react@next

# Specific version
npm install @ausus/renderer-react@0.2.0-alpha.4

See The React renderer — npm dist-tag policy.

Where to go next

  • Core Concepts — the model behind everything you just used.
  • The PHP DSL — every builder method.
  • Workflows — transitions, guards, and the explicit workflow API in depth.
  • The HTTP API — the full route and error reference.

You have built a complete AUSUS application from zero. Everything larger is the same four moves: describe the domain, boot the Application, expose it, render it.