Skip to content

improve/sync: thread explicit written-file list into auto-sync for deterministic commits #652

Description

@itlackey

Summary

Thread an explicit list of files written during an akm improve run through to the end-of-run git auto-sync (saveGitStash), so the commit stages exactly the files the run modified — deterministic, rather than the current "stage everything under akm-managed dirs" heuristic.

Background

PR #651 fixed a production incident where end-of-run auto-sync refused to commit for ~1.5 days because stray non-akm files (tasks.bak-…, data.js, akm-health-report.html, reports/) in the stash root tripped the old all-or-nothing git add -A + refuse-guard. The fix replaced that with a scoped-staging precedence in saveGitStash (src/sources/providers/git.ts):

  1. Explicit listopts.paths?: string[]git add -- <those> (chunked). (exposed, but no caller passes it yet)
  2. Managed pathspecs (default)git add -- <each TYPE_DIRS value> .akm (those that exist). This is what akm improve currently uses.
  3. git add -A — last resort only.

The opts.paths parameter exists and is tested, but akm improve's sync call site (src/commands/improve/improve.ts, the saveGitStashFn(undefined, message, writableOverride, { push, repoDir }) call) passes undefined, so improve falls back to path 2.

Why this issue

Path 2 (managed-pathspec scoping) is correct and safe — it commits everything akm owns and never stages stray non-akm files. But it is coarse: it stages any dirty file under memories/, knowledge/, lessons/, facts/, etc., even ones a human edited out-of-band during the run, not just the files this improve run actually wrote. Threading the exact written-file set makes auto-sync deterministic and attributable: the commit contains precisely the run's accepted-proposal writes and nothing else.

Proposed work

  1. Collect written paths in the improve result. During a run, accumulate the repo-relative path of every file akm writes:
    • accepted-proposal target files (reflect / distill / consolidate / extract / recombine / procedural promotions),
    • memory-inference .derived.md writes,
    • any direct edits / schema-repair / lint-fix writes,
    • removals (deletes) so they can be git add -- (staged for deletion) too.
      Expose as e.g. result.writtenPaths: string[] (deduped, repo-relative).
  2. Thread it into the sync call. Pass { paths: result.writtenPaths } into saveGitStash at the improve sync site, so precedence path 1 is used.
  3. Keep the managed-pathspec fallback for callers that don't/can't supply a list (e.g. akm sync, akm push). Empty/undefined paths must still behave as today (path 2).
  4. Edge cases: if writtenPaths is non-empty but a path was later reverted/removed by a downstream pass (e.g. proposal reverted, orphan purge), ensure the staged set reflects the final on-disk state (stage deletions; don't add a path that no longer exists without also handling the delete). Verify git diff --cached --quiet still short-circuits empty commits.

Acceptance criteria

  • A run that writes files A, B under memories/ while an unrelated human edit C also sits dirty under memories/ → the auto-sync commit contains only A and B, not C. (With managed-pathspec scoping today, C would be swept in.)
  • result.writtenPaths is populated and deduped; deletions handled.
  • akm sync / akm push (no explicit list) unchanged — still managed-pathspec scoped.
  • Tests cover: written-list staging excludes unrelated managed-dir edits; deletions staged; empty list falls back to managed pathspecs; reverted/removed path handled.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions