The inconsistency
IngestFailure.name is meant to be the item that failed. For a directory ingest it is the full absolute path on the server — because it is whatever the run's own loop happened to be holding — while for a single-file ingest it is the basename.
It travels: IngestFailureOut publishes it on the wire, so REST, the CLI and MCP all report the same string, and the browser renders it in the ingest run's failure table.
Why it is a leak rather than only an inconsistency
Source.path and Asset.uri are deliberately unpublished — server-side absolute paths are useless to a client and needlessly disclosive, which is why SourceOut.name carries Path(source.path).name and reaching bytes goes through a download route keyed on an asset id. IngestFailure.name is the one place a server path reaches a client anyway, and it does so by accident rather than by decision.
For a hosted workspace that is somebody else's directory layout in a table. The frontend already renders it defensively — the ingest screen keeps the full string in a title attribute and shows a shortened form in the cell — which is a mitigation for a wire problem rather than a fix.
The decision to take
Either:
- Normalize at the source —
IngestService records Path(item).name for a directory walk, matching the single-file path and SourceOut.name's precedent. Simple, and it makes the field mean one thing. The cost: two files with the same basename in one directory become indistinguishable in the failure report.
- Keep the path but publish a basename — add the split to
IngestFailure (name + something like location) and let the wire model publish only the first. More faithful, one more field, and a migration if it is stored rather than derived.
Option 1 is the smaller change and matches every neighbouring decision; option 2 preserves more information for a directory of similarly-named files. Worth ten minutes rather than a guess.
Notes
The inconsistency
IngestFailure.nameis meant to be the item that failed. For a directory ingest it is the full absolute path on the server — because it is whatever the run's own loop happened to be holding — while for a single-file ingest it is the basename.It travels:
IngestFailureOutpublishes it on the wire, so REST, the CLI and MCP all report the same string, and the browser renders it in the ingest run's failure table.Why it is a leak rather than only an inconsistency
Source.pathandAsset.uriare deliberately unpublished — server-side absolute paths are useless to a client and needlessly disclosive, which is whySourceOut.namecarriesPath(source.path).nameand reaching bytes goes through a download route keyed on an asset id.IngestFailure.nameis the one place a server path reaches a client anyway, and it does so by accident rather than by decision.For a hosted workspace that is somebody else's directory layout in a table. The frontend already renders it defensively — the ingest screen keeps the full string in a
titleattribute and shows a shortened form in the cell — which is a mitigation for a wire problem rather than a fix.The decision to take
Either:
IngestServicerecordsPath(item).namefor a directory walk, matching the single-file path andSourceOut.name's precedent. Simple, and it makes the field mean one thing. The cost: two files with the same basename in one directory become indistinguishable in the failure report.IngestFailure(name+ something likelocation) and let the wire model publish only the first. More faithful, one more field, and a migration if it is stored rather than derived.Option 1 is the smaller change and matches every neighbouring decision; option 2 preserves more information for a directory of similarly-named files. Worth ten minutes rather than a guess.
Notes