mirror of
https://github.com/Kaelio/ktx.git
synced 2026-06-28 08:49:38 +02:00
fix(ingest): honor storage.git.auto_commit and memory.auto_commit
Both documented flags were read only for status display; every ingest path squash-committed to main unconditionally, so setting either to false was a silent no-op (the reported symptom: 'Memory ingest (external_ingest): ...' commits despite memory.auto_commit: false). Gate the commit at the squash-merge onto main — the one point where ingest work becomes a permanent commit (intermediate session-worktree commits must still happen for the squash to collapse). When auto-commit is off, apply the squash to main's working tree and leave it staged instead of committing, so the run is never silently discarded: - GitService.stageSquashMergeIntoMain: shares the merge core with squashMergeIntoMain but stops before committing and returns the staged tree SHA (a valid diff/read ref). - memory.auto_commit gates MemoryAgentService (its DB writes are eager, so the staged files stay consistent); the commit-message job is skipped. - storage.git.auto_commit gates IngestBundleRunner; the wiki index is reconciled from the staged tree via the existing syncFromCommit (git diff/show accept a write-tree ref), and SL reindex already reads from files. Config descriptions now state precisely what each flag gates and the staged semantics when false.
This commit is contained in:
parent
1a6da14f62
commit
a02fcab487
15 changed files with 303 additions and 43 deletions
|
|
@ -400,6 +400,66 @@ describe('GitService', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('stageSquashMergeIntoMain', () => {
|
||||
it('applies the branch to main without committing, leaving the changes staged', async () => {
|
||||
const { commitHash: baseSha } = await writeAndCommit('seed.md', 'seed');
|
||||
const parent = await realpath(join(tempDir, '..'));
|
||||
const wtDir = join(parent, `wt-${Date.now()}-stage`);
|
||||
await service.addWorktree(wtDir, 'session/stage', baseSha);
|
||||
|
||||
const scoped = service.forWorktree(wtDir);
|
||||
await writeFile(join(wtDir, 'a.yaml'), 'one: 1\n', 'utf-8');
|
||||
await scoped.commitFile('a.yaml', 'wip a', 'System User', 'system@example.com');
|
||||
|
||||
const result = await service.stageSquashMergeIntoMain('session/stage');
|
||||
expect(result.ok).toBe(true);
|
||||
if (!result.ok) {
|
||||
throw new Error('unreachable');
|
||||
}
|
||||
expect(result.touchedPaths).toEqual(['a.yaml']);
|
||||
expect(result.stagedTree).toMatch(/^[0-9a-f]{40}$/);
|
||||
|
||||
// HEAD did not advance: no commit was created.
|
||||
expect(await service.revParseHead()).toBe(baseSha);
|
||||
// The change is in main's working tree...
|
||||
await expect(readFile(join(tempDir, 'a.yaml'), 'utf-8')).resolves.toBe('one: 1\n');
|
||||
// ...and staged in the index for the user to commit.
|
||||
const stagedNames = await createSimpleGit(tempDir).raw(['diff', '--cached', '--name-only']);
|
||||
expect(stagedNames).toContain('a.yaml');
|
||||
// The staged tree is usable as a diff/read ref for DB sync.
|
||||
const treeListing = await createSimpleGit(tempDir).raw(['ls-tree', '-r', '--name-only', result.stagedTree]);
|
||||
expect(treeListing).toContain('a.yaml');
|
||||
|
||||
await service.removeWorktree(wtDir).catch(() => undefined);
|
||||
await rm(wtDir, { recursive: true, force: true }).catch(() => undefined);
|
||||
});
|
||||
|
||||
it('reports conflicts without committing or mutating main', async () => {
|
||||
const { commitHash: baseSha } = await writeAndCommit('conflict.md', 'base\n');
|
||||
const parent = await realpath(join(tempDir, '..'));
|
||||
const wtDir = join(parent, `wt-${Date.now()}-stage-conflict`);
|
||||
await service.addWorktree(wtDir, 'session/stage-conflict', baseSha);
|
||||
const scoped = service.forWorktree(wtDir);
|
||||
await writeFile(join(wtDir, 'conflict.md'), 'from-branch\n', 'utf-8');
|
||||
await scoped.commitFile('conflict.md', 'branch edit', 'System User', 'system@example.com');
|
||||
|
||||
// Move main ahead with a conflicting change.
|
||||
await writeAndCommit('conflict.md', 'from-main\n');
|
||||
const mainHead = await service.revParseHead();
|
||||
|
||||
const result = await service.stageSquashMergeIntoMain('session/stage-conflict');
|
||||
expect(result.ok).toBe(false);
|
||||
if (result.ok) {
|
||||
throw new Error('unreachable');
|
||||
}
|
||||
expect(result.conflictPaths).toContain('conflict.md');
|
||||
expect(await service.revParseHead()).toBe(mainHead);
|
||||
|
||||
await service.removeWorktree(wtDir).catch(() => undefined);
|
||||
await rm(wtDir, { recursive: true, force: true }).catch(() => undefined);
|
||||
});
|
||||
});
|
||||
|
||||
describe('squashMergeIntoMain', () => {
|
||||
it('merges a session branch as one commit on main, returning the new SHA + touched paths', async () => {
|
||||
const { commitHash: baseSha } = await writeAndCommit('seed.md', 'seed');
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue