Authored by Opus 4.8 (Claude Code). Session de8830c3-8d1b-47f8-90db-bd236c8b9bd2.
Resolves #12191
Related: #12186
IssueSyncer needsUpdate compared oldIssue.updated, but the persisted metadata field is updatedAt (MetadataManager + every writer). So oldIssue.updated was always undefined → needsUpdate always true → every issue re-formatted, re-hashed and re-written every sync, defeating the content-hash short-circuit. One-character field fix.
FAIR-band: over-target — sole active implementer (@neo-gpt rate-limited, @neo-gemini benched) + operator-directed lane (epic #12186 P2).
Evidence: L1 (unit: an unchanged archived issue is left on disk untouched — 1 new test, 209 green) → L1 required (no runtime-only ACs). No residuals.
Deltas from ticket (if any)
#12191's second item — hoisting the per-iteration #planBuckets(metadata) out of the reconcile loop (:1010) — was investigated and dropped as unsafe (not deferred):
#planBuckets derives each issue's oldVersion from metadata.issues[*].path (:300-306), and the reconcile loop mutates metadata.issues[issueNumber].path per-iteration (:1046, after each archive move). The call is therefore not loop-invariant — per-iteration recomputation reflects in-progress moves; hoisting to a pre-loop snapshot would change chunk-packing behavior.
- The loop iterates active-closed issues only (
:1000-1006 skip already-archived + non-closed), typically few — so the real cost is small, not the ~77M-touch figure the investigation estimated (which assumed iteration over the full archive).
A genuine optimization needs a deeper refactor (stable plan + incremental packing) — out of scope and low-value at this N. The field fix is the substantive bug in #12191.
Test Evidence
npm run test-unit -- test/playwright/unit/ai/services/github-workflow/ → 209 passed (1 new, 0 regressions).
- New test (
IssueSyncer.spec): an unchanged archived issue (same updatedAt; sealed-chunk guarantees the path matches, so updatedAt is the sole discriminator) is not re-rendered — its on-disk sentinel survives the sync. Fails pre-fix (always-true needsUpdate overwrites it).
node --check; pre-commit whitespace/shorthand hooks green.
Post-Merge Validation
Authored by Opus 4.8 (Claude Code). Session de8830c3-8d1b-47f8-90db-bd236c8b9bd2.
Resolves #12191 Related: #12186
IssueSyncerneedsUpdatecomparedoldIssue.updated, but the persisted metadata field isupdatedAt(MetadataManager+ every writer). SooldIssue.updatedwas alwaysundefined→needsUpdatealwaystrue→ every issue re-formatted, re-hashed and re-written every sync, defeating the content-hash short-circuit. One-character field fix.FAIR-band: over-target — sole active implementer (@neo-gpt rate-limited, @neo-gemini benched) + operator-directed lane (epic #12186 P2).
Evidence: L1 (unit: an unchanged archived issue is left on disk untouched — 1 new test, 209 green) → L1 required (no runtime-only ACs). No residuals.
Deltas from ticket (if any)
#12191's second item — hoisting the per-iteration
#planBuckets(metadata)out of the reconcile loop (:1010) — was investigated and dropped as unsafe (not deferred):#planBucketsderives each issue'soldVersionfrommetadata.issues[*].path(:300-306), and the reconcile loop mutatesmetadata.issues[issueNumber].pathper-iteration (:1046, after each archive move). The call is therefore not loop-invariant — per-iteration recomputation reflects in-progress moves; hoisting to a pre-loop snapshot would change chunk-packing behavior.:1000-1006skip already-archived + non-closed), typically few — so the real cost is small, not the ~77M-touch figure the investigation estimated (which assumed iteration over the full archive).A genuine optimization needs a deeper refactor (stable plan + incremental packing) — out of scope and low-value at this N. The field fix is the substantive bug in #12191.
Test Evidence
npm run test-unit -- test/playwright/unit/ai/services/github-workflow/→ 209 passed (1 new, 0 regressions).IssueSyncer.spec): an unchanged archived issue (sameupdatedAt; sealed-chunk guarantees the path matches, soupdatedAtis the sole discriminator) is not re-rendered — its on-disk sentinel survives the sync. Fails pre-fix (always-trueneedsUpdateoverwrites it).node --check; pre-commit whitespace/shorthand hooks green.Post-Merge Validation
stats.pulledreflects only genuinely-changed issues, not the whole corpus).