Bootstraps the web/ SPA: Vite 6 + React 19 + TypeScript 5.8, Vitest
with jsdom, @testing-library/react, and a green smoke test asserting
the App renders its Collection heading.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Preserves the documented set-to-current idempotent no-op: re-setting an
already-public object's visibility no longer rejects when a required field
was introduced after publish. Adds a regression test.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
set_visibility now gates the transition to Public: every field definition
with required=true must have a value on the object (typed inventory-minimum
columns are already NOT NULL, so only flexible required fields are checked).
Missing values yield VisibilityError::MissingRequiredFields(keys); the admin
publish endpoint maps it to 422. The gate runs in db so every caller is
protected and the check is atomic with the transition.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wire best-effort Meilisearch index sync into the admin write paths
(create/update/delete/set_fields/set_visibility). Adds
SearchClient::sync_object (reindex if the object exists, remove if gone —
one uniform path), an optional AppState.search client, and a reindex
helper that logs failures via tracing without failing the committed
write. Server gains MEILI_URL/MEILI_MASTER_KEY/MEILI_INDEX config;
search stays disabled (no-op) when unset. reindex_all remains the
recovery path.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
GET/POST /api/admin/vocabularies and GET/POST /api/admin/vocabularies/{id}/terms;
reads gated on ViewInternal, writes on EditCatalogue; labels round-trip verified.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- GET /api/admin/field-definitions (ViewInternal) — lists all registered
field definitions with key, data_type, vocabulary_id, authority_kind,
required, group, and localized labels
- PUT /api/admin/objects/{id}/fields (EditCatalogue) — replaces an
object's flexible-field values with replace semantics; validates every
key against the registry (UnknownField → 422, TypeMismatch → 422,
Unresolved → 422, ObjectNotFound → 404, Db → 500)
- FieldDefinitionView DTO added; both handlers registered in OpenAPI
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
POST /api/admin/objects (draft|internal only; public rejected 422),
PUT /api/admin/objects/{id} (preserves visibility; 204/404),
DELETE /api/admin/objects/{id} (204/404). Every write records
AuditActor::User(<session-user-uuid>). Tests: lifecycle, public-rejection,
unauthenticated-rejection.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements build_document in the search crate: resolves Term and Authority
flexible-field values to their human-readable labels so reindex_all produces
documents that Meilisearch can match on label text, not raw UUIDs.
Adds integration test covering the full reindex→search round-trip.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Idempotent seed of a representative subset: 3 vocabularies + 12 descriptive field
definitions with term/authority bindings. Empty vocabularies; wiring deferred.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>