Deterministic Admin Agent product file actions
This PR moves uploaded product CSV rename/import work from model-reconstructed mutations to fingerprinted server-owned preflight, approval, execution, and verification.
Typed server executors for product CSV work: catalogFileAction for file-backed renames/updates and catalogCsvImport for create/import workflows.
Preflight now persists bounded approval evidence and fingerprints the file, catalog snapshot, operation spec, and derived plan before any write.
Two-column rename files no longer fall into generic import logic, LLM-authored projections, or retry loops after terminal preflight failures.
Actual catalog writes still stage drafts through existing catalog product staging paths; the PR changes ownership and verification around those writes.
1. Why this exists
- Uploaded files were treated like generic imports too early.
- The model had to infer mappings and catalog fields before deterministic preflight.
- A rename could invent unsupported fields such as
productNameand never persist an executable plan. Currencywas copied into custom attributes instead of structurallistPriceCurrencies.
- The user states the operation; the server binds it to the current uploaded file.
- Preflight produces one concrete plan with bounded examples and a typed executor.
- Approval runs the exact fingerprinted plan after re-reading file and catalog state.
- CSV currency remains structural and passes product attribute validation.
Rows keep USD in listPriceCurrencies, not attributes.currency.
14 change, 43 are already correct, and 16 unmatched rows are reported/skipped.
Plus typecheck, types build, Prettier, and git diff --check.
2. What changes
catalogFileAction executors.Behavior matrix
| Scenario | Old failure mode | New behavior | Primary guard |
|---|---|---|---|
| Single attached two-column rename file | LLM tries generic import fields or asks for product category/channel/currency. | Bypasses LLM-authored catalog projection and preflights rename from catalog value coverage. | preflightAttachedCatalogProductRename |
| Ambiguous or missing source matches | Could partially invent or retry. | Persists blocked diagnostics; only preflightPassed or preflightPartial can create an approval. |
blockingIssues |
| Approved file changed after preflight | Execution could trust stale model payload. | Re-reads R2 file and rejects drift before catalog reads/writes. | CATALOG_FILE_DRIFT |
| Catalog changed after preflight | Approval could write against a different matching set. | Re-plans against the current catalog and blocks on drift/truncation. | CATALOG_DRIFT |
| Duplicate approval click | Could stage twice or re-enter the agent. | Returns stored terminal result without new reads or writes. | terminalResult |
| CSV import execution reports success without exact drafts | Could claim complete with missing or wrong drafts. | Fails closed unless verified draft skuIds/write fingerprints match expected output. | readStagedCatalogWriteFingerprints |
3. How it works
askProductCatalogAgent now receives an explicit operation such as fileRename, fileImport, read, or directMutation.
Tool availability follows that operation, not the model’s interpretation of CSV columns.
Preflight reads the current-turn file and a bounded catalog snapshot, infers or applies an operation spec, and returns awaitingApproval only when the plan is executable.
Blocked plans persist issues, not approval executors.
The pending action plan carries an executor with kind, runId, and planFingerprint.
Approval dispatch is typed; the router does not classify by English summary text.
Execution re-reads file/catalog, re-plans, writes drafts, then verifies exact staged SKU IDs or write fingerprints before reporting success.
Duplicate approvals return stored terminal evidence.
File-action planner rules
| Rule | Why it matters | Visible tests |
|---|---|---|
| Infer rename direction from catalog value coverage. | A two-column file can be a complete rename without import-only fields. | catalogProductFileAction.spec.ts |
Use indexed column refs: { header, index }. |
Duplicate headers and reordered columns are detected instead of silently misread. | STALE_COLUMN_REF, duplicate-header mapping tests |
| Collapse identical duplicate rows. | Retried/exported rows do not create false conflicts. | “collapses duplicate rows when they describe the exact same update” |
| Block contradictory duplicate source keys. | No last-row-wins behavior for catalog writes. | DUPLICATE_SOURCE_KEY |
Support unmatchedPolicy: skip. |
Merge-style files can update matched products while reporting unmatched rows. | preflightPartial |
CSV import fixes
- Alternate headers could throw or trigger LLM JSON fallback.
- Duplicate headers were not safe if only header text identified mappings.
Currencybecame a custom attribute.- Terminal blockers could be summarized away and retried.
needsMappingsupports one deterministic same-tool retry.needsUserInputbundles all unresolved core mappings into one form.- Currency populates
listPriceCurrencies. - Terminal failures set
terminal=trueanddoNotRetry=true.
4. What it does not change
- No new Prisma migration or catalog table schema is introduced in the supplied file list.
- No client-side UI change is visible; the work is server-side Admin Agent behavior.
- Dealops 1 catalog tables are not the target; this is
dealops3/configAgentproduct catalog agent work. - The Product Catalog Agent still stages draft catalog changes; it just no longer owns approved uploaded-file execution.
- Generic structured bulk imports remain available for non-file product payloads.
- Existing direct mutation approval remains separate under
catalogDirectMutation, with added lock/receipt guardrails.
5. Risks / rollback / open questions
needsUserInput, preflightBlocked, CATALOG_DRIFT, or CATALOG_TOO_LARGE_FOR_SAFE_PREFLIGHT. That is intentional for deterministic approval, but it changes user-facing behavior.
Because the change is mostly server routing/state/tool policy, rollback is code-level: disable typed file-action routing and return to prior Product Catalog Agent tool exposure.
Be careful: rolling back also reopens the original retry-loop and unsupported projection failure modes.
- Organization lock prevents concurrent approved catalog execution.
- Compare-and-set prevents superseded runs from writing terminal state.
- Execution leases prevent an in-progress action from being overwritten by a new user turn.
- Confirm fingerprint canonicalization is stable across all plan shapes.
- Check that approval summaries remain concise with bounded examples only.
- Verify truncation thresholds are acceptable for large org catalogs.
Reviewer checklist
terminalResult.