diff --git a/scripts/one-way-doors.ts b/scripts/one-way-doors.ts index 1f566fabb..6735c386d 100644 --- a/scripts/one-way-doors.ts +++ b/scripts/one-way-doors.ts @@ -65,7 +65,7 @@ const DESTRUCTIVE_PATTERNS: RegExp[] = [ // Credentials / auth — allow filler words ("the", "my") between verb and noun /\brevoke\s+[\w\s]*\b(api key|token|credential|access key|password)\b/i, /\breset\s+[\w\s]*\b(api key|token|password|credential)\b/i, - /\brotate\s+[\w\s]*\b(api key|token|secret|credential|access key)\b/i, + /\brotate\s+[\w\s]*\b(api key|token|secret|credential|access key|password)\b/i, // Scope / architecture forks (reversible with effort — still deserve confirmation) /\barchitectur(e|al)\s+(change|fork|shift|decision)\b/i, diff --git a/test/plan-tune.test.ts b/test/plan-tune.test.ts index 40a1465b6..6ddd6f5d5 100644 --- a/test/plan-tune.test.ts +++ b/test/plan-tune.test.ts @@ -426,6 +426,19 @@ describe('one-way-doors classifier', () => { } }); + test('rotate password is one-way (parity with revoke/reset)', () => { + const cases = [ + 'Rotate the database password?', + 'rotate password for the service account', + ]; + for (const summary of cases) { + const result = classifyQuestion({ summary }); + expect(result.oneWay).toBe(true); + expect(result.reason).toBe('keyword'); + expect(result.matched).toBeDefined(); + } + }); + test('skill-category fallback fires for cso:approval and land-and-deploy:approval', () => { expect(isOneWayDoor({ skill: 'cso', category: 'approval' })).toBe(true); expect(isOneWayDoor({ skill: 'land-and-deploy', category: 'approval' })).toBe(true);