v2.2.265: /meet 검수 루프·편집·액션 상세·논의 매듭

/meet 파이프라인 대폭 개선:
- 검수 루프: 회의록 검증 후 지적분만 수정, 통과까지 반복 (전체 재작성 X)
- 편집(Editor): 검수 통과 후 보고서 형태로 구조 정리 (환각 방지 재검증)
- 액션 상세 확장: task 노트에 2~3문장 자기완결 설명 자동 생성
- 주요 논의/쟁점: 각 쟁점을 논점→논의→결론으로 그 자리에서 매듭
- 자동 등록 0건 fix: 기한 없는 확정·기한미정을 작성일(오늘)로 등록 (폴백)

신규 설정:
- g1nation.meetVerifyMaxRounds (기본 2)
- g1nation.meetEditorPass (기본 ON)
- g1nation.meetTaskDateFallback (기본 today)
- g1nation.meetTaskDetailExpand (기본 ON)

신규 프롬프트:
- buildMeetRevisePrompt (지적분만 수정)
- buildMeetEditorPrompt (근거 유지 편집)
- buildTaskDetailPrompt (액션 상세 확장)

신규 파서:
- parseTaskDetails (task 상세 맵핑)

신규 테스트:
- classifyAction dateFallback 동작 5건
- parseTaskDetails 파싱 4건

tsc ✓ · jest 715/715 ✓

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-07-02 18:22:52 +09:00
parent 40d003635e
commit 8f1ce4d617
6 changed files with 392 additions and 51 deletions
+55 -1
View File
@@ -3,7 +3,7 @@
* 정책: 확정+기한만 자동, 진행미정/기한미정/조건부는 보류, 반복은 첫 1회,
* 과거 날짜는 등록하되 완료확인 표기, 기한 해석 불가 확정건은 보류(추측 등록 금지).
*/
import { classifyAction, parseConfirmArgs, normalizeDate, nextWeekday, taskKey } from '../src/features/datacollect/scheduling/meetRegistration';
import { classifyAction, parseConfirmArgs, normalizeDate, nextWeekday, taskKey, parseTaskDetails } from '../src/features/datacollect/scheduling/meetRegistration';
import { parseActionItems } from '../src/features/datacollect/scheduling/calendarHelpers';
import { looksDegenerate } from '../src/features/datacollect/llm';
@@ -64,6 +64,60 @@ describe('classifyAction — 등록 게이트 분기', () => {
});
});
describe('classifyAction — dateFallbackToday (작성일 폴백)', () => {
test('확정 + 기한 해석 불가 → 작성일(오늘)로 auto', () => {
const c = classifyAction(row('추후 논의', '확정'), MEET, TODAY, true);
expect(c).toMatchObject({ route: 'auto', date: '2026-06-11', pastNote: false });
});
test('기한미정 → 작성일(오늘)로 auto', () => {
const c = classifyAction(row('', '기한미정'), MEET, TODAY, true);
expect(c).toMatchObject({ route: 'auto', date: '2026-06-11', pastNote: false });
});
test('폴백 켜져도 명시 기한이 있으면 그 날짜 유지', () => {
const c = classifyAction(row('2026-06-20', '확정'), MEET, TODAY, true);
expect(c).toMatchObject({ route: 'auto', date: '2026-06-20' });
});
test('폴백 켜져도 진행미정은 여전히 보류', () => {
const c = classifyAction(row('', '진행미정'), MEET, TODAY, true);
expect(c).toMatchObject({ route: 'hold', kind: 'undecided' });
});
test('폴백 켜져도 조건부는 여전히 보류', () => {
const c = classifyAction(row('', '조건부: 계약 체결 후'), MEET, TODAY, true);
expect(c).toMatchObject({ route: 'hold', kind: 'conditional' });
});
});
describe('parseTaskDetails — 액션 상세 확장 파싱', () => {
const tasks = [{ work: '국내외 서비스 사례 분석' }, { work: 'API 연동 방식 검토' }];
test('[n] 블록을 taskKey(work)로 매핑', () => {
const raw = '[1] 무신사·트루핏 등 국내외 사례를 조사해 벤치마킹 표로 정리한다.\n[2] 외부 API 연동 가능 여부와 인증 방식을 확인한다.';
const m = parseTaskDetails(raw, tasks);
expect(m.get(taskKey('국내외 서비스 사례 분석'))).toMatch(/무신사/);
expect(m.get(taskKey('API 연동 방식 검토'))).toMatch(/인증 방식/);
});
test('여러 줄 설명은 한 문단으로 합쳐짐', () => {
const raw = '[1] 첫째 줄.\n 둘째 줄 이어짐.\n[2] 두 번째.';
const m = parseTaskDetails(raw, tasks);
expect(m.get(taskKey('국내외 서비스 사례 분석'))).toBe('첫째 줄. 둘째 줄 이어짐.');
});
test('빈/깨진 응답 → 빈 맵(폴백은 buildNotes placeholder)', () => {
expect(parseTaskDetails('', tasks).size).toBe(0);
expect(parseTaskDetails('설명만 있고 번호 없음', tasks).size).toBe(0);
});
test('입력 범위 밖 번호는 무시', () => {
const m = parseTaskDetails('[1] ok\n[9] 범위밖', tasks);
expect(m.size).toBe(1);
});
});
describe('parseConfirmArgs / normalizeDate', () => {
test('혼합 답변 파싱', () => {
const { decisions, errors } = parseConfirmArgs('1=6/20 2=ok 3=skip 4=2026-07-01', 2026);