Problem
The weekly sync-content-check.yml workflow creates automated PRs listing all 32 synced files as "changed" every run, even when upstream doc content is byte-for-byte identical to the previous week.
Evidence: PR #15 and PR #16 list the exact same 32 files across the same 5 repositories — only the pinned SHAs differ. The has_changes gate introduced in PR #10 is not filtering these out.
Root Cause
writeFileSafe in cmd/sync-content/sync.go detects unchanged files by comparing against existing content on disk:
func writeFileSafe(path string, data []byte) (bool, error) {
existing, err := os.ReadFile(path)
if err == nil && bytes.Equal(existing, data) {
return false, nil
}
// ...
return true, os.WriteFile(path, data, 0o644)
}
However, content/docs/projects/*/ is gitignored — these files are never committed. On a fresh GitHub Actions runner, os.ReadFile always returns "file not found", so writeFileSafe always returns written = true, and every file is recorded as "changed."
Design Constraint
The architecture is intentionally ephemeral: no cached content, no committed doc files, everything fetched fresh on the runner. Any fix must stay within this model.
Possible Approaches (needs research)
-
Workflow reorder (no code changes): Run --write with the old lock first to establish a baseline on disk, then --update-lock, then --write again with the new lock so writeFileSafe can compare. Trade-off: 3 invocations instead of 2 (more API calls, but negligible at weekly cadence).
-
Dual-lock comparison in code: Accept both old and new lock files in a single --write pass; fetch content at both SHAs and compare in memory. Trade-off: more complex code, but single invocation.
-
Content hash in lockfile: Store a content hash per file in .content-lock.json alongside the branch SHA; compare hashes before fetching. Trade-off: lockfile grows, but avoids re-fetching unchanged content entirely.
References
Problem
The weekly
sync-content-check.ymlworkflow creates automated PRs listing all 32 synced files as "changed" every run, even when upstream doc content is byte-for-byte identical to the previous week.Evidence: PR #15 and PR #16 list the exact same 32 files across the same 5 repositories — only the pinned SHAs differ. The
has_changesgate introduced in PR #10 is not filtering these out.Root Cause
writeFileSafeincmd/sync-content/sync.godetects unchanged files by comparing against existing content on disk:However,
content/docs/projects/*/is gitignored — these files are never committed. On a fresh GitHub Actions runner,os.ReadFilealways returns "file not found", sowriteFileSafealways returnswritten = true, and every file is recorded as "changed."Design Constraint
The architecture is intentionally ephemeral: no cached content, no committed doc files, everything fetched fresh on the runner. Any fix must stay within this model.
Possible Approaches (needs research)
Workflow reorder (no code changes): Run
--writewith the old lock first to establish a baseline on disk, then--update-lock, then--writeagain with the new lock sowriteFileSafecan compare. Trade-off: 3 invocations instead of 2 (more API calls, but negligible at weekly cadence).Dual-lock comparison in code: Accept both old and new lock files in a single
--writepass; fetch content at both SHAs and compare in memory. Trade-off: more complex code, but single invocation.Content hash in lockfile: Store a content hash per file in
.content-lock.jsonalongside the branch SHA; compare hashes before fetching. Trade-off: lockfile grows, but avoids re-fetching unchanged content entirely.References
has_changesgate (014-skip-lockfile-only-pr)specs/014-skip-lockfile-only-pr/spec.md