Compare commits
1 commit
main
...
fix/classi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
20c6e388e3 |
2 changed files with 65 additions and 1 deletions
|
|
@ -163,3 +163,53 @@ describe('classifyDiffAgainstPlan — delete-only commits', () => {
|
||||||
expect(r.result).toBe('EMPTY')
|
expect(r.result).toBe('EMPTY')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Pseudo-paths in plans (code-snippets, attribute-syntax, ellipses) moeten
|
||||||
|
// niet als plan-paden meetellen — anders krijg je PARTIAL terwijl het werk
|
||||||
|
// volledig gedaan is. Regression-guard voor T-815-incident (sprint
|
||||||
|
// cmoyiu4yd000zf917acq9twtr, 2026-05-09).
|
||||||
|
describe('classifyDiffAgainstPlan — plan met pseudo-paths', () => {
|
||||||
|
it('negeert `data-debug-label="..."` als pseudo-pad en classificeert ALIGNED', () => {
|
||||||
|
const plan = [
|
||||||
|
'Verwijder alle voorkomens van `data-debug-label="..."` uit:',
|
||||||
|
'',
|
||||||
|
'- `app/components/shared/status-bar.tsx`',
|
||||||
|
'- `app/components/shared/header.tsx`',
|
||||||
|
].join('\n')
|
||||||
|
const diff = makeDiff([
|
||||||
|
'app/components/shared/status-bar.tsx',
|
||||||
|
'app/components/shared/header.tsx',
|
||||||
|
])
|
||||||
|
const r = classifyDiffAgainstPlan({ diff, plan })
|
||||||
|
expect(r.result).toBe('ALIGNED')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('negeert ellipsis-tokens (drie of meer dots) als pad', () => {
|
||||||
|
const plan = 'Refactor `foo(...)` naar `bar()`. Files: `src/a.ts`.'
|
||||||
|
const diff = makeDiff(['src/a.ts'])
|
||||||
|
const r = classifyDiffAgainstPlan({ diff, plan })
|
||||||
|
expect(r.result).toBe('ALIGNED')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('negeert tokens met operators/quotes als pad', () => {
|
||||||
|
const plan = 'Wijzig `props={x: 1}` en `useState<string>()` in `src/c.tsx`.'
|
||||||
|
const diff = makeDiff(['src/c.tsx'])
|
||||||
|
const r = classifyDiffAgainstPlan({ diff, plan })
|
||||||
|
expect(r.result).toBe('ALIGNED')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('accepteert package.json en andere extension-only paths', () => {
|
||||||
|
const plan = 'Update `package.json` en `tsconfig.json`.'
|
||||||
|
const diff = makeDiff(['package.json', 'tsconfig.json'])
|
||||||
|
const r = classifyDiffAgainstPlan({ diff, plan })
|
||||||
|
expect(r.result).toBe('ALIGNED')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('blijft PARTIAL retourneren wanneer een echt plan-pad ontbreekt', () => {
|
||||||
|
const plan = 'Wijzig `src/foo.ts` en `src/bar.ts`. Verwijder `data-x="..."`.'
|
||||||
|
const diff = makeDiff(['src/foo.ts'])
|
||||||
|
const r = classifyDiffAgainstPlan({ diff, plan })
|
||||||
|
expect(r.result).toBe('PARTIAL')
|
||||||
|
expect(r.reasoning).toMatch(/bar\.ts/)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ function extractPlanPaths(plan: string): string[] {
|
||||||
let m: RegExpExecArray | null
|
let m: RegExpExecArray | null
|
||||||
while ((m = backtickRe.exec(plan)) !== null) {
|
while ((m = backtickRe.exec(plan)) !== null) {
|
||||||
const p = m[1].trim()
|
const p = m[1].trim()
|
||||||
if ((p.includes('/') || p.includes('.')) && !p.includes(' ') && p.length > 3) paths.add(p)
|
if (looksLikePath(p)) paths.add(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
const bulletRe = /^[-*]\s+\*{0,2}([^\s*][^\s]*)\.([a-zA-Z]{1,6})\*{0,2}\s*[:\n]/gm
|
const bulletRe = /^[-*]\s+\*{0,2}([^\s*][^\s]*)\.([a-zA-Z]{1,6})\*{0,2}\s*[:\n]/gm
|
||||||
|
|
@ -38,6 +38,20 @@ function extractPlanPaths(plan: string): string[] {
|
||||||
return [...paths]
|
return [...paths]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Heuristic: does this backtick-quoted token look like a file path?
|
||||||
|
// Excludes code-snippets like `data-debug-label="..."`, `foo()`, `<div>` —
|
||||||
|
// anything containing operator/quote/bracket chars or an ellipsis is rejected.
|
||||||
|
// Accepts paths with a slash (multi-segment) or a recognisable file-extension
|
||||||
|
// suffix (1–6 alphanumeric chars after a final dot, e.g. `.tsx`, `.json`).
|
||||||
|
function looksLikePath(p: string): boolean {
|
||||||
|
if (p.length <= 3) return false
|
||||||
|
if (p.includes(' ')) return false
|
||||||
|
if (/[="'<>()[\]{};,]/.test(p)) return false
|
||||||
|
if (/\.{2,}/.test(p)) return false
|
||||||
|
if (!p.includes('/') && !/\.[a-zA-Z][a-zA-Z0-9]{0,5}$/.test(p)) return false
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// Path match: exact or suffix match so "classify.ts" matches "src/verify/classify.ts".
|
// Path match: exact or suffix match so "classify.ts" matches "src/verify/classify.ts".
|
||||||
function pathMatches(planPath: string, diffPaths: string[]): boolean {
|
function pathMatches(planPath: string, diffPaths: string[]): boolean {
|
||||||
const norm = planPath.replace(/\\/g, '/')
|
const norm = planPath.replace(/\\/g, '/')
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue