Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .config/dotnet-tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,21 @@
"isRoot": true,
"tools": {
"csharpier": {
"version": "1.2.6",
"version": "1.3.0",
"commands": [
"csharpier"
],
"rollForward": false
},
"husky": {
"version": "0.9.0",
"version": "0.9.1",
"commands": [
"husky"
],
"rollForward": false
},
"dotnet-outdated-tool": {
"version": "4.7.1",
"version": "4.8.1",
"commands": [
"dotnet-outdated"
],
Expand Down
3 changes: 1 addition & 2 deletions .github/skills/local-strict-review/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,7 @@ Rules that bind this task: quote `code-review`'s "Review the Change" section int
the Return line below, not a PR comment or a coverage marker.
Return: one finding per line, file:line, the concrete failure scenario, no severity theater.
Bounds: read-only. No edit, no stage, no commit, no push, no PR-hosted write of any kind.
If a rule you were given does not cover what you find, stop and report it. Do not guess, and do
not read a governance file to resolve it.
<AGENTS.md's own unresolved-rule closing line, quoted verbatim from "Context and Delegation Discipline", not restated here>
Comment thread
ptr727 marked this conversation as resolved.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
```

**Model tier:** the strongest tier this session can reach, per `AGENTS.md` "Match the model tier to the judgment" and "Never tier down the seat holding the judgment", applied here to the reviewer rather than the author. Run the pass on the same tier that authored the change when only one tier is reachable, a second, adversarially-prompted look still catches what the authoring pass's own "looks ready" judgment did not.
Expand Down
16 changes: 4 additions & 12 deletions .github/skills/resync-a-repo/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,21 +49,13 @@ Preserve the evidence RESYNC.md section 2 requires, and do not leave the finding

## Apply, in this order

1. **The instruction set first.** `AGENTS.md` and `GOVERNANCE.md` verbatim sections, then
`CODESTYLE.md` and `WORKFLOW.md`, including the `AGENTS.md` skill-dependency pointer paragraph
(naming `scripts/skills_install.py` and where the fleet's Skills live) as one more verbatim
unit carried in this same step, not a separate pass. **Before any verbatim re-vendor in this
step, run the `carried-instruction-file-guard` skill's distinctive-phrase probe against the
target file, every time, without exception.** This is not advisory language to weigh against
how routine the diff looks, a diff that looks routine is exactly the shape the
AGENTS.md-overwrite incident took. Do not proceed to the re-vendor until the probe has run and
any local addition it finds has a destination, per that skill's own procedure.
1. **The instruction set first.** `CLAUDE.md`, then `AGENTS.md` and `GOVERNANCE.md` verbatim sections, then `CODESTYLE.md` and `WORKFLOW.md`, including the `AGENTS.md` skill-dependency pointer paragraph (naming `scripts/skills_install.py` and where the fleet's Skills live) as one more verbatim unit carried in this same step, not a separate pass. `CLAUDE.md` is the single `@AGENTS.md`-import file that gets `AGENTS.md` into a Claude Code session's context at all, a separate baseline entry from `AGENTS.md` itself, so carrying one without the other still leaves that provider unconfigured. **Before touching `AGENTS.md`, `GOVERNANCE.md`, `CODESTYLE.md`, or `WORKFLOW.md` in this step, run the `carried-instruction-file-guard` skill's distinctive-phrase probe against the target file, every time, without exception, regardless of whether the update is a verbatim re-vendor or an intent-fidelity edit.** This is not advisory language to weigh against how routine the diff looks, a diff that looks routine is exactly the shape the AGENTS.md-overwrite incident took. Do not proceed to the re-vendor until the probe has run and any local addition it finds has a destination, per that skill's own procedure. `CLAUDE.md` is outside that guard's scope: it carries no mixed or repo-specific content by design, so its re-vendor is an ordinary verbatim-fidelity copy, no probe needed.
2. **Deletions second, before any re-vendor.** Only a `retire` disposition in
`spec/divergences.json` authorizes removing a file, and the removal is swept tree-wide, per
RESYNC.md section 4, before the deletion counts as done.
3. **Verbatim re-vendors** for everything the probe in step 1 cleared. A finding classified
modified rather than stale gets its diff read before being overwritten, since it may be an
improvement the hub should adopt instead of a mistake to erase.
3. **Verbatim re-vendors** for `CLAUDE.md` and everything else the probe in step 1 cleared. A
finding classified modified rather than stale gets its diff read before being overwritten,
since it may be an improvement the hub should adopt instead of a mistake to erase.
4. **Interface workflows.** Honor the named contract, required jobs, the ruleset-bound check name,
the artifact-name handoff, rather than copying bytes.
5. **Settings, rulesets, and secrets.** Run
Expand Down
11 changes: 7 additions & 4 deletions .github/skills/standup-a-repo/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,13 @@ maintainer can supply what section 0A lists.
section 2, then write or repair its `registry/repos.json` entry and confirm it with
`spec/validate.py`.

4. **The instruction set, before authoring anything.** STANDUP.md section 1A: carry `AGENTS.md`,
`GOVERNANCE.md`, `CODESTYLE.md`, `WORKFLOW.md` and `AUDIT.md`, adapted rather than cloned for
the ones that describe a repo, plus `.markdownlint-cli2.jsonc` and `cspell.json`. Read
`CODESTYLE.md` and the `GOVERNANCE.md` documentation-style rules before writing any repo
4. **The instruction set, before authoring anything.** STANDUP.md section 1A: carry `CLAUDE.md`,
`AGENTS.md`, `GOVERNANCE.md`, `CODESTYLE.md`, `WORKFLOW.md` and `AUDIT.md`, adapted rather
than cloned for the ones that describe a repo, plus `.markdownlint-cli2.jsonc` and
`cspell.json`. `CLAUDE.md` is the fixed, verbatim `@AGENTS.md`-import file that gets
`AGENTS.md` into a Claude Code session's context at all, a separate baseline entry from
`AGENTS.md` itself, so carrying one without the other still leaves that provider unconfigured.
Read `CODESTYLE.md` and the `GOVERNANCE.md` documentation-style rules before writing any repo
content of your own, the same window-closes shape as signing in step 1.

5. **Capture the source, if one exists.** STANDUP.md section 1B, only when the repo's content
Expand Down
2 changes: 1 addition & 1 deletion AUDIT.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# AUDIT.md

How this repository audits itself against its committed baseline and reports drift. This is the repo-scoped adaptation of the fleet-wide AUDIT.md kept at the fleet hub, and the hub's fleet-wide audit remains authoritative. General settings, rulesets, and secret names are hub-hosted ground truth (`repo-config/` and `spec/secrets.json` in `github.com/ptr727/ProjectTemplate`, checked from a hub checkout rather than carried here, per [GOVERNANCE.md "Hub-Hosted Tooling"][governance-hub-hosted-tooling]), and the prose authorities are [`GOVERNANCE.md`][governance], [`CODESTYLE.md`][codestyle], and [`WORKFLOW.md`][workflow].
How this repository audits itself against its committed baseline and reports drift. This is the repo-scoped adaptation of the fleet-wide AUDIT.md kept at the fleet hub, and the hub's fleet-wide audit remains authoritative. General settings, rulesets, and secret names are hub-hosted ground truth (`repo-config/` and `spec/secrets.json`, checked from a hub checkout rather than carried here, per [GOVERNANCE.md "Hub-Hosted Tooling"][governance-hub-hosted-tooling]), and the prose authorities are [`GOVERNANCE.md`][governance], [`CODESTYLE.md`][codestyle], and [`WORKFLOW.md`][workflow].

The audit is read-only: it diffs live state against the committed baseline and reports findings, and it never applies changes. The verdict vocabulary is [`WORKFLOW.md`][workflow]'s: **operational / not operational**, **N/A**, **defect**, and the applicable/absent rule.

Expand Down
5 changes: 5 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Claude Code Entry Point

@AGENTS.md

Claude Code reads `CLAUDE.md`, not `AGENTS.md`, so the import line above is what gets this repository's rules into a Claude Code session at all. See `AGENTS.md` for what is authoritative and why. This file carries no rule of its own, and adds none beyond the import line.
20 changes: 10 additions & 10 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,23 @@
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="AwesomeAssertions" Version="9.5.0" />
<PackageVersion Include="AwesomeAssertions" Version="9.6.0" />
<PackageVersion Include="BenchmarkDotNet" Version="0.15.8" />
<PackageVersion Include="CliWrap" Version="3.10.2" />
<PackageVersion Include="CliWrap" Version="3.10.5" />
<PackageVersion Include="coverlet.collector" Version="10.0.1" />
<PackageVersion Include="Microsoft.Data.Sqlite" Version="10.0.10" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.10" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.8.1" />
<PackageVersion Include="Microsoft.Data.Sqlite" Version="10.0.11" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.11" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.9.0" />
<PackageVersion Include="ptr727.Utilities" Version="4.0.28" />
<PackageVersion Include="Serilog" Version="4.4.0" />
<PackageVersion Include="Serilog.Enrichers.Thread" Version="4.0.0" />
<PackageVersion Include="Serilog.Extensions.Logging" Version="10.0.0" />
<PackageVersion Include="Serilog.Sinks.Console" Version="6.1.1" />
<PackageVersion Include="Serilog.Sinks.File" Version="7.0.0" />
<PackageVersion Include="SQLitePCLRaw.bundle_e_sqlite3" Version="3.0.4" />
<PackageVersion Include="System.CommandLine" Version="2.0.10" />
<PackageVersion Include="xunit.analyzers" Version="1.27.0" />
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5" />
<PackageVersion Include="xunit.v3" Version="3.2.2" />
<PackageVersion Include="SQLitePCLRaw.bundle_e_sqlite3" Version="3.0.5" />
<PackageVersion Include="System.CommandLine" Version="2.0.11" />
<PackageVersion Include="xunit.analyzers" Version="2.0.0" />
<PackageVersion Include="xunit.runner.visualstudio" Version="4.0.0" />
<PackageVersion Include="xunit.v3" Version="4.0.0" />
</ItemGroup>
</Project>
3 changes: 2 additions & 1 deletion GOVERNANCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -253,13 +253,14 @@ Every repo's GitHub repository details (the About panel) follow a fixed conventi
## Repository Layout

- [`AGENTS.md`](./AGENTS.md): the agent entry point, carrying context and delegation rules plus the map to the sections above.
- [`CLAUDE.md`](./CLAUDE.md): imports `AGENTS.md`, since Claude Code reads `CLAUDE.md` and never `AGENTS.md` on its own. Carries no rule of its own.
- [`GOVERNANCE.md`](./GOVERNANCE.md), [`CODESTYLE.md`](./CODESTYLE.md), [`WORKFLOW.md`](./WORKFLOW.md), [`AUDIT.md`](./AUDIT.md): the governance and audit docs. This file is the cross-cutting-rules authority, `AUDIT.md` the self-audit of settings, rulesets, and secret names.
- [`.github/copilot-instructions.md`](./.github/copilot-instructions.md): the Copilot bootstrap and review runbook. [`ARCHITECTURE.md`](./ARCHITECTURE.md): this project's architecture, processing pipeline, per-command implementation details, and test layout.
- [`PhotoCleaner/`](./PhotoCleaner/): the console application. [`PhotoCleanerTests/`](./PhotoCleanerTests/): the xUnit test project. [`PhotoCleanerBenchmarks/`](./PhotoCleanerBenchmarks/): the BenchmarkDotNet project. [`PhotoCleaner.slnx`](./PhotoCleaner.slnx) is the solution.
- [`Docker/`](./Docker/): the Dockerfile and the Docker Hub overview the release pipeline publishes.
- [`.github/workflows/`](./.github/workflows/): the pull request gate, the reusable build tasks, and the publisher.
- [`version.json`](./version.json): the version floor. [`Directory.Build.props`](./Directory.Build.props) and [`Directory.Packages.props`](./Directory.Packages.props): the shared build properties and the central package versions.

Branch rulesets, general settings, and secret names are hub-hosted (`repo-config/` and `spec/secrets.json` in `github.com/ptr727/ProjectTemplate`), checked against this repo from a hub checkout rather than carried here, per "Hub-Hosted Tooling".
Branch rulesets, general settings, and secret names are hub-hosted (`repo-config/` and `spec/secrets.json`), checked against this repo from a hub checkout rather than carried here, per "Hub-Hosted Tooling".

After editing a doc, run the linters (see [`OPERATIONS.md`](./OPERATIONS.md) "Local Verification") before commit. The Comments, Character Set, and Line Endings rules above are the frequent regressions, so hold them exactly.
13 changes: 13 additions & 0 deletions OPERATIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,19 @@ python3 [path-to-hub]/scripts/prose_lint.py . --diff origin/develop

Publishing never happens as a side effect of a merge. A release is a `workflow_dispatch` of [`publish-release.yml`](./.github/workflows/publish-release.yml), and the same workflow runs on a weekly schedule so the image picks up base-image and tool updates. Merging to `main` publishes nothing on its own.

### Fix a red Dependabot PR

A grouped update (`nuget-deps`, `actions-deps`) can go red for a reason the bump itself cannot fix, because Dependabot only edits version numbers, never source or project files. Two known failure classes: a formatter tool bump (`csharpier` in [`.config/dotnet-tools.json`](./.config/dotnet-tools.json)) changes a formatting rule and flags an untouched file elsewhere in the tree, or a test SDK bump (`Microsoft.NET.Test.Sdk`, `xunit.v3`) turns on the test project's `IsTestingPlatformApplication` flag, which the .NET 10 SDK now refuses to run through the classic VSTest-based `dotnet test` command it uses. Set `<IsTestingPlatformApplication>false</IsTestingPlatformApplication>` on the test project to keep it on the `xunit.runner.visualstudio` adapter path, since [Microsoft's own opt-in](https://aka.ms/dotnet-test-mtp-error) to the new Microsoft.Testing.Platform runner needs a `global.json` change that breaks the `--collect:"XPlat Code Coverage"` argument this repo's CI relies on.

Diagnose from the failing job's own log rather than the checks summary, since a check only names the job that failed:

```sh
gh pr checks [N] --repo ptr727/PhotoCleaner
gh api repos/ptr727/PhotoCleaner/actions/jobs/[job-id]/logs
Comment thread
ptr727 marked this conversation as resolved.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
```

Push the fix as a commit directly onto the Dependabot branch rather than waiting on a rebase. The merge bot's "Disable auto-merge on maintainer push" job exists for exactly this, so a maintainer push onto a Dependabot branch is expected and safe.

## Backup and Recovery

The repository is the record, and GitHub holds it. Nothing here keeps state outside git.
Expand Down
14 changes: 7 additions & 7 deletions PhotoCleaner/Database.cs
Original file line number Diff line number Diff line change
Expand Up @@ -145,9 +145,9 @@ internal Task InsertAsync(FileRecord record, CancellationToken cancellationToken
async cmd =>
{
cmd.CommandText = """
INSERT OR IGNORE INTO files (path, sha256, sha1, file_size, mtime_ticks, is_processed)
VALUES (@path, @sha256, @sha1, @fileSize, @mtimeTicks, @isProcessed)
""";
INSERT OR IGNORE INTO files (path, sha256, sha1, file_size, mtime_ticks, is_processed)
VALUES (@path, @sha256, @sha1, @fileSize, @mtimeTicks, @isProcessed)
""";
_ = cmd.Parameters.AddWithValue("@path", record.Path);
_ = cmd.Parameters.AddWithValue("@sha256", record.Sha256);
_ = cmd.Parameters.AddWithValue("@sha1", record.Sha1);
Expand All @@ -171,10 +171,10 @@ internal Task UpdateHashesAsync(
async cmd =>
{
cmd.CommandText = """
UPDATE files
SET sha256 = @sha256, sha1 = @sha1, file_size = @fileSize, mtime_ticks = @mtimeTicks, is_processed = 0
WHERE path = @path
""";
UPDATE files
SET sha256 = @sha256, sha1 = @sha1, file_size = @fileSize, mtime_ticks = @mtimeTicks, is_processed = 0
WHERE path = @path
""";
_ = cmd.Parameters.AddWithValue("@path", path);
_ = cmd.Parameters.AddWithValue("@sha256", sha256);
_ = cmd.Parameters.AddWithValue("@sha1", sha1);
Expand Down
2 changes: 2 additions & 0 deletions PhotoCleanerTests/PhotoCleanerTests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
<PropertyGroup>
<IsTestProject>true</IsTestProject>
<RootNamespace>PhotoCleanerTests</RootNamespace>
<!-- Keeps `dotnet test` on the VSTest adapter (xunit.runner.visualstudio) instead of MTP's own entry point, which .NET 10 SDK now hard-blocks under classic `dotnet test`; see https://aka.ms/dotnet-test-mtp-error -->
<IsTestingPlatformApplication>false</IsTestingPlatformApplication>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AwesomeAssertions" />
Expand Down