diff --git a/sdk/src/query/phase-lifecycle.test.ts b/sdk/src/query/phase-lifecycle.test.ts
index 9969149cd..1c40f7c55 100644
--- a/sdk/src/query/phase-lifecycle.test.ts
+++ b/sdk/src/query/phase-lifecycle.test.ts
@@ -226,6 +226,44 @@ describe('replaceInCurrentMilestone', () => {
// v1.18 old plans line should remain untouched
expect(result).toContain('**Plans:** 1/1 plans');
});
+
+ it('replaces inside active even when footer text exists after ', async () => {
+ const { replaceInCurrentMilestone } = await import('./phase-lifecycle.js');
+ // Scenario: active milestone is the last block, but a footer
+ // (e.g. "---\n*Last updated*") follows it. The fast-path sees after.trim()
+ // non-empty and replaces in the footer instead of inside the active block.
+ const content = [
+ '# Roadmap',
+ '',
+ '',
+ 'v1.0 (Archived)
',
+ '',
+ '**Plans:** 1/1 plans',
+ '',
+ ' ',
+ '',
+ '',
+ 'v2.0 (Active)
',
+ '',
+ '**Plans:** 1/2 plans',
+ '',
+ ' ',
+ '',
+ '---',
+ '*Last updated: 2026-01-01*',
+ ].join('\n');
+
+ const pattern = /\*\*Plans:\*\* [^\n]+/g;
+ const result = replaceInCurrentMilestone(content, pattern, '**Plans:** 2/2 plans complete');
+
+ // Active milestone inside last should be updated
+ expect(result).toContain('**Plans:** 2/2 plans complete');
+ // Archived milestone should remain untouched
+ expect(result).toContain('**Plans:** 1/1 plans');
+ // Footer should be preserved verbatim
+ expect(result).toContain('---');
+ expect(result).toContain('*Last updated: 2026-01-01*');
+ });
});
// ─── readModifyWriteRoadmapMd ───────────────────────────────────────────