immich server/: ac run immich-fault

immich server/ @ 02063972e8 → ac51228171 on ac/s7-fault: run status stopped.

no whole-PR certificate run status stopped: the run ended before its whole-PR certificate

Certified commits
3
R1 3
Files moved
18
1 file pinned
Specifier edits
59
summed over the steps
Rolled-back attempts
1
each one certified FAIL, then rolled back
Wall time
13 m 37 s
one invocation
Plan cost when recorded
$0.1998
planner spend in this run $0.0000
FieldValue
Targetimmich (profile), package root server/, github.com/immich-app/immich
Commitsbase 02063972e8 → head ac512281719130a99e5b3ede6cb06b648d294c6c; branch ac/s7-fault (a local branch; the certificates name its trees)
Treesbase 424df72c3407dd1e7df5ee4e81a1d5fcd85cc01c
Planfrom the plan file plan.yaml: 59 steps, 367 moves, sha256 b4c7c548883c0635951f893c7717727f3d086a0c4e23b2db442e796dd416cc7f
Rungs requestedR0, R1
Whole-PR digest–
Costplanner spend in this run $0.0000; the plan cost $0.1998 when it was recorded
Injected fault--inject-fault 2:M2: applied to step 2, M2 (Drop one importer rewrite): server/src/services/workflow-execution.service.ts: import specifier 'src/modules/album/album.dto.js' left as in base, 'src/dtos/album.dto.js'

The plan: Nemotron draws the map

Deterministic seeds decided where files go; plan.yaml marks 8 placements by the planner and 2 by the critic. This run dir has no plan.json, so the report can't name the models that made the plan. The plan has 38 modules, 20 platform folders and 1 shared folder over 269 planned files: 259 from the deterministic seeds draft, 8 by the planner, 2 by the critic. 30 placements are marked contested.

Placements the planner and the critic decided (10)
FileGroupDecided byRationale
src/app.common.tsplatform/bootstrapplannerBootstrap wiring for Express and telemetry; belongs to platform plumbing.
src/controllers/app.controller.tsplatform/bootstrapplannerRoot app controller only serves health/info endpoints; platform plumbing.
src/dtos/editing.dto.tsmodule/assetplannerEditing DTOs are used by asset edit flows.
src/dtos/env.dto.tsshared/sharedplannerEnvironment schema DTO is a pure type shared across config.
src/dtos/onboarding.dto.tsmodule/system-metadataplannerOnboarding DTOs used by system‑metadata controller.
src/repositories/app.repository.tsmodule/maintenancecriticOnly the maintenance module consumes this repository; fixes the impure‑shared warning.
src/utils/editor.tsplatform/mediacriticUtility is only imported by platform/media; moving it out of shared resolves the single‑consumer warning.
src/utils/object.tsshared/sharedplannerPlain object utilities.
src/utils/pagination.tsshared/sharedplannerPagination helpers used everywhere.
src/utils/profile-image.tsmodule/userplannerProfile image generation used by user feature.

plan.yaml · the plan cost $0.1998 when it was recorded; a replay from the cassette costs $0

Before and after: the module graph

Folders
14 → 17
head: 3 modules, 14 other folders
Files that changed folder
18 of 563
source files under the module root, tests included
Import pairs inside one folder
23.4% → 23.0%
479 → 472 file pairs
Largest import cycle
12 of 14 → 15 of 17
strongly connected folders, the composition root left out

At the base commit the 563 source files under src/ sit in 14 folders outside the layout; at the head commit 18 sit in 3 feature modules and 545 are still in 14 folders outside the layout. The file-level import pairs are the same 2,051 on both sides (a certified move keeps every import resolving to the same file); only their grouping changes. The share of pairs inside one folder falls: 479 of 2,051 (23.4%) at base, 472 of 2,051 (23.0%) at head. The largest blocks of internal pairs are src/schema/ 152, src/services/ 111, src/repositories/ 89 at base and src/schema/ 152, src/services/ 104, src/repositories/ 85 at head. Grouping by feature doesn't remove the imports between features; it makes them visible. The findings below name what ties the modules together (1 god-injection base class and 1 module cycle). R4 was not part of this run, so no boundary rules hold them yet.

Import cycles at base: 12 folders

src/ src/commands/ src/controllers/ src/cores/ src/dtos/ src/emails/ src/maintenance/ src/middleware/ src/repositories/ src/schema/ src/services/ src/utils/

Import cycles at head: 15 folders

module/activity module/album module/api-key src/ src/commands/ src/controllers/ src/cores/ src/dtos/ src/emails/ src/maintenance/ src/middleware/ src/repositories/ src/schema/ src/services/ src/utils/

Files flow: where the 563 source files under src/ went
module/platform/shared/left where it was (not in the layout)
src/schema/ 168src/services/ 102src/controllers/ 82src/repositories/ 63src/dtos/ 55src/utils/ 43src/ 10src/commands/ 10src/emails/ 9src/middleware/ 8src/maintenance/ 6src/workers/ 3src/bin/ 2src/cores/ 2module/album 6module/activity 6module/api-key 6src/schema/ 168src/services/ 96src/controllers/ 76src/repositories/ 59src/dtos/ 53src/utils/ 43src/commands/ 10src/emails/ 9src/middleware/ 8src/maintenance/ 6src/workers/ 3src/bin/ 2src/cores/ 2src/ 10

Left: the folders at the base commit, by size. Right: where their files are at the head commit, grouped by layout kind. A band is a number of files; every node is at least one label tall, so small groups are drawn larger than their share.

Table view
Base folderHead folderFiles
src/src/10
src/bin/src/bin/2
src/commands/src/commands/10
src/controllers/module/activity2
src/controllers/module/album2
src/controllers/module/api-key2
src/controllers/src/controllers/76
src/cores/src/cores/2
src/dtos/module/activity1
src/dtos/module/api-key1
src/dtos/src/dtos/53
src/emails/src/emails/9
src/maintenance/src/maintenance/6
src/middleware/src/middleware/8
src/repositories/module/activity1
src/repositories/module/album2
src/repositories/module/api-key1
src/repositories/src/repositories/59
src/schema/src/schema/168
src/services/module/activity2
src/services/module/album2
src/services/module/api-key2
src/services/src/services/96
src/utils/src/utils/43
src/workers/src/workers/3
Before: 14 folders at 02063972e8
imports along the orderimports against the order (they close cycles)imports inside one folder (diagonal)
src/bin/src/workers/src/services/src/controllers/src/repositories/src/emails/src/schema/src/dtos/src/utils/src/maintenance/src/cores/src/src/middleware/src/commands/src/bin/src/workers/src/services/src/controllers/src/repositories/src/emails/src/schema/src/dtos/src/utils/src/maintenance/src/cores/src/src/middleware/src/commands/

1,572 file pairs cross folders in 73 folder edges. Rows import columns. Folders are ordered importers first (strongly connected sets, then a greedy order inside each), so a cell below the diagonal is an import against the order. Darker means more file pairs (1, 2–3, 4–9, 10–29, 30+).

Table view
Importer folderImported folderFile pairsType-only
src/src/commands/10
src/src/controllers/10
src/src/dtos/70
src/src/maintenance/60
src/src/middleware/60
src/src/repositories/180
src/src/schema/61
src/src/services/60
src/src/utils/50
src/bin/src/20
src/bin/src/repositories/60
src/bin/src/services/10
src/bin/src/utils/20
src/commands/src/10
src/commands/src/dtos/10
src/commands/src/services/90
src/controllers/src/1171
src/controllers/src/dtos/11234
src/controllers/src/middleware/500
src/controllers/src/repositories/90
src/controllers/src/services/543
src/controllers/src/utils/130
src/cores/src/31
src/cores/src/repositories/80
src/cores/src/utils/20
src/dtos/src/909
src/dtos/src/repositories/10
src/dtos/src/schema/30
src/dtos/src/utils/110
src/emails/src/repositories/40
src/emails/src/utils/30
src/maintenance/src/71
src/maintenance/src/controllers/22
src/maintenance/src/cores/10
src/maintenance/src/dtos/70
src/maintenance/src/middleware/10
src/maintenance/src/repositories/121
src/maintenance/src/services/75
src/maintenance/src/utils/50
src/middleware/src/51
src/middleware/src/dtos/30
src/middleware/src/repositories/70
src/middleware/src/services/30
src/middleware/src/utils/60
src/repositories/src/11110
src/repositories/src/dtos/281
src/repositories/src/emails/40
src/repositories/src/schema/850
src/repositories/src/utils/300
src/schema/src/724
src/schema/src/dtos/50
src/schema/src/repositories/30
src/schema/src/utils/30
src/services/src/12925
src/services/src/cores/140
src/services/src/dtos/1241
src/services/src/maintenance/10
src/services/src/middleware/10
src/services/src/repositories/10918
src/services/src/schema/110
src/services/src/utils/1190
src/utils/src/348
src/utils/src/cores/30
src/utils/src/dtos/210
src/utils/src/middleware/10
src/utils/src/repositories/201
src/utils/src/schema/20
src/workers/src/60
src/workers/src/maintenance/10
src/workers/src/middleware/10
src/workers/src/repositories/60
src/workers/src/services/10
src/workers/src/utils/30
After: 17 folders at ac51228171
imports along the orderimports against the order (they close cycles)imports inside one folder (diagonal)
src/bin/src/workers/src/services/src/controllers/src/repositories/module/activitymodule/albummodule/api-keysrc/emails/src/schema/src/dtos/src/utils/src/maintenance/src/cores/src/src/middleware/src/commands/src/bin/src/workers/src/services/src/controllers/src/repositories/module/activitymodule/albummodule/api-keysrc/emails/src/schema/src/dtos/src/utils/src/maintenance/src/cores/src/src/middleware/src/commands/

1,579 file pairs cross folders in 100 folder edges. Rows import columns. Folders are ordered importers first (strongly connected sets, then a greedy order inside each), so a cell below the diagonal is an import against the order. Darker means more file pairs (1, 2–3, 4–9, 10–29, 30+).

Table view
Importer folderImported folderFile pairsType-only
module/activitysrc/100
module/activitysrc/dtos/31
module/activitysrc/middleware/10
module/activitysrc/schema/20
module/activitysrc/services/10
module/activitysrc/utils/10
module/albumsrc/90
module/albumsrc/dtos/91
module/albumsrc/middleware/10
module/albumsrc/schema/50
module/albumsrc/services/10
module/albumsrc/utils/50
module/api-keysrc/100
module/api-keysrc/dtos/21
module/api-keysrc/middleware/10
module/api-keysrc/schema/20
module/api-keysrc/services/10
module/api-keysrc/utils/30
src/src/commands/10
src/src/controllers/10
src/src/dtos/70
src/src/maintenance/60
src/src/middleware/60
src/src/repositories/180
src/src/schema/61
src/src/services/60
src/src/utils/50
src/bin/src/20
src/bin/src/repositories/60
src/bin/src/services/10
src/bin/src/utils/20
src/commands/src/10
src/commands/src/dtos/10
src/commands/src/services/90
src/controllers/module/activity10
src/controllers/module/album10
src/controllers/module/api-key10
src/controllers/src/1081
src/controllers/src/dtos/10431
src/controllers/src/middleware/470
src/controllers/src/repositories/90
src/controllers/src/services/513
src/controllers/src/utils/130
src/cores/src/31
src/cores/src/repositories/80
src/cores/src/utils/20
src/dtos/src/859
src/dtos/src/repositories/10
src/dtos/src/schema/30
src/dtos/src/utils/110
src/emails/src/repositories/40
src/emails/src/utils/30
src/maintenance/src/71
src/maintenance/src/controllers/22
src/maintenance/src/cores/10
src/maintenance/src/dtos/70
src/maintenance/src/middleware/10
src/maintenance/src/repositories/121
src/maintenance/src/services/75
src/maintenance/src/utils/50
src/middleware/src/51
src/middleware/src/dtos/30
src/middleware/src/repositories/70
src/middleware/src/services/30
src/middleware/src/utils/60
src/repositories/module/activity10
src/repositories/module/album20
src/repositories/module/api-key10
src/repositories/src/10110
src/repositories/src/dtos/271
src/repositories/src/emails/40
src/repositories/src/schema/760
src/repositories/src/utils/270
src/schema/src/724
src/schema/src/dtos/50
src/schema/src/repositories/30
src/schema/src/utils/30
src/services/module/activity30
src/services/module/album40
src/services/module/api-key20
src/services/src/12425
src/services/src/cores/140
src/services/src/dtos/1151
src/services/src/maintenance/10
src/services/src/middleware/10
src/services/src/repositories/10418
src/services/src/schema/110
src/services/src/utils/1130
src/utils/src/348
src/utils/src/cores/30
src/utils/src/dtos/210
src/utils/src/middleware/10
src/utils/src/repositories/201
src/utils/src/schema/20
src/workers/src/60
src/workers/src/maintenance/10
src/workers/src/middleware/10
src/workers/src/repositories/60
src/workers/src/services/10
src/workers/src/utils/30

Computed by architecture-cleaner analyzer (TypeScript module resolution), production importers under the module root with TypeScript 6.0.3, from git objects of both commits; stored in graph.json. Folders outside the layout are collapsed to their first directory under the module root, so this graph can count fewer folders than the findings log.

The ladder

One transformation per commit: each of the 3 committed steps was certified against its parent before the next one started, with the static checks (K0, K1, K2, K3a, K3b, K3c, K4, K6, K7). The rung certificate of R1 (partial: the run stopped inside the rung) adds the oracles (K8: tsc, unit, openapi). There is no whole-PR certificate (run status: stopped). A FAIL rolls the commit back, and the step replans with the plan entries the failure names pinned in place.

RungStatusStepsRung certificateDuration
R0passed––4 m 04 s
R1running3 certified, 35 pendingPASS (static checks and oracles) partial9 m 33 s
R2not-requested0 certified, 21 pending–0.0 s
R3not-requested0 certified–0.0 s
R4not-requested0 certified–0.0 s
Wall time: 13 m 37 s
preflightbaseline test runssteps (move, commit, certify)rung certificates with oracles
3 m 50 s7 m 11 s2 m 22 s
Table view
PhaseTime
preflight14.2 s
baseline test runs3 m 50 s
steps (move, commit, certify)7 m 11 s
rung certificates with oracles2 m 22 s
other (findings, report, bookkeeping)0.1 s
wall time13 m 37 s

Failed attempts and rollbacks (1)

1 attempt failed (certified FAIL and rolled back). A commit that fails its certificate is rolled back. They are recorded here and in run.json, never hidden.

Step 2 module/album, attempt 1 of 2; the step ended certified

attempt 1: failed, rolled back round 1, from fd2b27e474, 7 moves, 1 m 44 s

injected fault M2 M2 (Drop one importer rewrite): server/src/services/workflow-execution.service.ts: import specifier 'src/modules/album/album.dto.js' left as in base, 'src/dtos/album.dto.js'

certificate FAIL (K3a, K3b, K3c): K3a server/src/services/workflow-execution.service.ts: line 14: import 'src/dtos/album.dto.js': expected file server/src/modules/album/album.dto.ts (typescript), got unresolved under server/tsconfig.build.json, server/tsconfig.json; K3b server/src/services/workflow-execution.service.ts: site 7 'src/dtos/album.dto.js' (import): base server/src/modules/album/album.dto.ts, head unresolved; K3c server/src/services/workflow-execution.service.ts: nest-cli/emit: import 'src/dtos/album.dto.js' resolves to unresolved (ERR_MODULE_NOT_FOUND), expected server/src/modules/album/album.dto.ts; base server/src/services/workflow-execution.service.ts 'src/dtos/album.dto.js'

Offending: server/src/services/workflow-execution.service.ts

Pinned by this failure: server/src/dtos/album.dto.ts

Certificate of the rolled-back commit: FAIL (static checks) certificates/failed/002-R1-module-album-attempt1.json

certificate JSON · digest sha256:72254a76e23cdf2167d261fbf37d829a80b34856d77581e94722cc575009b8e4

ac certify fd2b27e474..1389c1c3ae (package root server/), profile immich
trees: e6c86400e6..b7b27b645c
pathMap: 7 moves, 20 edited in place, 3437 unchanged, 0 added, 0 deleted

PASS  K0  Envelope: inside the envelope: 678 TypeScript vs 3 JavaScript sources under server; no automock, nested package scope, symlink, same-stem pair or unreadable runner config around 7 moved files (5.0 s)
PASS  K1  Move-set integrity: 7 moves, 20 files edited in place, nothing added or deleted (2.0 s)
PASS  K2  Byte-span confinement: 35 specifier spans changed in 25 files; every other byte identical (0.8 s)
FAIL  K3a Resolution identity under TypeScript: 1 violations (1 resolution mismatches) (1.5 s)
        server/src/services/workflow-execution.service.ts: line 14: import 'src/dtos/album.dto.js': expected file server/src/modules/album/album.dto.ts (typescript), got unresolved under server/tsconfig.build.json, server/tsconfig.json
FAIL  K3b Resolution identity under the test runner: vitest 4.1.11: 1 violations (1 dangling) (8.0 s)
        server/src/services/workflow-execution.service.ts: site 7 'src/dtos/album.dto.js' (import): base server/src/modules/album/album.dto.ts, head unresolved
FAIL  K3c Resolution identity under the runtime: 1 violations under nest-cli/emit (6.8 s)
        server/src/services/workflow-execution.service.ts: nest-cli/emit: import 'src/dtos/album.dto.js' resolves to unresolved (ERR_MODULE_NOT_FOUND), expected server/src/modules/album/album.dto.ts; base server/src/services/workflow-execution.service.ts 'src/dtos/album.dto.js'
PASS  K4  Location scan: 7 moved files expose no location; no computed path reaches a change; no old path in code, config or CI (8 findings in docs, comments or ungated coverage settings) (6.7 s)
        finding (opaque-dynamic-load) server/src/repositories/database.repository.ts: line 510: import(filePath) loads a module by a path K4 can't evaluate
        finding (old-path-in-docs) .github/pull_request_template.md: line 36: 'src/services/' is the directory server/src/services, whose contents change with the move of server/src/services/album.service.spec.ts (moved to server/src/modules/album/album.service.spec.ts)
        finding (old-path-in-docs) .github/pull_request_template.md: line 37: 'src/repositories/' is the directory server/src/repositories, whose contents change with the move of server/src/repositories/album-user.repository.ts (moved to server/src/modules/album/album-user.repository.ts)
        … 6 more findings
PASS  K6  Test inventory: vitest 4.1.11: 105 test files, 2337 tests, per-file load status and case names equal (50.3 s)
PASS  K7  Emit layout: 7 emitting programs keep their outDir and rootDir (1 by TypeScript's default) (0.5 s)
        finding (default-root-dir) e2e/tsconfig.json: emits into e2e/dist without a rootDir; TypeScript 6.0+ and before 6.0's default stays e2e and e2e

verdict: FAIL (static checks only)
digest: sha256:72254a76e23cdf2167d261fbf37d829a80b34856d77581e94722cc575009b8e4

The step then replanned without 1 pinned file: attempt 2 passed.

Every step

Open a step for its moves, its attempts and its certificate.

001 R1 module/activity certified 6 files moved, 17 specifier edits in 12 files, 2 m 25 s

Commit fd2b27e4749f4fe14a54c2474a00c0bffb4513ec, tree e6c86400e6aba4584b58dce69f982ffda608e85f

6 paths moved
server/src/controllers/activity.controller.spec.ts
  → server/src/modules/activity/activity.controller.spec.ts
server/src/controllers/activity.controller.ts
  → server/src/modules/activity/activity.controller.ts
server/src/dtos/activity.dto.ts
  → server/src/modules/activity/activity.dto.ts
server/src/repositories/activity.repository.ts
  → server/src/modules/activity/activity.repository.ts
server/src/services/activity.service.spec.ts
  → server/src/modules/activity/activity.service.spec.ts
server/src/services/activity.service.ts
  → server/src/modules/activity/activity.service.ts

attempt 1: passed round 1, from 02063972e8, 6 moves, 2 m 25 s

Certificate: PASS (static checks) certificates/001-R1-module-activity.json

certificate JSON · digest sha256:909262855d4acc3f478bb34600f1209cda980a479c70a3cf7232da297ffac519

ac certify 02063972e8..37bbc12433 (package root server/), profile immich
trees: 424df72c34..e6c86400e6
pathMap: 6 moves, 8 edited in place, 3450 unchanged, 0 added, 0 deleted

PASS  K0  Envelope: inside the envelope: 678 TypeScript vs 3 JavaScript sources under server; no automock, nested package scope, symlink, same-stem pair or unreadable runner config around 6 moved files (9.6 s)
PASS  K1  Move-set integrity: 6 moves, 8 files edited in place, nothing added or deleted (1.6 s)
PASS  K2  Byte-span confinement: 17 specifier spans changed in 12 files; every other byte identical (0.8 s)
PASS  K3a Resolution identity under TypeScript: 6647 sites in 1100 files resolve to map(base) under every program (458 files in several); 17 rewritten specifiers keep their form (1.5 s)
PASS  K3b Resolution identity under the test runner: vitest 4.1.11: 4837 sites in 672 files resolve to map(base) (8.2 s)
PASS  K3c Resolution identity under the runtime: 6192 runtime sites resolve to map(base) under nest-cli/emit (3442 rewritten by the build) (6.7 s)
PASS  K4  Location scan: 6 moved files expose no location; no computed path reaches a change; no old path in code, config or CI (8 findings in docs, comments or ungated coverage settings) (7.0 s)
        finding (opaque-dynamic-load) server/src/repositories/database.repository.ts: line 510: import(filePath) loads a module by a path K4 can't evaluate
        finding (old-path-in-docs) .github/pull_request_template.md: line 36: 'src/services/' is the directory server/src/services, whose contents change with the move of server/src/services/activity.service.spec.ts (moved to server/src/modules/activity/activity.service.spec.ts)
        finding (old-path-in-docs) .github/pull_request_template.md: line 37: 'src/repositories/' is the directory server/src/repositories, whose contents change with the move of server/src/repositories/activity.repository.ts (moved to server/src/modules/activity/activity.repository.ts)
        … 6 more findings
PASS  K6  Test inventory: vitest 4.1.11: 105 test files, 2337 tests, per-file load status and case names equal (98.1 s)
PASS  K7  Emit layout: 7 emitting programs keep their outDir and rootDir (1 by TypeScript's default) (0.7 s)
        finding (default-root-dir) e2e/tsconfig.json: emits into e2e/dist without a rootDir; TypeScript 6.0+ and before 6.0's default stays e2e and e2e

verdict: PASS (static checks only)
digest: sha256:909262855d4acc3f478bb34600f1209cda980a479c70a3cf7232da297ffac519
002 R1 module/album certified 1 failed attempt 6 files moved, 28 specifier edits in 20 files, 3 m 15 s

Commit 3876d5c90ee7a1290810181b572a1d92e7f36dc5, tree 7c25e3c579de431aa67a951a497dbfb36026a76e

6 paths moved
server/src/controllers/album.controller.spec.ts
  → server/src/modules/album/album.controller.spec.ts
server/src/controllers/album.controller.ts
  → server/src/modules/album/album.controller.ts
server/src/repositories/album-user.repository.ts
  → server/src/modules/album/album-user.repository.ts
server/src/repositories/album.repository.ts
  → server/src/modules/album/album.repository.ts
server/src/services/album.service.spec.ts
  → server/src/modules/album/album.service.spec.ts
server/src/services/album.service.ts
  → server/src/modules/album/album.service.ts

attempt 1: failed, rolled back round 1, from fd2b27e474, 7 moves, 1 m 44 s

injected fault M2 M2 (Drop one importer rewrite): server/src/services/workflow-execution.service.ts: import specifier 'src/modules/album/album.dto.js' left as in base, 'src/dtos/album.dto.js'

certificate FAIL (K3a, K3b, K3c): K3a server/src/services/workflow-execution.service.ts: line 14: import 'src/dtos/album.dto.js': expected file server/src/modules/album/album.dto.ts (typescript), got unresolved under server/tsconfig.build.json, server/tsconfig.json; K3b server/src/services/workflow-execution.service.ts: site 7 'src/dtos/album.dto.js' (import): base server/src/modules/album/album.dto.ts, head unresolved; K3c server/src/services/workflow-execution.service.ts: nest-cli/emit: import 'src/dtos/album.dto.js' resolves to unresolved (ERR_MODULE_NOT_FOUND), expected server/src/modules/album/album.dto.ts; base server/src/services/workflow-execution.service.ts 'src/dtos/album.dto.js'

Offending: server/src/services/workflow-execution.service.ts

Pinned by this failure: server/src/dtos/album.dto.ts

Certificate of the rolled-back commit: FAIL (static checks) certificates/failed/002-R1-module-album-attempt1.json

certificate JSON · digest sha256:72254a76e23cdf2167d261fbf37d829a80b34856d77581e94722cc575009b8e4

ac certify fd2b27e474..1389c1c3ae (package root server/), profile immich
trees: e6c86400e6..b7b27b645c
pathMap: 7 moves, 20 edited in place, 3437 unchanged, 0 added, 0 deleted

PASS  K0  Envelope: inside the envelope: 678 TypeScript vs 3 JavaScript sources under server; no automock, nested package scope, symlink, same-stem pair or unreadable runner config around 7 moved files (5.0 s)
PASS  K1  Move-set integrity: 7 moves, 20 files edited in place, nothing added or deleted (2.0 s)
PASS  K2  Byte-span confinement: 35 specifier spans changed in 25 files; every other byte identical (0.8 s)
FAIL  K3a Resolution identity under TypeScript: 1 violations (1 resolution mismatches) (1.5 s)
        server/src/services/workflow-execution.service.ts: line 14: import 'src/dtos/album.dto.js': expected file server/src/modules/album/album.dto.ts (typescript), got unresolved under server/tsconfig.build.json, server/tsconfig.json
FAIL  K3b Resolution identity under the test runner: vitest 4.1.11: 1 violations (1 dangling) (8.0 s)
        server/src/services/workflow-execution.service.ts: site 7 'src/dtos/album.dto.js' (import): base server/src/modules/album/album.dto.ts, head unresolved
FAIL  K3c Resolution identity under the runtime: 1 violations under nest-cli/emit (6.8 s)
        server/src/services/workflow-execution.service.ts: nest-cli/emit: import 'src/dtos/album.dto.js' resolves to unresolved (ERR_MODULE_NOT_FOUND), expected server/src/modules/album/album.dto.ts; base server/src/services/workflow-execution.service.ts 'src/dtos/album.dto.js'
PASS  K4  Location scan: 7 moved files expose no location; no computed path reaches a change; no old path in code, config or CI (8 findings in docs, comments or ungated coverage settings) (6.7 s)
        finding (opaque-dynamic-load) server/src/repositories/database.repository.ts: line 510: import(filePath) loads a module by a path K4 can't evaluate
        finding (old-path-in-docs) .github/pull_request_template.md: line 36: 'src/services/' is the directory server/src/services, whose contents change with the move of server/src/services/album.service.spec.ts (moved to server/src/modules/album/album.service.spec.ts)
        finding (old-path-in-docs) .github/pull_request_template.md: line 37: 'src/repositories/' is the directory server/src/repositories, whose contents change with the move of server/src/repositories/album-user.repository.ts (moved to server/src/modules/album/album-user.repository.ts)
        … 6 more findings
PASS  K6  Test inventory: vitest 4.1.11: 105 test files, 2337 tests, per-file load status and case names equal (50.3 s)
PASS  K7  Emit layout: 7 emitting programs keep their outDir and rootDir (1 by TypeScript's default) (0.5 s)
        finding (default-root-dir) e2e/tsconfig.json: emits into e2e/dist without a rootDir; TypeScript 6.0+ and before 6.0's default stays e2e and e2e

verdict: FAIL (static checks only)
digest: sha256:72254a76e23cdf2167d261fbf37d829a80b34856d77581e94722cc575009b8e4

attempt 2: passed round 1, from fd2b27e474, 6 moves, 1 m 31 s

Certificate: PASS (static checks) certificates/002-R1-module-album.json

certificate JSON · digest sha256:78e6b72fe0cab9da2574f6a455ed76aba71f3ef704d946bbdb494822634d0562

ac certify fd2b27e474..21972e12b6 (package root server/), profile immich
trees: e6c86400e6..7c25e3c579
pathMap: 6 moves, 16 edited in place, 3442 unchanged, 0 added, 0 deleted

PASS  K0  Envelope: inside the envelope: 678 TypeScript vs 3 JavaScript sources under server; no automock, nested package scope, symlink, same-stem pair or unreadable runner config around 6 moved files (5.0 s)
PASS  K1  Move-set integrity: 6 moves, 16 files edited in place, nothing added or deleted (2.0 s)
PASS  K2  Byte-span confinement: 28 specifier spans changed in 20 files; every other byte identical (0.8 s)
PASS  K3a Resolution identity under TypeScript: 6647 sites in 1100 files resolve to map(base) under every program (458 files in several); 28 rewritten specifiers keep their form (1.5 s)
PASS  K3b Resolution identity under the test runner: vitest 4.1.11: 4837 sites in 672 files resolve to map(base) (8.1 s)
PASS  K3c Resolution identity under the runtime: 6192 runtime sites resolve to map(base) under nest-cli/emit (3442 rewritten by the build) (6.4 s)
PASS  K4  Location scan: 6 moved files expose no location; no computed path reaches a change; no old path in code, config or CI (8 findings in docs, comments or ungated coverage settings) (6.5 s)
        finding (opaque-dynamic-load) server/src/repositories/database.repository.ts: line 510: import(filePath) loads a module by a path K4 can't evaluate
        finding (old-path-in-docs) .github/pull_request_template.md: line 36: 'src/services/' is the directory server/src/services, whose contents change with the move of server/src/services/album.service.spec.ts (moved to server/src/modules/album/album.service.spec.ts)
        finding (old-path-in-docs) .github/pull_request_template.md: line 37: 'src/repositories/' is the directory server/src/repositories, whose contents change with the move of server/src/repositories/album-user.repository.ts (moved to server/src/modules/album/album-user.repository.ts)
        … 6 more findings
PASS  K6  Test inventory: vitest 4.1.11: 105 test files, 2337 tests, per-file load status and case names equal (49.3 s)
PASS  K7  Emit layout: 7 emitting programs keep their outDir and rootDir (1 by TypeScript's default) (0.5 s)
        finding (default-root-dir) e2e/tsconfig.json: emits into e2e/dist without a rootDir; TypeScript 6.0+ and before 6.0's default stays e2e and e2e

verdict: PASS (static checks only)
digest: sha256:78e6b72fe0cab9da2574f6a455ed76aba71f3ef704d946bbdb494822634d0562
003 R1 module/api-key certified 6 files moved, 14 specifier edits in 11 files, 1 m 31 s

Commit ac512281719130a99e5b3ede6cb06b648d294c6c, tree 5912e52b568ec2cca43143258ebd3aee23713da2

6 paths moved
server/src/controllers/api-key.controller.spec.ts
  → server/src/modules/api-key/api-key.controller.spec.ts
server/src/controllers/api-key.controller.ts
  → server/src/modules/api-key/api-key.controller.ts
server/src/dtos/api-key.dto.ts
  → server/src/modules/api-key/api-key.dto.ts
server/src/repositories/api-key.repository.ts
  → server/src/modules/api-key/api-key.repository.ts
server/src/services/api-key.service.spec.ts
  → server/src/modules/api-key/api-key.service.spec.ts
server/src/services/api-key.service.ts
  → server/src/modules/api-key/api-key.service.ts

attempt 1: passed round 1, from 3876d5c90e, 6 moves, 1 m 31 s

Certificate: PASS (static checks) certificates/003-R1-module-api-key.json

certificate JSON · digest sha256:5791cb1a9e19bfecb7516d1acad9515378a4d03b6a2dcc0c29aeb4d5de0d7fed

ac certify 3876d5c90e..1230e2184d (package root server/), profile immich
trees: 7c25e3c579..5912e52b56
pathMap: 6 moves, 7 edited in place, 3451 unchanged, 0 added, 0 deleted

PASS  K0  Envelope: inside the envelope: 678 TypeScript vs 3 JavaScript sources under server; no automock, nested package scope, symlink, same-stem pair or unreadable runner config around 6 moved files (5.1 s)
PASS  K1  Move-set integrity: 6 moves, 7 files edited in place, nothing added or deleted (2.2 s)
PASS  K2  Byte-span confinement: 14 specifier spans changed in 11 files; every other byte identical (0.7 s)
PASS  K3a Resolution identity under TypeScript: 6647 sites in 1100 files resolve to map(base) under every program (458 files in several); 14 rewritten specifiers keep their form (1.5 s)
PASS  K3b Resolution identity under the test runner: vitest 4.1.11: 4837 sites in 672 files resolve to map(base) (8.3 s)
PASS  K3c Resolution identity under the runtime: 6192 runtime sites resolve to map(base) under nest-cli/emit (3442 rewritten by the build) (6.7 s)
PASS  K4  Location scan: 6 moved files expose no location; no computed path reaches a change; no old path in code, config or CI (8 findings in docs, comments or ungated coverage settings) (6.3 s)
        finding (opaque-dynamic-load) server/src/repositories/database.repository.ts: line 510: import(filePath) loads a module by a path K4 can't evaluate
        finding (old-path-in-docs) .github/pull_request_template.md: line 36: 'src/services/' is the directory server/src/services, whose contents change with the move of server/src/services/api-key.service.spec.ts (moved to server/src/modules/api-key/api-key.service.spec.ts)
        finding (old-path-in-docs) .github/pull_request_template.md: line 37: 'src/repositories/' is the directory server/src/repositories, whose contents change with the move of server/src/repositories/api-key.repository.ts (moved to server/src/modules/api-key/api-key.repository.ts)
        … 6 more findings
PASS  K6  Test inventory: vitest 4.1.11: 105 test files, 2337 tests, per-file load status and case names equal (49.2 s)
PASS  K7  Emit layout: 7 emitting programs keep their outDir and rootDir (1 by TypeScript's default) (0.5 s)
        finding (default-root-dir) e2e/tsconfig.json: emits into e2e/dist without a rootDir; TypeScript 6.0+ and before 6.0's default stays e2e and e2e

verdict: PASS (static checks only)
digest: sha256:5791cb1a9e19bfecb7516d1acad9515378a4d03b6a2dcc0c29aeb4d5de0d7fed

56 steps never ran (run status: stopped).

Time per step
certify (static checks)move and commitfault, amend, rollbackfailed attempts (rolled back)
050 s100 s150 s200 s1R13

Seconds per step, from run.json attempt timings.

Table view
#RungGroupMove + commit (s)Certify (s)Other (s)Failed attempts (s)
1R1module/activity8.8135.60.10.0
2R1module/album9.081.70.1104.0
3R1module/api-key9.082.00.10.0

Rung certificates

R1 rung certificate: PASS (static checks and oracles) rungs/R1.json

certificate JSON · digest sha256:8ea0a7596256d5ec473d99d32b042f0e410bb145c4b461fc4b0fed6527024777

ac certify 02063972e8..ac51228171 (package root server/), profile immich
trees: 424df72c34..5912e52b56
pathMap: 18 moves, 18 edited in place, 3428 unchanged, 0 added, 0 deleted

PASS  K0  Envelope: inside the envelope: 678 TypeScript vs 3 JavaScript sources under server; no automock, nested package scope, symlink, same-stem pair or unreadable runner config around 18 moved files (0.3 s)
PASS  K1  Move-set integrity: 18 moves, 18 files edited in place, nothing added or deleted (2.4 s)
PASS  K2  Byte-span confinement: 59 specifier spans changed in 30 files; every other byte identical (0.9 s)
PASS  K3a Resolution identity under TypeScript: 6647 sites in 1100 files resolve to map(base) under every program (458 files in several); 59 rewritten specifiers keep their form (1.5 s)
PASS  K3b Resolution identity under the test runner: vitest 4.1.11: 4837 sites in 672 files resolve to map(base) (8.1 s)
PASS  K3c Resolution identity under the runtime: 6192 runtime sites resolve to map(base) under nest-cli/emit (3442 rewritten by the build) (6.5 s)
PASS  K4  Location scan: 18 moved files expose no location; no computed path reaches a change; no old path in code, config or CI (8 findings in docs, comments or ungated coverage settings) (6.2 s)
        finding (opaque-dynamic-load) server/src/repositories/database.repository.ts: line 510: import(filePath) loads a module by a path K4 can't evaluate
        finding (old-path-in-docs) .github/pull_request_template.md: line 36: 'src/services/' is the directory server/src/services, whose contents change with the move of server/src/services/activity.service.spec.ts (moved to server/src/modules/activity/activity.service.spec.ts)
        finding (old-path-in-docs) .github/pull_request_template.md: line 37: 'src/repositories/' is the directory server/src/repositories, whose contents change with the move of server/src/repositories/activity.repository.ts (moved to server/src/modules/activity/activity.repository.ts)
        … 6 more findings
PASS  K6  Test inventory: vitest 4.1.11: 105 test files, 2337 tests, per-file load status and case names equal (0.0 s)
PASS  K7  Emit layout: 7 emitting programs keep their outDir and rootDir (1 by TypeScript's default) (0.5 s)
        finding (default-root-dir) e2e/tsconfig.json: emits into e2e/dist without a rootDir; TypeScript 6.0+ and before 6.0's default stays e2e and e2e
PASS  K8  Oracles on the commit: tsc exit 0; unit 105/105 files and 2,337/2,337 cases equal to base (head: 105/105 files load, 2,337 passed, 0 failed, 0 skipped); openapi byte-identical to the base's open-api/immich-openapi-specs.json (947,932 bytes) (114.0 s)

verdict: PASS (static checks and oracles)
digest: sha256:8ea0a7596256d5ec473d99d32b042f0e410bb145c4b461fc4b0fed6527024777

The whole PR

No whole-PR certificate.

Findings: recorded, never fixed

A good restructure makes existing bugs obvious; fixing them is never part of it. These 23 findings were found by deterministic collectors and are left exactly as they are in the code.

No narration: every finding shows its deterministic evidence only.

Sources
SourceWhat it contributed
targetfacts at 02063972e8: 269 movable files, 1759 edges, 269 placed by the plan
plan-graphno committed boundary rules (R4 didn't run): cycles from the plan's groups over the facts, 1 strongly connected sets; no back-edges without R4's order
facts3 tables with writers in several groups, 1 god-injection base classes
analyzer458 production files analyzed, 0 import test code
validator10 warnings on the run's plan
certificate4 certificates read (steps, rungs, whole PR)
run8 run.json findings

module-cycle (1)

55 groups form one import cycle F-f0f96c77a088

Evidence
55 of 59 groups are strongly connected (from the planner's facts at 02063972e8, grouped by the run's plan.yaml): each reaches every other through imports.
Members: module/activity, module/album, module/api-key, module/asset, module/auth, module/cluster-group, module/config, module/database-backup, module/download, module/duplicate, module/integrity, module/job, module/library, module/machine-learning, module/maintenance, module/map, module/memory, module/notification, module/ocr, module/partner, module/person, module/plugin, module/queue, module/search, module/server, module/session, module/shared-link, module/stack, module/sync, module/system-metadata, module/tag, module/timeline, module/trash, module/user, module/version, module/video-stream, module/view, module/workflow, platform/access, platform/bootstrap, platform/cron, platform/database, platform/event, platform/file, platform/http-response, platform/logging, platform/media, platform/metadata, platform/move, platform/registry, platform/storage, platform/telemetry, platform/transcoding, platform/websocket, shared/shared.
Inside the set: 506 edges between them over 1437 file pairs. These 118 edges (193 file pairs) close every cycle; without them the set is acyclic:
- module/search → module/asset (7 file pairs, e.g. server/src/controllers/search.controller.ts → server/src/dtos/asset-response.dto.ts)
- module/auth → module/user (6 file pairs, e.g. server/src/controllers/auth.controller.ts → server/src/dtos/user.dto.ts)
- module/album → module/asset (5 file pairs, e.g. server/src/controllers/album.controller.ts → server/src/dtos/asset-ids.response.dto.ts)
- module/shared-link → module/asset (5 file pairs, e.g. server/src/controllers/shared-link.controller.ts → server/src/dtos/asset-ids.response.dto.ts)
- platform/media → module/asset (5 file pairs, e.g. server/src/repositories/media.repository.ts → server/src/dtos/editing.dto.ts)
- platform/storage → module/asset (5 file pairs, e.g. server/src/cores/storage.core.ts → server/src/repositories/asset.repository.ts)
- module/stack → module/asset (4 file pairs, e.g. server/src/controllers/stack.controller.ts → server/src/dtos/asset-ids.response.dto.ts)
- module/tag → module/asset (4 file pairs, e.g. server/src/controllers/tag.controller.ts → server/src/dtos/asset-ids.response.dto.ts)
- platform/access → module/person (4 file pairs, e.g. server/src/repositories/access.repository.ts → server/src/dtos/person.dto.ts)
- module/asset → platform/registry (3 file pairs, e.g. server/src/services/asset-file.service.ts → server/src/services/base.service.ts)
- module/job → module/asset (3 file pairs, e.g. server/src/services/job.service.ts → server/src/dtos/asset-response.dto.ts)
- module/notification → module/asset (3 file pairs, e.g. server/src/services/notification.service.ts → server/src/dtos/asset-response.dto.ts)
- module/partner → module/user (3 file pairs, e.g. server/src/dtos/partner.dto.ts → server/src/dtos/user.dto.ts)
- module/search → module/person (3 file pairs, e.g. server/src/controllers/search.controller.ts → server/src/dtos/person.dto.ts)
- module/server → module/user (3 file pairs, e.g. server/src/controllers/server.controller.ts → server/src/dtos/license.dto.ts)
- … 103 more edges (130 file pairs)

Files: server/src/controllers/album.controller.ts server/src/controllers/auth.controller.ts server/src/controllers/search.controller.ts server/src/controllers/server.controller.ts server/src/controllers/shared-link.controller.ts server/src/controllers/stack.controller.ts server/src/controllers/tag.controller.ts server/src/cores/storage.core.ts and 14 more

Source: plan-graph

god-injection (1)

BaseService injects 56 members into 51 classes F-190847636c3d

Evidence
BaseService (server/src/services/base.service.ts) takes 56 constructor-injected members (56 of them repositories), and 51 classes extend it, so each receives all 56.
Each subclass reads 5.7 of them on average through this.<member> (median 5, min 1, max 15 in MetadataService); every member has a reader.
Most read: logger (33 classes), jobRepository (23 classes), assetRepository (21 classes), eventRepository (18 classes), userRepository (18 classes).
Per class (members read of 56):
- MetadataService (server/src/services/metadata.service.ts): 15 (albumRepository, assetJobRepository, assetRepository, cryptoRepository, databaseRepository, eventRepository, jobRepository, logger, … 7 more)
- PersonService (server/src/services/person.service.ts): 15 (assetJobRepository, assetRepository, clusterGroupRepository, cryptoRepository, databaseRepository, jobRepository, logger, machineLearningRepository, … 7 more)
- AssetService (server/src/services/asset.service.ts): 13 (albumRepository, assetEditRepository, assetJobRepository, assetRepository, eventRepository, jobRepository, logger, ocrRepository, … 5 more)
- AuthService (server/src/services/auth.service.ts): 10 (apiKeyRepository, cryptoRepository, eventRepository, jobRepository, logger, mediaRepository, oauthRepository, sessionRepository, … 2 more)
- LibraryService (server/src/services/library.service.ts): 10 (assetJobRepository, assetRepository, cronRepository, cryptoRepository, databaseRepository, eventRepository, jobRepository, libraryRepository, … 2 more)
- UserService (server/src/services/user.service.ts): 10 (albumRepository, assetRepository, configRepository, cryptoRepository, eventRepository, jobRepository, logger, mediaRepository, … 2 more)
- CliService (server/src/services/cli.service.ts): 9 (appRepository, assetRepository, cryptoRepository, databaseRepository, personRepository, sessionRepository, storageRepository, systemMetadataRepository, … 1 more)
- DuplicateService (server/src/services/duplicate.service.ts): 9 (albumRepository, assetJobRepository, assetRepository, cryptoRepository, duplicateRepository, eventRepository, jobRepository, logger, … 1 more)
- IntegrityService (server/src/services/integrity.service.ts): 9 (assetRepository, cronRepository, databaseRepository, eventRepository, integrityRepository, jobRepository, logger, storageRepository, … 1 more)
- NotificationService (server/src/services/notification.service.ts): 9 (albumRepository, assetJobRepository, assetRepository, emailRepository, jobRepository, logger, notificationRepository, userRepository, … 1 more)
- VersionService (server/src/services/version.service.ts): 9 (configRepository, cronRepository, databaseRepository, jobRepository, logger, serverInfoRepository, systemMetadataRepository, versionRepository, … 1 more)
- MediaService (server/src/services/media.service.ts): 8 (assetJobRepository, assetRepository, jobRepository, logger, mediaRepository, ocrRepository, personRepository, storageRepository)
- MemoryService (server/src/services/memory.service.ts): 8 (accessRepository, assetRepository, databaseRepository, logger, memoryRepository, personRepository, systemMetadataRepository, userRepository)
- StorageTemplateService (server/src/services/storage-template.service.ts): 8 (albumRepository, assetJobRepository, databaseRepository, jobRepository, logger, moveRepository, storageRepository, userRepository)
- UserAdminService (server/src/services/user-admin.service.ts): 8 (albumRepository, assetRepository, cryptoRepository, eventRepository, jobRepository, logger, sessionRepository, userRepository)
- WorkflowExecutionService (server/src/services/workflow-execution.service.ts): 8 (configRepository, cryptoRepository, databaseRepository, jobRepository, logger, pluginRepository, storageRepository, workflowRepository)
- AlbumService (server/src/services/album.service.ts): 7 (accessRepository, albumRepository, albumUserRepository, eventRepository, logger, mapRepository, userRepository)
- AssetMediaService (server/src/services/asset-media.service.ts): 7 (albumRepository, assetRepository, eventRepository, jobRepository, logger, sharedLinkRepository, storageRepository)
- ServerService (server/src/services/server.service.ts): 7 (configRepository, cryptoRepository, logger, serverInfoRepository, storageRepository, systemMetadataRepository, userRepository)
- JobService (server/src/services/job.service.ts): 6 (assetEditRepository, assetRepository, eventRepository, jobRepository, logger, websocketRepository)
- OcrService (server/src/services/ocr.service.ts): 6 (assetJobRepository, assetRepository, jobRepository, logger, machineLearningRepository, ocrRepository)
- SmartInfoService (server/src/services/smart-info.service.ts): 6 (assetJobRepository, databaseRepository, jobRepository, logger, machineLearningRepository, searchRepository)
- StorageService (server/src/services/storage.service.ts): 6 (assetRepository, configRepository, databaseRepository, logger, storageRepository, systemMetadataRepository)
- TranscodingService (server/src/services/transcoding.service.ts): 6 (databaseRepository, logger, processRepository, storageRepository, videoStreamRepository, websocketRepository)
- ClusterGroupService (server/src/services/cluster-group.service.ts): 5 (clusterGroupRepository, eventRepository, jobRepository, personRepository, userRepository)
- DownloadService (server/src/services/download.service.ts): 5 (assetRepository, downloadRepository, logger, storageRepository, userRepository)
- MaintenanceService (server/src/services/maintenance.service.ts): 5 (appRepository, eventRepository, logger, storageRepository, systemMetadataRepository)
- QueueService (server/src/services/queue.service.ts): 5 (cronRepository, databaseRepository, eventRepository, jobRepository, logger)
- SearchService (server/src/services/search.service.ts): 5 (assetRepository, machineLearningRepository, partnerRepository, personRepository, searchRepository)
- TagService (server/src/services/tag.service.ts): 5 (accessRepository, assetRepository, eventRepository, jobRepository, tagRepository)
- HlsService (server/src/services/hls.service.ts): 4 (cryptoRepository, storageRepository, videoStreamRepository, websocketRepository)
- SharedLinkService (server/src/services/shared-link.service.ts): 4 (cryptoRepository, logger, sharedLinkAssetRepository, sharedLinkRepository)
- SyncService (server/src/services/sync.service.ts): 4 (logger, sessionRepository, syncCheckpointRepository, syncRepository)
- SystemConfigService (server/src/services/system-config.service.ts): 4 (configRepository, eventRepository, logger, machineLearningRepository)
- TrashService (server/src/services/trash.service.ts): 4 (eventRepository, jobRepository, logger, trashRepository)
- DatabaseService (server/src/services/database.service.ts): 3 (configRepository, databaseRepository, logger)
- MapService (server/src/services/map.service.ts): 3 (albumRepository, mapRepository, partnerRepository)
- NotificationAdminService (server/src/services/notification-admin.service.ts): 3 (emailRepository, notificationRepository, userRepository)
- PartnerService (server/src/services/partner.service.ts): 3 (logger, partnerRepository, userRepository)
- SessionService (server/src/services/session.service.ts): 3 (cryptoRepository, logger, sessionRepository)
- StackService (server/src/services/stack.service.ts): 3 (assetRepository, eventRepository, stackRepository)
- ApiKeyService (server/src/services/api-key.service.ts): 2 (apiKeyRepository, cryptoRepository)
- AssetFileService (server/src/services/asset-file.service.ts): 2 (assetFileRepository, jobRepository)
- TelemetryService (server/src/services/telemetry.service.ts): 2 (telemetryRepository, userRepository)
- TimelineService (server/src/services/timeline.service.ts): 2 (assetRepository, partnerRepository)
- WorkflowService (server/src/services/workflow.service.ts): 2 (pluginRepository, workflowRepository)
- ActivityService (server/src/services/activity.service.ts): 1 (activityRepository)
- AuthAdminService (server/src/services/auth-admin.service.ts): 1 (userRepository)
- PluginService (server/src/services/plugin.service.ts): 1 (pluginRepository)
- SystemMetadataService (server/src/services/system-metadata.service.ts): 1 (systemMetadataRepository)
- ViewService (server/src/services/view.service.ts): 1 (viewRepository)

Files: server/src/services/activity.service.ts server/src/services/album.service.ts server/src/services/api-key.service.ts server/src/services/asset-file.service.ts server/src/services/asset-media.service.ts server/src/services/asset.service.ts server/src/services/auth-admin.service.ts server/src/services/auth.service.ts and 44 more

Source: facts

multi-writer-table (3)

Table asset is written by 4 groups F-471f7dbe2fc2

Evidence
Table asset is written by 4 files in 4 plan groups (and read by 23 files in 22 groups):
- module/asset: server/src/repositories/asset.repository.ts
- module/duplicate: server/src/repositories/duplicate.repository.ts
- module/stack: server/src/repositories/stack.repository.ts
- module/trash: server/src/repositories/trash.repository.ts

Files: server/src/repositories/asset.repository.ts server/src/repositories/duplicate.repository.ts server/src/repositories/stack.repository.ts server/src/repositories/trash.repository.ts

Source: facts

Table cluster_group is written by 2 groups F-bdcfcf0f1886

Evidence
Table cluster_group is written by 2 files in 2 plan groups:
- module/cluster-group: server/src/repositories/cluster-group.repository.ts
- module/person: server/src/repositories/person.repository.ts

Files: server/src/repositories/cluster-group.repository.ts server/src/repositories/person.repository.ts

Source: facts

Table session_sync_checkpoint is written by 2 groups F-e7b889cd5d1e

Evidence
Table session_sync_checkpoint is written by 2 files in 2 plan groups (and read by 1 file in 1 group):
- module/session: server/src/repositories/session.repository.ts
- module/sync: server/src/repositories/sync-checkpoint.repository.ts

Files: server/src/repositories/session.repository.ts server/src/repositories/sync-checkpoint.repository.ts

Source: facts

injected-fault (1)

Injected fault (--inject-fault): step 2 F-0daf1592fc4d

Evidence
step 2 (R1): seeded wrong move (--inject-fault): M2 (Drop one importer rewrite): server/src/services/workflow-execution.service.ts: import specifier 'src/modules/album/album.dto.js' left as in base, 'src/dtos/album.dto.js'

Source: run; steps 2

pinned (1)

Files pinned by a replan: step 2 F-0056b169b7fc

Evidence
step 2 (R1): attempt 1 (failed): certificate FAIL (K3a, K3b, K3c): K3a server/src/services/workflow-execution.service.ts: line 14: import 'src/dtos/album.dto.js': expected file server/src/modules/album/album.dto.ts (typescript), got unresolved under server/tsconfig.build.json, server/tsconfig.json; K3b server/src/services/workflow-execution.service.ts: site 7 'src/dtos/album.dto.js' (import): base server/src/modules/album/album.dto.ts, head unresolved; K3c server/src/services/workflow-execution.service.ts: nest-cli/emit: import 'src/dtos/album.dto.js' resolves to unresolved (ERR_MODULE_NOT_FOUND), expected server/src/modules/album/album.dto.ts; base server/src/services/workflow-execution.service.ts 'src/dtos/album.dto.js'
1 file: server/src/dtos/album.dto.ts

Files: server/src/dtos/album.dto.ts

Source: run; steps 2

plan-warning (10)

Plan warning (one-file-platform): platform/http-client F-14d96dec2313

Evidence
The plan validator's rule one-file-platform, on the run's plan.yaml (group platform/http-client): platform/http-client holds one file, and no other planned group uses it: consider folding it into the concern or feature it serves

Files: server/src/utils/fetch.ts

Source: validator

Plan warning (one-file-platform): platform/check F-175fe3a4820a

Evidence
The plan validator's rule one-file-platform, on the run's plan.yaml (group platform/check): platform/check holds one file, and no other planned group uses it: consider folding it into the concern or feature it serves

Files: server/src/commands/schema-check.ts

Source: validator

Plan warning (singleton-group): module/machine-learning F-545a4b88a62f

Evidence
The plan validator's rule singleton-group, on the run's plan.yaml (group module/machine-learning): module/machine-learning holds one file

Files: server/src/repositories/machine-learning.repository.ts

Source: validator

Plan warning (unsupported-merge): module/auth F-692ad4017c11

Evidence
The plan validator's rule unsupported-merge, on the run's plan.yaml (group module/auth): module/auth takes draft group "grant" (src/commands/grant-admin.ts): no shared word, edge, table or route

Source: validator

Plan warning (platform-feature): platform/move F-6dc722024105

Evidence
The plan validator's rule platform-feature, on the run's plan.yaml (group platform/move): src/repositories/move.repository.ts writes tables (move_history): a feature, not plumbing

Files: server/src/repositories/move.repository.ts

Source: validator

Plan warning (one-file-platform): platform/move F-af06f12438b8

Evidence
The plan validator's rule one-file-platform, on the run's plan.yaml (group platform/move): platform/move holds one file, and only platform/storage uses it: consider folding it there

Files: server/src/repositories/move.repository.ts

Source: validator

Plan warning (platform-feature): platform/bootstrap F-e1c975dc8064

Evidence
The plan validator's rule platform-feature, on the run's plan.yaml (group platform/bootstrap): src/controllers/app.controller.ts serves routes: a feature, not plumbing

Files: server/src/controllers/app.controller.ts

Source: validator

Plan warning (impure-shared): shared/shared F-e3b63d08f742

Evidence
The plan validator's rule impure-shared, on the run's plan.yaml (group shared/shared): src/services/api.service.ts is a service; shared/ holds pure code only

Files: server/src/services/api.service.ts

Source: validator

Plan warning (one-file-platform): platform/transcoding F-fa4aa2cae116

Evidence
The plan validator's rule one-file-platform, on the run's plan.yaml (group platform/transcoding): platform/transcoding holds one file, and no other planned group uses it: consider folding it into the concern or feature it serves

Files: server/src/services/transcoding.service.ts

Source: validator

Plan warning (unsupported-merge): module/auth F-ff0edb54f50f

Evidence
The plan validator's rule unsupported-merge, on the run's plan.yaml (group module/auth): module/auth takes draft group "password-login" (src/commands/password-login.ts): no shared word, edge, table or route

Source: validator

old-path-in-docs (2)

Docs name old paths (K4): docs/docs/developer/architecture.mdx F-68921a0c7282

Evidence
K4 reported 3 distinct findings in docs/docs/developer/architecture.mdx, 12 times (steps 1–3, rungs/R1.json):
- line 60: 'src/repositories' is the directory server/src/repositories, whose contents change with the move of server/src/repositories/activity.repository.ts (moved to server/src/modules/activity/activity.repository.ts) (steps 1–3, rungs/R1.json)
- line 60: 'src/services' is the directory server/src/services, whose contents change with the move of server/src/services/activity.service.spec.ts (moved to server/src/modules/activity/activity.service.spec.ts) (steps 1–3, rungs/R1.json)
- line 64: 'src/controllers' is the directory server/src/controllers, whose contents change with the move of server/src/controllers/activity.controller.spec.ts (moved to server/src/modules/activity/activity.controller.spec.ts) (steps 1–3, rungs/R1.json)

Files: docs/docs/developer/architecture.mdx

Source: certificate; steps 1, 2, 3; certificates rungs/R1.json

Docs name old paths (K4): .github/pull_request_template.md F-8759fa26d236

Evidence
K4 reported 3 distinct findings in .github/pull_request_template.md, 12 times (steps 1–3, rungs/R1.json):
- line 36: 'src/services/' is the directory server/src/services, whose contents change with the move of server/src/services/activity.service.spec.ts (moved to server/src/modules/activity/activity.service.spec.ts) (steps 1–3, rungs/R1.json)
- line 37: 'src/repositories/' is the directory server/src/repositories, whose contents change with the move of server/src/repositories/activity.repository.ts (moved to server/src/modules/activity/activity.repository.ts) (steps 1–3, rungs/R1.json)
- line 37: 'src/services/' is the directory server/src/services, whose contents change with the move of server/src/services/activity.service.spec.ts (moved to server/src/modules/activity/activity.service.spec.ts) (steps 1–3, rungs/R1.json)

Files: .github/pull_request_template.md

Source: certificate; steps 1, 2, 3; certificates rungs/R1.json

coverage-scope (1)

Coverage globs match other files (K4): server/test/vitest.config.mjs F-800da5f4439a

Evidence
K4 reported 2 distinct findings in server/test/vitest.config.mjs, 8 times (steps 1–3, rungs/R1.json):
- line 19: 'src/services/**' is the glob server/src/services/** whose matches change (2 moved files, e.g. server/src/services/activity.service.spec.ts -> server/src/modules/activity/activity.service.spec.ts) of server/src/services/activity.service.spec.ts (moved to server/src/modules/activity/activit… (steps 1–3, rungs/R1.json)
- line 21: 'src/services/*.spec.ts' is the glob server/src/services/*.spec.ts whose matches change (1 moved files, e.g. server/src/services/activity.service.spec.ts -> server/src/modules/activity/activity.service.spec.ts) of server/src/services/activity.service.spec.ts (moved to server/src/modules/ac… (steps 1–3, rungs/R1.json)

Files: server/test/vitest.config.mjs

Source: certificate; steps 1, 2, 3; certificates rungs/R1.json

default-root-dir (1)

Emit without a rootDir (K7): e2e/tsconfig.json F-966da7d59056

Evidence
K7 reported one finding in e2e/tsconfig.json, 4 times (steps 1–3, rungs/R1.json):
- emits into e2e/dist without a rootDir; TypeScript 6.0+ and before 6.0's default stays e2e and e2e (steps 1–3, rungs/R1.json)

Files: e2e/tsconfig.json

Source: certificate; steps 1, 2, 3; certificates rungs/R1.json

opaque-dynamic-load (1)

A module loaded by a computed path (K4): server/src/repositories/database.repository.ts F-70d9e19108a1

Evidence
K4 reported one finding in server/src/repositories/database.repository.ts, 4 times (steps 1–3, rungs/R1.json):
- line 510: import(filePath) loads a module by a path K4 can't evaluate (steps 1–3, rungs/R1.json)

Files: server/src/repositories/database.repository.ts

Source: certificate; steps 1, 2, 3; certificates rungs/R1.json

ungated-coverage-glob (1)

Coverage globs match other files, with no gate: server/test/vitest.config.mjs F-89500b32bdee

Evidence
The run recorded 2 distinct findings in server/test/vitest.config.mjs, 6 times (steps 1–3):
- server/test/vitest.config.mjs line 19 [test.coverage.include]: coverage glob 'src/services/**' (server/src/services/**) matches differently for server/src/services/activity.service.ts (moving to server/src/modules/activity/activity.service.ts), server/src/services/activity.service.spec.ts (moving t… (steps 1–3)
- server/test/vitest.config.mjs line 21 [test.coverage.exclude]: coverage glob 'src/services/*.spec.ts' (server/src/services/*.spec.ts) matches differently for server/src/services/activity.service.spec.ts (moving to server/src/modules/activity/activity.service.spec.ts); nothing in server/test/vitest.… (steps 1–3)

Files: server/test/vitest.config.mjs

Source: run; steps 1, 2, 3

findings.json · findings.md · the PR body

Table access

No plan/plan.json with the planner's facts in this run dir, so there are no table accesses to show.

Cost

This run: the plan cost $0.1998 when it was recorded; planner spend during the run $0.0000. Replaying the plan from the committed cassettes costs $0.

The whole project's spend on Nebius Token Factory, from the gateway's ledger (budget/ledger.jsonl): $24.3980 of the $50.00 hard cap. The lines marked reconciled bring the ledger to the Nebius console bill: what Token Factory billed differently from the gateway's count (a lost call, cached input at its cached price, calls it didn't bill). The G3 runs (opencode with Nemotron 3 Super and, in S9, with Ultra: the generic-agent baseline) account for $8.9572 of it. The runs tf-probes, g1, g3-smoke, g3-immich, g3-mcphub, g3u-smoke, g3u-immich, g3u-immich-persistent are carried over from the gate spike as one line each.

LLM spend by budget run
tf-probes$0.0250g1$1.7552g3-smoke$0.0143g3-immich$4.9333g3-mcphub$0.8603s5$1.2877 of $3.00s6$0.4343 of $3.00s7$1.1213 of $5.00s8$0.1114 of $3.00s8b$3.7268 of $7.00s8c$0.5941 of $3.00s8d$6.3850 of $10.00g3u-smoke$0.0570g3u-immich$0.5456g3u-immich-persistent$2.5468
Table view
Budget runPaid callsCostCapModelsNotes
tf-probes82$0.0250––carried over from spike/gates:spike/llm/ledger.jsonl: 82 paid calls, $0.0250
g153$1.7552––carried over from spike/gates:spike/llm/ledger.jsonl: 53 paid calls, $1.7042 reconciled, $0.0510: Nebius console 2026-10-14 21:31 UTC ($9.31) vs ledger ($9.259): Super output +58k and input +8k tokens are one G1 call lost to 'fetch failed' at 2026-10-02T13:46:58Z (estimate $0.0601), which the spike gateway booked at $0 and Token Factory billed
g3-smoke6$0.0143––carried over from spike/gates:spike/llm/ledger.jsonl: 6 paid calls, $0.0143
g3-immich251$4.9333––carried over from spike/gates:spike/llm/ledger.jsonl: 251 paid calls, $4.9333
g3-mcphub62$0.8603––carried over from spike/gates:spike/llm/ledger.jsonl: 62 paid calls, $0.8603
s517$1.2877$3.00Nemotron 3 Ultra
s638$0.4343$3.00Nemotron 3 Ultra, Nemotron 3.5 Lightning
s712$1.1213$5.00Nemotron 3 Ultra
s83$0.1114$3.00Nemotron 3 Ultra
s8b70$3.7268$7.00Nemotron 3 Ultra, Nemotron 3.5 Super VLreconciled, −$0.2723: Nebius console export, 2026-09-24 to 10-24 (corupta, 2026-10-24): $24.40 billed; Ultra's cached input billed at its cached price, $0.12/M (from 2026-10-20 20:01 UTC at the latest; S7's hits on 2026-10-17 were billed in full), which the ledger charged at $1.00/M: 252,416 tokens; Super VL's cached input at $0.06/M, which the ledger charged at $0.30/M: 208,896 tokens reconciled, $0.0000: Nebius console export, 2026-09-24 to 10-24 (corupta, 2026-10-24): $24.40 billed; the Super VL call stopped at 2026-10-20T20:54Z leaves no trace in the bill (Super VL's billed input, 0.57M, is the 200 responses' 565,968 uncached prompt tokens; billed without a cache hit, its ~11k-token prompt would have made it 0.58M; sub-cent either way): its $0.0387 reservation settles at $0
s8c3$0.5941$3.00Nemotron 3.5 Super VLreconciled, −$0.7033: Nebius console export, 2026-09-24 to 10-24 (corupta, 2026-10-24): $24.40 billed; Super VL's cached input at $0.06/M, which the ledger charged at $0.30/M: 6,528 tokens; the non-streamed call cut at 600 s (HTTP 504, 2026-10-21T00:31Z), booked at its $0.7017 estimate, was not billed (Super VL's billed output, 9.09M, matches its 200 responses' 9,092,987 tokens to the console's 0.01M)
s8d30$6.3850$10.00Nemotron 3.5 Super VLreconciled, −$0.0345: Nebius console export, 2026-09-24 to 10-24 (corupta, 2026-10-24): $24.40 billed; Super VL's cached input at $0.06/M, which the ledger charged at $0.30/M: 143,616 tokens
g3u-smoke7$0.0570––carried over from spike/g3-ultra:spike/llm/ledger.jsonl: 7 paid calls, $0.0762 reconciled, −$0.0191: Nebius console export, 2026-09-24 to 10-24 (corupta, 2026-10-24): $24.40 billed; Ultra's cached input billed at its cached price, $0.12/M (from 2026-10-20 20:01 UTC at the latest; S7's hits on 2026-10-17 were billed in full), which the ledger charged at $1.00/M: 21,760 tokens
g3u-immich49$0.5456––carried over from spike/g3-ultra:spike/llm/ledger.jsonl: 49 paid calls, $1.8630 reconciled, −$1.3174: Nebius console export, 2026-09-24 to 10-24 (corupta, 2026-10-24): $24.40 billed; Ultra's cached input billed at its cached price, $0.12/M (from 2026-10-20 20:01 UTC at the latest; S7's hits on 2026-10-17 were billed in full), which the ledger charged at $1.00/M: 1,497,088 tokens
g3u-immich-persistent170$2.5468––carried over from spike/g3-ultra:spike/llm/ledger.jsonl: 170 paid calls, $11.7612 reconciled, −$9.2144: Nebius console export, 2026-09-24 to 10-24 (corupta, 2026-10-24): $24.40 billed; Ultra's cached input billed at its cached price, $0.12/M (from 2026-10-20 20:01 UTC at the latest; S7's hits on 2026-10-17 were billed in full), which the ledger charged at $1.00/M: 10,470,912 tokens
total853$24.3980$50.00

A/B: the same task, a generic agent

G3 gave opencode 1.18.34 with NVIDIA Nemotron 3 Super on Nebius Token Factory the same restructure, with a fixed prompt and no human input. Its branches were certified in session S4 by our checker, the version before this run's (certificate format 1; K7 came later). The same agent on Nemotron 3 Ultra, the model ac plans with (S9), is on the site's A/B page: its single-shot changed nothing, and its persistent run hit its $12 cap in turn 1 with every import of the moved files dangling.

immich server/ @ 02063972e8

Target: immich @ 02063972e8. Base: tsc 0 errors; vitest 105 test files, 2,337 cases (docs/gates.md, G2); the OpenAPI spec is 947,932 bytes (the certificates' K8).

A green test count is not proof. On immich, the branch g3/opencode-super (opencode + Nemotron 3 Super, single-shot) still passes all 2,337 of the base's test cases, while 66 of its 171 test files fail to load and tsc reports 1,680 errors (its certificate: K6, K8).

ac, the full ladder (ours)ac, one R1-style step (ours)opencode + Nemotron 3 Super, single-shotopencode + Nemotron 3 Super, persistentSource
Whoac architecture-cleaner: Nemotron 3 Ultra plans, the executor moves, the certifier checks every commitac architecture-cleaner's executor, one move of 292 of the 302 controllers, services, repositories and DTOs (the 3 index.ts barrels and the barrel's spec, two process.cwd() specs, the three import.meta.dirname users and services/shared/user-methods.ts stay)opencode 1.18.34, NVIDIA Nemotron 3 Super on Nebius Token Factory, one `opencode run` with the fixed promptthe same agent; whenever it stopped, an automated reply reported typecheck and test results and asked it to continue
Branchac/s6-ladder (S6)
run report
ac/s3-r1 (S3)g3/opencode-super (G3 (S2))g3/opencode-super-persistent (G3 (S2))
OutcomeCompleted unattended: 63 commits, each certified on its first attempt; every rung and the whole PR PASS with the oraclesOne certified commitStopped after 169 s and asked the user to run its sed commandsStopped by the $5 budget cap during turn 5, after 33 mindocs/sessions/s6.md; docs/sessions/s3.md; docs/gates.md (G3 table)
What the diff is367 files moved into 38 modules, 20 platform folders and shared/; 2,474 specifier edits in 515 files; 2 boundary-rule files added and proven inert; every other byte identical
certificate K1: 367 moved, 180 edited in place, 2 added (2 inert tool configs), 0 deleted
292 files moved into 100 modules; 1,445 specifier edits in 386 files; every other byte identical
certificate K1: 292 moved, 152 edited in place, 0 added, 0 deleted
211 files copied into src/features/, originals kept; every rewritten import corrupted
certificate K1: 0 moved, 0 edited in place, 211 added (31 of them copies of files that still exist), 0 deleted
297 moved, 562 TypeScript files copied (a src.backup/ tree), 68 edited, 12 Python scripts left behind
certificate K1: 365 moved, 0 edited in place, 614 added (291 of them copies of files that still exist), 0 deleted
docs/storyboard.md (lead table); docs/gates.md (G3 table); runs/immich-ladder/certificate.txt (K1, K2)
certificate line: each arm's certificate, K1
tsc errors (base: 0)0
certificate K8: tsc exit 0
0
certificate K8: tsc exit 0
1,680
certificate K8: tsc exited 1 on head (1,680 diagnostics), 0 on base
768 (nest build: 4,675)
certificate K8: tsc exited 1 on head (768 diagnostics), 0 on base
docs/storyboard.md (lead table); docs/gates.md (G3 table); runs/immich-ladder/certificate.txt (K8)
certificate line: each arm's certificate, K8 tsc
Unit suite (base: 105 files, 2,337 cases)105/105 files, 2,337/2,337 cases
certificate K8: unit 105/105 files and 2,337/2,337 cases equal to base (head: 105/105 files load, 2,337 passed, 0 failed, 0 skipped)
105/105 files, 2,337/2,337 cases
certificate K8: unit 105/105 files and 2,337/2,337 cases equal to base (head: 105/105 files load, 2,337 passed, 0 failed, 0 skipped)
2,337/2,337 cases pass, but 66 of 171 spec files never load
certificate K8: unit 105/171 files and 2,337/2,337 cases equal to base (head: 105/171 files load, 2,337 passed, 0 failed, 0 skipped)
5/105 files, 52 cases
certificate K8: unit 5/105 files and 52/2,337 cases equal to base (head: 5/105 files load, 52 passed, 0 failed, 0 skipped)
docs/storyboard.md (lead table); runs/immich-ladder/certificate.txt (K6, K8)
certificate line: each arm's certificate, K8 unit
OpenAPI spec (base: 947,932 bytes)byte-identical
certificate K8: openapi byte-identical to the base's open-api/immich-openapi-specs.json (947,932 bytes)
byte-identical
certificate K8: openapi byte-identical to the base's open-api/immich-openapi-specs.json (947,932 bytes)
byte-identical: the copies are dead code
certificate K8: openapi byte-identical to the base's open-api/immich-openapi-specs.json (947,932 bytes), but 1 step failed on head
not produced: sync-open-api crashes
certificate K8: openapi produced nothing
docs/storyboard.md (lead table); docs/gates.md (G3 table); runs/immich-ladder/certificate.txt (K8)
certificate line: each arm's certificate, K8 openapi
Non-specifier lines changed in moved or copied files0 (K2)
certificate K2: 2474 specifier spans changed in 515 files; every other byte identical
0 (K2)
certificate K2: 1445 specifier spans changed in 386 files; every other byte identical
1,104 (2.4%)
certificate K2: no file paired with a base file changed, so there was nothing to compare; K1 counts the 211 added files
0, plus 926 in modified files
certificate K2: no file paired with a base file changed, so there was nothing to compare; K1 counts the 614 added files
G3 arms: docs/gates.md (G3 table, the gate's line diff); ac arms: the certificates' K2
certificate line: each arm's certificate, K2
New dangling specifiers0 (K3a, K3b, K3c: every site resolves to its base target)
certificate K3b: vitest 4.1.11: 4837 sites in 672 files resolve to map(base)
0 (K3a, K3b, K3c)
certificate K3b: vitest 4.1.11: 4837 sites in 672 files resolve to map(base)
0: its 552 rewritten imports are syntax errors, not specifiers, so the analyzer counts none; the rest still import the originals
certificate K3b: vitest 4.1.11: 4837 sites in 672 files resolve to map(base)
2,923
certificate K3b: vitest 4.1.11: 4548 violations (2802 dangling, 131 retargeted, 1615 dangling in added files)
G3 arms: docs/gates.md (G3 table, the gate's TypeScript analyzer); ac arms: the certificates' K3a–K3c
certificate line: each arm's certificate, K3b
Behavior changes no unit test catchesNone found: K4 finds no moved file whose location reaches behavior; 12 logged stack traces now name the new path (K4 findings)
certificate K4: 367 moved files expose no location; no computed path reaches a change; no old path in code, config or CI (13 findings in docs, comments or ungated coverage settings)
None found (K4; 11 logged stack traces now name the new path)
certificate K4: 292 moved files expose no location; no computed path reaches a change; no old path in code, config or CI (9 findings in docs, comments or ungated coverage settings)
0 claims survived review: the copies are dead code
certificate K4: 0 path-exposing uses in moved files (and 3 in added files), 0 computed paths into changed directories, 0 old paths in code/config (0 in coverage settings)
2 survived review: database.repository.ts and config.repository.ts moved but kept their import.meta.dirname paths (migrations, helmet.json)
certificate K4: 12 path-exposing uses in moved files (and 15 in added files), 2 computed paths into changed directories, 52 old paths in code/config (0 in coverage settings)
G3 arms: docs/gates.md (Addendum: G3 review); ac arms: the certificates' K4
certificate line: each arm's certificate, K4
LLM cost$0.1998 for the plan when recorded (Nemotron 3 Ultra) and $0.0010 for the findings narration (Nemotron 3.5 Lightning); a replay costs $0$0$0.30 (0.98M input tokens, 51 calls)$4.63 (15.3M input tokens, 200 calls)runs/immich-ladder/run.json (cost); docs/sessions/s6.md; docs/storyboard.md (lead table); docs/gates.md (G3 table)
Time1 h 55 m wall time on a quiet machine, certificates includedexecutor 14 s; certificate: static checks 124 s, oracles 111 s169 s33 mindocs/sessions/s6.md; docs/storyboard.md (lead table)
ac certify verdictPASS (with oracles)PASS (with oracles)FAIL (with oracles)
failing: K1, K3a, K4, K6, K8
FAIL (with oracles)
failing: K1, K3a, K3b, K3c, K4, K6, K8
the certificate files; one row per check below
K0 EnvelopePASSPASSPASSPASS
K1 Move-set integrityPASSPASSFAILFAIL
K2 Byte-span confinementPASSPASSPASS
nothing to compare: K1 counts the 211 added files
PASS
nothing to compare: K1 counts the 614 added files
K3a Resolution identity under TypeScriptPASSPASSFAILFAIL
K3b Resolution identity under the test runnerPASSPASSPASSFAIL
K3c Resolution identity under the runtimePASSPASSPASSFAIL
K4 Location scanPASSPASSFAILFAIL
K6 Test inventoryPASSPASSFAILFAIL
K7 Emit layoutPASSnot checked: K7 came after this certificate (S6)not checked: K7 came after this certificate (S6)not checked: K7 came after this certificate (S6)
K8 Oracles on the commitPASSPASSFAILFAIL

Where the gate's numbers and the certificates' differ

The single-shot branch's restructure is a dead copy tree: nothing imports its 211 copies, so the tests still run against the original files. (docs/gates.md (G3 table; Addendum: G3 review))

The persistent run is the reference baseline, because single-shot runs quit within 3 minutes. (docs/gates.md (Plan corrections, item 3))

The certificates

ac, the full ladder: PASS sha256:9a5b2404631c…

certificate JSON

ac certify 02063972e8..c4be040b57 (package root server/), profile immich
trees: 424df72c34..4e457bb4e8
pathMap: 367 moves, 180 edited in place, 2917 unchanged, 2 added, 0 deleted

PASS  K0  Envelope: inside the envelope: 678 TypeScript vs 3 JavaScript sources under server; no automock, nested package scope, symlink, same-stem pair or unreadable runner config around 367 moved files (0.2 s)
PASS  K1  Move-set integrity: 367 moves, 180 files edited in place, 2 inert tool configs added, nothing else added or deleted (8.3 s)
        finding (tool-config-added) server/.dependency-cruiser-known-violations.json: added at the package root and proven inert: in no tsconfig program, loaded by no reference, packed by no published package, named or globbed by no other file, and the tool runs nowhere; CI path filters match it, so it triggers their jobs: 'server/**' in .github/workflows/docker.yml (line 45), 'server/**' in .github/workflows/test.yml (line 46)
        finding (tool-config-added) server/.dependency-cruiser.json: added at the package root and proven inert: in no tsconfig program, loaded by no reference, packed by no published package, named or globbed by no other file, and the tool runs nowhere; CI path filters match it, so it triggers their jobs: 'server/**' in .github/workflows/docker.yml (line 45), 'server/**' in .github/workflows/test.yml (line 46)
PASS  K2  Byte-span confinement: 2474 specifier spans changed in 515 files; every other byte identical (3.4 s)
PASS  K3a Resolution identity under TypeScript: 6647 sites in 1100 files resolve to map(base) under every program (458 files in several); 2474 rewritten specifiers keep their form (1.8 s)
PASS  K3b Resolution identity under the test runner: vitest 4.1.11: 4837 sites in 672 files resolve to map(base) (5.1 s)
PASS  K3c Resolution identity under the runtime: 6192 runtime sites resolve to map(base) under nest-cli/emit (3442 rewritten by the build) (6.7 s)
PASS  K4  Location scan: 367 moved files expose no location; no computed path reaches a change; no old path in code, config or CI (13 findings in docs, comments or ungated coverage settings) (5.7 s)
        finding (stack-text) server/src/repositories/metadata.repository.ts: moved to server/src/platform/metadata/metadata.repository.ts; 1 stack-trace reads passed along as text (line 133); their frames now name the new path
        finding (stack-text) server/src/repositories/websocket.repository.ts: moved to server/src/platform/websocket/websocket.repository.ts; 1 stack-trace reads passed along as text (line 95); their frames now name the new path
        finding (stack-text) server/src/services/asset-media.service.ts: moved to server/src/modules/asset/asset-media.service.ts; 1 stack-trace reads passed along as text (line 224); their frames now name the new path
        … 23 more findings
PASS  K6  Test inventory: vitest 4.1.11: 105 test files, 2337 tests, per-file load status and case names equal (0.0 s)
PASS  K7  Emit layout: 7 emitting programs keep their outDir and rootDir (1 by TypeScript's default) (0.5 s)
        finding (default-root-dir) e2e/tsconfig.json: emits into e2e/dist without a rootDir; TypeScript 6.0+ and before 6.0's default stays e2e and e2e
PASS  K8  Oracles on the commit: tsc exit 0; unit 105/105 files and 2,337/2,337 cases equal to base (head: 105/105 files load, 2,337 passed, 0 failed, 0 skipped); openapi byte-identical to the base's open-api/immich-openapi-specs.json (947,932 bytes) (114.9 s)

verdict: PASS (static checks and oracles)
digest: sha256:9a5b2404631c2ac668462ad93fcbcd116637244e2b4f76e987bcc4e23e8be2b5
ac, one R1-style step: PASS sha256:2e9e00c96572…

certificate JSON

ac certify 02063972e8..ff429b0d24 (package root server/)
pathMap: 292 moves, 152 edited in place, 3020 unchanged, 0 added, 0 deleted

PASS  K0  Envelope: inside the envelope: 678 TypeScript vs 3 JavaScript sources under server; no automock, nested package scope, symlink, same-stem pair or unreadable runner config around 292 moved files (8.5 s)
PASS  K1  Move-set integrity: 292 moves, 152 files edited in place, nothing added or deleted (1.5 s)
PASS  K2  Byte-span confinement: 1445 specifier spans changed in 386 files; every other byte identical (2.9 s)
PASS  K3a Resolution identity under TypeScript: 6647 sites in 1100 files resolve to map(base) under every program (458 files in several); 1445 rewritten specifiers keep their form (1.5 s)
PASS  K3b Resolution identity under the test runner: vitest 4.1.11: 4837 sites in 672 files resolve to map(base) (5.8 s)
PASS  K3c Resolution identity under the runtime: 6192 runtime sites resolve to map(base) under nest-cli/emit (3442 rewritten by the build) (7.2 s)
PASS  K4  Location scan: 292 moved files expose no location; no computed path reaches a change; no old path in code, config or CI (9 findings in docs, comments or ungated coverage settings) (5.5 s)
        finding (stack-text) server/src/repositories/metadata.repository.ts: moved to server/src/modules/metadata/metadata.repository.ts; 1 stack-trace reads passed along as text (line 133); their frames now name the new path
        finding (stack-text) server/src/repositories/websocket.repository.ts: moved to server/src/modules/websocket/websocket.repository.ts; 1 stack-trace reads passed along as text (line 95); their frames now name the new path
        finding (stack-text) server/src/services/asset-media.service.ts: moved to server/src/modules/asset-media/asset-media.service.ts; 1 stack-trace reads passed along as text (line 224); their frames now name the new path
        … 18 more findings
PASS  K6  Test inventory: vitest 4.1.11: 105 test files, 2337 tests, per-file load status and case names equal (91.2 s)
PASS  K8  Oracles on the commit: tsc exit 0; unit 105/105 files and 2,337/2,337 cases equal to base (head: 105/105 files load, 2,337 passed, 0 failed, 0 skipped); openapi byte-identical to the base's open-api/immich-openapi-specs.json (947,932 bytes) (110.7 s)

verdict: PASS (static checks and oracles)
digest: sha256:2e9e00c96572e7347cf00084cacd7e371c3004c261092888575bb32d99138820

Ac-Certificate: pass (full) K0=pass K1=pass K2=pass K3=pass K4=pass K6=pass K8=pass
Ac-Certificate-Base: 02063972e82b1bfd17b6fc2e3b3ff39936229ea5
Ac-Certificate-Digest: sha256:2e9e00c96572e7347cf00084cacd7e371c3004c261092888575bb32d99138820
opencode + Nemotron 3 Super, single-shot: FAIL sha256:8cbe6c92987f…

certificate JSON

ac certify 02063972e8..03e76f3faa (package root server/)
pathMap: 0 moves, 0 edited in place, 3464 unchanged, 211 added, 0 deleted

PASS  K0  Envelope: inside the envelope: 889 TypeScript vs 3 JavaScript sources under server; no automock, nested package scope, symlink, same-stem pair or unreadable runner config around 0 moved files (9.1 s)
FAIL  K1  Move-set integrity: 211 violations (211 added, 0 deleted, 31 copies) (1.2 s)
        server/src/features/activity/activity.controller.spec.ts: added: no file in base has its content (modulo specifiers)
        server/src/features/activity/activity.controller.ts: added: no file in base has its content (modulo specifiers)
        server/src/features/activity/activity.dto.ts: added: no file in base has its content (modulo specifiers)
        server/src/features/activity/activity.repository.ts: added: a copy of server/src/repositories/activity.repository.ts, which still exists
        server/src/features/activity/activity.service.spec.ts: added: no file in base has its content (modulo specifiers)
        server/src/features/activity/activity.service.ts: added: no file in base has its content (modulo specifiers)
        server/src/features/album/album.controller.spec.ts: added: no file in base has its content (modulo specifiers)
        server/src/features/album/album.controller.ts: added: no file in base has its content (modulo specifiers)
        … 203 more
PASS  K2  Byte-span confinement: 0 specifier spans changed in 0 files; every other byte identical (0.0 s)
FAIL  K3a Resolution identity under TypeScript: 211 violations (211 added source files) (1.3 s)
        server/src/features/activity/activity.controller.spec.ts: added: its 3 reference sites have no counterpart in base
        server/src/features/activity/activity.controller.ts: added: its 7 reference sites have no counterpart in base
        server/src/features/activity/activity.dto.ts: added: its 4 reference sites have no counterpart in base
        server/src/features/activity/activity.repository.ts: added: its 10 reference sites have no counterpart in base
        server/src/features/activity/activity.service.spec.ts: added: its 6 reference sites have no counterpart in base
        server/src/features/activity/activity.service.ts: added: its 3 reference sites have no counterpart in base
        server/src/features/album/album.controller.spec.ts: added: its 3 reference sites have no counterpart in base
        server/src/features/album/album.controller.ts: added: its 6 reference sites have no counterpart in base
        … 203 more
PASS  K3b Resolution identity under the test runner: vitest 4.1.11: 4837 sites in 672 files resolve to map(base) (6.6 s)
PASS  K3c Resolution identity under the runtime: 6192 runtime sites resolve to map(base) under nest-cli/emit (3442 rewritten by the build) (7.6 s)
        finding (added-file-unresolved) server/src/features/plugin/plugin.dto.ts: nest-cli/emit: import '@immich/plugin-sdk' doesn't resolve (ERR_MODULE_NOT_FOUND) in a file with no base counterpart
        finding (added-file-unresolved) server/src/features/workflow/workflow.controller.spec.ts: nest-cli/emit: import '@immich/plugin-sdk' doesn't resolve (ERR_MODULE_NOT_FOUND) in a file with no base counterpart
        finding (added-file-unresolved) server/src/features/workflow/workflow.service.ts: nest-cli/emit: import '@immich/plugin-sdk' doesn't resolve (ERR_MODULE_NOT_FOUND) in a file with no base counterpart
FAIL  K4  Location scan: 0 path-exposing uses in moved files (and 3 in added files), 0 computed paths into changed directories, 0 old paths in code/config (0 in coverage settings) (0.9 s)
        server/src/features/library/library.service.spec.ts: added with no base counterpart; line 888: process.cwd()
        server/src/features/library/library.service.spec.ts: added with no base counterpart; line 1284: process.cwd()
        server/src/features/library/library.service.spec.ts: added with no base counterpart; line 1298: process.cwd()
        finding (stack-text) server/src/features/asset-media/asset-media.service.ts: added with no base counterpart; 1 stack-trace reads passed along as text (line 224); their frames now name the new path
        finding (stack-text) server/src/features/auth/auth.service.ts: added with no base counterpart; 1 stack-trace reads passed along as text (line 403); their frames now name the new path
        finding (stack-text) server/src/features/duplicate/duplicate.service.ts: added with no base counterpart; 1 stack-trace reads passed along as text (line 104); their frames now name the new path
        … 2 more findings
FAIL  K6  Test inventory: vitest 4.1.11: 66 violations (66 of 171 test files fail to load (base: 0), 66 new test files) (94.6 s)
        server/src/features/activity/activity.controller.spec.ts: new in the vitest test list (added in head; fails to load: x Unexpected token `[`. Expected a string literal ,-[/tmp/ac-s4-certify/g3-immich-opencode-super.full/worktree-head/server/src/features/activity/activity.controller.spec.ts:2:1] 1 | import request fro)
        server/src/features/activity/activity.service.spec.ts: new in the vitest test list (added in head; fails to load: x Unexpected token `[`. Expected a string literal ,-[/tmp/ac-s4-certify/g3-immich-opencode-super.full/worktree-head/server/src/features/activity/activity.service.spec.ts:2:1] 1 | import { BadRequestEx)
        server/src/features/album/album.controller.spec.ts: new in the vitest test list (added in head; fails to load: x Unexpected token `[`. Expected a string literal ,-[/tmp/ac-s4-certify/g3-immich-opencode-super.full/worktree-head/server/src/features/album/album.controller.spec.ts:2:1] 1 | import request from 'sup)
        server/src/features/album/album.service.spec.ts: new in the vitest test list (added in head; fails to load: x Unexpected token `[`. Expected a string literal ,-[/tmp/ac-s4-certify/g3-immich-opencode-super.full/worktree-head/server/src/features/album/album.service.spec.ts:2:1] 1 | import { BadRequestExceptio)
        server/src/features/app/app.controller.spec.ts: new in the vitest test list (added in head; fails to load: x Unexpected token `[`. Expected a string literal ,-[/tmp/ac-s4-certify/g3-immich-opencode-super.full/worktree-head/server/src/features/app/app.controller.spec.ts:2:1] 1 | import request from 'superte)
        server/src/features/asset/asset.controller.spec.ts: new in the vitest test list (added in head; fails to load: x Unexpected token `[`. Expected a string literal ,-[/tmp/ac-s4-certify/g3-immich-opencode-super.full/worktree-head/server/src/features/asset/asset.controller.spec.ts:2:1] 1 | import request from 'sup)
        server/src/features/asset/asset.service.spec.ts: new in the vitest test list (added in head; fails to load: x Unexpected token `[`. Expected a string literal ,-[/tmp/ac-s4-certify/g3-immich-opencode-super.full/worktree-head/server/src/features/asset/asset.service.spec.ts:3:1] 1 | import { BadRequestExceptio)
        server/src/features/asset-media/asset-media.controller.spec.ts: new in the vitest test list (added in head; fails to load: x Unexpected token `[`. Expected a string literal ,-[/tmp/ac-s4-certify/g3-immich-opencode-super.full/worktree-head/server/src/features/asset-media/asset-media.controller.spec.ts:2:1] 1 | import reque)
        … 58 more
FAIL  K8  Oracles on the commit: tsc exited 1 on head (1,680 diagnostics), 0 on base; unit 105/171 files and 2,337/2,337 cases equal to base (head: 105/171 files load, 2,337 passed, 0 failed, 0 skipped); openapi byte-identical to the base's open-api/immich-openapi-specs.json (947,932 bytes), but 1 step failed on head (118.9 s)
        tsc: `node_modules/.bin/tsc --noEmit --incremental false --pretty false` exited 1 on head (1,680 diagnostics), exit 0 on base (log /tmp/ac-s4-certify/g3-immich-opencode-super.full/oracles/head/tsc/step-1.log)
        server/src/features/activity/activity.controller.spec.ts: tsc: TS1005 ',' expected.
        server/src/features/activity/activity.controller.spec.ts: tsc: TS1002 Unterminated string literal.
        server/src/features/activity/activity.controller.spec.ts: tsc: TS1005 ',' expected.
        server/src/features/activity/activity.controller.spec.ts: tsc: TS1005 ',' expected.
        server/src/features/activity/activity.controller.spec.ts: tsc: TS1002 Unterminated string literal.
        server/src/features/activity/activity.controller.spec.ts: tsc: TS1005 ',' expected.
        server/src/features/activity/activity.controller.ts: tsc: TS1005 ',' expected.
        … 72 more

verdict: FAIL (static checks and oracles)
digest: sha256:8cbe6c92987fbc9132d2af720c36a32a3e5f5598021c85169e7d39de6b6d2beb

Ac-Certificate: fail (full) K0=pass K1=fail K2=pass K3=fail K4=fail K6=fail K8=fail
Ac-Certificate-Base: 02063972e82b1bfd17b6fc2e3b3ff39936229ea5
Ac-Certificate-Digest: sha256:8cbe6c92987fbc9132d2af720c36a32a3e5f5598021c85169e7d39de6b6d2beb
opencode + Nemotron 3 Super, persistent: FAIL sha256:90d2d44bbd64…

certificate JSON

ac certify 02063972e8..1da2348b7c (package root server/)
pathMap: 365 moves, 0 edited in place, 3099 unchanged, 614 added, 0 deleted

PASS  K0  Envelope: inside the envelope: 1242 TypeScript vs 3 JavaScript sources under server; no automock, nested package scope, symlink, same-stem pair or unreadable runner config around 365 moved files (23.1 s)
FAIL  K1  Move-set integrity: 979 violations (614 added, 0 deleted, 291 copies) (1.2 s)
        server/fix_app_files.py: added: no file in base has its content (modulo specifiers)
        server/fix_features_only.py: added: no file in base has its content (modulo specifiers)
        server/fix_indexes.py: added: no file in base has its content (modulo specifiers)
        server/fix_quotes.py: added: no file in base has its content (modulo specifiers)
        server/fix_quotes2.py: added: no file in base has its content (modulo specifiers)
        server/fix_restructure.py: added: no file in base has its content (modulo specifiers)
        server/rebuild_indexes.py: added: no file in base has its content (modulo specifiers)
        server/regenerate_indexes.py: added: no file in base has its content (modulo specifiers)
        … 971 more
        finding (ambiguous-match) 30 groups of identical files were paired by where their directories' other files went, then by name and path similarity
PASS  K2  Byte-span confinement: 0 specifier spans changed in 0 files; every other byte identical (4.2 s)
FAIL  K3a Resolution identity under TypeScript: 3097 violations (2168 resolution mismatches, 365 program or format changes, 564 added source files) (2.6 s)
        server/src/app.common.ts: line 9: import 'src/maintenance/maintenance-worker.service.js': expected file server/src.backup/maintenance/maintenance-worker.service.ts (typescript), got file server/src/maintenance/maintenance-worker.service.ts (typescript) under server/tsconfig.build.json, server/tsconfig.json
        server/src/app.common.ts: line 10: import 'src/middleware/websocket.adapter.js': expected file server/src.backup/middleware/websocket.adapter.ts (typescript), got file server/src/middleware/websocket.adapter.ts (typescript) under server/tsconfig.build.json, server/tsconfig.json
        server/src/app.common.ts: line 11: import 'src/repositories/config.repository.js': expected file server/src.backup/repositories/config.repository.ts (typescript), got unresolved under server/tsconfig.build.json, server/tsconfig.json
        server/src/app.common.ts: line 12: import 'src/repositories/logging.repository.js': expected file server/src.backup/repositories/logging.repository.ts (typescript), got unresolved under server/tsconfig.build.json, server/tsconfig.json
        server/src/app.common.ts: line 13: import 'src/repositories/telemetry.repository.js': expected file server/src.backup/repositories/telemetry.repository.ts (typescript), got unresolved under server/tsconfig.build.json, server/tsconfig.json
        server/src/app.common.ts: line 14: import 'src/services/api.service.js': expected file server/src.backup/services/api.service.ts (typescript), got unresolved under server/tsconfig.build.json, server/tsconfig.json
        server/src/app.common.ts: line 15: import 'src/utils/misc.js': expected file server/src.backup/utils/misc.ts (typescript), got file server/src/utils/misc.ts (typescript) under server/tsconfig.build.json, server/tsconfig.json
        server/src/app.module.ts: line 11: import 'src/controllers/index.js': expected file server/src.backup/controllers/index.ts (typescript), got file server/src/controllers/index.ts (typescript) under server/tsconfig.build.json, server/tsconfig.json
        … 3089 more
FAIL  K3b Resolution identity under the test runner: vitest 4.1.11: 4548 violations (2802 dangling, 131 retargeted, 1615 dangling in added files) (7.5 s)
        server/src/app.common.ts: site 8 'src/maintenance/maintenance-worker.service.js' (import): base server/src.backup/maintenance/maintenance-worker.service.ts, head server/src/maintenance/maintenance-worker.service.ts
        server/src/app.common.ts: site 9 'src/middleware/websocket.adapter.js' (import): base server/src.backup/middleware/websocket.adapter.ts, head server/src/middleware/websocket.adapter.ts
        server/src/app.common.ts: site 10 'src/repositories/config.repository.js' (import): base server/src.backup/repositories/config.repository.ts, head unresolved
        server/src/app.common.ts: site 11 'src/repositories/logging.repository.js' (import): base server/src.backup/repositories/logging.repository.ts, head unresolved
        server/src/app.common.ts: site 12 'src/repositories/telemetry.repository.js' (import): base server/src.backup/repositories/telemetry.repository.ts, head unresolved
        server/src/app.common.ts: site 13 'src/services/api.service.js' (import): base server/src.backup/services/api.service.ts, head unresolved
        server/src/app.common.ts: site 14 'src/utils/misc.js' (import): base server/src.backup/utils/misc.ts, head server/src/utils/misc.ts
        server/src/app.module.ts: site 10 'src/controllers/index.js' (import): base server/src.backup/controllers/index.ts, head server/src/controllers/index.ts
        … 4540 more
FAIL  K3c Resolution identity under the runtime: 2287 violations under nest-cli/emit (10.0 s)
        server/src/app.common.ts: nest-cli/emit: import 'src/maintenance/maintenance-worker.service.js' (emitted as './maintenance/maintenance-worker.service.js') resolves to server/src/maintenance/maintenance-worker.service.ts, expected server/src.backup/maintenance/maintenance-worker.service.ts; base server/src/app.common.ts 'src/maintenance/maintenance-worker.service.js'
        server/src/app.common.ts: nest-cli/emit: import 'src/middleware/websocket.adapter.js' (emitted as './middleware/websocket.adapter.js') resolves to server/src/middleware/websocket.adapter.ts, expected server/src.backup/middleware/websocket.adapter.ts; base server/src/app.common.ts 'src/middleware/websocket.adapter.js'
        server/src/app.common.ts: nest-cli/emit: import 'src/repositories/config.repository.js' resolves to unresolved (ERR_MODULE_NOT_FOUND), expected server/src.backup/repositories/config.repository.ts; base server/src/app.common.ts 'src/repositories/config.repository.js'
        server/src/app.common.ts: nest-cli/emit: import 'src/repositories/logging.repository.js' resolves to unresolved (ERR_MODULE_NOT_FOUND), expected server/src.backup/repositories/logging.repository.ts; base server/src/app.common.ts 'src/repositories/logging.repository.js'
        server/src/app.common.ts: nest-cli/emit: import 'src/repositories/telemetry.repository.js' resolves to unresolved (ERR_MODULE_NOT_FOUND), expected server/src.backup/repositories/telemetry.repository.ts; base server/src/app.common.ts 'src/repositories/telemetry.repository.js'
        server/src/app.common.ts: nest-cli/emit: import 'src/services/api.service.js' resolves to unresolved (ERR_MODULE_NOT_FOUND), expected server/src.backup/services/api.service.ts; base server/src/app.common.ts 'src/services/api.service.js'
        server/src/app.common.ts: nest-cli/emit: import 'src/utils/misc.js' (emitted as './utils/misc.js') resolves to server/src/utils/misc.ts, expected server/src.backup/utils/misc.ts; base server/src/app.common.ts 'src/utils/misc.js'
        server/src/app.module.ts: nest-cli/emit: import 'src/controllers/index.js' (emitted as './controllers/index.js') resolves to server/src/controllers/index.ts, expected server/src.backup/controllers/index.ts; base server/src/app.module.ts 'src/controllers/index.js'
        … 293 more
        finding (added-file-unresolved) server/src.backup/app.common.ts: nest-cli/emit: import 'src/repositories/config.repository.js' doesn't resolve (ERR_MODULE_NOT_FOUND) in a file with no base counterpart
        finding (added-file-unresolved) server/src.backup/app.common.ts: nest-cli/emit: import 'src/repositories/logging.repository.js' doesn't resolve (ERR_MODULE_NOT_FOUND) in a file with no base counterpart
        finding (added-file-unresolved) server/src.backup/app.common.ts: nest-cli/emit: import 'src/repositories/telemetry.repository.js' doesn't resolve (ERR_MODULE_NOT_FOUND) in a file with no base counterpart
        … 18 more findings
FAIL  K4  Location scan: 12 path-exposing uses in moved files (and 15 in added files), 2 computed paths into changed directories, 52 old paths in code/config (0 in coverage settings) (7.5 s)
        server/src/main.ts: moved to server/src.backup/main.ts; line 101: import.meta.filename: dirname(import.meta.filename) pointed at server/src (exists in base), now at server/src.backup (exists in head)
        server/src/main.ts: moved to server/src.backup/main.ts; line 116: new Worker(workerFile) (worker_threads)
        server/src/repositories/database.repository.ts: moved to server/src.backup/repositories/database.repository.ts; line 510: import(filePath) with a computed specifier (resolved relative to this file)
        server/src/repositories/database.repository.ts: moved to server/src.backup/repositories/database.repository.ts; line 511: import.meta.dirname: join(import.meta.dirname, '..', 'schema/migrations') pointed at server/src/schema/migrations (exists in base), now at server/src.backup/schema/migrations (exists in head)
        server/src/repositories/storage.repository.spec.ts: moved to server/src.backup/repositories/storage.repository.spec.ts; line 38: process.cwd()
        server/src/services/cli.service.ts: moved to server/src.backup/services/cli.service.ts; line 25: import.meta.dirname: join(import.meta.dirname, '../schema/migrations') pointed at server/src/schema/migrations (exists in base), now at server/src.backup/schema/migrations (exists in head)
        server/src/services/library.service.spec.ts: moved to server/src.backup/services/library.service.spec.ts; line 888: process.cwd()
        server/src/services/library.service.spec.ts: moved to server/src.backup/services/library.service.spec.ts; line 1284: process.cwd()
        … 73 more
        finding (stack-text) server/src/main.ts: moved to server/src.backup/main.ts; 1 stack-trace reads passed along as text (line 129); their frames now name the new path
        finding (stack-text) server/src/repositories/metadata.repository.ts: moved to server/src.backup/repositories/metadata.repository.ts; 1 stack-trace reads passed along as text (line 133); their frames now name the new path
        finding (stack-text) server/src/repositories/websocket.repository.ts: moved to server/src.backup/repositories/websocket.repository.ts; 1 stack-trace reads passed along as text (line 95); their frames now name the new path
        … 54 more findings
FAIL  K6  Test inventory: vitest 4.1.11: 189 violations (100 of 105 test files fail to load (base: 0), 4 files that no longer load, 96 dropped test files, 89 new test files, 2285 lost tests) (53.7 s)
        server/src/controllers/activity.controller.spec.ts: no longer in the vitest test list (now at server/src.backup/controllers/activity.controller.spec.ts)
        server/src/controllers/album.controller.spec.ts: no longer in the vitest test list (now at server/src.backup/controllers/album.controller.spec.ts)
        server/src/controllers/api-key.controller.spec.ts: no longer in the vitest test list (now at server/src.backup/controllers/api-key.controller.spec.ts)
        server/src/controllers/app.controller.spec.ts: no longer in the vitest test list (now at server/src.backup/controllers/app.controller.spec.ts)
        server/src/controllers/asset-media.controller.spec.ts: no longer in the vitest test list (now at server/src.backup/controllers/asset-media.controller.spec.ts)
        server/src/controllers/asset.controller.spec.ts: no longer in the vitest test list (now at server/src.backup/controllers/asset.controller.spec.ts)
        server/src/controllers/auth.controller.spec.ts: no longer in the vitest test list (now at server/src.backup/controllers/auth.controller.spec.ts)
        server/src/controllers/config.controller.spec.ts: no longer in the vitest test list (now at server/src.backup/controllers/config.controller.spec.ts)
        … 181 more
FAIL  K8  Oracles on the commit: tsc exited 1 on head (768 diagnostics), 0 on base; unit 5/105 files and 52/2,337 cases equal to base (head: 5/105 files load, 52 passed, 0 failed, 0 skipped); openapi produced nothing (39.3 s)
        tsc: `node_modules/.bin/tsc --noEmit --incremental false --pretty false` exited 1 on head (768 diagnostics), exit 0 on base (log /tmp/ac-s4-certify/g3-immich-opencode-super-persistent.full/oracles/head/tsc/step-1.log)
        server/src/bin/sync-sql.ts: tsc: TS1002 Unterminated string literal.
        server/src/bin/sync-sql.ts: tsc: TS1002 Unterminated string literal.
        server/src/bin/sync-sql.ts: tsc: TS1002 Unterminated string literal.
        server/src/bin/sync-sql.ts: tsc: TS1002 Unterminated string literal.
        server/src/bin/sync-sql.ts: tsc: TS1002 Unterminated string literal.
        server/src/bin/sync-sql.ts: tsc: TS1002 Unterminated string literal.
        server/src/bin/sync-sql.ts: tsc: TS1002 Unterminated string literal.
        … 206 more

verdict: FAIL (static checks and oracles)
digest: sha256:90d2d44bbd647b5f1b99d61590fbaa9f993572e244c7a8e91fd60c1b0161651e

Ac-Certificate: fail (full) K0=pass K1=fail K2=pass K3=fail K4=fail K6=fail K8=fail
Ac-Certificate-Base: 02063972e82b1bfd17b6fc2e3b3ff39936229ea5
Ac-Certificate-Digest: sha256:90d2d44bbd647b5f1b99d61590fbaa9f993572e244c7a8e91fd60c1b0161651e

Rendered by ac report from the run dir; the certificate files sit next to this page.