Add OPERATIONS.md - #37
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## develop #37 +/- ##
========================================
Coverage 44.96% 44.96%
========================================
Files 25 25
Lines 3398 3398
Branches 259 259
========================================
Hits 1528 1528
Misses 1824 1824
Partials 46 46 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
Adds a new OPERATIONS.md document to capture this repository’s operational runbooks (local gate chain, release mechanics, backup/recovery, logging, external tool expectations, and configuration layout), aligning with the fleet requirement to have a standard operations destination.
Changes:
- Introduces
OPERATIONS.mdwith operational runbooks for local validation and release publishing. - Documents backup/recovery expectations for user-owned state (
*.db,*.bak) and basic troubleshooting guidance. - Summarizes tool/runtime dependencies (exiftool, ffmpeg, Docker) and repository layout pointers.
Suppressed comments (3)
OPERATIONS.md:41
- This section documents a
verifycommand (--quick, Docker/Immich decoder, its own DB), but the CLI currently only definesprocess,undo,import,index, andtrash(seePhotoCleaner/CommandLine.cs). As written, this is misleading operational guidance.
### Verify a media library
`verify` runs Immich's own decoder inside the `immich-server` image, so it needs a reachable Docker daemon and cannot run from inside this project's own container. Use `--quick` where Docker is unavailable, which runs the structural checks only and cannot detect an undecodable file.
Give `verify` its own database. It records verified state in the same `is_processed` column that `process` writes, so pointing `--db` at a `Process.db` makes `verify` skip every file as already verified when they were only processed, and nothing detects it.
OPERATIONS.md:67
- The documented exit-code contract (including an exit code
2for per-file failures) doesn't match the implementation:CommandRunner.RunAsyncreturns0on completion and1on cancellation/exception, and the commands log per-file failures instead of returning a distinct exit code.
Exit codes distinguish the two failure kinds, and a script should branch on them rather than on output:
- `0`: every file succeeded.
- `1`: the command could not run, meaning an unhandled exception, a fatal configuration error, a cancellation, or a failed `verify` preflight.
- `2`: the command ran to completion and one or more files failed.
OPERATIONS.md:75
- This bullet ties Docker specifically to a
verifycommand that doesn't exist in the current CLI. Consider describing Docker in terms of the actual repo operations (containerized linters and building/running the published image).
- **Docker** is a runtime dependency of `verify` only, which pulls `ghcr.io/immich-app/immich-server:release` and runs Immich's own `MediaRepository` inside it. Behavior therefore tracks Immich across releases rather than being reimplemented here.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.
Suppressed comments (4)
OPERATIONS.md:77
- This bullet states Docker is a runtime dependency of a
verifyfeature that doesn’t exist in this repository. As written, it incorrectly implies the application needs Docker at runtime.
- **Docker** is a runtime dependency of `verify` only, which pulls `ghcr.io/immich-app/immich-server:release` and runs Immich's own `MediaRepository` inside it. Behavior therefore tracks Immich across releases rather than being reimplemented here.
OPERATIONS.md:43
verifyis referenced as an available command (and--quick/--dbbehavior is documented), but there is noverifysubcommand or related implementation anywhere in the repo. This section is currently misleading and should either be removed or rewritten to reflect only existing commands.
This issue also appears on line 77 of the same file.
### Verify a media library
`verify` runs Immich's own decoder inside the `immich-server` image, so it needs a reachable Docker daemon and cannot run from inside this project's own container. Use `--quick` where Docker is unavailable, which runs the structural checks only and cannot detect an undecodable file.
Give `verify` its own database. It records verified state in the same `is_processed` column that `process` writes, so pointing `--db` at a `Process.db` makes `verify` skip every file as already verified when they were only processed, and nothing detects it.
OPERATIONS.md:70
- The documented exit code meanings don’t match the application:
Program.Main/CommandRunner.RunAsynconly ever return0(work completed) or1(cancellation/unhandled exception). There is no code path that returns exit code2, and per-file failures currently still produce exit code0.
Exit codes distinguish the two failure kinds, and a script should branch on them rather than on output:
- `0`: every file succeeded.
- `1`: the command could not run, meaning an unhandled exception, a fatal configuration error, a cancellation, or a failed `verify` preflight.
- `2`: the command ran to completion and one or more files failed.
OPERATIONS.md:75
- This claims exiftool is always passed
-validate, but there are no exiftool invocations in the codebase that include-validate(for exampleMediaUtilities.GetExifToolJsonAsyncuses-groupNames -json). Update this line so it doesn’t state a flag that isn’t actually used.
- **exiftool** reads and writes metadata, and is always passed `-validate`. It is installed in the Docker image, and a native run needs it on `PATH`.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.
Suppressed comments (4)
OPERATIONS.md:77
- “The Docker linters pull
:latestdeliberately, so a local run matches whatever CI resolved” is not accurate for this repo: CI uses pinned GitHub Actions for markdownlint/cspell/actionlint (see.github/workflows/validate-task.yml), and only editorconfig-checker runs via Docker:latest. Reword to avoid implying local Docker tags mirror CI’s pinned tool versions.
The Docker linters pull `:latest` deliberately, so a local run matches whatever CI resolved.
OPERATIONS.md:62
- The exit-code section currently implies
1only means cancellation/unhandled exception, but this program also bypasses startup on parse/validation errors and returnsParseResult.InvokeAsync()directly (Program.cs), which can yield a non-zero exit for invalid arguments. Document that1can also indicate invocation/validation failure so scripts don’t misclassify argument errors as crashes.
- `0`: the command ran to completion.
- `1`: the command could not run, meaning a cancellation or an unhandled exception.
OPERATIONS.md:7
- The PR description says OPERATIONS.md documents how to run a
verifyworkflow safely and calls out twoverify-specific traps, but this file does not mentionverify, and the repo does not appear to define averifycommand/subcommand or workflow (noverifyinPhotoCleaner/CommandLine.csor.github/workflows/). Either add the intendedverifyrunbook section here or update the PR description to match the repository’s actual surfaces.
## Runbooks
### Run the gates the way CI runs them
OPERATIONS.md:26
- This command list is missing the workflow lint that exists in
.vscode/tasks.json(Lint: Workflowsruns actionlint). Add the actionlint container invocation so the documented “lint set” matches the local gate surface.
docker run --rm --pull=always -v "$PWD":/workdir --workdir /workdir davidanson/markdownlint-cli2:latest "**/*.md"
docker run --rm --pull=always -v "$PWD":/workdir --workdir /workdir ghcr.io/streetsidesoftware/cspell:latest --no-progress README.md HISTORY.md
docker run --rm --pull=always -v "$PWD":/check --workdir /check mstruebing/editorconfig-checker:latest
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.
Suppressed comments (3)
OPERATIONS.md:19
- The sentence says CI runs
dotnet test“unchanged”, butvalidate-task.ymlruns tests with coverage collection (dotnet test --collect:"XPlat Code Coverage" --results-directory ./coverage). This is a concrete difference when trying to mirror CI locally; please either reflect the actual command or reword to avoid claiming it’s identical.
CI substitutes `dotnet csharpier check .` for the format step, after a `dotnet tool restore`, and runs the style verify and `dotnet test` unchanged. So a local run that formats a file leaves CI clean, while an unformatted commit fails there rather than being fixed.
OPERATIONS.md:9
- The PR description says this document includes guidance/traps for running a
verifycommand (including DB isolation and Docker-in-Docker constraints), but this file doesn’t mention averifycommand/workflow, and the repo doesn’t appear to define one. Either add the missingverifyrunbook content here, or update the PR description to remove that claim so the PR accurately reflects what’s being introduced.
## Runbooks
### Run the gates the way CI runs them
Local and CI runs read the same committed configuration, but they invoke it differently: locally the formatter writes, and in CI it only verifies. The [`.NET Format`](./.vscode/tasks.json) task is the local clean-compile chain, meaning `dotnet csharpier format`, then `dotnet build`, then the style verify. Run the chain and the suite before committing, since the chain never runs the tests and a change that compiles and formats cleanly can still be broken:
OPERATIONS.md:26
- This “lint set” snippet claims to match the
Lint:tasks, but it omits the workflow lint (Lint: Workflows) command that exists in.vscode/tasks.json(and in CI viaraven-actions/actionlint). As written, someone following this runbook won’t run the full local lint surface.
docker run --rm --pull=always -v "$PWD":/workdir --workdir /workdir davidanson/markdownlint-cli2:latest "**/*.md"
docker run --rm --pull=always -v "$PWD":/workdir --workdir /workdir ghcr.io/streetsidesoftware/cspell:latest --no-progress README.md HISTORY.md
docker run --rm --pull=always -v "$PWD":/check --workdir /check mstruebing/editorconfig-checker:latest
Suppressed findings answeredAnswering the collapsed findings across both rounds. All correct, and all one root error: I wrote this file describing the Round 1 on
|
Suppressed findings answered (round 3)Three more from review 4839547991 on 1.
|
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.
Suppressed comments (1)
OPERATIONS.md:80
- This implies the Docker linter runs will match CI, but CI pins markdownlint/cspell/actionlint via action wrappers (see .github/workflows/validate-task.yml) while the local tasks pull Docker
:latest. That means local results can legitimately differ from CI over time; only editorconfig-checker is run via Docker in CI.
The Docker linters pull `:latest` deliberately, so a local run matches whatever CI resolved.
Suppressed findings answered (round 4)One from the round on
|
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.
Suppressed comments (2)
OPERATIONS.md:78
- This implies the API key is only read from a file, but the CLI also accepts an inline
--apikeyvalue (mutually exclusive with--apikey-file). Consider rewording to recommend--apikey-filerather than stating it as the only supported mechanism.
The Immich API key is read from a file with `--apikey-file` rather than an argument, which keeps it out of shell history and out of the process list.
OPERATIONS.md:80
- This sentence contradicts the earlier note that local lint containers track
:latestwhile CI uses SHA-pinned action wrappers (so local results can legitimately differ from CI). Reword to avoid implying local lint runs will match CI.
The Docker linters pull `:latest` deliberately, so a local run matches whatever CI resolved.
Suppressed findings answered (round 5)Two from review 4839577417 on 1.
|
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.
Suppressed comments (1)
OPERATIONS.md:65
- Exit code
1can also be returned for command-line parse/validation errors (e.g.,CommandLine.BypassStartup()short-circuits whenparseResult.Errors.Count > 0). The current wording implies1only means cancellation or an unhandled exception, which is incomplete and slightly misleading.
- `1`: the command could not run, meaning a cancellation or an unhandled exception.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.
Suppressed comments (1)
OPERATIONS.md:16
- The local gate snippet runs
dotnet csharpier/dotnet huskywithout first restoring local tools. On a fresh clone, this can fail because the repo uses a tool manifest (.config/dotnet-tools.json) and CI explicitly runsdotnet tool restorebefore CSharpier. Adddotnet tool restoreto the recommended local command chain so it works out of the box and better matches CI.
dotnet csharpier format --log-level=debug .
dotnet build
dotnet format style --verify-no-changes --severity=info --verbosity=detailed
dotnet test
dotnet husky run
Suppressed findings answered (round 6)One from review 4839596687 on
|
* Add OPERATIONS.md The hub requires this file of every repo, checked for presence only, so its content is entirely this repository's own. It exists because the convention was emerging unevenly and the same operational material was landing under ad-hoc names. Carry the five declared headings with real content rather than a stub: the local gates that mirror CI, the dispatch-driven release, the runtime dependency verify has on Docker and on a database of its own, what the three exit codes distinguish, and why exiftool validation warnings are logged at debug level only. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Say how the CI invocation differs from the local one The runbook claimed CI runs the same commands, and it does not: locally csharpier formats and writes, while CI runs it in check mode and only verifies. A reader following the text would expect CI to fix formatting. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Describe the commands this branch actually has The file documented the verify command, the exiftool -validate flag, and a per-file-failure exit code of 2. None of those exist on develop: they are on the unmerged verify branch, and this branch was cut from develop. Describe what is here instead. Exit codes are 0 and 1, a per-file failure does not change them, exiftool is invoked without -validate, and the application needs no Docker daemon at runtime. The verify operational content follows once that work merges. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Restore the line endings on OPERATIONS.md Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Name the two places CI differs, and the lint step the snippet omitted The text claimed CI runs dotnet test unchanged, and it adds coverage collection so coverlet can emit the report Codecov consumes. The lint snippet listed three of the four Lint tasks, leaving out actionlint, so following it ran less than the local surface it claimed to match. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Say that the local linters and CI's are the same rules, not the same builds The local commands pull :latest while CI reaches three of the four through SHA-pinned wrappers, so a local result can differ from CI once an upstream release lands ahead of the pin. Name CI as authoritative and the difference as a version gap. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Recommend the key file rather than naming it the only mechanism The CLI accepts an inline --apikey too, and the two are mutually exclusive by validator, so stating the file form as the only one was wrong. Recommend it and say why. Drop the trailing claim that a local Docker linter run matches what CI resolved, which contradicted the paragraph above it saying local pulls latest while CI reaches three of the four through pinned wrappers. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Name the parse error among the exit-1 causes BypassStartup short-circuits when the parse result carries errors, so a command-line mistake exits 1 before any work starts. The list named only cancellation and unhandled exceptions. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Suppressed finding answered (round 8, on the merged head)One collapsed finding from review 4839755012 on Correct, and fixed in
|
Closes the
OPERATIONS.mdgap from the 2026-08-02 hub audit (audit run 2026-08-02T16:10Z | hub 69688ec).The hub declares this file for every repo (
appliesTo: "*"inspec/files.json) and checks it for presence only, so its content is entirely this repository's own. It is mandatory because the convention was emerging unevenly across the fleet, with the same operational material landing under ad-hoc names, which is the improvisation the declared destinations exist to prevent.What it carries
The five declared headings, with real content rather than a stub:
csharpier, build,dotnet formatverify,dotnet test,dotnet husky run) and the containerized lint set, plus how to cut a release and how to runverifysafely..bakfiles thatundorestores.verifyneeds, plus reading the Immich API key from a file so it stays out of shell history and the process list.repo-config/is outside the Actions-owned.github/.The document describes only what is on
develop. An earlier revision documented theverifycommand, the exiftool-validateflag and an exit code2, none of which exist on this branch: they are on the unmerged verify work in #35. Copilot caught that across two rounds and it is corrected in550565b. The verify operational content, including that it needs its own database and cannot run inside this project's own container, follows once #35 merges.Note this repository is on the fleet's leading edge here. No repo carried
OPERATIONS.mdat audit time, since the requirement was promoted to hubmainonly in ptr727/ProjectTemplate#517.Verification
markdownlint, editorconfig-checker, and the hub prose gate all report clean. Inline links are correct for this file, which
spec/section-model.mdnames in the inline-link exception rather than the reference-style default.