Skip to content

Prune and refresh managed starter files during sync - #186

Merged
yourconscience merged 2 commits into
mainfrom
feat/sync-starter-manifest
Sep 22, 2026
Merged

yourconscience merged 2 commits into
mainfrom
feat/sync-starter-manifest

Conversation

@yourconscience

@yourconscience yourconscience commented Sep 22, 2026

Copy link
Copy Markdown
Owner

Why

setup copies the starter content once, and nothing ever updates or removes it afterwards. That is why a config root created by v0.8.0 kept the old per-provider digest modules (amp_digest.py, factory_digest.py, hermes_digest.py, omp-memory.ts) after v0.9.0 deleted them upstream, and why its memory/hooks/session-end.sh kept calling code that no longer exists. Deleting files is not something sync did at all.

What this does

sync now reconciles the managed starter code layer (memory/hooks/, memory/lib/) against the running release:

  • scaffolds missing files (as before)
  • refreshes files dotagents wrote that you have not modified, so an upgraded CLI cannot leave a half-old hook layer behind
  • removes files a release stopped shipping
  • reports and leaves alone anything you edited yourself

Ownership is tracked in .dotagents-starter.json at the config root (meant to be committed with dotagents.yaml), so every machine shares the same baseline.

Safety rule

dotagents only ever refreshes or removes content it wrote itself. A managed file is touched only when its hash matches the manifest baseline or a known earlier-release hash (legacyStarterHashes, seeded with the v0.8.0 versions that changed or were retired). Content dotagents does not recognize is never overwritten — it is reported as kept (modified by you) and no ownership is recorded for it.

AGENTS.md, dotagents.yaml, agents/*.md, and skills/ are never managed this way: those stay user content and are still only created when missing.

Removals honour the existing setup-driven confirmation flow (ConfirmRemovals), so dotagents setup previews them per item.

Example

Upgrading a real v0.8.0 config root:

starter files: updated 7, removed 5, scaffolded 0, kept modified 0
  updated: memory/hooks/common.sh, memory/hooks/session-end.sh, memory/hooks/stop.sh,
           memory/hooks/sync-memory-to-vault.sh, memory/hooks/sync-vault-to-memory.sh,
           memory/lib/basic_memory.py, memory/lib/sync.py
  removed: memory/hooks/README-codex-omp.md, memory/hooks/omp-memory.ts,
           memory/lib/amp_digest.py, memory/lib/factory_digest.py, memory/lib/hermes_digest.py

Verification

  • go test ./... passes, including 9 new tests: scaffold + manifest recording, idempotent second run, manifest-baseline refresh, legacy-hash refresh, unrecognized content never touched, retired-file removal only when unmodified, manifest-tracked removal, declined confirmation keeps the file, managed-path/legacy-path guards, and an end-to-end runSync case
  • End-to-end check against a temp config root seeded with the real v0.8.0 memory layer: 7 refreshed, 5 removed, manifest written, and the resulting hooks are byte-identical to the release
  • go build ./..., go vet ./..., golangci-lint v2.12.2 (cold cache, Go 1.24.2) — clean
  • Memory suite (26 tests), npm wrapper tests, release-script tests — pass

Notes

legacyStarterHashes is a one-time bridge for roots created before the manifest existed. Every release after this one is covered by the manifest, so the table does not need to grow.

Docs updated: README memory section, docs/setup.md (new "Managed starter files"), memory/README.md.

Summary by Sourcery

Keep the managed memory starter code layer synchronized with the running release while preserving user-owned customizations.

New Features:

  • Reconcile managed memory starter files during sync by scaffolding missing files, refreshing unmodified files, and removing retired files.
  • Track starter-file ownership and baselines in a config-root manifest, including compatibility with pre-manifest releases.

Bug Fixes:

  • Prevent stale managed hooks and libraries from persisting after CLI upgrades while protecting user modifications from overwrite or removal.

Enhancements:

  • Add safety checks for manifest paths and restrict reconciliation to the managed memory code layer.
  • Report updated, removed, scaffolded, and user-modified starter files during sync, with confirmation for removals.

Documentation:

  • Document managed starter-file reconciliation, ownership tracking, and customization safety in the README and setup and memory guides.

Tests:

  • Add coverage for scaffolding, idempotency, baseline and legacy refreshes, safe removals, declined confirmations, user edits, path guards, and end-to-end sync reconciliation.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@sourcery-ai

sourcery-ai Bot commented Sep 22, 2026

Copy link
Copy Markdown

Reviewer's Guide

Sync now reconciles only the managed memory hooks and libraries against the running release using a committed hash manifest plus v0.8.0 compatibility hashes: it scaffolds and refreshes recognized content, safely removes retired files with confirmation, preserves and reports edits, and documents the ownership model.

Sequence diagram for managed starter reconciliation during sync

sequenceDiagram
    participant Sync
    participant Reconciler as reconcileStarterFiles
    participant Manifest as .dotagents-starter.json
    participant Disk as ConfigRoot
    participant Confirm as ConfirmRemovals

    Sync->>Reconciler: reconcileStarterFiles
    Reconciler->>Manifest: loadStarterManifest
    Reconciler->>Disk: shippedStarterFiles
    loop managed memory/hooks and memory/lib files
        Reconciler->>Disk: read starter path
        alt missing
            Reconciler->>Disk: writeStarterFile
        else manifest or legacy hash matches
            Reconciler->>Disk: writeStarterFile
        else unrecognized or user-modified
            Reconciler-->>Sync: report kept modified
        end
    end
    opt retired managed files
        Reconciler->>Confirm: promptYesNoDefaultNo
        alt confirmed and recognized hash
            Reconciler->>Disk: remove retired file
        else declined or modified
            Reconciler-->>Sync: report kept modified
        end
    end
    Reconciler->>Manifest: saveStarterManifest
    Reconciler-->>Sync: report changes
Loading

Flow diagram for safe managed starter file decisions

flowchart TD
    A[dotagents sync] --> B[Load manifest and shipped managed files]
    B --> C{File exists?}
    C -->|No| D[Scaffold file]
    C -->|Yes| E{Disk hash matches shipped or ownership baseline?}
    E -->|Yes| F[Refresh file and record shipped hash]
    E -->|No| G[Keep file and report modified]
    B --> H[Find manifest or legacy files no longer shipped]
    H --> I{Retired file hash recognized?}
    I -->|No| G
    I -->|Yes| J{Removal confirmed?}
    J -->|Yes| K[Remove file]
    J -->|No| G
    D --> L[Save .dotagents-starter.json]
    F --> L
    K --> L
    G --> L
Loading

File-Level Changes

Change Details Files
Add manifest-backed reconciliation for the managed memory starter code layer.
  • Restrict ownership to memory/hooks/ and memory/lib/.
  • Hash shipped and on-disk content with manifest and v0.8.0 legacy baselines.
  • Scaffold missing files and refresh recognized unmodified files.
  • Persist the current shipped-file hashes in .dotagents-starter.json.
internal/app/starter_manifest.go
internal/app/starter_manifest_test.go
Integrate starter reconciliation into sync with safe retirement and reporting.
  • Run reconciliation during sync before memory tool installation.
  • Remove retired files only when their content matches a recorded or legacy baseline.
  • Reuse setup removal confirmation and retain declined or user-modified files.
  • Report updated, removed, scaffolded, and user-modified files.
internal/app/sync.go
internal/app/starter_manifest.go
internal/app/starter_manifest_test.go
Document managed starter-file lifecycle and ownership boundaries.
  • Describe refresh, scaffolding, removal, and user-edit safety behavior.
  • Explain committing .dotagents-starter.json with the config root.
  • Clarify that general starter content remains user-owned.
README.md
docs/setup.md
memory/README.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai

coderabbitai Bot commented Sep 22, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: c71ddefa-b294-4bfb-b86d-082d224ae2a6


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 2 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="internal/app/starter_manifest.go" line_range="258-263" />
<code_context>
+		}
+	}
+
+	// Managed files this release no longer ships.
+	for _, path := range sortedKeysNative(manifest.Files) {
+		if _, stillShipped := shipped[path]; stillShipped {
+			continue
+		}
+		removed, kept, err := retireStarterFile(root, path, manifest.Files[path], legacy[path], streams, confirm)
+		if err != nil {
+			return changes, err
</code_context>
<issue_to_address>
**🚨 issue (security):** A committed `.dotagents-starter.json` can contain a path such as `../../some/file`; the manifest removal loop passes it to `retireStarterFile`, whose `filepath.Join` resolves outside the config root and removes the external file when its hash matches the recorded value.

**Triggers:** When a config root contains a malicious or corrupted manifest with a path-traversal entry and a matching hash.

**Suggested fix:** Reject manifest entries unless they are relative, clean, and satisfy `isManagedStarterPath`, and ensure the resolved target remains under `root` before reading or removing it.
</issue_to_address>

### Comment 2
<location path="internal/app/sync.go" line_range="77" />
<code_context>
+	if err != nil {
+		return err
+	}
+	starterChanges.report(os.Stdout)
+
 	toolInstalls, err := installMemoryTools(repoRoot)
</code_context>
<issue_to_address>
**issue (bug_risk):** The starter reconciliation report is written to `os.Stdout` instead of the sync operation's configured output stream, so callers that pass `Stdout: io.Discard` or a buffer still receive starter-change output on the process stdout.

**Triggers:** When sync is invoked by the TUI, web server, or a test/API caller that supplies a custom `runOptions.Stdout`.

**Suggested fix:** Call `starterChanges.report(setupStreams(opts).out)` or otherwise reuse the configured output writer.

```suggestion
	starterChanges.report(setupStreams(opts).out)
```
</issue_to_address>

Sourcery assessment

Needs a human reviewer. 2 findings to address first, and sync now persists changes to the config root and can delete starter files that a release no longer ships. If the ownership or hash checks are wrong, a file could be removed or stale code could be refreshed; reverting the CLI would not restore a deleted file without another copy, although the scope is bounded and modified files are intended to be protected.

Blocking findings: internal/app/starter_manifest.go:263, internal/app/sync.go:77


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Comment thread internal/app/starter_manifest.go
Comment thread internal/app/sync.go Outdated
@yourconscience
yourconscience merged commit 7612526 into main Sep 22, 2026
7 checks passed
@yourconscience
yourconscience deleted the feat/sync-starter-manifest branch September 22, 2026 14:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant