Skip to content

Commit 4f30435

Browse files
skymanbpclaude
andcommitted
1.1.0: 计划页完善 + 候选忽略 + 备份范围(2026-08-31 用户三批裁定)+ 发布收口
- 计划页:执行态从 journal 折叠(Pm.Plan.planExecs/planExecuted,计划文件永不回写); pm plan list|rm|prune + GUI 计划页标注/淡化/逐行两击删除/清理已执行; prune 保守判据(待裁决残余不算已执行,journal 读不出该根不删) - 候选忽略:pm album ignore|unignore 按内容 sha 记主库 .pm/album-ignore.json (requireMain 预检 + root lock 事务,照片零改动;失效记录只提示不自动清); GUI 归档页候选卡「忽略」+ 折叠区取消;albumCandidates 上游过滤 CLI/GUI 同源 - 备份范围 = 主库 − 暂存区:To-Be-Sync'd 只是中转不进备份盘,收窄在 Pm.Diff.backupDiff 单点(比对/apply 缓存重算/status 全部继承); 清暂存三副本屏障按 sha 认归档层备份见证,不受影响 - serve:--writable 写端点九 → 十二(/api/album/ignore、/api/plan/delete、 /api/plans/prune);GET /api/plans 加 done/failed/executed/lastRunAt - 测试:PlanExecTests 3 例 + Album/ServeP8/Planner 新 6 例,418 → 427; 突变 m1–m7 逐个判红;警告 0;750 行预算达标 - 发布:版本串五处 bump 1.0.0 → 1.1.0、release-notes/v1.1.0.md、 REVIEW-LOG「1.1.0 增补批」节(第一方全量自审 0C/0M/1minor 登记)、 HISTORY 两行、双语 README(功能三/四、命令提要、GUI 两页、路线图)、 DESIGN §5 表 + DESIGN-COMMANDS §9/增补表/状态行、DESIGN-GUI 路由清单 - .gitignore 补 .ccm/(cc-memory 本机工作档,含本机路径,同 .ce/ 惯例) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 9832d31 commit 4f30435

28 files changed

Lines changed: 1100 additions & 88 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ photo-manager.cabal
88

99
# cc-memory plugin data (session-local, not project source)
1010
memory/
11+
.ccm/
1112

1213
# Local scratch
1314
*.local.*

README.md

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ a whole (`pm undo`).
3030
> review gate (**recorded round by round in [docs/REVIEW-LOG.md](docs/REVIEW-LOG.md);
3131
> the convergence verdict is whatever its last section says and is not copied
3232
> here**), and each gate with an observable automated anchor gets a "delete it and
33-
> exactly one test turns red" mutation case (418 tests, 0 GHC warnings); gates
33+
> exactly one test turns red" mutation case (427 tests, 0 GHC warnings); gates
3434
> without an anchor (the GUI has no harness; concurrent interleavings have no
3535
> deterministic observation point) are registered in REVIEW-LOG as residuals rather
3636
> than passed off as covered.
@@ -58,13 +58,17 @@ Adversarial review archive: [docs/reviews/](docs/reviews/).
5858
finished jpgs from the same source into `相册\` (album) as well (grouped with the
5959
finished item: if the finished copy did not land, the album copy is not executed);
6060
`pm album add <event folder>/<file>…` picks already-archived finished jpgs into the
61-
album (`pm album candidates` lists candidates and non-jpg files); `pm convert
61+
album (`pm album candidates` lists candidates and non-jpg files;
62+
`pm album ignore|unignore` ignores/restores unwanted candidates by content sha — a
63+
record in the main library's `.pm` only, zero photo changes); `pm convert
6264
<library-relative path>…` derives a jpg from tif/png etc. under finished/album (local
6365
python + Pillow; the original stays untouched in place) and lands it in the same
6466
finished event folder (`--also-album` also into the album) or in the album.
6567
- **Feature 4 · Backup**: `pm backup` — main library → backup drive, one-way
66-
incremental; the drive is recognised by root UUID, not by drive letter; anything
67-
extra on the backup drive (EXTRA) is only reported, never touched.
68+
incremental; the drive is recognised by root UUID, not by drive letter; **backup
69+
scope = main library minus the staging area** (`To-Be-Sync'd\` is transit only and
70+
never enters the backup drive, ruled 2026-08-31); anything extra on the backup
71+
drive (EXTRA) is only reported, never touched.
6872
- **Feature 5 · Showcase distribution**: `pm vault status` (album ↔ vault nine-state
6973
diff, `--json` compatible with the legacy script), `pm vault push` (copy into a
7074
category + DRIFT adjudication plan + print the explicit git steps — pm never runs
@@ -147,14 +151,19 @@ repos is generated from Settings, pm never runs git), **Sort new photos** (enter
147151
source directory → segment by capture time → per segment enter a place / pick an
148152
existing event → generate a copy plan; the source directory is read-only; "AI suggest
149153
place" only pre-fills), **Archive** (staging event folder → Raw/finished, optionally
150-
into the album at the same time; tick finished jpgs into the album; derive jpgs from
151-
tif/png etc. — all three only produce plans), **Categorise & push** (album photos the
154+
into the album at the same time; tick finished jpgs into the album — unwanted
155+
candidates can be **ignored** (remembered by content sha, undoable any time from the
156+
collapsed list); derive jpgs from tif/png etc. — all three only produce plans),
157+
**Categorise & push** (album photos the
152158
vault does not have yet: pick a category on the thumbnail, fill in place / coordinates /
153159
title record (optionally pre-filled by "AI suggest category/place"), or pick the fourth
154160
button "not syncing for now" → one button "save decisions and generate push plan" does
155-
all three), **Plans** (item-by-item detail; since 0.6.0 pending plans can be **executed
156-
directly** — the same button clicked twice to confirm, the execution chain is the same
157-
as `pm apply`, `pm undo` works afterwards), **Settings** (paths and concurrency: vault /
161+
all three), **Plans** (item-by-item detail + execution-state badges — executed /
162+
partial / not executed, folded from the journal, executed rows dimmed; plans can be
163+
deleted one by one or pruned in one click ("clear executed"), which only removes the
164+
regenerable plan files; since 0.6.0 pending plans can be **executed directly** — the
165+
same button clicked twice to confirm, the execution chain is the same as `pm apply`,
166+
`pm undo` works afterwards), **Settings** (paths and concurrency: vault /
158167
photos.json / worker count editable, backup drive registrable, the portfolio repo path
159168
and both repos' push targets for the publish commands customisable; changes take
160169
effect immediately, and so does `pm config set` run in a terminal; the main library
@@ -168,10 +177,12 @@ pm sort <source dir> # sort new photos: segment by EXIF capture tim
168177
pm sort <source> --place Atlanta --from 2026-08-01 --to 2026-08-03 # generate a copy plan
169178
pm import [--also-album] # staging → Raw\<year>\<event>-Raw + 成片\<event> archive plan (--also-album: finished jpgs also into the album)
170179
pm album add 26-06-R66/_DSC9621.jpg … # finished → album (flat, jpg only; same name / different bytes in the album → awaits adjudication)
171-
pm album candidates # read-only: finished jpgs not yet in the album (per event folder) + non-jpg under finished/album (→ pm convert)
180+
pm album candidates # read-only: finished jpgs not yet in the album (per event folder) + non-jpg under finished/album (→ pm convert) + ignored list
181+
pm album ignore 26-06-R66/x.jpg … # ignore a candidate: recorded by content sha in the main library's .pm/album-ignore.json (zero photo
182+
# changes; survives renames/moves, a re-export with new bytes reappears); unignore takes a path or sha
172183
pm convert 成片/26-06-R66/x.tif --also-album # non-jpg → derived jpg (Pillow, written to .pm/derived) → same finished event folder (+ album) plan; original untouched
173184
pm backup init E:\Photography # once: register the backup drive (recognised by UUID, not by drive letter)
174-
pm backup # main library → backup drive, one-way incremental (EXTRA is only reported, never touched)
185+
pm backup # main library → backup drive, one-way incremental (staging To-Be-Sync'd excluded; EXTRA is only reported, never touched)
175186
pm clean staging # only cleans staging files that have same-sha copies in BOTH the archive tier and the backup drive
176187
pm vault status # album ↔ vault showcase nine-state diff (six states compatible with sync_photos.py,
177188
# --json copies those six field by field; the other three: UNPUSHABLE/UNSTABLE/HELD)
@@ -191,6 +202,8 @@ pm versions # version groups / exact duplicates outside the
191202
pm dedupe # exact duplicates → a quarantine plan adjudicated copy by copy (all await adjudication; which to keep
192203
# is not chosen for you — approve each with pm resolve --item N --unskip)
193204
205+
pm plan [list] # list plans with execution state (executed / partial / not executed — folded from the journal; plan files are never written back)
206+
pm plan rm <id> … / pm plan prune # delete plan files / one-click prune of executed plans (only regenerable files are removed; journal/undo unaffected)
194207
pm apply <planId> # execute a plan (--dry full preview / --only 1,3-5 partial execution)
195208
pm resolve <id> --item N [--unskip] # skip that item (default action) / --unskip restores it to pending;
196209
# dedupe plans all await adjudication — only items approved with --unskip execute on apply
@@ -278,11 +291,12 @@ path).
278291
spawn / hand over the token / kill; everything goes through `pm serve` (127.0.0.1 +
279292
random port + Bearer token with constant-time comparison + Host/Origin checks);
280293
serve has three authorisation levels: **read-only by default**; `--writable` opens
281-
nine write endpoints — generate push / sort / archive / album / convert plans (writes
294+
twelve write endpoints — generate push / sort / archive / album / convert plans (writes
282295
`.pm/plans`; convert additionally writes derived files into `.pm/derived`, the
283-
originals untouched), record "not syncing for now" decisions and photo records, edit
284-
configuration (main library path read-only), register the backup drive — **none of
285-
them touch photo bytes**; the AI suggestion endpoint `POST /api/suggest` is
296+
originals untouched), record "not syncing for now" decisions and photo records,
297+
record "ignore this candidate" decisions, delete/prune plan files (only the
298+
regenerable `.pm/plans` files), edit configuration (main library path read-only),
299+
register the backup drive — **none of them touch photo bytes**; the AI suggestion endpoint `POST /api/suggest` is
286300
read-only level and only produces suggestions; only `--allow-apply` opens `POST
287301
/api/apply` (the sole endpoint that touches photo bytes; since 0.6.0 the GUI passes
288302
it at launch, the page confirms with two clicks; execution happens inside the serve
@@ -349,7 +363,7 @@ All measured on the real library (commands and sources reproducible, not estimat
349363
|---|---|---|
350364
| Incremental scan (4633 files, of which 122 newly hashed / 14.0 GiB, workers=16) | 19.4 s | `pm scan` 2026-08-26 |
351365
| First full hash (480 GiB class) | ~10–25 min | first library build, recorded |
352-
| Test suite (418 tests, whole suite serialised — required by process-level stdout redirection) | 10–40 s | `stack test` |
366+
| Test suite (427 tests, whole suite serialised — required by process-level stdout redirection) | 10–40 s | `stack test` |
353367
| GHC warnings | 0 | `stack build` |
354368
| Adversarial review gate | recorded per round (NO-GO findings verified first-hand → class-level fix → focused re-review; convergence = the last section's verdict) | [REVIEW-LOG](docs/REVIEW-LOG.md) |
355369
| Mutation verification | one mutation per load-bearing gate with an observable automated anchor, its paired test turns red (all discrimination tables of rounds 34–36 and the P7 rounds pass; gates without an anchor registered as residuals) | REVIEW-LOG convergence evidence per round |
@@ -463,6 +477,12 @@ binaries in a Release are not built on the author's machine.
463477
"Archive" + two AI entry points (`claude -p` read-only, pre-fill only); one shell for
464478
external processes, `Pm.Subprocess`; full first-party review + two Opus gate rounds +
465479
a single CI chain (this release's binaries are produced by GitHub Actions).
480+
- ~~Plans page completion / candidate ignore / backup scope~~ ✅ 1.1.0 (three user
481+
rulings 2026-08-31): execution-state badges folded from the journal + `pm plan
482+
list|rm|prune` (plan files are never written back); `pm album ignore|unignore` by
483+
content sha (a local `.pm` decision, zero photo changes); backup scope = main
484+
library minus the staging area (`To-Be-Sync'd\` is transit only; narrowed at the
485+
single point `Pm.Diff.backupDiff`).
466486

467487
**Known limitations**:
468488

0 commit comments

Comments
 (0)