SyncController now handles the full mutation vocabulary:
- inspection.create (idempotent by uuid; project/layer derived from feature;
authz member + 'create inspections'; status defaults to completed).
- issue.create (idempotent; authz member + 'create issues').
- issue.update (by server id; authz member + 'edit issues'; sets resolved_at
when resolved/closed; last-write-wins conflict).
- feature.update (by server id; authz member + 'update progress'; recomputes
phase progress; last-write-wins conflict).
- Conflict detection: client_updated_at vs server updated_at → returns
'conflict' with the current server value.
Added uuid + client_updated_at to features/inspections/issues (guarded migration)
and their fillables. Tests: 16 passing (added inspection/issue/feature + conflict).
Note: 2 PRE-EXISTING test failures remain (not from this work, sqlite-only):
ExampleTest expects '/'=200 (app redirects), and the dashboard route uses MySQL
FIELD() which sqlite lacks.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ProjectApiController bundle now supports incremental sync:
- ?since=<ISO8601> returns only records changed after that time (phases, layers,
features, inspections, issues, templates), each filtered by its own updated_at.
- 'deleted' tombstones (soft-deleted ids since 'since') for phases/layers/
features/inspections/issues so the device can purge locally.
- Bundle now also includes inspections, issues and inspection templates
(with version + content hash for incremental template download).
- New GET /api/v1/templates (accessible projects, ?since= delta).
Tests: 12 passing (added delta, tombstones, templates cases). Note: the 'since'
query param must be URL-encoded by clients (ISO8601 '+' offset).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Milestone 1 (auth foundation):
- Installed laravel/sanctum; HasApiTokens on User; published config + migration.
- routes/api.php with /api/v1; Sanctum 'ability' middleware alias registered.
- AuthController: POST login (long-lived revocable device token w/ ability
mobile-sync + devices table), GET me, POST logout. New Device model/table.
Milestone 2 (vertical slice, offline-first):
- progress_updates: +uuid (client-generated) +client_updated_at.
- ProjectApiController: GET projects (accessibleBy), GET projects/{id}/bundle
(project/phases/layers/features, membership-authorized).
- SyncController: POST sync — batch ops, idempotent by uuid, per-op result
(applied/duplicate/error), server-set user_id, authz by permission+membership.
Currently handles progress_update.create.
Tests: tests/Feature/Api/MobileApiTest (9 passing) — auth, accessible projects,
bundle authz, sync apply+idempotency, permission enforcement.
Also fixed a latent schema bug: projects.reference (and external_reference_1)
existed in the live DB but had no migration — added a guarded migration so fresh
installs match production.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>