fix(extraction): a video .ts is never pending on the git path or a scoped sync

Review found two places that decide "is this a source file" without the
MPEG-TS check the scan applies: the git fast path's candidate loop in
getChangedFiles and the scoped-sync path filter (watcher events). An
untracked clip in a git repo was reported as `added`, skipped by sync
without a record, and reported as `added` again on every status; a tracked
.ts that became a clip stayed `modified` with its stale nodes. Both now
treat the clip as non-source: removed when tracked, ignored otherwise.

Regression tests fail without this change and pass with it, on both arms.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
danusha2345
2026-09-23 14:42:06 +00:00
committed by Forge
co-authored by Claude Opus 5.5
parent 1dbb3d90f1
commit 3111045f6e
2 changed files with 57 additions and 2 deletions
+48
View File
@@ -12,6 +12,7 @@ import { describe, it, expect, afterEach } from 'vitest';
import * as fs from 'fs';
import * as path from 'path';
import * as os from 'os';
import { execFileSync } from 'child_process';
import { CodeGraph } from '../src';
import { scanDirectoryAsync, type ScanSkipStats } from '../src/extraction';
import { detectLanguage, isMpegTransportStream, MPEG_TS_SNIFF_BYTES } from '../src/extraction/grammars';
@@ -154,3 +155,50 @@ describe('MPEG-TS video named .ts is skipped, real TypeScript is indexed (#1910)
}
});
});
describe('a video .ts never stays pending (#1910)', () => {
const dirs: string[] = [];
afterEach(() => { for (const d of dirs.splice(0)) fs.rmSync(d, { recursive: true, force: true }); });
const gitProject = (): string => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-mpegts-git-'));
dirs.push(dir);
const git = (...a: string[]) => execFileSync('git', a, { cwd: dir, stdio: 'pipe' });
git('init', '-q'); git('config', 'user.email', 't@t'); git('config', 'user.name', 't');
fs.writeFileSync(path.join(dir, 'app.ts'), 'export const a = 1;\n');
git('add', '.'); git('commit', '-qm', 'init');
return dir;
};
it('an untracked clip is not reported as added, before or after sync', async () => {
const dir = gitProject();
const cg = await CodeGraph.init(dir, { index: true });
try {
fs.writeFileSync(path.join(dir, 'clip.ts'), makeMpegTs(40));
expect(cg.getChangedFiles()).toEqual({ added: [], modified: [], removed: [] });
await cg.sync();
expect(cg.getChangedFiles()).toEqual({ added: [], modified: [], removed: [] });
expect(cg.getFiles().map((f) => f.path)).toEqual(['app.ts']);
} finally {
cg.close();
}
});
it('a tracked TypeScript file that becomes a clip is removed, on the git path and on a scoped sync', async () => {
for (const scoped of [false, true]) {
const dir = gitProject();
fs.writeFileSync(path.join(dir, 'clip.ts'), 'export const clip = 1;\n');
const cg = await CodeGraph.init(dir, { index: true });
try {
expect(cg.getNodesInFile('clip.ts').some((n) => n.name === 'clip')).toBe(true);
fs.writeFileSync(path.join(dir, 'clip.ts'), makeMpegTs(40));
if (!scoped) expect(cg.getChangedFiles()).toEqual({ added: [], modified: [], removed: ['clip.ts'] });
await (scoped ? cg.sync({ paths: ['clip.ts'] }) : cg.sync());
expect(cg.getFiles().map((f) => f.path)).toEqual(['app.ts']);
expect(cg.getNodesInFile('clip.ts')).toEqual([]);
expect(cg.getChangedFiles()).toEqual({ added: [], modified: [], removed: [] });
} finally {
cg.close();
}
}
});
});
+9 -2
View File
@@ -3156,7 +3156,10 @@ export class ExtractionOrchestrator {
(p) =>
isSourceFile(p, overrides) &&
!scope.ignores(p) &&
fs.existsSync(path.join(this.rootDir, p))
fs.existsSync(path.join(this.rootDir, p)) &&
// Same rule as the scan (#1910): a reported video clip is not a
// source file, so a tracked one is removed and a new one ignored.
!isMpegTsVideoFile(this.rootDir, p)
);
trackedFiles = [];
for (const p of unique) {
@@ -3411,7 +3414,11 @@ export class ExtractionOrchestrator {
for (const filePath of candidates) {
const tracked = this.queries.getFileByPath(filePath);
const fullPath = path.join(this.rootDir, filePath);
if (!isSourceFile(filePath, overrides) || scope.ignores(filePath) || !fs.existsSync(fullPath)) {
// A `.ts` that is an MPEG transport stream is not source (#1910): the
// scan never lists it, so git must not report it as pending either —
// an untracked clip would otherwise stay "added" after every sync.
if (!isSourceFile(filePath, overrides) || scope.ignores(filePath) || !fs.existsSync(fullPath)
|| isMpegTsVideoFile(this.rootDir, filePath)) {
if (tracked) removed.push(filePath);
continue;
}