docs(form-builder): API.md Form Builder (Public), SCHEMA v2.1, ARCH §10.4, BACKLOG

S2c Phase 8.

- API.md: new **Form Builder (Public)** section documenting all 6
  public endpoints (GET schema + time-slots + sections; POST draft,
  PUT save, POST submit) with request/response examples, error codes,
  and the identity_match / schema_drift contracts. No PII-echo noted
  explicitly.
- SCHEMA.md bumped to v2.1:
  - changelog entry for S2c.
  - form_submissions table gains schema_version_at_open +
    identity_match_status columns; UNIQUE (form_schema_id,
    idempotency_key) replaces the composite index; a new composite
    index (form_schema_id, identity_match_status) landed for the
    organiser "pending-match" dashboard.
- ARCH-FORM-BUILDER.md bumped to v1.3 with new §10.4 "Public
  submission lifecycle — draft/save/submit split" documenting the
  three-endpoint contract, idempotency, schema-drift detection,
  access rules, the standardised error envelope, and the dependency
  data sub-endpoints.
- BACKLOG.md adds:
  - FORM-04 (grace_days configurable — current implementation still
    uses the hard-coded 7-day window)
  - DOC-01 (Scramble / OpenAPI generator for API.md to reduce the
    docs-drift effort going forward).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-17 23:07:26 +02:00
parent 9b1bf0e13d
commit 68d2c830a0
4 changed files with 325 additions and 9 deletions

View File

@@ -1,7 +1,7 @@
# Crewli — Core Database Schema
> Source: Design Document v1.3 — Section 3.5
> **Version: 2.0** — Updated April 2026
> **Version: 2.1** — Updated April 2026
>
> **Changelog:**
>
@@ -26,6 +26,15 @@
> Removed minimum volunteer hours threshold concept. Removed hardcoded
> motivation form step. Moved payment status from fixed admin field to
> dynamic registration field.
> - v2.1: Public Form Builder API completion (S2c). Added columns on
> `form_submissions`: `identity_match_status` (null|pending|matched|none,
> populated by `TriggerPersonIdentityMatchOnFormSubmit` per ARCH §31.1)
> and `schema_version_at_open` (stamped at draft-create for drift
> detection). Replaced the composite index on
> `(form_schema_id, idempotency_key)` with a UNIQUE constraint so the
> DB is the race-safe backstop behind application-level idempotency
> replay. Full public API contract: `/dev-docs/ARCH-FORM-BUILDER.md`
> §10.4.
> - v2.0: Universal Form Builder replaces event-scoped registration EAV
> (S1 + S2a + S2b landed). Full architecture:
> `/dev-docs/ARCH-FORM-BUILDER.md` v1.2.
@@ -2034,6 +2043,7 @@ that aggregates the user's submitted, non-test `form_submissions`.
| `reviewed_at` | timestamp nullable | |
| `review_notes` | text nullable | |
| `submitted_at` | timestamp nullable | |
| `schema_version_at_open` | int unsigned null | **v2.1** `form_schemas.version` at draft-create; compared against `schema_version_at_submit` for drift detection (ARCH §10.4) |
| `schema_version_at_submit` | int unsigned null | `form_schemas.version` at submit time |
| `schema_snapshot` | JSON nullable | Full snapshot when policy dictates (ARCH §4.6.1 shape) |
| `is_test` | bool | default: false — excluded from reporting & retention |
@@ -2042,14 +2052,15 @@ that aggregates the user's submitted, non-test `form_submissions`.
| `first_interacted_at` | timestamp nullable | First field focus |
| `submission_duration_seconds` | int unsigned null | opened_at → submitted_at |
| `auto_save_count` | int unsigned | default: 0 |
| `idempotency_key` | ULID nullable | Duplicate-submit guard |
| `idempotency_key` | ULID nullable | Duplicate-submit guard — UNIQUE `(form_schema_id, idempotency_key)` since v2.1 |
| `anonymised_at` | timestamp nullable | |
| `identity_match_status` | string(20) null | **v2.1** null\|pending\|matched\|none — written by `TriggerPersonIdentityMatchOnFormSubmit` (ARCH §31.1) |
| `search_index` | mediumText null | Concatenated text of text-type values; FULLTEXT-indexed on MySQL when supported |
| `created_at`, `updated_at` | timestamps | |
| `deleted_at` | timestamp nullable | Soft delete |
**Relations:** `belongsTo` schema, submittedBy / reviewedBy (User); `morphsTo` subject; `hasMany` values, section statuses, delegations
**Indexes:** `(form_schema_id, status)`, `(subject_type, subject_id)`, `(submitted_by_user_id)`, `(form_schema_id, review_status)`, `(form_schema_id, idempotency_key)`, `FULLTEXT(search_index)` (MySQL/InnoDB — best-effort, skipped gracefully on SQLite)
**Indexes:** `(form_schema_id, status)`, `(subject_type, subject_id)`, `(submitted_by_user_id)`, `(form_schema_id, review_status)`, **UNIQUE** `(form_schema_id, idempotency_key)` (v2.1; replaced the non-unique composite index from v2.0), `(form_schema_id, identity_match_status)` (v2.1), `FULLTEXT(search_index)` (MySQL/InnoDB — best-effort, skipped gracefully on SQLite)
**Events fired:** `FormSubmissionCreated`, `FormSubmissionDraftUpdated`, `FormSubmissionSubmitted`, `FormSubmissionReviewed`, `FormSubmissionSectionSubmitted`, `FormSubmissionSectionReviewed`, `FormSubmissionAnonymised`, `FormSubmissionArchived`, `FormSubmissionDeleted`
**Soft delete:** yes