Skip to content

feat: add progress feedback for liberate/init commands#32

Merged
TonyCasey merged 2 commits intomainfrom
git-64
Feb 12, 2026
Merged

feat: add progress feedback for liberate/init commands#32
TonyCasey merged 2 commits intomainfrom
git-64

Conversation

@TonyCasey
Copy link
Copy Markdown
Owner

@TonyCasey TonyCasey commented Feb 12, 2026

Summary

  • Adds ILiberateProgress type and optional onProgress callback to ILiberateOptions
  • LiberateService emits progress at triage, enriching, and complete phases
  • Both init and liberate CLI commands write in-place progress to stderr using \r
  • Existing callers (hooks, tests, SessionCaptureService) are unaffected — onProgress is optional

Closes GIT-64

Test plan

  • npm run pre-commit passes (type-check + lint)
  • All 312 tests pass
  • Manual: git-mem liberate --enrich --commit-count 10 shows per-commit progress
  • Manual: git-mem init with ANTHROPIC_API_KEY in .env shows progress

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Real-time progress reporting during liberate operations with phases (triage, processing, complete), per-item counts, commit identifiers, and facts-extracted totals.
    • CLI stderr updates support in-place TTY progress and newline-based fallbacks; ensures a final summary line on completion or error.
    • Added an optional progress callback API so callers can receive progress events.
  • Tests

    • Added tests validating triage/processing/complete progress flows, including zero-item scenarios.

…T-64)

Add ILiberateProgress type and optional onProgress callback to
ILiberateOptions. LiberateService emits progress at triage, enriching,
and complete phases. Both init and liberate CLI commands use the
callback to write in-place progress to stderr.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings February 12, 2026 23:16
@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.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 12, 2026

📝 Walkthrough

Walkthrough

Adds progress reporting to the liberate flow: a new ILiberateProgress type and optional onProgress callback are introduced; LiberateService emits triage/processing/complete progress events; CLI commands use a stderr progress handler and a wrapper to ensure formatted progress output.

Changes

Cohort / File(s) Summary
Interfaces
src/application/interfaces/ILiberateService.ts
Add ILiberateProgress type and extend ILiberateOptions with readonly onProgress?: (progress: ILiberateProgress) => void.
Service implementation
src/application/services/LiberateService.ts
Emit progress events from liberate: triage (phase:'triage'), per-commit processing (phase:'processing', includes sha, subject, incremental current, cumulative factsExtracted), and final complete (phase:'complete'). Introduce highInterestTotal and commitIndex to drive events.
CLI / progress handling
src/commands/progress.ts, src/commands/init.ts, src/commands/liberate.ts
New createStderrProgressHandler and liberateWithProgress added. Commands now wrap liberate calls with liberateWithProgress and pass the stderr handler as onProgress to surface triage/processing/complete updates to stderr, handling TTY/non-TTY output and ensuring trailing newline on exit.
Tests
tests/unit/application/services/LiberateService.test.ts
Add tests asserting progress emissions for triage, processing, and complete phases, including a zero-high-interest-commits scenario; import and use ILiberateProgress in tests.

Sequence Diagram(s)

sequenceDiagram
    participant CLI as "CLI Command"
    participant Wrapper as "liberateWithProgress"
    participant Service as "LiberateService"
    participant Handler as "createStderrProgressHandler / stderr"

    CLI->>Wrapper: call liberateWithProgress(fn, onProgress)
    Wrapper->>Service: fn() -> liberate({ onProgress })
    Service->>Handler: onProgress({ phase: "triage", total })
    Service->>Handler: onProgress({ phase: "processing", current, total, sha, subject, factsExtracted })
    Service->>Handler: onProgress({ phase: "complete", current: total, total, factsExtracted })
    Handler-->>CLI: write updates to stderr (TTY-aware)
    Wrapper-->>CLI: ensure final newline / emit final complete if needed
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 3 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add progress feedback for liberate/init commands' clearly and concisely summarizes the main change—adding progress reporting functionality to two CLI commands.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into main

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch git-64

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

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@src/commands/liberate.ts`:
- Around line 52-62: The onProgress callback in liberate.ts duplicates logic
found in init.ts; extract this shared formatting into a helper factory (e.g.,
createStderrProgressHandler) that returns (p: ILiberateProgress) => void and
implements the triage/enriching/complete branches (use p.sha.slice(0,7),
truncate p.subject to 60 chars, and write to process.stderr as currently done);
replace the local onProgress in liberate.ts (and the one in init.ts) by calling
createStderrProgressHandler() to get the handler and pass that to the liberate
service so both commands reuse the same implementation.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds progress reporting to the liberate workflow so the CLI can provide real-time feedback during commit triage and per-commit processing, without impacting existing service callers (progress is optional).

Changes:

  • Introduces ILiberateProgress and an optional onProgress callback on ILiberateOptions.
  • Emits progress events from LiberateService at triage, per-commit processing, and completion.
  • Updates init and liberate commands to render in-place progress updates to stderr.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 8 comments.

File Description
src/commands/liberate.ts Adds an onProgress callback to print triage + per-commit progress to stderr.
src/commands/init.ts Adds the same stderr progress reporting when init runs liberate with enrichment.
src/application/services/LiberateService.ts Emits progress callbacks during triage/per-commit loop and on completion.
src/application/interfaces/ILiberateService.ts Defines ILiberateProgress and adds onProgress to options.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 52 to 71
const onProgress = (p: ILiberateProgress): void => {
if (p.phase === 'triage') {
process.stderr.write(`Found ${p.total} high-interest commits to analyze.\n`);
} else if (p.phase === 'enriching') {
const sha = p.sha.slice(0, 7);
const subject = p.subject.length > 60 ? p.subject.slice(0, 57) + '...' : p.subject;
process.stderr.write(`\r [${p.current}/${p.total}] ${sha} ${subject} (${p.factsExtracted} facts)`);
} else if (p.phase === 'complete') {
process.stderr.write('\n');
}
};

const result = await liberateService.liberate({
since,
maxCommits,
dryRun: options.dryRun,
threshold,
enrich: options.enrich,
onProgress,
});
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

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

If liberateService.liberate() throws after writing an in-place progress line, the terminal may be left mid-line and subsequent error output will be messy. Consider wrapping the call in a try/finally that ensures a trailing newline is written if progress was started.

Suggested change
const onProgress = (p: ILiberateProgress): void => {
if (p.phase === 'triage') {
process.stderr.write(`Found ${p.total} high-interest commits to analyze.\n`);
} else if (p.phase === 'enriching') {
const sha = p.sha.slice(0, 7);
const subject = p.subject.length > 60 ? p.subject.slice(0, 57) + '...' : p.subject;
process.stderr.write(`\r [${p.current}/${p.total}] ${sha} ${subject} (${p.factsExtracted} facts)`);
} else if (p.phase === 'complete') {
process.stderr.write('\n');
}
};
const result = await liberateService.liberate({
since,
maxCommits,
dryRun: options.dryRun,
threshold,
enrich: options.enrich,
onProgress,
});
let inPlaceProgressStarted = false;
const onProgress = (p: ILiberateProgress): void => {
if (p.phase === 'triage') {
process.stderr.write(`Found ${p.total} high-interest commits to analyze.\n`);
} else if (p.phase === 'enriching') {
const sha = p.sha.slice(0, 7);
const subject = p.subject.length > 60 ? p.subject.slice(0, 57) + '...' : p.subject;
inPlaceProgressStarted = true;
process.stderr.write(`\r [${p.current}/${p.total}] ${sha} ${subject} (${p.factsExtracted} facts)`);
} else if (p.phase === 'complete') {
if (inPlaceProgressStarted) {
process.stderr.write('\n');
inPlaceProgressStarted = false;
}
}
};
const result = await (async () => {
try {
return await liberateService.liberate({
since,
maxCommits,
dryRun: options.dryRun,
threshold,
enrich: options.enrich,
onProgress,
});
} finally {
if (inPlaceProgressStarted) {
process.stderr.write('\n');
inPlaceProgressStarted = false;
}
}
})();

Copilot uses AI. Check for mistakes.
Comment on lines +199 to +207
const onProgress = (p: ILiberateProgress): void => {
if (p.phase === 'triage') {
process.stderr.write(`Found ${p.total} high-interest commits to analyze.\n`);
} else if (p.phase === 'enriching') {
const sha = p.sha.slice(0, 7);
const subject = p.subject.length > 60 ? p.subject.slice(0, 57) + '...' : p.subject;
process.stderr.write(`\r [${p.current}/${p.total}] ${sha} ${subject} (${p.factsExtracted} facts)`);
} else if (p.phase === 'complete') {
process.stderr.write('\n');
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

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

The progress output uses \r unconditionally. When stderr is not a TTY (CI logs, redirected output), this will leave raw carriage returns in logs. Consider gating the in-place update behind process.stderr.isTTY and falling back to newline-based progress (or no progress) when not a TTY; also clear the line before rewriting so shorter subjects don't leave trailing characters from the previous update.

Suggested change
const onProgress = (p: ILiberateProgress): void => {
if (p.phase === 'triage') {
process.stderr.write(`Found ${p.total} high-interest commits to analyze.\n`);
} else if (p.phase === 'enriching') {
const sha = p.sha.slice(0, 7);
const subject = p.subject.length > 60 ? p.subject.slice(0, 57) + '...' : p.subject;
process.stderr.write(`\r [${p.current}/${p.total}] ${sha} ${subject} (${p.factsExtracted} facts)`);
} else if (p.phase === 'complete') {
process.stderr.write('\n');
const isTTY = process.stderr.isTTY;
let lastProgressLength = 0;
const onProgress = (p: ILiberateProgress): void => {
if (p.phase === 'triage') {
process.stderr.write(`Found ${p.total} high-interest commits to analyze.\n`);
} else if (p.phase === 'enriching') {
const sha = p.sha.slice(0, 7);
const subject = p.subject.length > 60 ? p.subject.slice(0, 57) + '...' : p.subject;
const line = ` [${p.current}/${p.total}] ${sha} ${subject} (${p.factsExtracted} facts)`;
if (isTTY) {
const paddedLine = line.padEnd(lastProgressLength, ' ');
lastProgressLength = line.length;
process.stderr.write(`\r${paddedLine}`);
} else {
process.stderr.write(`${line}\n`);
}
} else if (p.phase === 'complete') {
if (isTTY) {
process.stderr.write('\n');
}

Copilot uses AI. Check for mistakes.
Comment on lines 211 to 215
const result = await liberateService.liberate({
maxCommits: commitCount,
enrich: true,
onProgress,
});
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

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

If liberateService.liberate() throws after writing an in-place progress line, the terminal may be left mid-line and subsequent error output will be messy. Consider wrapping the call in a try/finally that ensures a trailing newline is written if progress was started.

Copilot uses AI. Check for mistakes.
export interface ILiberateProgress {
/** Current phase of processing. */
readonly phase: 'triage' | 'enriching' | 'complete';
/** Current commit index (1-based). */
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

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

ILiberateProgress.current is documented as 1-based, but the service emits { phase: 'triage', current: 0, ... }. Either update the documentation to explicitly allow 0 for non-per-commit phases (triage/complete), or change the emitted value to match the documented contract.

Suggested change
/** Current commit index (1-based). */
/**
* Current commit index.
* 1-based during per-commit phases (e.g. 'enriching'); 0 during 'triage' and 'complete'.
*/

Copilot uses AI. Check for mistakes.
let commitIndex = 0;
for (const scored of triageResult.highInterest) {
commitIndex++;
options?.onProgress?.({ phase: 'enriching', current: commitIndex, total: highInterestTotal, sha: scored.commit.sha, subject: scored.commit.subject, factsExtracted: totalFactsExtracted });
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

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

The service emits phase: 'enriching' for every high-interest commit even when options.enrich is false and no LLM enrichment occurs. This makes the phase name misleading for consumers; consider renaming the phase (e.g., to 'processing'/'extracting') or only emitting 'enriching' when enrichment is actually enabled, with a separate phase for heuristic-only processing.

Suggested change
options?.onProgress?.({ phase: 'enriching', current: commitIndex, total: highInterestTotal, sha: scored.commit.sha, subject: scored.commit.subject, factsExtracted: totalFactsExtracted });
const phase = shouldEnrich ? 'enriching' : 'processing';
options?.onProgress?.({ phase, current: commitIndex, total: highInterestTotal, sha: scored.commit.sha, subject: scored.commit.subject, factsExtracted: totalFactsExtracted });

Copilot uses AI. Check for mistakes.
Comment on lines +80 to +87
const highInterestTotal = triageResult.highInterest.length;
options?.onProgress?.({ phase: 'triage', current: 0, total: highInterestTotal, sha: '', subject: '', factsExtracted: 0 });

// Process each high-interest commit
let commitIndex = 0;
for (const scored of triageResult.highInterest) {
commitIndex++;
options?.onProgress?.({ phase: 'enriching', current: commitIndex, total: highInterestTotal, sha: scored.commit.sha, subject: scored.commit.subject, factsExtracted: totalFactsExtracted });
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

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

New progress-callback behavior is introduced here but there are existing unit/integration tests for LiberateService and none assert the progress contract. Consider adding tests that verify onProgress is called with expected phases/order and that current/total/factsExtracted values match the documented behavior (including the zero-high-interest case).

Copilot uses AI. Check for mistakes.
Comment on lines +199 to +209
const onProgress = (p: ILiberateProgress): void => {
if (p.phase === 'triage') {
process.stderr.write(`Found ${p.total} high-interest commits to analyze.\n`);
} else if (p.phase === 'enriching') {
const sha = p.sha.slice(0, 7);
const subject = p.subject.length > 60 ? p.subject.slice(0, 57) + '...' : p.subject;
process.stderr.write(`\r [${p.current}/${p.total}] ${sha} ${subject} (${p.factsExtracted} facts)`);
} else if (p.phase === 'complete') {
process.stderr.write('\n');
}
};
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

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

The onProgress formatter logic here is duplicated in src/commands/liberate.ts. To avoid the two commands drifting (message text, truncation rules, TTY handling, etc.), consider extracting a shared helper for rendering ILiberateProgress to stderr.

Copilot uses AI. Check for mistakes.
Comment on lines +52 to +58
const onProgress = (p: ILiberateProgress): void => {
if (p.phase === 'triage') {
process.stderr.write(`Found ${p.total} high-interest commits to analyze.\n`);
} else if (p.phase === 'enriching') {
const sha = p.sha.slice(0, 7);
const subject = p.subject.length > 60 ? p.subject.slice(0, 57) + '...' : p.subject;
process.stderr.write(`\r [${p.current}/${p.total}] ${sha} ${subject} (${p.factsExtracted} facts)`);
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

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

The progress output uses \r unconditionally. When stderr is not a TTY (CI logs, redirected output), this will leave raw carriage returns in logs. Consider gating the in-place update behind process.stderr.isTTY and falling back to newline-based progress (or no progress) when not a TTY; also clear the line before rewriting so shorter subjects don't leave trailing characters from the previous update.

Suggested change
const onProgress = (p: ILiberateProgress): void => {
if (p.phase === 'triage') {
process.stderr.write(`Found ${p.total} high-interest commits to analyze.\n`);
} else if (p.phase === 'enriching') {
const sha = p.sha.slice(0, 7);
const subject = p.subject.length > 60 ? p.subject.slice(0, 57) + '...' : p.subject;
process.stderr.write(`\r [${p.current}/${p.total}] ${sha} ${subject} (${p.factsExtracted} facts)`);
let lastProgressLineLength = 0;
const onProgress = (p: ILiberateProgress): void => {
if (p.phase === 'triage') {
process.stderr.write(`Found ${p.total} high-interest commits to analyze.\n`);
} else if (p.phase === 'enriching') {
const sha = p.sha.slice(0, 7);
const subject = p.subject.length > 60 ? p.subject.slice(0, 57) + '...' : p.subject;
const line = ` [${p.current}/${p.total}] ${sha} ${subject} (${p.factsExtracted} facts)`;
if (process.stderr.isTTY) {
const clear = '\r' + ' '.repeat(lastProgressLineLength) + '\r';
process.stderr.write(clear + line);
lastProgressLineLength = line.length;
} else {
process.stderr.write(line + '\n');
}

Copilot uses AI. Check for mistakes.
- Extract shared createStderrProgressHandler() into src/commands/progress.ts
- Add liberateWithProgress() try/finally wrapper for terminal cleanup
- Gate \r updates behind process.stderr.isTTY; use newlines in non-TTY
- Rename phase 'enriching' to 'processing' (accurate for heuristic-only)
- Fix doc: current is 0 for triage/complete phases, 1-based for processing
- Add 2 unit tests verifying onProgress contract and zero-commit edge case

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@TonyCasey TonyCasey merged commit dca092a into main Feb 12, 2026
2 of 3 checks passed
@TonyCasey TonyCasey deleted the git-64 branch February 12, 2026 23:26
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@src/application/interfaces/ILiberateService.ts`:
- Around line 12-15: Update the ILiberateService.docs for the current property
to match runtime behavior: state that current is 0 during 'triage', is a 1-based
commit index during 'processing', and equals total when phase is 'complete'
(instead of saying 0 for complete); reference the ILiberateService interface and
its phase and current properties so consumers see the exact semantics.

In `@tests/unit/application/services/LiberateService.test.ts`:
- Around line 98-143: The tests in LiberateService.test.ts call service.liberate
and indirectly exercise real git/FS via GitClient/MemoryRepository; change them
to be true unit tests by mocking those dependencies (or move the file to
tests/integration). Replace real GitClient/MemoryRepository usage in the test
setup with manual mocks/stubs that implement the minimal behaviors used by
LiberateService.liberate (e.g., return controlled commit lists, shas, subjects,
and file contents) so ILiberateProgress events can be asserted
deterministically; ensure the test injects the mocked client into the service
instance (or the constructor/factory used by LiberateService) rather than
invoking real git commands. Also keep the same assertions on events
(triage/processing/complete) but driven from the mocked responses to keep the
test fast and isolated.

Comment on lines +12 to +15
/** Current phase of processing. */
readonly phase: 'triage' | 'processing' | 'complete';
/** Current commit index (1-based during 'processing'; 0 for triage/complete). */
readonly current: number;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Clarify current semantics in progress docs.

LiberateService emits current = total on the complete phase, but the comment says 0 for triage/complete. Align the doc (or the emitter) to avoid ambiguity for consumers.

✏️ Suggested doc tweak
-  /** Current commit index (1-based during 'processing'; 0 for triage/complete). */
+  /** Current commit index (1-based during 'processing'; 0 for triage; equals total for complete). */
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/** Current phase of processing. */
readonly phase: 'triage' | 'processing' | 'complete';
/** Current commit index (1-based during 'processing'; 0 for triage/complete). */
readonly current: number;
/** Current phase of processing. */
readonly phase: 'triage' | 'processing' | 'complete';
/** Current commit index (1-based during 'processing'; 0 for triage; equals total for complete). */
readonly current: number;
🤖 Prompt for AI Agents
In `@src/application/interfaces/ILiberateService.ts` around lines 12 - 15, Update
the ILiberateService.docs for the current property to match runtime behavior:
state that current is 0 during 'triage', is a 1-based commit index during
'processing', and equals total when phase is 'complete' (instead of saying 0 for
complete); reference the ILiberateService interface and its phase and current
properties so consumers see the exact semantics.

Comment on lines +98 to +143
it('should call onProgress with triage, processing, and complete phases', async () => {
const events: ILiberateProgress[] = [];

await service.liberate({
cwd: repoDir,
dryRun: true,
threshold: 1,
onProgress: (p) => events.push({ ...p }),
});

assert.ok(events.length >= 2, 'should emit at least triage + complete');

// First event is triage
assert.equal(events[0].phase, 'triage');
assert.equal(events[0].current, 0);
assert.ok(events[0].total >= 0);

// Last event is complete
const last = events[events.length - 1];
assert.equal(last.phase, 'complete');

// All middle events are processing
const processingEvents = events.filter(e => e.phase === 'processing');
for (let i = 0; i < processingEvents.length; i++) {
assert.equal(processingEvents[i].current, i + 1, 'current should be 1-based');
assert.ok(processingEvents[i].sha.length > 0, 'sha should be non-empty');
assert.ok(processingEvents[i].subject.length > 0, 'subject should be non-empty');
}
});

it('should emit triage and complete even with zero high-interest commits', async () => {
const events: ILiberateProgress[] = [];

await service.liberate({
cwd: repoDir,
dryRun: true,
threshold: 100,
onProgress: (p) => events.push({ ...p }),
});

assert.equal(events.length, 2);
assert.equal(events[0].phase, 'triage');
assert.equal(events[0].total, 0);
assert.equal(events[1].phase, 'complete');
assert.equal(events[1].factsExtracted, 0);
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Unit-test isolation: consider mocking Git/FS dependencies.

These new tests still exercise real git commands and filesystem via GitClient/MemoryRepository. For unit tests, prefer manual mocks or move this to tests/integration to keep unit tests isolated and fast.

As per coding guidelines: "tests/unit/**/*.{ts,tsx}: Create unit tests with manually mocked dependencies (no testing framework) in tests/unit/ directory".

🤖 Prompt for AI Agents
In `@tests/unit/application/services/LiberateService.test.ts` around lines 98 -
143, The tests in LiberateService.test.ts call service.liberate and indirectly
exercise real git/FS via GitClient/MemoryRepository; change them to be true unit
tests by mocking those dependencies (or move the file to tests/integration).
Replace real GitClient/MemoryRepository usage in the test setup with manual
mocks/stubs that implement the minimal behaviors used by
LiberateService.liberate (e.g., return controlled commit lists, shas, subjects,
and file contents) so ILiberateProgress events can be asserted
deterministically; ensure the test injects the mocked client into the service
instance (or the constructor/factory used by LiberateService) rather than
invoking real git commands. Also keep the same assertions on events
(triage/processing/complete) but driven from the mocked responses to keep the
test fast and isolated.

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.

2 participants