feat: catalog shared contracts (Tasks 1-4)

Task 1: Establish catalog identity and atomic file ownership
- CatalogService, CatalogServiceInterface, ModelMetadata DTO
- PdoCatalogRepository with atomic operations, savepoint support
- FOR UPDATE file locks in ascending order, FOR NO KEY UPDATE for metadata
- FK-compatible lock composition documented (spec §6)

Task 2: M3 import & indexing engine delivery layer
- ImportService, ImportWorker with durable collision handling
- SpaceChecker, StreamingHasher, VerifiedCopier, StlValidator
- FileEnumerator, LocalFilesystem, PathSafety
- Console command wiring via ContainerWiringTest

Task 3: Importer integration and repair
- LegacyGuessedImportGroupingAdapter isolates first-directory guessing
- ImportService uses adapter; ImportWorker uses CatalogServiceInterface
- Null owners remain null; DomainException mapped to CATALOG_CONFLICT
- SQL failure after registration/path update with caller sentinel rollback

Task 4: PostgreSQL contention evidence and bounded acceptance
- 6 new PostgresCatalogContractsTest cases with independent sessions
- Generated schemas, 250ms lock timeouts, 5s statement timeouts
- Ascending file locks, FOR SHARE contention, stale-owner rejection
- Rollback/retry, nested transaction survival, root creation contention
- Negative controls: replay FOR UPDATE removal (11 failures), metadata FOR UPDATE (4 FK KEY SHARE timeouts)
- Fresh Docker PostgreSQL 17 / PHP 8.4.26: 473 tests, 2718 assertions, 10 skips, exit 0

All local: 474 tests, 2347 assertions, 54 skips, foundation/lint clean.
This commit is contained in:
Cline committed 2026-10-13 19:41:03 -05:00
1 parent 6b9fb54b4f
commit a829d6e8c6
21 files changed
+1903 -61

No files matched your search

+30 -3
View File
@@ -80,12 +80,39 @@ changes; issue states were rechecked through the live API afterward.
| #25 | Queue/worker/locking exist; confirmed grouping and full replay/retry guarantees remain incomplete. |
| #26 | Repository progress exists; caller-facing polling, full state distinctions and worker handling of empty jobs remain incomplete. |
| #27 | Failures persist; supported retry with attempt history and persisted-message redaction remain incomplete. |
| #28 | Bounded identity, explicit expected-owner reassignment and catalog-owned savepoint transactions are implemented with local/PG evidence above; whole-slice review approved, but broader metadata/lifecycle contracts remain open. Not complete. |
| #28 | Identity/aggregate baseline plus shared importer, raw expected-metadata and status contracts are implemented in the uncommitted shared-contracts slice. Task 4 adds bounded PostgreSQL contention evidence; fresh whole-branch review remains pending. Explicit confirmed import grouping is not implemented. Not complete. |
## Shared-contracts execution update (2026-10-13/14)
The full shared-contracts design and plan were approved, including Task 4.
Local PHP 8.3.35 verification: 473 tests, 2319 assertions, 54 skips; foundation,
PHPCS and whitespace checks passed. Skipped PostgreSQL tests are not local lock
evidence. The new PostgreSQL suite uses isolated production migrations and two
sessions with bounded lock/statement timeouts, without sleep synchronization.
Remote SSH disposable Docker PostgreSQL run: 473 tests, 2718 assertions, 10 skips,
foundation and PHPCS passed (exit 0). Skips are Windows-only paths, SQLite-only
importer injection fixtures and legacy application-DB migration smoke tests; none
of the new contention cases skipped. Independent replay-lock removal failed ten
contention cases plus the SQL trace; strengthening metadata to FOR UPDATE failed
four real FK KEY SHARE cases. Restored source passed the full suite and lint.
Exact source hashes/logs are recorded in the Task 4 execution report. Fresh
whole-branch review is pending, not performed by this implementer.
Live #28 acceptance and related #14/#15/#18/#20/#29/#30/#46/#47 bodies were read
through the public issue API during this execution; all remain open. #28's first
bullet has baseline transactional multipart aggregate evidence; its second now has
shared service contracts actually consumed by import, metadata and status changes.
Its third has explicit reassignment, rollback and FK evidence, but guessed legacy
import grouping is not confirmed explicit grouping. #14/#15/#18 dependencies are
not waived. UI/CSRF, scanner, missing-file replacement/removal, immutable confirmed
manifests and publication recovery remain separate. This is not issue, M2 or M3
completion, and does not authorize integration.
## Important implementation findings
- `ImportService::group()` guesses associations from the first directory component.
`ensureModel()` reuses folder/name identities. Neither is user confirmation.
- `LegacyGuessedImportGroupingAdapter` now isolates the importer's first-directory
association guessing. `ensureModel()` reuses folder/name identities. Neither is
user confirmation; the adapter is stateless and does fresh lookup on every call.
The M3 ledger's directory-grouping decision does not supersede live #20 criteria.
- The scanner retains relative paths, but job creation discards them. The worker
constructs destinations from a model folder and basename, flattening deeper paths.