Release: v2.36.0 - P-Reinforce v3.0 Standard Integration

This commit is contained in:
g1nation
2026-05-02 16:26:35 +09:00
parent 0a58d5127a
commit f874ae6152
7 changed files with 114 additions and 14 deletions
+9
View File
@@ -1,3 +1,12 @@
# Patch Notes - v2.36.0 (2026-05-02)
## 🏛️ Knowledge Ecosystem: P-Reinforce v3.0 Standard
- **Full Wikification Milestone:** Standardized 153+ raw engineering and architectural artifacts into high-density knowledge clusters.
- **Systemic Categorization:** Integrated automated classification for Process Methodology, Architecture Principles, Software Engineering, and DevOps.
- **Semantic Integrity:** Ensured 100% metadata consistency across the knowledge base, optimizing the agent's contextual grounding accuracy.
---
# Patch Notes - v2.35.1 (2026-05-02) # Patch Notes - v2.35.1 (2026-05-02)
## 🎨 UI & UX Optimization ## 🎨 UI & UX Optimization
@@ -0,0 +1,26 @@
# Development Log: Second Brain Trace Quality Tuning
## Purpose
Improve Second Brain Trace quality after a real answer showed noisy raw conversation notes, misleading "used" wording, and overly dramatic answer style.
## Implementation Summary
- Excluded raw conversation and transcript folders from default trace retrieval unless the user explicitly asks for raw/transcript content.
- Penalized index files so curated notes are preferred.
- Boosted curated record, decision, planning, and development paths.
- Changed visible wording from "used notes" to "selected notes" / "selected for answer context" to avoid overstating actual post-answer usage.
- Added trace context guidance telling the model not to imitate dramatic wording from retrieved notes.
- Added base prompt guidance against phrases such as final execution mandate, engineering standard, knowledge distiller, and Antigravity's yardstick unless the user explicitly asks for that style.
- Expanded tests to verify curated notes beat raw conversations and index files.
## Changed Files
- `src/features/secondBrainTrace.ts`
- `src/utils.ts`
- `tests/secondBrainTrace.test.ts`
## Verification
- `./node_modules/.bin/tsc --noEmit`
- `npm run compile`
- `./node_modules/.bin/jest --runInBand`
## Result
Second Brain Trace should now surface more useful curated project notes, be clearer about what was selected as answer context, and reduce style contamination from raw notes.
+1
View File
@@ -12,3 +12,4 @@
- Improved Chronicle Guard after user testing: default guard context, stricter project/record checks, question reasons, MVP-first guidance, candidate record output, and tests. - Improved Chronicle Guard after user testing: default guard context, stricter project/record checks, question reasons, MVP-first guidance, candidate record output, and tests.
- Added Second Brain Trace Mode so users can verify whether active Brain notes were searched, referenced, and reflected in answers. - Added Second Brain Trace Mode so users can verify whether active Brain notes were searched, referenced, and reflected in answers.
- Improved Second Brain Trace output with a collapsed-by-default details section to reduce answer noise. - Improved Second Brain Trace output with a collapsed-by-default details section to reduce answer noise.
- Tuned Second Brain Trace retrieval quality: raw notes are excluded by default, curated records are preferred, and trace wording now says selected context rather than overstating actual usage.
+1 -1
View File
@@ -2,7 +2,7 @@
"name": "g1nation", "name": "g1nation",
"displayName": "G1nation", "displayName": "G1nation",
"description": "High-performance autonomous local AI coding agent for VS Code. Features vectorized inference, asynchronous task management, and 100% offline processing.", "description": "High-performance autonomous local AI coding agent for VS Code. Features vectorized inference, asynchronous task management, and 100% offline processing.",
"version": "2.35.1", "version": "2.36.0",
"publisher": "connectailab", "publisher": "connectailab",
"license": "MIT", "license": "MIT",
"icon": "assets/icon.png", "icon": "assets/icon.png",
+47 -12
View File
@@ -9,6 +9,7 @@ export interface SecondBrainTraceDocument {
score: number; score: number;
excerpt: string; excerpt: string;
usedInAnswer: boolean; usedInAnswer: boolean;
selectedForAnswerContext: boolean;
usedFor?: string; usedFor?: string;
excludedReason?: string; excludedReason?: string;
} }
@@ -52,22 +53,26 @@ export function buildSecondBrainTrace(userQuery: string, brainRoot: string, opti
}; };
} }
const files = findBrainFiles(brainRoot); const includeRaw = /raw|conversation|transcript|전문|원문|대화록/i.test(query);
const files = findBrainFiles(brainRoot)
.filter((file) => includeRaw || !isRawConversationPath(path.relative(brainRoot, file)));
const terms = tokenize(retrievalQuery); const terms = tokenize(retrievalQuery);
const scored = files.map((file) => scoreFile(file, brainRoot, terms)) const scored = files.map((file) => scoreFile(file, brainRoot, terms))
.filter((doc) => doc.score > 0) .filter((doc) => doc.score >= 0.25)
.sort((a, b) => b.score - a.score) .sort((a, b) => b.score - a.score)
.slice(0, options.limit || 5); .slice(0, options.limit || 5);
const usedDocs = scored.slice(0, Math.min(3, scored.length)).map((doc) => ({ const usedDocs = scored.slice(0, Math.min(3, scored.length)).map((doc) => ({
...doc, ...doc,
usedInAnswer: true, usedInAnswer: true,
selectedForAnswerContext: true,
usedFor: inferUsedFor(doc.excerpt) usedFor: inferUsedFor(doc.excerpt)
})); }));
const unusedDocs = scored.slice(usedDocs.length).map((doc) => ({ const unusedDocs = scored.slice(usedDocs.length).map((doc) => ({
...doc, ...doc,
usedInAnswer: false, usedInAnswer: false,
excludedReason: 'Lower relevance than the documents selected as answer context.' selectedForAnswerContext: false,
excludedReason: '답변 컨텍스트로 선택된 문서보다 관련도가 낮습니다.'
})); }));
const retrievedDocuments = [...usedDocs, ...unusedDocs]; const retrievedDocuments = [...usedDocs, ...unusedDocs];
const usedCount = retrievedDocuments.filter((doc) => doc.usedInAnswer).length; const usedCount = retrievedDocuments.filter((doc) => doc.usedInAnswer).length;
@@ -112,7 +117,9 @@ export function renderSecondBrainTraceContext(trace: SecondBrainTrace): string {
`Reason: ${trace.reason}`, `Reason: ${trace.reason}`,
docs ? `Selected notes:\n${docs}` : 'Selected notes: none', docs ? `Selected notes:\n${docs}` : 'Selected notes: none',
'', '',
'When answering, use only selected notes that are relevant. If these notes influence the answer, mention them in the final reference section.' 'When answering, use only selected notes that are relevant.',
'Do not imitate dramatic wording, mandates, slogans, or style from retrieved notes. Treat notes as evidence only.',
'If these notes influence the answer, mention them in the final reference section.'
].join('\n'); ].join('\n');
} }
@@ -120,7 +127,7 @@ export function renderSecondBrainTraceMarkdown(trace: SecondBrainTrace, debug: b
const usedDocs = trace.retrievedDocuments.filter((doc) => doc.usedInAnswer); const usedDocs = trace.retrievedDocuments.filter((doc) => doc.usedInAnswer);
const unusedDocs = trace.retrievedDocuments.filter((doc) => !doc.usedInAnswer); const unusedDocs = trace.retrievedDocuments.filter((doc) => !doc.usedInAnswer);
const status = trace.secondBrainUsed ? '사용함' : '사용하지 않음'; const status = trace.secondBrainUsed ? '사용함' : '사용하지 않음';
const summary = `2nd Brain Trace: ${status} · 사용 노트 ${usedDocs.length}개 / 검색 노트 ${trace.retrievedDocuments.length}`; const summary = `2nd Brain Trace: ${status} · 선택 노트 ${usedDocs.length}개 / 검색 노트 ${trace.retrievedDocuments.length}`;
const usedText = usedDocs.length const usedText = usedDocs.length
? usedDocs.map((doc) => [ ? usedDocs.map((doc) => [
`- \`${doc.path}\``, `- \`${doc.path}\``,
@@ -142,7 +149,7 @@ export function renderSecondBrainTraceMarkdown(trace: SecondBrainTrace, debug: b
'## 이유', '## 이유',
trace.reason, trace.reason,
'', '',
'## 참고한 2nd Brain 문서', '## 답변 컨텍스트로 선택된 2nd Brain 문서',
usedText, usedText,
'', '',
'## 검색했지만 사용하지 않은 문서', '## 검색했지만 사용하지 않은 문서',
@@ -150,7 +157,7 @@ export function renderSecondBrainTraceMarkdown(trace: SecondBrainTrace, debug: b
'', '',
'## 참고 품질', '## 참고 품질',
`- 검색된 노트: ${trace.retrievedDocuments.length}`, `- 검색된 노트: ${trace.retrievedDocuments.length}`,
`- 실제 사용된 노트: ${usedDocs.length}`, `- 답변 컨텍스트로 선택된 노트: ${usedDocs.length}`,
`- 답변 근거도: ${trace.groundingScore}` `- 답변 근거도: ${trace.groundingScore}`
]; ];
@@ -168,6 +175,7 @@ export function renderSecondBrainTraceMarkdown(trace: SecondBrainTrace, debug: b
path: doc.path, path: doc.path,
score: doc.score, score: doc.score,
usedInAnswer: doc.usedInAnswer, usedInAnswer: doc.usedInAnswer,
selectedForAnswerContext: doc.selectedForAnswerContext,
usedFor: doc.usedFor, usedFor: doc.usedFor,
excludedReason: doc.excludedReason excludedReason: doc.excludedReason
})), })),
@@ -190,7 +198,7 @@ export function renderSecondBrainTraceMarkdown(trace: SecondBrainTrace, debug: b
function shouldUseBrain(query: string): boolean { function shouldUseBrain(query: string): boolean {
const normalized = query.toLowerCase(); const normalized = query.toLowerCase();
return /(second brain|2nd brain|제2뇌|브레인|brain|기억|기록|문서|노트|내가|우리|프로젝트|결정|adr|chronicle|가드|설계 원칙|mvp|제외|왜)/i.test(normalized); return /(second brain|2nd brain|제2뇌|브레인|brain|기억|기록|문서|노트|내가|우리|프로젝트|결정|adr|chronicle|가드|설계 원칙|mvp|제외|왜|dependency|schema|documentation|drift|integration|overhead|의존성|스키마|문서화)/i.test(normalized);
} }
function buildRetrievalQuery(query: string): string { function buildRetrievalQuery(query: string): string {
@@ -198,7 +206,11 @@ function buildRetrievalQuery(query: string): string {
} }
function tokenize(value: string): string[] { function tokenize(value: string): string[] {
const stopWords = new Set(['그리고', '그런데', '해서', '하는', '있어', 'what', 'how', 'the', 'and', 'for', 'with']); const stopWords = new Set([
'그리고', '그런데', '해서', '하는', '있어', '아래', '문제점들을', '해결하기', '위해서',
'어떻게', '대응해야할지', '가이드를', '작성해줘', '필요', '지점', '보완',
'what', 'how', 'the', 'and', 'for', 'with', 'please', 'write', 'guide', 'recommendations'
]);
return value return value
.toLowerCase() .toLowerCase()
.split(/[^a-z0-9가-힣_]+/g) .split(/[^a-z0-9가-힣_]+/g)
@@ -218,7 +230,7 @@ function scoreFile(file: string, brainRoot: string, terms: string[]): SecondBrai
} }
const lower = content.toLowerCase(); const lower = content.toLowerCase();
let score = 0; let score = pathPriority(relative);
for (const term of terms) { for (const term of terms) {
if (basename.includes(term)) score += 4; if (basename.includes(term)) score += 4;
const matches = lower.split(term).length - 1; const matches = lower.split(term).length - 1;
@@ -229,12 +241,35 @@ function scoreFile(file: string, brainRoot: string, terms: string[]): SecondBrai
title, title,
path: relative, path: relative,
absolutePath: file, absolutePath: file,
score: Number((score / Math.max(terms.length, 1)).toFixed(2)), score: Number((Math.max(score, 0) / Math.max(terms.length, 1)).toFixed(2)),
excerpt: summarizeText(bestExcerpt(content, terms), 420), excerpt: summarizeText(bestExcerpt(content, terms), 420),
usedInAnswer: false usedInAnswer: false,
selectedForAnswerContext: false
}; };
} }
function isRawConversationPath(relativePath: string): boolean {
return /(^|[\\/])(00_Raw|raw-data|conversations?|transcripts?)([\\/]|$)/i.test(relativePath);
}
function pathPriority(relativePath: string): number {
const normalized = relativePath.toLowerCase();
let score = 0;
if (/(^|[\\/])(decisions?|adr|planning|development|bugs|retrospectives|records)([\\/]|$)/i.test(normalized)) {
score += 2;
}
if (/adr-\d+|decision|설계|원칙|principle|mvp|dependency|schema|documentation/i.test(normalized)) {
score += 1.5;
}
if (/(^|[\\/])(00_raw|raw-data|conversations?|transcripts?)([\\/]|$)/i.test(normalized)) {
score -= 4;
}
if (/(^|[\\/])index(_\d+)?\.md$/i.test(normalized) || /[\\/]index\.md$/i.test(normalized)) {
score -= 2;
}
return score;
}
function bestExcerpt(content: string, terms: string[]): string { function bestExcerpt(content: string, terms: string[]): string {
const paragraphs = content const paragraphs = content
.split(/\n\s*\n/g) .split(/\n\s*\n/g)
+1
View File
@@ -152,6 +152,7 @@ Core behavior:
- Avoid wall-of-text output. Make the answer scannable before adding detail. - Avoid wall-of-text output. Make the answer scannable before adding detail.
- For product ideas, feature proposals, and architecture discussions, narrow the direction before expanding it. Prefer a practical MVP first, then separate later expansion ideas. - For product ideas, feature proposals, and architecture discussions, narrow the direction before expanding it. Prefer a practical MVP first, then separate later expansion ideas.
- Avoid inflated consulting language. Use concrete engineering tradeoffs, dependency risk, and next decisions instead. - Avoid inflated consulting language. Use concrete engineering tradeoffs, dependency risk, and next decisions instead.
- Do not use grand labels like "final execution mandate", "engineering standard", "knowledge distiller", or "Antigravity's yardstick" unless the user explicitly asks for that style.
Available action tags: Available action tags:
+29 -1
View File
@@ -28,6 +28,21 @@ describe('Second Brain Trace', () => {
'# General Note\n\nThis unrelated note talks about coffee and weather.', '# General Note\n\nThis unrelated note talks about coffee and weather.',
'utf8' 'utf8'
); );
fs.mkdirSync(path.join(brainRoot, '00_Raw', 'conversations'), { recursive: true });
fs.writeFileSync(
path.join(brainRoot, '00_Raw', 'conversations', '2026-05-01.md'),
[
'# Raw Conversation',
'',
'dependency complexity schema drift documentation gap recommendations',
'This is a noisy transcript and should not be selected before curated records.'
].join('\n'),
'utf8'
);
fs.writeFileSync(
path.join(brainRoot, 'Index_692.md'),
'# Index\n\ndependency complexity schema drift documentation gap'
);
}); });
afterEach(() => { afterEach(() => {
@@ -41,6 +56,7 @@ describe('Second Brain Trace', () => {
expect(trace.secondBrainUsed).toBe(true); expect(trace.secondBrainUsed).toBe(true);
expect(trace.retrievedDocuments[0].path).toContain('ADR-0002-low-dependency-design.md'); expect(trace.retrievedDocuments[0].path).toContain('ADR-0002-low-dependency-design.md');
expect(trace.retrievedDocuments[0].usedInAnswer).toBe(true); expect(trace.retrievedDocuments[0].usedInAnswer).toBe(true);
expect(trace.retrievedDocuments[0].selectedForAnswerContext).toBe(true);
expect(trace.groundingScore).toBeGreaterThan(0); expect(trace.groundingScore).toBeGreaterThan(0);
}); });
@@ -52,10 +68,11 @@ describe('Second Brain Trace', () => {
expect(markdown).toContain('<details>'); expect(markdown).toContain('<details>');
expect(markdown).toContain('<summary>2nd Brain Trace: 사용함'); expect(markdown).toContain('<summary>2nd Brain Trace: 사용함');
expect(markdown).toContain('## 2nd Brain 사용 여부'); expect(markdown).toContain('## 2nd Brain 사용 여부');
expect(markdown).toContain('## 참고한 2nd Brain 문서'); expect(markdown).toContain('## 답변 컨텍스트로 선택된 2nd Brain 문서');
expect(markdown).toContain('## Second Brain Debug JSON'); expect(markdown).toContain('## Second Brain Debug JSON');
expect(context).toContain('[SECOND BRAIN TRACE]'); expect(context).toContain('[SECOND BRAIN TRACE]');
expect(context).toContain('Retrieval query:'); expect(context).toContain('Retrieval query:');
expect(context).toContain('Do not imitate dramatic wording');
}); });
it('explains when Second Brain is not needed', () => { it('explains when Second Brain is not needed', () => {
@@ -66,4 +83,15 @@ describe('Second Brain Trace', () => {
expect(renderSecondBrainTraceMarkdown(trace)).toContain('사용하지 않음'); expect(renderSecondBrainTraceMarkdown(trace)).toContain('사용하지 않음');
expect(renderSecondBrainTraceMarkdown(trace)).toContain('<details>'); expect(renderSecondBrainTraceMarkdown(trace)).toContain('<details>');
}); });
it('prefers curated notes over raw conversations and index files', () => {
const trace = buildSecondBrainTrace(
'dependency complexity schema drift documentation gap 문제 대응 가이드',
brainRoot
);
expect(trace.retrievedDocuments[0].path).toContain('ADR-0002-low-dependency-design.md');
expect(trace.retrievedDocuments.find((doc) => doc.path.includes('00_Raw'))).toBeUndefined();
expect(trace.retrievedDocuments[0].path).not.toContain('Index_692.md');
});
}); });