Files
MeshVault/docs/explicit-grouping.md
T

3.7 KiB

Explicit import grouping

Explicit grouping is opt-in. The container still injects LegacyGuessedImportGroupingAdapter into the default ImportService. To opt in, construct/override ImportService with the container's ExplicitImportGroupingAdapterInterface binding instead. All repositories must share the same PDO connection.

Call import($source, $extensions, $contexts). Context keys are scan-relative paths; '*' supplies a fallback. Every successfully scanned file must resolve to a context with literal boolean confirmed => true and either:

  • a positive integer model_id (optional folder/title assertions must match), or
  • nonempty folder_path and model_name strings.

No source-path guessing occurs in the explicit adapter. Missing confirmation/context is rejected. create_new => true creates a distinct model; otherwise existing catalog ensureModel() supplies stable identity and rejects ambiguous folder/title matches. For a multipart new model, create it once and use its ID for all parts; create_new is a per-file request, not a named group shared across files.

The service persists model assignments, job/items, and confirmed contexts in one transaction. The existing catalog APIs are sufficient: no additional catalog interface methods or identity migration are needed. Existing root-lock and nested-savepoint behavior is reused.

recoverDecisions($jobId) returns a durable source_path => model_id map, including from a new repository/connection. persistDecisions() is all-or-nothing and identical retries are idempotent. Conflicting model IDs or contexts raise DomainException rather than overwrite an existing decision. Job deletion cascades to decisions. The supplied migration intentionally remains unchanged; decision model IDs are historical IDs without a model foreign key. Recovery does not recreate deleted models or guess replacement IDs.

Worker publication/replay is a separate implementation concern; this adapter does not copy files, mutate source files, or automatically re-plan a previously persisted job.

Verification

Integration tests in ExplicitGroupingTest exercise strict confirmation, missing contexts, stable/recreated/ambiguous identities, ID assertions, per-file/wildcard service contexts, durable recovery, immutable retry decisions, and caller-owned rollback. Linux PostgreSQL runs use sh bin/test-postgres.sh in an isolated /tmp directory and disposable Docker resources. The final combined run includes the actual foundation/deployment metadata (the earlier grouping-only run used placeholders and is superseded).

Publication and recovery

The worker chooses a collision-free destination before committing the immutable item plan. It then publishes to that exact name using a no-overwrite hard link. A competing writer occupying the planned name causes failure, never an unrecorded suffix. Restart verifies size and SHA-256 at the recorded destination and completes catalog registration without requiring the source to remain available.

Filesystem changes cannot roll back with SQL. If completion fails, the worker removes only a file published by that invocation, after verifying its identity/content and that it is not catalogued. Adopted files are never removed. Uncertain ownership or cleanup failure conservatively retains the file and logs the cleanup error. Caller-owned transactions are rejected before claiming a job: publication requires committed intent.

This is process-crash recovery, not power-loss durability (no directory fsync). Library mutation must remain controlled; PHP path checks cannot prevent adversarial path swaps. Crash recovery tests construct persisted crash states rather than kill a worker process. See .superpowers/shared-contracts/explicit-grouping-report.md for RED/GREEN evidence.