@tienne/gestalt 0.51.0 → 0.53.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +7 -4
- package/dist/package.json +4 -3
- package/dist/plugin/review-agents/quality-reviewer/AGENT.md +30 -3
- package/dist/plugin/role-agents/_shared/references/README.md +13 -0
- package/dist/plugin/role-agents/_shared/references/ai-tell-quick-rules.md +66 -15
- package/dist/plugin/role-agents/_shared/references/style-guide.md +40 -0
- package/dist/plugin/role-agents/code-review-responder/AGENT.md +1 -1
- package/dist/plugin/role-agents/code-review-writer/AGENT.md +1 -1
- package/dist/plugin/role-agents/harness-architect/AGENT.md +1 -1
- package/dist/plugin/role-agents/humanize-monolith/AGENT.md +20 -2
- package/dist/plugin/role-agents/jira-writer/AGENT.md +11 -11
- package/dist/plugin/role-agents/slack-messenger/AGENT.md +1 -1
- package/dist/plugin/skills/dispatch/SKILL.md +3 -3
- package/dist/plugin/skills/execute/SKILL.md +1 -1
- package/dist/plugin/skills/jira-create/SKILL.md +5 -5
- package/dist/plugin/skills/presentation/SKILL.md +6 -6
- package/dist/plugin/skills/review-reply/SKILL.md +3 -3
- package/dist/plugin/skills/slack-send/SKILL.md +2 -2
- package/dist/src/cli/commands/humanize-check.d.ts +8 -0
- package/dist/src/cli/commands/humanize-check.d.ts.map +1 -0
- package/dist/src/cli/commands/humanize-check.js +29 -0
- package/dist/src/cli/commands/humanize-check.js.map +1 -0
- package/dist/src/cli/index.d.ts.map +1 -1
- package/dist/src/cli/index.js +11 -0
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/code-graph/rrf.d.ts +4 -4
- package/dist/src/code-graph/rrf.js +4 -4
- package/dist/src/execute/parallel-groups.d.ts +1 -1
- package/dist/src/execute/parallel-groups.js +1 -1
- package/dist/src/gestalt/surface-labels.d.ts +3 -3
- package/dist/src/gestalt/surface-labels.js +1 -1
- package/dist/src/humanize/change-rate.d.ts +13 -0
- package/dist/src/humanize/change-rate.d.ts.map +1 -0
- package/dist/src/humanize/change-rate.js +86 -0
- package/dist/src/humanize/change-rate.js.map +1 -0
- package/dist/src/humanize/check.d.ts +39 -0
- package/dist/src/humanize/check.d.ts.map +1 -0
- package/dist/src/humanize/check.js +150 -0
- package/dist/src/humanize/check.js.map +1 -0
- package/dist/src/humanize/detectors.d.ts +28 -0
- package/dist/src/humanize/detectors.d.ts.map +1 -0
- package/dist/src/humanize/detectors.js +134 -0
- package/dist/src/humanize/detectors.js.map +1 -0
- package/dist/src/humanize/index.d.ts +5 -0
- package/dist/src/humanize/index.d.ts.map +1 -0
- package/dist/src/humanize/index.js +5 -0
- package/dist/src/humanize/index.js.map +1 -0
- package/dist/src/humanize/rules.d.ts +34 -0
- package/dist/src/humanize/rules.d.ts.map +1 -0
- package/dist/src/humanize/rules.js +95 -0
- package/dist/src/humanize/rules.js.map +1 -0
- package/package.json +4 -3
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/plugin/review-agents/quality-reviewer/AGENT.md +30 -3
- package/plugin/role-agents/_shared/references/README.md +13 -0
- package/plugin/role-agents/_shared/references/ai-tell-quick-rules.md +66 -15
- package/plugin/role-agents/_shared/references/style-guide.md +40 -0
- package/plugin/role-agents/code-review-responder/AGENT.md +1 -1
- package/plugin/role-agents/code-review-writer/AGENT.md +1 -1
- package/plugin/role-agents/harness-architect/AGENT.md +1 -1
- package/plugin/role-agents/humanize-monolith/AGENT.md +20 -2
- package/plugin/role-agents/jira-writer/AGENT.md +11 -11
- package/plugin/role-agents/slack-messenger/AGENT.md +1 -1
- package/plugin/skills/dispatch/SKILL.md +3 -3
- package/plugin/skills/execute/SKILL.md +1 -1
- package/plugin/skills/jira-create/SKILL.md +5 -5
- package/plugin/skills/presentation/SKILL.md +6 -6
- package/plugin/skills/review-reply/SKILL.md +3 -3
- package/plugin/skills/slack-send/SKILL.md +2 -2
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 룰 ID별 결정적 탐지기.
|
|
3
|
+
*
|
|
4
|
+
* 여기 있는 건 정규식만으로 오탐 없이 셀 수 있는 룰뿐이다. D-5 의인화 주어처럼
|
|
5
|
+
* 뜻을 봐야 판단되는 룰은 일부러 뺐다 — 검사가 오탐으로 멈추면 아무도 안 쓴다.
|
|
6
|
+
* 탐지기가 없는 룰은 모델이 자체검증으로 본다.
|
|
7
|
+
*/
|
|
8
|
+
const SAMPLE_CAP = 3;
|
|
9
|
+
/** 표 안 압축과 코드는 룰 적용 대상이 아니라서 센 뒤 빼야 한다 */
|
|
10
|
+
function proseOnly(text) {
|
|
11
|
+
return text
|
|
12
|
+
.replace(/```[\s\S]*?```/g, '\n')
|
|
13
|
+
.replace(/`[^`\n]+`/g, ' ')
|
|
14
|
+
.split('\n')
|
|
15
|
+
.filter((line) => !line.trimStart().startsWith('|'))
|
|
16
|
+
.join('\n');
|
|
17
|
+
}
|
|
18
|
+
function matcher(ruleId, re, onProse = true) {
|
|
19
|
+
return {
|
|
20
|
+
ruleId,
|
|
21
|
+
run: (text) => {
|
|
22
|
+
const target = onProse ? proseOnly(text) : text;
|
|
23
|
+
return [...target.matchAll(re)].map((m) => m[0].trim());
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/** 문장 첫머리 접속사는 문장을 갈라서 봐야 한다 */
|
|
28
|
+
function sentenceInitial(ruleId, words) {
|
|
29
|
+
const re = new RegExp(`^(?:${words.join('|')})[\\s,]`);
|
|
30
|
+
return {
|
|
31
|
+
ruleId,
|
|
32
|
+
run: (text) => {
|
|
33
|
+
const hits = [];
|
|
34
|
+
for (const chunk of splitSentences(proseOnly(text))) {
|
|
35
|
+
const stripped = chunk.replace(/^[\s>*\-#\d.)]+/, '');
|
|
36
|
+
const match = stripped.match(re);
|
|
37
|
+
if (match)
|
|
38
|
+
hits.push(match[0].trim());
|
|
39
|
+
}
|
|
40
|
+
return hits;
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
export function splitSentences(text) {
|
|
45
|
+
return text
|
|
46
|
+
.split(/(?<=[.!?…])\s+|\n+/)
|
|
47
|
+
.map((s) => s.trim())
|
|
48
|
+
.filter(Boolean);
|
|
49
|
+
}
|
|
50
|
+
// 한글 뒤에는 \b가 성립하지 않는다(자모가 \w가 아님). 어미 끝은 후행 부정 탐색으로 막는다.
|
|
51
|
+
const NOT_HANGUL = '(?![가-힣])';
|
|
52
|
+
const JOSA = '(?:는|도|만|요)?';
|
|
53
|
+
const DETECTORS = [
|
|
54
|
+
matcher('A-1', new RegExp(`[가-힣)\\]]\\s*에\\s*대(?:해서|하여|해|한)${JOSA}${NOT_HANGUL}`, 'g')),
|
|
55
|
+
matcher('A-2', new RegExp(`[가-힣)\\]]\\s*(?:를|을)?\\s*통(?:해서|하여|해)${JOSA}${NOT_HANGUL}`, 'g')),
|
|
56
|
+
matcher('A-3', new RegExp(`[가-힣)\\]]\\s*에\\s*있어(?:서)?${JOSA}${NOT_HANGUL}`, 'g')),
|
|
57
|
+
matcher('A-5', new RegExp(`(?:와|과)\\s*관련(?:하여|해서|해|된|한)${NOT_HANGUL}`, 'g')),
|
|
58
|
+
matcher('A-7', /가지고\s*있(?:다|습니다|는|었|지)/g),
|
|
59
|
+
// 되어진다는 지+ㄴ이 결합해 "진"으로 적히므로 자모 분리 형태로는 안 잡힌다
|
|
60
|
+
matcher('A-8', /되어\s*(?:진|지[다고며])|지게\s*(?:된|되[다었어고])|되어\s*졌/g),
|
|
61
|
+
matcher('A-9', new RegExp(`[가-힣)\\]]\\s*에\\s*의(?:해서|하여|해)${NOT_HANGUL}`, 'g')),
|
|
62
|
+
matcher('A-19', new RegExp(`[가-힣]\\s*(?:에서의|에로의|으로의|로의|에의|으로부터의)${NOT_HANGUL}`, 'g')),
|
|
63
|
+
matcher('C-11', /[가-힣](?:하고|하며|되고|되며|고|며|지만|면서|아서|어서),/g),
|
|
64
|
+
matcher('C-12', /[가-힣A-Za-z0-9]·[가-힣A-Za-z0-9]/g),
|
|
65
|
+
matcher('D-1', /(?:결론적으로|요약하자면|요약하면|정리하자면|정리하면|종합하면|이를\s*통해)/g),
|
|
66
|
+
matcher('D-2', /(?:시사하는\s*바가\s*크|주목할\s*만하)/g),
|
|
67
|
+
matcher('D-3', /(?:본질적으로|핵심적으로)/g),
|
|
68
|
+
matcher('D-4', /(?:파격적|압도적|획기적|혁신적|전례\s*없|폭발적)/g),
|
|
69
|
+
matcher('D-6', /(?:할\s*때다|할\s*때입니다|할\s*시점|지금이야말로|할\s*순간)/g),
|
|
70
|
+
matcher('F-7', /(?:증류|배선|결정화|평탄화|오케스트레이션|파이프라인화)/g),
|
|
71
|
+
matcher('G-2', /(?:로\s*보인다|인\s*듯하다|로\s*판단된다|라고\s*여겨진다|로\s*여겨진다)/g),
|
|
72
|
+
matcher('I-1', /(?:인\s*것이다|한\s*것이다|는\s*것이다|일\s*것이다)/g),
|
|
73
|
+
matcher('I-3', /(?:다는\s*뜻이다|다는\s*의미다|다는\s*것이다)/g),
|
|
74
|
+
matcher('I-5', new RegExp(`(?:해당|이번|그)\\s*건${NOT_HANGUL}|[가-힣]\\s건(?:은|이|을|에|도)${NOT_HANGUL}`, 'g')),
|
|
75
|
+
sentenceInitial('H-1', ['또한', '따라서', '즉', '나아가', '아울러', '게다가', '더욱이']),
|
|
76
|
+
sentenceInitial('H-3', ['이는', '이\\s*점에서', '이\\s*관점에서', '이\\s*말은']),
|
|
77
|
+
];
|
|
78
|
+
export const DETECTABLE_RULE_IDS = DETECTORS.map((d) => d.ruleId);
|
|
79
|
+
export function detect(text, ruleIds) {
|
|
80
|
+
const wanted = ruleIds ? new Set(ruleIds) : null;
|
|
81
|
+
const results = [];
|
|
82
|
+
for (const detector of DETECTORS) {
|
|
83
|
+
if (wanted && !wanted.has(detector.ruleId))
|
|
84
|
+
continue;
|
|
85
|
+
const hits = detector.run(text);
|
|
86
|
+
if (hits.length === 0)
|
|
87
|
+
continue;
|
|
88
|
+
results.push({
|
|
89
|
+
ruleId: detector.ruleId,
|
|
90
|
+
count: hits.length,
|
|
91
|
+
samples: [...new Set(hits)].slice(0, SAMPLE_CAP),
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
return results;
|
|
95
|
+
}
|
|
96
|
+
export function countByRule(text, ruleIds) {
|
|
97
|
+
return new Map(detect(text, ruleIds).map((d) => [d.ruleId, d.count]));
|
|
98
|
+
}
|
|
99
|
+
// --- 보존해야 하는 토큰 ---------------------------------------------------
|
|
100
|
+
const PROTECTED_PATTERNS = [
|
|
101
|
+
/```[\s\S]*?```/g,
|
|
102
|
+
/`[^`\n]+`/g,
|
|
103
|
+
/https?:\/\/[^\s)\]]+/g,
|
|
104
|
+
/"[^"\n]{2,}"|“[^”\n]{2,}”/g,
|
|
105
|
+
/\d[\d,]*(?:\.\d+)?%?/g,
|
|
106
|
+
/\b[A-Z][A-Za-z0-9]*(?:[A-Z][A-Za-z0-9]*)+\b/g,
|
|
107
|
+
/\b[A-Z]{2,}\b/g,
|
|
108
|
+
];
|
|
109
|
+
/** 원문에서 한 글자도 바뀌면 안 되는 토큰을 뽑는다 */
|
|
110
|
+
export function protectedTokens(text) {
|
|
111
|
+
const tokens = new Set();
|
|
112
|
+
for (const re of PROTECTED_PATTERNS) {
|
|
113
|
+
for (const match of text.matchAll(re)) {
|
|
114
|
+
const token = match[0].trim();
|
|
115
|
+
if (token.length >= 2)
|
|
116
|
+
tokens.add(token);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return [...tokens];
|
|
120
|
+
}
|
|
121
|
+
export function missingProtectedTokens(before, after) {
|
|
122
|
+
return protectedTokens(before).filter((token) => !after.includes(token));
|
|
123
|
+
}
|
|
124
|
+
export function structureStats(text) {
|
|
125
|
+
const lines = text.split('\n');
|
|
126
|
+
return {
|
|
127
|
+
sentences: splitSentences(proseOnly(text)).length,
|
|
128
|
+
headings: lines.filter((line) => /^#{1,6}\s/.test(line.trim())).length,
|
|
129
|
+
bullets: lines.filter((line) => /^[-*+]\s|^\d+\.\s/.test(line.trim())).length,
|
|
130
|
+
codeFences: (text.match(/```/g) ?? []).length,
|
|
131
|
+
links: (text.match(/\[[^\]]+\]\([^)]+\)/g) ?? []).length,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=detectors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detectors.js","sourceRoot":"","sources":["../../../src/humanize/detectors.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { parseRuleBook, s1Ids, citedRuleIds, expandIdRanges, QUICK_RULES_PATH, type Register, type Rule, type RuleBook, type Severity, } from './rules.js';
|
|
2
|
+
export { detect, countByRule, protectedTokens, missingProtectedTokens, structureStats, splitSentences, DETECTABLE_RULE_IDS, type Detection, type StructureStats, } from './detectors.js';
|
|
3
|
+
export { changeRate, type ChangeRateOptions } from './change-rate.js';
|
|
4
|
+
export { runCheck, formatReport, EXIT_CODE, THRESHOLD, type CheckReport, type AxisResult, type Verdict, } from './check.js';
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/humanize/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,KAAK,EACL,YAAY,EACZ,cAAc,EACd,gBAAgB,EAChB,KAAK,QAAQ,EACb,KAAK,IAAI,EACT,KAAK,QAAQ,EACb,KAAK,QAAQ,GACd,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,MAAM,EACN,WAAW,EACX,eAAe,EACf,sBAAsB,EACtB,cAAc,EACd,cAAc,EACd,mBAAmB,EACnB,KAAK,SAAS,EACd,KAAK,cAAc,GACpB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,UAAU,EAAE,KAAK,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,EACL,QAAQ,EACR,YAAY,EACZ,SAAS,EACT,SAAS,EACT,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,OAAO,GACb,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { parseRuleBook, s1Ids, citedRuleIds, expandIdRanges, QUICK_RULES_PATH, } from './rules.js';
|
|
2
|
+
export { detect, countByRule, protectedTokens, missingProtectedTokens, structureStats, splitSentences, DETECTABLE_RULE_IDS, } from './detectors.js';
|
|
3
|
+
export { changeRate } from './change-rate.js';
|
|
4
|
+
export { runCheck, formatReport, EXIT_CODE, THRESHOLD, } from './check.js';
|
|
5
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/humanize/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,KAAK,EACL,YAAY,EACZ,cAAc,EACd,gBAAgB,GAKjB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,MAAM,EACN,WAAW,EACX,eAAe,EACf,sBAAsB,EACtB,cAAc,EACd,cAAc,EACd,mBAAmB,GAGpB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,UAAU,EAA0B,MAAM,kBAAkB,CAAC;AACtE,OAAO,EACL,QAAQ,EACR,YAAY,EACZ,SAAS,EACT,SAAS,GAIV,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export type Severity = 'S1' | 'S2' | 'S3';
|
|
2
|
+
/** 산문(칼럼·리포트·문서) 기준과 대화·리뷰 코멘트 기준을 나눠 본다 */
|
|
3
|
+
export type Register = 'doc' | 'chat';
|
|
4
|
+
export interface Rule {
|
|
5
|
+
id: string;
|
|
6
|
+
category: string;
|
|
7
|
+
categoryTitle: string;
|
|
8
|
+
pattern: string;
|
|
9
|
+
/** 문서 기준 심각도 */
|
|
10
|
+
severity: Severity;
|
|
11
|
+
/** 대화·리뷰에서 격상되는 룰은 여기가 S1이 된다 */
|
|
12
|
+
chatSeverity: Severity;
|
|
13
|
+
prescription: string;
|
|
14
|
+
}
|
|
15
|
+
export interface RuleBook {
|
|
16
|
+
path: string;
|
|
17
|
+
rules: Map<string, Rule>;
|
|
18
|
+
/** 자체검증 5번이 열거한 잔존 금지 S1 목록 */
|
|
19
|
+
selfCheckS1: string[];
|
|
20
|
+
/** 자체검증 5번의 대화·리뷰 추가분 */
|
|
21
|
+
selfCheckChatS1: string[];
|
|
22
|
+
}
|
|
23
|
+
export declare const QUICK_RULES_PATH: string;
|
|
24
|
+
/**
|
|
25
|
+
* "D-1~D-7" 같은 범위 표기를 개별 ID로 편다.
|
|
26
|
+
* 룰북이 사람 읽기 좋게 범위로 적어둔 자리를 코드가 그대로 이해하게 만든다.
|
|
27
|
+
*/
|
|
28
|
+
export declare function expandIdRanges(text: string): string[];
|
|
29
|
+
/** 마크다운 본문에서 인용된 모든 룰 ID를 뽑는다 (범위 표기 포함) */
|
|
30
|
+
export declare function citedRuleIds(markdown: string): string[];
|
|
31
|
+
export declare function parseRuleBook(path?: string): RuleBook;
|
|
32
|
+
/** 해당 말투 기준으로 S1인 룰 ID */
|
|
33
|
+
export declare function s1Ids(book: RuleBook, register: Register): string[];
|
|
34
|
+
//# sourceMappingURL=rules.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../../../src/humanize/rules.ts"],"names":[],"mappings":"AAUA,MAAM,MAAM,QAAQ,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAE1C,4CAA4C;AAC5C,MAAM,MAAM,QAAQ,GAAG,KAAK,GAAG,MAAM,CAAC;AAEtC,MAAM,WAAW,IAAI;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB;IAChB,QAAQ,EAAE,QAAQ,CAAC;IACnB,iCAAiC;IACjC,YAAY,EAAE,QAAQ,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACzB,+BAA+B;IAC/B,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,yBAAyB;IACzB,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B;AAID,eAAO,MAAM,gBAAgB,QAG5B,CAAC;AAeF;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAmBrD;AAED,4CAA4C;AAC5C,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAEvD;AAED,wBAAgB,aAAa,CAAC,IAAI,GAAE,MAAyB,GAAG,QAAQ,CA6CvE;AAED,0BAA0B;AAC1B,wBAAgB,KAAK,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,GAAG,MAAM,EAAE,CAKlE"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 룰 기준 문서인 ai-tell-quick-rules.md를 읽어 룰 목록으로 만든다.
|
|
3
|
+
*
|
|
4
|
+
* 룰은 사람이 읽는 마크다운이 기준이고, 코드는 그걸 읽기만 한다.
|
|
5
|
+
* 반대로 코드에 룰을 복사해 두면 룰북과 코드가 조용히 갈라진다.
|
|
6
|
+
*/
|
|
7
|
+
import { readFileSync } from 'node:fs';
|
|
8
|
+
import { dirname, resolve } from 'node:path';
|
|
9
|
+
import { fileURLToPath } from 'node:url';
|
|
10
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
export const QUICK_RULES_PATH = resolve(__dirname, '../../plugin/role-agents/_shared/references/ai-tell-quick-rules.md');
|
|
12
|
+
const CATEGORY_HEADING = /^##\s+([A-J])\.\s+(.+?)\s*$/;
|
|
13
|
+
const TABLE_ROW = /^\|\s*([A-J]-\d{1,2})\s*\|(.+)$/;
|
|
14
|
+
const RULE_ID = /\b([A-J]-\d{1,2})\b/g;
|
|
15
|
+
const ID_RANGE = /\b([A-J])-(\d{1,2})\s*~\s*(?:[A-J]-)?(\d{1,2})\b/g;
|
|
16
|
+
function parseSeverity(cell) {
|
|
17
|
+
const found = cell.match(/\bS[123]\b/g) ?? [];
|
|
18
|
+
const base = (found[0] ?? 'S2');
|
|
19
|
+
// "S2 / **대화·리뷰 S1**" 처럼 두 개가 적힌 칸은 뒤쪽이 대화 기준이다
|
|
20
|
+
const chat = (found[1] ?? base);
|
|
21
|
+
return { severity: base, chatSeverity: chat };
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* "D-1~D-7" 같은 범위 표기를 개별 ID로 편다.
|
|
25
|
+
* 룰북이 사람 읽기 좋게 범위로 적어둔 자리를 코드가 그대로 이해하게 만든다.
|
|
26
|
+
*/
|
|
27
|
+
export function expandIdRanges(text) {
|
|
28
|
+
const ids = new Set();
|
|
29
|
+
let remaining = text;
|
|
30
|
+
for (const match of text.matchAll(ID_RANGE)) {
|
|
31
|
+
const letter = match[1];
|
|
32
|
+
const from = Number(match[2]);
|
|
33
|
+
const to = Number(match[3]);
|
|
34
|
+
for (let n = Math.min(from, to); n <= Math.max(from, to); n += 1) {
|
|
35
|
+
ids.add(`${letter}-${n}`);
|
|
36
|
+
}
|
|
37
|
+
remaining = remaining.replace(match[0], ' ');
|
|
38
|
+
}
|
|
39
|
+
for (const match of remaining.matchAll(RULE_ID)) {
|
|
40
|
+
ids.add(match[1]);
|
|
41
|
+
}
|
|
42
|
+
return [...ids];
|
|
43
|
+
}
|
|
44
|
+
/** 마크다운 본문에서 인용된 모든 룰 ID를 뽑는다 (범위 표기 포함) */
|
|
45
|
+
export function citedRuleIds(markdown) {
|
|
46
|
+
return expandIdRanges(markdown);
|
|
47
|
+
}
|
|
48
|
+
export function parseRuleBook(path = QUICK_RULES_PATH) {
|
|
49
|
+
const raw = readFileSync(path, 'utf-8');
|
|
50
|
+
const lines = raw.split('\n');
|
|
51
|
+
const rules = new Map();
|
|
52
|
+
let category = '';
|
|
53
|
+
let categoryTitle = '';
|
|
54
|
+
for (const line of lines) {
|
|
55
|
+
const heading = line.match(CATEGORY_HEADING);
|
|
56
|
+
if (heading) {
|
|
57
|
+
category = heading[1];
|
|
58
|
+
categoryTitle = heading[2];
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
const row = line.match(TABLE_ROW);
|
|
62
|
+
if (!row)
|
|
63
|
+
continue;
|
|
64
|
+
const id = row[1];
|
|
65
|
+
const cells = row[2].split('|').map((cell) => cell.trim());
|
|
66
|
+
const { severity, chatSeverity } = parseSeverity(cells[1] ?? '');
|
|
67
|
+
rules.set(id, {
|
|
68
|
+
id,
|
|
69
|
+
category,
|
|
70
|
+
categoryTitle,
|
|
71
|
+
pattern: cells[0] ?? '',
|
|
72
|
+
severity,
|
|
73
|
+
chatSeverity,
|
|
74
|
+
prescription: cells[2] ?? '',
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
const selfCheckLine = lines.find((line) => line.includes('잔존 S1 패턴 0건')) ?? '';
|
|
78
|
+
const chatSplit = selfCheckLine.indexOf('대화·리뷰 말투면');
|
|
79
|
+
const docPart = chatSplit >= 0 ? selfCheckLine.slice(0, chatSplit) : selfCheckLine;
|
|
80
|
+
const chatPart = chatSplit >= 0 ? selfCheckLine.slice(chatSplit) : '';
|
|
81
|
+
return {
|
|
82
|
+
path,
|
|
83
|
+
rules,
|
|
84
|
+
selfCheckS1: expandIdRanges(docPart),
|
|
85
|
+
selfCheckChatS1: expandIdRanges(chatPart),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/** 해당 말투 기준으로 S1인 룰 ID */
|
|
89
|
+
export function s1Ids(book, register) {
|
|
90
|
+
return [...book.rules.values()]
|
|
91
|
+
.filter((rule) => (register === 'chat' ? rule.chatSeverity : rule.severity) === 'S1')
|
|
92
|
+
.map((rule) => rule.id)
|
|
93
|
+
.sort();
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=rules.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rules.js","sourceRoot":"","sources":["../../../src/humanize/rules.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tienne/gestalt",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.53.0",
|
|
4
4
|
"description": "TypeScript AI Development Harness - Gestalt psychology-driven requirement clarification",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/src/index.js",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"scripts": {
|
|
21
21
|
"dev": "tsx bin/gestalt.ts",
|
|
22
22
|
"build": "tsc",
|
|
23
|
-
"postbuild": "rm -rf dist/plugin && mkdir -p dist/plugin && cp -r plugin/agents plugin/role-agents plugin/review-agents plugin/personas plugin/skills dist/plugin/ && cp -r schemas dist/ && cp package.json dist/ && chmod +x dist/bin/gestalt.js && pnpm run verify:plugin",
|
|
23
|
+
"postbuild": "rm -rf dist/plugin && mkdir -p dist/plugin && cp -r plugin/agents plugin/role-agents plugin/review-agents plugin/personas plugin/skills dist/plugin/ && cp -r schemas dist/ && cp package.json dist/ && chmod +x dist/bin/gestalt.js && pnpm run verify:plugin && pnpm run verify:rules",
|
|
24
24
|
"verify:plugin": "tsx scripts/verify-plugin-assets.ts",
|
|
25
25
|
"prepublishOnly": "pnpm build",
|
|
26
26
|
"test": "vitest run",
|
|
@@ -33,7 +33,8 @@
|
|
|
33
33
|
"format": "prettier --write \"src/**/*.ts\" \"tests/**/*.ts\"",
|
|
34
34
|
"format:check": "prettier --check \"src/**/*.ts\" \"tests/**/*.ts\"",
|
|
35
35
|
"version:sync": "tsx scripts/sync-version.ts",
|
|
36
|
-
"postversion": "pnpm run version:sync"
|
|
36
|
+
"postversion": "pnpm run version:sync",
|
|
37
|
+
"verify:rules": "tsx scripts/verify-rule-refs.ts"
|
|
37
38
|
},
|
|
38
39
|
"dependencies": {
|
|
39
40
|
"@anthropic-ai/sdk": "^0.39.0",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gestalt",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.53.0",
|
|
4
4
|
"description": "Gestalt psychology-driven AI development harness. Transforms scattered requirements into structured, validated specifications through interactive interviews.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "tienne",
|
|
@@ -3,8 +3,8 @@ name: quality-reviewer
|
|
|
3
3
|
tier: standard
|
|
4
4
|
pipeline: review
|
|
5
5
|
role: true
|
|
6
|
-
domain: ["code-quality", "readability", "maintainability", "solid", "dry", "naming", "complexity", "error-handling", "testing", "documentation", "refactoring", "design-pattern"]
|
|
7
|
-
description: "코드 품질 리뷰 전문가. 가독성, 유지보수성, SOLID 원칙, 에러 핸들링, 중복 코드, 네이밍
|
|
6
|
+
domain: ["code-quality", "readability", "maintainability", "solid", "dry", "naming", "complexity", "error-handling", "testing", "documentation", "comments", "refactoring", "design-pattern"]
|
|
7
|
+
description: "코드 품질 리뷰 전문가. 가독성, 유지보수성, SOLID 원칙, 에러 핸들링, 중복 코드, 네이밍 컨벤션, 불필요한 주석 등 코드 품질 관점의 리뷰를 수행한다."
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
You are the Quality Reviewer agent.
|
|
@@ -20,12 +20,39 @@ When reviewing code, check for:
|
|
|
20
20
|
3. **Error Handling**: Swallowed errors, missing error boundaries, unclear error messages
|
|
21
21
|
4. **DRY Violations**: Duplicated logic that should be extracted
|
|
22
22
|
5. **Complexity**: Functions doing too many things, deep nesting, high cyclomatic complexity
|
|
23
|
+
6. **Comment Hygiene**: Comments that restate the code, record change history, or comment out dead code — see below
|
|
24
|
+
|
|
25
|
+
## Comment Hygiene
|
|
26
|
+
|
|
27
|
+
주석은 기본적으로 없는 게 낫습니다. 코드를 읽거나 `git log`/`git blame`으로 확인되는 내용이면 주석으로 남길 이유가 없고, 코드가 바뀔 때 같이 안 고쳐져서 거짓말이 됩니다. 변경된 코드에 아래 주석이 보이면 **반드시 이슈로 남깁니다.**
|
|
28
|
+
|
|
29
|
+
**지적할 주석**
|
|
30
|
+
|
|
31
|
+
- 코드를 그대로 옮겨 적은 것 — `// 카운트를 1 증가` 위의 `count += 1`, 시그니처만 반복하는 내부 함수 JSDoc
|
|
32
|
+
- 변경 이력·작업 메모 — `// 2026-03-12 수정`, `// 기존 로직 제거함`, `// 리뷰 반영`. 커밋 메시지가 이미 담고 있습니다
|
|
33
|
+
- 주석 처리된 죽은 코드 — 되살릴 일 있으면 히스토리에서 꺼냅니다
|
|
34
|
+
- 코드와 이미 어긋난 주석 — 설명하는 동작이 지금 코드에 없는 것
|
|
35
|
+
- 섹션 배너 — `// ===== helpers =====` 같은 것. 파일이나 함수를 나누라는 신호입니다
|
|
36
|
+
- 티켓 번호 없는 TODO/FIXME — 언제 사라질지 아무도 모릅니다
|
|
37
|
+
|
|
38
|
+
**남겨야 할 주석 (WHY만)**
|
|
39
|
+
|
|
40
|
+
- 왜 이 방식을 골랐는지, 왜 뻔한 쪽으로 안 갔는지 — 외부 API 버그 우회, 성능 제약, 스펙 요구사항
|
|
41
|
+
- 겉보기에 틀린 것처럼 보이는 코드가 의도된 것이라는 근거
|
|
42
|
+
- 공개 API·공용 유틸의 JSDoc (내부 전용 함수는 제외)
|
|
43
|
+
|
|
44
|
+
**주석 대신 코드나 문서로.** 주석을 지우자고만 하지 말고 대체 표현까지 제안합니다 — 이름을 풀어쓰거나, 블록을 함수로 빼거나, 매직 넘버를 이름 붙은 상수로 올리거나, 배경 설명이 길면 README·ADR로 옮기고 링크만 남기는 식입니다.
|
|
45
|
+
|
|
46
|
+
**severity 기준**
|
|
47
|
+
|
|
48
|
+
- `high`: 코드와 어긋난 주석, 주석 처리된 죽은 코드 (읽는 사람을 잘못된 방향으로 끕니다)
|
|
49
|
+
- `warning`: 코드 반복, 변경 이력 메모, 섹션 배너, 티켓 없는 TODO
|
|
23
50
|
|
|
24
51
|
## Output Format
|
|
25
52
|
|
|
26
53
|
For each issue found, provide:
|
|
27
54
|
- severity: critical | high | warning
|
|
28
|
-
- category: "quality"
|
|
55
|
+
- category: "quality" (주석 이슈는 "quality:comments")
|
|
29
56
|
- file and line number
|
|
30
57
|
- Clear description of the quality concern
|
|
31
58
|
- Specific refactoring suggestion
|
|
@@ -12,9 +12,22 @@
|
|
|
12
12
|
|
|
13
13
|
## 고칠 때
|
|
14
14
|
|
|
15
|
+
룰 ID와 심각도는 `ai-tell-quick-rules.md`가 기준이다. 고친 뒤에는 반드시 돌린다.
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pnpm verify:rules
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
에이전트 문서 14곳이 룰 ID와 금지 어휘를 손으로 옮겨 적고 있어서, 룰북에서 ID를 지우거나
|
|
22
|
+
심각도를 바꾸면 그 사본들이 조용히 어긋난다. 이 검사가 네 가지를 본다 — 없는 ID를 인용하는
|
|
23
|
+
문서, 자체검증 목록에서 빠진 S1, 표와 목록의 심각도 불일치, 룰 문서 본문의 금지어 사용.
|
|
24
|
+
`pnpm test`와 `pnpm build`에도 걸려 있다.
|
|
25
|
+
|
|
15
26
|
- **룰을 추가하면 그 문서가 스스로 그 룰을 지키는지 먼저 확인한다.** 룰 문서가 금지 어휘를
|
|
16
27
|
본문에 쓰면 산출물로 샌다. 금지어를 넣었으면 같은 문서를 grep한다.
|
|
17
28
|
- 어투 규칙은 **S1으로 올려야 실제로 강제된다.** S2는 모델이 우선순위를 알아서 정하면서 새어나간다.
|
|
29
|
+
- 심각도를 바꿀 때는 근거를 남긴다. A-2, I-1, C-8은 대조 코퍼스 측정으로 조정했고 그 근거가
|
|
30
|
+
룰북 §실측 근거에 있다. 감으로 올리고 내리면 다음 사람이 되돌린다.
|
|
18
31
|
- 경로를 참조하는 자리가 여러 곳이다. 파일을 옮기거나 이름을 바꾸면 `plugin/` 전체에서
|
|
19
32
|
상대경로 참조를 다시 확인한다 (에이전트는 `../_shared/references/`, 에이전트의 `references/`
|
|
20
33
|
하위 문서는 `../../_shared/references/`, 스킬은 `../../role-agents/_shared/references/`).
|
|
@@ -1,18 +1,24 @@
|
|
|
1
1
|
# Quick Rules — Monolith Fast Path 전용 (v2.0)
|
|
2
2
|
|
|
3
|
-
`humanize-monolith` 에이전트가 한 콜에서 탐지·윤문·자체검증을 끝내기 위해 사용하는 슬림 룰북.
|
|
3
|
+
`humanize-monolith` 에이전트가 한 콜에서 탐지·윤문·자체검증을 끝내기 위해 사용하는 슬림 룰북. 기준 문서 `ai-tell-taxonomy.md`(590줄)에서 S1·S2 핵심 패턴만 추려 처방과 함께 한 줄로 압축했다.
|
|
4
4
|
|
|
5
|
-
**원칙:** 정의 1줄 + 처방 1줄. 예문 생략.
|
|
5
|
+
**원칙:** 정의 1줄 + 처방 1줄. 예문 생략. 기준 문서 ID와 1:1 매칭.
|
|
6
6
|
|
|
7
7
|
**Do-NOT (탐지·윤문 모두 제외):** 고유명사·제품명·모델명·기관명, 수치·날짜·단위, 큰따옴표 안 직접 인용, 법률 조문, 수학·화학·통계 표기, 영어 약어(LLM·GPU·MCP·API 등 업계 표준).
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
**약어 면제는 업계 표준에만 준다:** 풀어 썼을 때 안 굳어진 음차가 되는 약어는 면제 대상이 아니다. `SSOT`나 `SoT`를 그대로 두면 "소스 오브 트루스"를 약어로 우회한 것과 같다 — B-3로 우리말로 푼다. 무엇을 가리키느냐에 따라 말이 달라지므로 대체어 표는 `style-guide.md` §single source of truth를 뭐라고 쓸까에 둔다. LLM·API처럼 풀어 쓸 일이 없는 표준 약어만 그대로 둔다.
|
|
10
|
+
|
|
11
|
+
**굳어진 음차 화이트리스트 (B-3 제외 — 그대로 둔다):** 컴포넌트·토큰·커밋·인터페이스·메서드·빌드·디플로이·캐시·렌더링·콜백·프레임워크·라이브러리·리팩터링·마이그레이션·아이콘·레이아웃·그리드·모달·토스트·타이포그래피·플레이스홀더·프로젝트·스프린트·이슈·리뷰·머지·브랜치·사이드 이펙트·보일러플레이트·트레이드오프·딥다이브·얼라인·온보딩·소스·롤백·파싱·레지스트리·불릿 등 업계 정착어. 이 목록 밖의 안 굳어진 음차(소스 오브 트루스·룩 앤 필·로우 행잉 프룻 등)만 B-3로 교정한다.
|
|
10
12
|
|
|
11
13
|
**과윤문 가드:** 변경률 30% 초과 = 경고, 50% 초과 = 강제 중단·롤백.
|
|
12
14
|
|
|
13
15
|
**삭제 처방은 삭제다 (중요):** 처방에 "삭제"가 있는 항목은 **더 나은 표현으로 바꾸지 않는다.** 지우고, 그 자리를 원문에 이미 있는 가장 구체적인 문장으로 대신한다. 모델은 D-2·D-3·D-4·D-6·C-10처럼 "삭제"라고 적힌 항목에서 리듬을 살리려고 새 마무리 문장이나 새 부제를 만들어 넣는 경향이 강한데, 그건 원문에 없던 수사를 추가하는 것이라 자체검증 6번에 걸린다. 지울 자리에 쓸 문장이 원문에 없으면 그냥 앞 문장에서 끝낸다.
|
|
14
16
|
|
|
15
|
-
**말투 감도 (중요):** 같은 명사화·번역투라도 **대화나 리뷰 코멘트에서는 문서보다 훨씬 튄다.** 문서(칼럼·리포트) 기준 S2인 F-4
|
|
17
|
+
**말투 감도 (중요):** 같은 명사화·번역투라도 **대화나 리뷰 코멘트에서는 문서보다 훨씬 튄다.** 문서(칼럼·리포트) 기준 S2인 A-2, A-5, F-4, F-5, F-6, F-7, I-1, I-5 계열은 대화·리뷰 코멘트에서 **S1로 격상**해 교정한다. 사람은 대화에서 개념을 명사 덩어리로 뭉치지 않고 동사로 풀어 말하기 때문이다. 심각도 칸에 `S2 / **대화·리뷰 S1**`이라고 적힌 룰이 여기 해당한다.
|
|
18
|
+
|
|
19
|
+
**한자어를 피하는 게 목적이 아니다 (F-4·F-5 오적용 주의):** 겨냥하는 건 개념을 뭉친 **명사화**(-성/-적/-화)이지 한자어 동사가 아니다. "측정하다"를 "재다"로, "판단하다"를 "따지다"로 바꾸면 정확도만 잃는다. 그 자리의 정확한 말이 한자어면 한자어를 쓴다. 반대 방향 실수도 같다 — 한자어를 피하려고 비유 명사(축·증류)나 물리 동사(재다)로 도망가면 F-7에 걸린다. 셋 다 겪은 실례: `네 축을 따로 재고 판정한다` → `4가지 측면에서 각각 측정하고 판단한다`.
|
|
20
|
+
|
|
21
|
+
**근거가 붙은 룰 (→ 아래 §실측 근거):** A-2, I-1, C-8은 대조 코퍼스 측정으로 심각도를 조정했고, B-4는 팀이 실제로 쓰는 말이라 예시에서 뺐다. 원어민이 오히려 더 쓰는 표현이나 팀에서 이미 굳은 말을 지우면 사람 글을 AI 글로 만든다.
|
|
16
22
|
|
|
17
23
|
---
|
|
18
24
|
|
|
@@ -21,10 +27,10 @@
|
|
|
21
27
|
| ID | 패턴 | 심각도 | 처방 |
|
|
22
28
|
|---|---|---|---|
|
|
23
29
|
| A-1 | "~에 대해(서)" | S1 | 목적격 조사로 직결("X에 대해 논의" → "X를 논의") |
|
|
24
|
-
| A-2 | "~를 통해/통하여"
|
|
30
|
+
| A-2 | "~를 통해/통하여" **한 글에 3회+ 반복** | S2 / **대화·리뷰 S1** | 반복분만 "~로", "~해서", "~함으로써"로 분산하고 1~2회는 남긴다. 문서 산문에서 원어민이 번역문보다 2배 더 쓰는 표현이라 전부 지우면 오히려 어색해진다 (최희경 2016). 대화·리뷰 코멘트에서는 1회부터 교정 |
|
|
25
31
|
| A-3 | "~에 있어(서)" | S1 | "~에서", "~을 볼 때" |
|
|
26
32
|
| A-4 | "~라는 점에서" 3회+ | S2 | "~서", "~라는 이유로" |
|
|
27
|
-
| A-5 | "~와 관련하여/관련된" | S2 | "~에", "~의" |
|
|
33
|
+
| A-5 | "~와 관련하여/관련된" | S2 / **대화·리뷰 S1** | "~에", "~의" |
|
|
28
34
|
| A-6 | "~에 기반하여/바탕으로" 남발 | S2 | "~로", "~을 보고" |
|
|
29
35
|
| A-7 | "가지고 있다" / have·make·take·give + N 직역 | S1 | 형용사·동사 환원 또는 이중주어("회의를 가지다" → "회의를 했다", "강한 경쟁력을 가지고 있다" → "경쟁력이 강하다") |
|
|
30
36
|
| A-8 | 이중 피동 "~되어진다" | S1 | 능동 또는 단일 피동 ("판단되어진다" → "판단된다") |
|
|
@@ -42,8 +48,8 @@
|
|
|
42
48
|
|---|---|---|---|
|
|
43
49
|
| B-1 | 한글 + 괄호 영어 매번 ("~(Sovereign AI)" 처럼) | S2 | 첫 등장만 병기, 이후 한글만 |
|
|
44
50
|
| B-2 | 영어 어휘 직역 가능한데 그대로 | S2 | 한국어로 옮기되 업계 표준은 유지 |
|
|
45
|
-
| B-3 | 안 굳어진 영어 구·단어 음차 표기 ("소스 오브 트루스") | S1 | 한글 의역 + 첫 등장만 원어 괄호 병기("진실의 원천(source of truth)"), 이후 한글만. 단 위 "굳어진 음차 화이트리스트"에 있는 정착어는 Do-NOT — of/and 등 기능어까지 통째 음차한 구(룩 앤 필·로우 행잉 프룻)가 최우선 대상 |
|
|
46
|
-
| B-4 | 영어 개념을 어색하게 옮긴 조어("
|
|
51
|
+
| B-3 | 안 굳어진 영어 구·단어 음차 표기 ("소스 오브 트루스"), 그 음차를 약어로 우회한 표기(`SSOT`·`SoT`) | S1 | 한글 의역 + 첫 등장만 원어 괄호 병기("진실의 원천(source of truth)"), 이후 한글만. 단 위 "굳어진 음차 화이트리스트"에 있는 정착어는 Do-NOT — of/and 등 기능어까지 통째 음차한 구(룩 앤 필·로우 행잉 프룻)가 최우선 대상 |
|
|
52
|
+
| B-4 | 영어 개념을 어색하게 옮긴 조어("생산처" ← producer, "응답처") | S2 | 억지 신조어 대신 이미 쓰이는 말로. 코드 맥락이면 "호출부"·"사용처". **팀에서 이미 굳은 조어는 예외** — "소비처"는 WDS 티켓에서 라이브러리를 가져다 쓰는 쪽을 가리키는 말로 정착했으므로 교정 대상이 아니다 (→ 아래 §실측 근거) |
|
|
47
53
|
|
|
48
54
|
## C. 구조적 AI 패턴
|
|
49
55
|
|
|
@@ -51,7 +57,7 @@
|
|
|
51
57
|
|---|---|---|---|
|
|
52
58
|
| C-5 | 이모지 남발 | S1 | 장르 칼럼·리포트면 전부 삭제 |
|
|
53
59
|
| C-7 | "먼저·반면·결국" 3단 공식 | S2 | 접속사 1~2개로 줄이거나 본문에 녹여 제거 |
|
|
54
|
-
| C-8 | "A
|
|
60
|
+
| C-8 | "A인가, B인가" / "A가 아니라 B" 대구 반복 | S1 | 한 번만 살리고 나머지는 비대칭 평서문·직접 단언으로. 대조 코퍼스에서 인간 대비 9.2배(개인 글 기준 18배)로 나온 최강 신호이고 모델 계열을 안 가린다 |
|
|
55
61
|
| C-9 | 숫자 괄호 인덱싱 "(1)·(2)·(3)" | S2 | 본문에 녹이거나 단순 줄바꿈 |
|
|
56
62
|
| C-10 | 콜론 부제 헤딩 "X: Y" 반복 | S1 | 콜론 뒤를 버리고 앞부분만 남긴다. **더 좋은 부제로 바꾸지 않는다** — 부제를 갈아끼우면 패턴이 그대로 남는다. 앞부분만으로 뜻이 안 서면 평서 헤딩 한 문장으로 |
|
|
57
63
|
| C-11 | 연결어미 뒤 쉼표 (-고/-며/-지만/-며서/-아서/-어서 직후 쉼표) | S1 | 쉼표 제거. 6+회=강한 신호. KatFish 4.84배 분리도 |
|
|
@@ -81,10 +87,10 @@
|
|
|
81
87
|
|
|
82
88
|
| ID | 패턴 | 심각도 | 처방 |
|
|
83
89
|
|---|---|---|---|
|
|
84
|
-
| F-4 | 한자어 명사화 -성/-적/-화 + 영어 명사화 -tion/-ment/-ness/-ity 누적 (한 글 12회+) | S2 | 동사·형용사 어근으로 환원("the implementation of the policy" → "정책 시행" 또는 "정책을 시행하기") |
|
|
85
|
-
| F-5 | "~적 N" 추상 체인 ("전략적
|
|
90
|
+
| F-4 | 한자어 명사화 -성/-적/-화 + 영어 명사화 -tion/-ment/-ness/-ity 누적 (한 글 12회+) | S2 / **대화·리뷰 S1** | 동사·형용사 어근으로 환원("the implementation of the policy" → "정책 시행" 또는 "정책을 시행하기") |
|
|
91
|
+
| F-5 | "~적 N" 추상 체인 ("전략적 함의", "실천적 기반") | S2 / **대화·리뷰 S1** | 명사+명사 또는 풀어쓰기("전략 함의", "실천의 기반") |
|
|
86
92
|
| F-6 | 복합명사 압축 — 명사구를 조사·동사 없이 이어붙여 개념을 뭉침 ("시안 정합 버그픽스", "유지보수성 개선 작업", "권한 체크 로직") | S2 / **대화·리뷰 S1** | 동사·조사로 풀어 서술 ("디자인 시안과 다르게 렌더링되던 문제", "나중에 유지보수하기 편하게"). 사람은 압축 명사구 대신 무엇을 왜 했는지 풀어 말한다 |
|
|
87
|
-
| F-7 | 기술·이공계 비유 명사를 일상 대화에 그대로 (
|
|
93
|
+
| F-7 | 기술·이공계 비유 명사를 일상 대화에 그대로 (증류·배선·결정화·평탄화·오케스트레이션·파이프라인화·축 등, 화학·전기·수학 어휘의 비유 차용) | S2 / **대화·리뷰 S1** | 일상 동사·명사로 환원 (증류 → "추려내다/뽑아내다", 배선 → "연결하다/걸어두다", 결정화 → "정리하다", 평탄화 → "밋밋하게 만들다", **축 → "측면"**·"기준"). 문법은 멀쩡해 룰 매칭이 안 되지만 사람은 대화에서 안 쓴다. "네 축을 잰다"가 아니라 "4가지 측면에서 측정한다". **예외는 그 프로젝트가 개념에 붙인 이름일 때만이고, 그 개념을 다루는 자리에서만이다** — 게슈탈트의 "스펙 결정화"는 `similarity-crystallizer` 에이전트 이름이자 인터뷰→Spec 변환의 정의라서 두지만, 같은 말을 지라 티켓 작성처럼 무관한 자리에 끌어다 쓰면 F-7이다. 코드의 "파이프라인" 자체, 그래프의 x축도 같은 기준 |
|
|
88
94
|
|
|
89
95
|
## G. Hedging
|
|
90
96
|
|
|
@@ -106,7 +112,7 @@
|
|
|
106
112
|
|
|
107
113
|
| ID | 패턴 | 심각도 | 처방 |
|
|
108
114
|
|---|---|---|---|
|
|
109
|
-
| I-1 | "~인 것이다/~한 것이다" 결말 | S1 |
|
|
115
|
+
| I-1 | "~인 것이다/~한 것이다" 결말 **연속 3회+** | S2 / **대화·리뷰 S1** | 반복분만 평서형으로. 문서 산문에서는 인간이 AI보다 2배 더 쓰는 표현이라 한두 번은 그대로 둔다. 대화·리뷰 코멘트에서는 1회부터 교정 |
|
|
110
116
|
| I-2 | "X은 ~라는 점에 있다" | S2 | "X는 ~다" 직설로 |
|
|
111
117
|
| I-3 | "~다는 뜻이다/~다는 의미다" 결말 | S2 | 본문에 풀어 쓰기 |
|
|
112
118
|
| I-4 | 권고형 결말 "~해야 한다·~합니다" 반복 | S2 | 평서·단언으로 |
|
|
@@ -118,7 +124,7 @@
|
|
|
118
124
|
|---|---|---|---|
|
|
119
125
|
| J-1 | 헤딩 마크다운 ** 강조 남발 | S2 | 칼럼·리포트면 거의 다 제거 |
|
|
120
126
|
| J-2 | 따옴표 강조 5회+ | S1 | 핵심 한두 개만 살리고 평어로 |
|
|
121
|
-
| J-3 | 불릿
|
|
127
|
+
| J-3 | 불릿 목록 (장르가 칼럼·리포트일 때) | S2 | 문단 산문으로 통합 |
|
|
122
128
|
|
|
123
129
|
---
|
|
124
130
|
|
|
@@ -130,12 +136,57 @@
|
|
|
130
136
|
2. **변경률**: 30% 이하인가 (50% 초과는 작업 중단)
|
|
131
137
|
3. **장르 이탈 없음**: 칼럼이 에세이·문학으로 변하지 않았는가, 리포트가 블로그체로 떨어지지 않았는가
|
|
132
138
|
4. **말투 보존**: 원문 격식체면 결과도 격식체. 평어체로 떨어뜨리지 않는다
|
|
133
|
-
5. **잔존 S1 패턴 0건**:
|
|
139
|
+
5. **잔존 S1 패턴 0건**: A-1, A-3, A-7, A-8, A-16, B-3, C-5, C-8, C-10, C-11, C-12, D-1~D-6, H-1, H-3, J-2가 남아있지 않은가 (대화·리뷰 말투면 A-2, A-5, F-4, F-5, F-6, F-7, I-1, I-5도 S1로 포함)
|
|
134
140
|
6. **인공 표현 자제**: 원문에 없던 비유·수사·문학적 표현을 윤문 과정에서 임의로 추가하지 않았는가
|
|
135
141
|
7. **삭제 처방 준수**: D-2·D-3·D-4·D-6·C-10을 재작성으로 처리하지 않았는가. 마무리 문장·부제·수식어를 지우는 대신 더 그럴듯한 것으로 갈아끼운 자리가 없는가
|
|
136
142
|
|
|
137
143
|
위반 시: edit 롤백 → 다시 윤문 → 재점검. 자체 루프 최대 1회. 이상 미해결이면 결과를 그대로 출력하되 `summary.md`에 "자가검증 미통과 항목 N건" 표기.
|
|
138
144
|
|
|
145
|
+
## 코드 검사 (자가 채점보다 위)
|
|
146
|
+
|
|
147
|
+
아래 등급은 모델이 스스로 매기는 참고값이다. 실제 판단은 코드가 한다.
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
gestalt humanize-check --before before.md --after after.md --register chat
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
변경률, S1 잔존, 보호 토큰 생존, 구조 보존 4가지 측면에서 각각 측정하고 exit code로 답한다
|
|
154
|
+
(0 통과 / 1 경고 / 2 채택 금지 / 3 판정 불가). 변경률 하나만 보면 구조 편집이 안 보인다 —
|
|
155
|
+
변경률 2.8%인데 문장 3할이 갈려나간 사례가 있었다. **이 출력대로 판정하고, 자기가 낸 값으로
|
|
156
|
+
덮어쓰지 않는다.** 측면별 임계와 탐지 대상 룰은 `src/humanize/`에 있다.
|
|
157
|
+
|
|
158
|
+
## 실측 근거
|
|
159
|
+
|
|
160
|
+
감으로 올리고 내린 심각도는 다음 사람이 되돌린다. 바꿀 때는 근거를 남긴다.
|
|
161
|
+
|
|
162
|
+
### 대조 코퍼스 (A-2, I-1, C-8)
|
|
163
|
+
|
|
164
|
+
AI 생성 산문 60편(모델 3종)과 2022년 이전 발행이 확인된 한국어 산문 60편을 로그우도비 G²로
|
|
165
|
+
비교한 결과다.
|
|
166
|
+
|
|
167
|
+
| 룰 | 측정 | 조정 |
|
|
168
|
+
|---|---|---|
|
|
169
|
+
| A-2 "~를 통해" | 비번역 한국어 84.4 vs 번역문 42.1 — 원어민이 2배 더 쓴다 (최희경 2016). AI 코퍼스에는 거의 안 나옴 | S1 → S2, 대화·리뷰만 S1 |
|
|
170
|
+
| I-1 "~한 것이다" | AI 20.4 vs 인간 43.0 (G²=6.2) — 인간이 2배 더 씀. 단락 말 위치·연속 반복도 AI가 더 적음 | S1 → S2(연속 3회+), 대화·리뷰만 S1 |
|
|
171
|
+
| C-8 부정 대구 | AI 5.8 vs 인간 0.6 (9.2배, G²=41.7) — 개인 글 대비로는 18배. 모델 3종 공통 | S2 → S1 |
|
|
172
|
+
|
|
173
|
+
읽을 때 두 가지를 같이 봐야 한다. 인간 코퍼스가 **편집된 출판 산문**이라 "인간 일반"이 아니라
|
|
174
|
+
"잘 쓴 글"과의 대조다. 그리고 이 측정은 **문서 산문**을 잰 것이라 대화·리뷰 코멘트에는 그대로
|
|
175
|
+
적용되지 않는다 — A-2와 I-1이 대화에서 S1로 남는 이유가 이것이다.
|
|
176
|
+
출처: [`epoko77-ai/im-not-ai`](https://github.com/epoko77-ai/im-not-ai)의 `empirical-validation.md`.
|
|
177
|
+
|
|
178
|
+
### 팀 실제 사용 (B-4)
|
|
179
|
+
|
|
180
|
+
룰이 팀에서 이미 굳은 말을 금지하고 있으면 룰이 틀린 것이다. 산출물을 어색하게 만든다.
|
|
181
|
+
|
|
182
|
+
| 룰 | 확인한 것 | 조정 |
|
|
183
|
+
|---|---|---|
|
|
184
|
+
| B-4 "소비처" | WDS 티켓 894, 919, 921에서 섹션 헤딩과 본문으로 일관되게 쓴다. 작성자 본인 어휘다 | 교정 예시에서 제외 |
|
|
185
|
+
|
|
186
|
+
**교정어를 정하기 전에 팀이 실제로 뭐라고 쓰는지 먼저 본다.** 지라·PR·슬랙을 검색하면 답이 있다.
|
|
187
|
+
"인수조건"도 같은 방법으로 걷어냈다 — WDS 전체에 5건뿐이고 그중 넷이 2025년 9월 한 묶음,
|
|
188
|
+
나머지 하나는 이 에이전트가 직접 넣은 것이었다. 팀이 쓰는 말이 아니라 템플릿이 심은 말이었다.
|
|
189
|
+
|
|
139
190
|
## 등급 기준 (자가 채점)
|
|
140
191
|
|
|
141
192
|
- **A**: S1 잔존 0, S2 잔존 2 이하, 변경률 10~25%, 자체검증 7항 모두 통과
|