One rule for three files. Bristlenose never writes over a file it couldn’t read. A file the researcher edits (people.yaml) is left where it is, and the message names the broken line. A file Bristlenose keeps for itself (the pipeline manifest, projects.json) is set aside as .damaged-<stamp> and a new one is started. The message appears once, where the researcher is looking: the terminal for the CLI, the app for the Mac.
The row, its diagnostic popover (click the glyph), and the context menu. A truncated file is the realistic case: a power cut or full disk while it was being written.
The folder is there, but its contents couldn’t be read.
/Users/sam/Research/Spring usability study
This project’s index file couldn’t be read — analysing will rebuild it, running every step again.
/Users/sam/Research/Spring usability study
PipelineRunner.swift reads the manifest with JSONSerialization. When parsing throws, which is what a truncated file does, the code falls into the same catch as a failed read, so the row says Can’t be read. That state also sets needsFolderAccess, so the popover offers Locate…, a folder-permission remedy for a folder that is fine. Only a file that parses but has the wrong shape reaches “Project file damaged”.
Either way the row is a dead end. pipelineIsFree refuses every .unreachable, so the menu has neither Analyse nor Re-analyse, although the shipped sentence says “re-analysing will rebuild it”. Re-analyse would be the wrong verb anyway: it’s --clean, which discards the researcher’s stars and tags.
A failed read stays .unreadable. A read that worked but won’t parse is .damaged, which is error kind, as it already is. .damaged counts as “pipeline free” for Analyse only. The run sets the file aside (pipeline-manifest.json.damaged-<stamp>) and runs every stage again. Nothing new is drawn: the pill, the glyph, the popover, Show Log and the menu item all ship today. The only change is the one sentence.
Not built (owner, 22 Oct): a last-good copy of the manifest. It’s parked until after V1. R111 came from the 19 Oct audit’s probe, a damaged manifest has never been seen in the wild, and since 04d30698 the write fsyncs before it replaces the file.
Same popover, same width (360 pt) and type sizes. Only the sentence changes, in all 21 locales.
This project’s index file couldn’t be read — analysing will rebuild it, running every step again.
/Users/sam/Research/Spring usability study
This project’s index file couldn’t be read. Analysing rebuilds it by running every step again; stars and tags on quotes that change may be lost.
/Users/sam/Research/Spring usability study
This project’s index file couldn’t be read — re-analysing will rebuild it.
/Users/sam/Research/Spring usability study
A keeps today’s shape (an em-dash clause, as in its siblings in the diagnostic.unreachable.* family) and its first half. It changes the verb to the one on the menu and says what the rebuild costs.
B is honest about a second cost: re-extracted quotes won’t come out identical, so curation on those that change can be orphaned. It runs to two clauses and adds a loss warning without a confirm. The house rule is to confirm only for a real loss, and this one is uncertain.
C needs no translation work, but its verb disagrees with the menu item. That mismatch is the bug this decision exists to remove.
projects.json is damaged (#19)At launch, after the file has been set aside. Shown once.
Drag a folder of interview recordings into the sidebar, or click + New Project to start.
Drag a folder of interview recordings into the sidebar, or click + New Project to start.
It was set aside and a new list started. Your projects are untouched in their folders, with their reports and edits — drag each folder into the sidebar to add it back.
Mechanism: a stock NSAlert (.warning), presented as a sheet on the first window once launch has settled. It isn’t app-modal, because the message is about this window’s empty sidebar. It shows once, after ProjectIndex.load() has moved the file to projects.json.damaged-<stamp>, and never again unless another file is damaged. There is no Show in Finder: the copy sits in the app’s container, where a researcher can do nothing with it.
Remedy 1 (do something): drag the folders back. The research is safe, because each project’s database and edits live in its own folder (bristlenose-output/.bristlenose/bristlenose.db, matched by the study marker). What is lost is the sidebar arrangement: groups, order, icons and agent access.
Edge case, accepted: if the set-aside itself fails (saveRefused), the same alert shows, but projects added back last only until the next launch. That launch tries the set-aside again.
people.yaml and the manifest (#17, #18)English by design (the terminal is the operator surface). Glyphs and colours come from ui_kinds.py.
✓ Extracted quotes 1m 12s ✓ Grouped themes 41s Traceback (most recent call last): File ".../bristlenose/pipeline.py", line 3359, in run existing_people = load_people_file(output_dir) … yaml.scanner.ScannerError: while scanning a quoted scalar in "<unicode string>", line 4, column 18
✓ Extracted quotes 1m 12s ✓ Grouped themes 41s ⚠ people.yaml couldn’t be read (line 4, column 18), so it was left as it is, and names you added aren’t in this report. Fix that line, then run again. ✓ Rendered report 3s
$ bristlenose run interviews/ Resuming from previous run... ✓ Transcribed 6 sessions 14m 02s … every stage runs again, LLM calls paid again
$ bristlenose run interviews/ ⚠ The pipeline manifest couldn’t be read, so it was set aside as pipeline-manifest.json.damaged-2026-10-22T21-04-05. Every stage runs again, including the LLM stages. ✓ Transcribed 6 sessions 14m 02s
$ bristlenose status interviews/ No pipeline manifest in interviews/bristlenose-output. Run bristlenose run interviews/ to start.
$ bristlenose status interviews/ ⚠ The pipeline manifest in interviews/bristlenose-output couldn’t be read, so Bristlenose can’t tell which stages are done. Run bristlenose run interviews/ to rebuild it — every stage runs again.
people.yaml is the researcher’s file (“edit them freely”, says its header), so a typo is theirs to fix, and the fix is one line in a file left exactly where they made it. The run gets the same tolerance in analyze, render and coverage. With no decision needed, write_people_file becomes atomic (tmp + fsync + replace), so Bristlenose can no longer truncate the file itself.
“Pipeline manifest” stays CLI vocabulary. status already uses it, and a CLI researcher can find the file. The Mac never says it: there it is the shipped “Project file damaged”.
| Where | Key | English | Status |
|---|---|---|---|
| Mac row + popover header | desktop.chrome.pipeline.unreachable.damaged | Project file damaged | shipped, unchanged |
| Mac popover sentence | desktop.pipeline.diagnostic.unreachable.damaged | option A / B / C above | owner to choose · 21 locales |
| Mac menu | desktop.menu.project.analyse | Analyse | shipped, newly offered here |
| Mac alert title | desktop.projectList.damagedTitle (new) | Your project list couldn’t be read | approved 22 Oct · 21 locales |
| Mac alert body | desktop.projectList.damagedBody (new) | It was set aside and a new list started. Your projects are untouched in their folders, with their reports and edits — drag each folder into the sidebar to add it back. | approved 22 Oct · 21 locales |
| Mac alert button | buttons.ok (common) | OK | shipped |
| CLI run | — (English) | people.yaml couldn’t be read (line N, column M), so it was left as it is, and names you added aren’t in this report. Fix that line, then run again. | approved 22 Oct |
| CLI run | — (English) | The pipeline manifest couldn’t be read, so it was set aside as … Every stage runs again, including the LLM stages. | drafted in brief |
| CLI status | — (English) | The pipeline manifest in X couldn’t be read, so Bristlenose can’t tell which stages are done. / Run … to rebuild it — every stage runs again. | drafted in brief |