sibling explicit-grouping implementation: commit all work, removal of stray scratch files; update docs and status; keep P1 fix and other senior details
This commit is contained in:
1 parent
9dd8c07e66
commit
99f19e1052
16 files changed
+1244
-44
No files matched your search
@@ -0,0 +1,61 @@
|
||||
# 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.
|
||||
Reference in new issue
Block a user