patina-cli 3.11.0 โ†’ 4.0.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.
Files changed (193) hide show
  1. package/.patina.default.yaml +29 -29
  2. package/CHANGELOG.md +53 -0
  3. package/NOTICE +21 -0
  4. package/README.md +117 -224
  5. package/README_JA.md +134 -77
  6. package/README_KR.md +132 -74
  7. package/README_ZH.md +137 -80
  8. package/SKILL.md +11 -20
  9. package/artifacts/rebaseline-2025/README.md +147 -0
  10. package/artifacts/rebaseline-2025/human-controls.public.jsonl +250 -0
  11. package/artifacts/rebaseline-2025/intake.example.jsonl +2 -0
  12. package/artifacts/rebaseline-2025/intake.local.example.jsonl +25 -0
  13. package/artifacts/rebaseline-2025/prompts.template.jsonl +7 -0
  14. package/artifacts/rebaseline-2025/sources.ko-public.jsonl +39 -0
  15. package/assets/brand/patina-badge.svg +18 -0
  16. package/assets/brand/patina-mark.svg +8 -0
  17. package/assets/demo/README.md +79 -0
  18. package/core/scoring.md +12 -12
  19. package/core/standalone-prompt.md +3 -1
  20. package/core/stylometry.md +93 -22
  21. package/docs/API.md +1554 -0
  22. package/docs/AUTHENTICATION.md +50 -26
  23. package/docs/AUTHENTICATION_KR.md +54 -29
  24. package/docs/BRANDING.md +9 -8
  25. package/docs/CLI.md +55 -14
  26. package/docs/COOKBOOK.md +8 -21
  27. package/docs/DEMO.md +32 -5
  28. package/docs/EXIT-CODES.md +2 -3
  29. package/docs/FALSE-POSITIVES.md +63 -0
  30. package/docs/FAQ.md +9 -1
  31. package/docs/FAQ_KR.md +3 -1
  32. package/docs/FLAG-PARITY.md +33 -47
  33. package/docs/ISSUE-WAVES.md +57 -0
  34. package/docs/PATTERNS-EN.md +67 -3
  35. package/docs/PATTERNS-JA.md +68 -2
  36. package/docs/PATTERNS-KO.md +70 -7
  37. package/docs/PATTERNS-ZH.md +67 -3
  38. package/docs/PATTERNS.md +5 -5
  39. package/docs/RESEARCH-DOCS-PLATFORM.md +54 -0
  40. package/docs/ROADMAP.md +46 -66
  41. package/docs/TRANSLATIONESE-KO.md +51 -0
  42. package/docs/audits/2026-05-deep-research.md +3 -1
  43. package/docs/benchmarks/README.md +51 -0
  44. package/docs/benchmarks/detector-comparison.json +69 -9
  45. package/docs/benchmarks/detector-comparison.md +10 -5
  46. package/docs/benchmarks/katfish-ko-latest.json +657 -0
  47. package/docs/benchmarks/katfish-ko-latest.md +77 -0
  48. package/docs/benchmarks/latest.json +1183 -108
  49. package/docs/benchmarks/latest.md +84 -60
  50. package/docs/benchmarks/lexicon-freshness-en-2026-05-22.json +1121 -0
  51. package/docs/benchmarks/lexicon-freshness-en-2026-05-22.md +136 -0
  52. package/docs/benchmarks/rebaseline-latest.json +381 -0
  53. package/docs/benchmarks/rebaseline-latest.md +121 -0
  54. package/docs/benchmarks/register-stratified-latest.json +164 -0
  55. package/docs/benchmarks/register-stratified-latest.md +99 -0
  56. package/docs/benchmarks/register-stratified.md +43 -0
  57. package/docs/integrations/github-action.md +44 -11
  58. package/docs/integrations/playground.md +58 -0
  59. package/docs/integrations/pre-commit.md +5 -5
  60. package/docs/integrations/release.md +5 -3
  61. package/docs/integrations/static-sites.md +83 -0
  62. package/docs/research/2025-rebaseline-plan.md +71 -2
  63. package/docs/research/2026-rebaseline.md +102 -0
  64. package/docs/research/adversarial-mps.md +41 -0
  65. package/docs/research/ai-human-metrics.md +35 -23
  66. package/docs/research/human-eval-panel.md +42 -0
  67. package/docs/research/judge-agreement.md +24 -0
  68. package/docs/research/ko-2025-corpus-sources.md +135 -0
  69. package/docs/research/lexicon-freshness-audit.md +64 -0
  70. package/docs/research/zh-ja-lexicon-calibration.md +60 -0
  71. package/docs/social/patina-launch-copy.md +173 -100
  72. package/docs/social/patina-launch-execution.md +94 -0
  73. package/docs/social/patina-launch-korean-first.md +83 -0
  74. package/docs/social/signs-of-ai-writing.md +26 -0
  75. package/docs/social/signs-of-ai-writing_KR.md +26 -0
  76. package/lexicon/ai-en.md +21 -24
  77. package/lexicon/ai-ja.md +158 -0
  78. package/lexicon/ai-ko.md +9 -9
  79. package/lexicon/ai-zh.md +158 -0
  80. package/lexicon/provenance/ai-en.json +970 -0
  81. package/lexicon/provenance/ai-ja.json +542 -0
  82. package/lexicon/provenance/ai-ko.json +866 -0
  83. package/lexicon/provenance/ai-zh.json +542 -0
  84. package/package.json +49 -8
  85. package/patterns/en-communication.md +5 -0
  86. package/patterns/en-content.md +5 -0
  87. package/patterns/en-filler.md +5 -0
  88. package/patterns/en-language.md +29 -1
  89. package/patterns/en-structure.md +5 -0
  90. package/patterns/en-style.md +5 -0
  91. package/patterns/en-viral-hook.md +42 -2
  92. package/patterns/ja-communication.md +5 -0
  93. package/patterns/ja-content.md +5 -0
  94. package/patterns/ja-filler.md +5 -0
  95. package/patterns/ja-language.md +33 -1
  96. package/patterns/ja-structure.md +12 -0
  97. package/patterns/ja-style.md +5 -0
  98. package/patterns/ja-viral-hook.md +41 -2
  99. package/patterns/ko-communication.md +5 -0
  100. package/patterns/ko-content.md +5 -0
  101. package/patterns/ko-filler.md +5 -0
  102. package/patterns/ko-language.md +33 -1
  103. package/patterns/ko-structure.md +25 -6
  104. package/patterns/ko-style.md +5 -0
  105. package/patterns/ko-viral-hook.md +38 -2
  106. package/patterns/zh-communication.md +5 -0
  107. package/patterns/zh-content.md +5 -0
  108. package/patterns/zh-filler.md +5 -0
  109. package/patterns/zh-language.md +37 -1
  110. package/patterns/zh-structure.md +12 -0
  111. package/patterns/zh-style.md +5 -0
  112. package/patterns/zh-viral-hook.md +38 -2
  113. package/playground/README.md +55 -0
  114. package/playground/analytics.js +4 -0
  115. package/playground/analyzer.js +883 -0
  116. package/playground/app.js +157 -0
  117. package/playground/data/lexicons.js +343 -0
  118. package/playground/index.html +138 -0
  119. package/playground/styles.css +267 -0
  120. package/profiles/namuwiki.md +111 -0
  121. package/scripts/adversarial-mps-report.mjs +201 -0
  122. package/scripts/badge-json.mjs +79 -0
  123. package/scripts/benchmark-report.mjs +56 -9
  124. package/scripts/check-release-metadata.mjs +0 -2
  125. package/scripts/detector-comparison.mjs +7 -7
  126. package/scripts/generate-playground-data.mjs +77 -0
  127. package/scripts/katfish-calibration.mjs +464 -0
  128. package/scripts/lexicon-freshness.mjs +485 -0
  129. package/scripts/lint.mjs +1 -1
  130. package/scripts/precommit-score.mjs +4 -3
  131. package/scripts/prose-score.mjs +81 -5
  132. package/scripts/rebaseline-intake.mjs +242 -0
  133. package/scripts/rebaseline-score.mjs +268 -0
  134. package/scripts/rebaseline-summary.mjs +773 -0
  135. package/scripts/rebaseline-web-collect.mjs +410 -0
  136. package/scripts/update-benchmark-ranges.mjs +1 -0
  137. package/src/api.js +69 -105
  138. package/src/auth.js +50 -2
  139. package/src/backends/claude-cli.js +19 -4
  140. package/src/backends/codex-cli.js +19 -3
  141. package/src/backends/contract.js +230 -1
  142. package/src/backends/gemini-cli.js +18 -5
  143. package/src/backends/index.js +87 -12
  144. package/src/backends/kimi-cli.js +161 -0
  145. package/src/cli.js +577 -567
  146. package/src/commands/doctor.js +2 -2
  147. package/src/config.js +29 -0
  148. package/src/errors.js +53 -1
  149. package/src/features/discourse-tells.js +68 -0
  150. package/src/features/index.js +82 -8
  151. package/src/features/lexicon.js +40 -6
  152. package/src/features/markup-leakage.js +69 -0
  153. package/src/features/segment.js +41 -0
  154. package/src/features/signal-strength.js +81 -0
  155. package/src/features/stylometry.js +231 -1
  156. package/src/features/translationese.js +127 -0
  157. package/src/loader.js +76 -0
  158. package/src/logger.js +22 -23
  159. package/src/model-defaults.js +55 -0
  160. package/src/ouroboros.js +31 -0
  161. package/src/output.js +102 -90
  162. package/src/prompt-builder.js +103 -68
  163. package/src/providers.js +51 -4
  164. package/src/scoring.js +210 -2
  165. package/src/security.js +75 -0
  166. package/tests/fixtures/live-quality/en/public-docs-01.md +26 -0
  167. package/tests/fixtures/live-quality/ko/public-docs-01.md +26 -0
  168. package/tests/fixtures/suspect-zones/expected-ranges.json +207 -16
  169. package/tests/fixtures/suspect-zones/ja/ai/ja-ai-04-lexicon.md +11 -0
  170. package/tests/fixtures/suspect-zones/ja/natural/ja-nat-04-lexicon-cold.md +11 -0
  171. package/tests/fixtures/suspect-zones/ko/ai/ko-ai-02.md +4 -5
  172. package/tests/fixtures/suspect-zones/ko/ai/ko-ai-07-ko-diagnostic.md +11 -0
  173. package/tests/fixtures/suspect-zones/zh/ai/zh-ai-04-lexicon.md +11 -0
  174. package/tests/fixtures/suspect-zones/zh/natural/zh-nat-04-lexicon-cold.md +11 -0
  175. package/tests/quality/README.md +188 -11
  176. package/tests/quality/adversarial-mps/fixtures.jsonl +10 -0
  177. package/tests/quality/benchmark.mjs +39 -1
  178. package/tests/quality/dogfood.mjs +5 -3
  179. package/tests/quality/live-fixtures.jsonl +2 -0
  180. package/tests/quality/live-quality.mjs +596 -0
  181. package/tests/quality/ranking-metrics.mjs +136 -0
  182. package/tests/quality/rebaseline-manifest.example.jsonl +5 -0
  183. package/vercel.json +53 -0
  184. package/SKILL-MAX.md +0 -455
  185. package/docs/internal/HARNESS.md +0 -14
  186. package/docs/internal/README.md +0 -14
  187. package/docs/internal/WARP.md +0 -23
  188. package/patina-max/SKILL.md +0 -523
  189. package/patina-max/composite.py +0 -457
  190. package/src/cache.js +0 -106
  191. package/src/commands/init.js +0 -208
  192. package/src/manifest.js +0 -162
  193. package/src/max-mode.js +0 -207
@@ -0,0 +1,136 @@
1
+ export function summarizeRanking(records = []) {
2
+ const normalized = records
3
+ .map((record) => ({
4
+ score: Number(record.score),
5
+ expected: Boolean(record.expected),
6
+ }))
7
+ .filter((record) => Number.isFinite(record.score));
8
+
9
+ const positives = normalized.filter((record) => record.expected).length;
10
+ const negatives = normalized.length - positives;
11
+ const sweep = thresholdSweep(normalized);
12
+ const best = bestF1Threshold(sweep);
13
+
14
+ return {
15
+ n: normalized.length,
16
+ positives,
17
+ negatives,
18
+ scoreRange: scoreRange(normalized),
19
+ roc_auc: round(rocAuc(normalized)),
20
+ pr_auc: round(averagePrecision(normalized)),
21
+ bestF1: best,
22
+ sweep,
23
+ };
24
+ }
25
+
26
+ export function thresholdSweep(records = []) {
27
+ const thresholds = thresholdCandidates(records);
28
+ return thresholds.map((threshold) => {
29
+ const metrics = emptyMetrics();
30
+ for (const record of records) {
31
+ updateMetrics(metrics, record.score >= threshold, record.expected);
32
+ }
33
+ return { threshold, ...summarize(metrics) };
34
+ });
35
+ }
36
+
37
+ export function bestF1Threshold(rows = []) {
38
+ if (!rows.length) return null;
39
+ return [...rows].sort((a, b) =>
40
+ b.f1 - a.f1 ||
41
+ b.recall - a.recall ||
42
+ b.precision - a.precision ||
43
+ b.accuracy - a.accuracy ||
44
+ a.threshold - b.threshold
45
+ )[0];
46
+ }
47
+
48
+ export function rocAuc(records = []) {
49
+ const positives = records.filter((record) => record.expected);
50
+ const negatives = records.filter((record) => !record.expected);
51
+ if (!positives.length || !negatives.length) return null;
52
+
53
+ let wins = 0;
54
+ for (const positive of positives) {
55
+ for (const negative of negatives) {
56
+ if (positive.score > negative.score) wins += 1;
57
+ else if (positive.score === negative.score) wins += 0.5;
58
+ }
59
+ }
60
+ return wins / (positives.length * negatives.length);
61
+ }
62
+
63
+ export function averagePrecision(records = []) {
64
+ const positives = records.filter((record) => record.expected).length;
65
+ if (!positives) return null;
66
+
67
+ const groups = scoreGroups(records);
68
+ let seen = 0;
69
+ let truePositives = 0;
70
+ let area = 0;
71
+
72
+ for (const group of groups) {
73
+ seen += group.total;
74
+ truePositives += group.positives;
75
+ if (group.positives) area += group.positives * (truePositives / seen);
76
+ }
77
+ return area / positives;
78
+ }
79
+
80
+ function thresholdCandidates(records) {
81
+ const uniqueScores = [...new Set(records.map((record) => record.score))]
82
+ .filter((score) => Number.isFinite(score))
83
+ .sort((a, b) => a - b);
84
+ return [...new Set([0, ...uniqueScores, 101])].sort((a, b) => a - b);
85
+ }
86
+
87
+ function scoreGroups(records) {
88
+ const map = new Map();
89
+ for (const record of records) {
90
+ const group = map.get(record.score) || { score: record.score, total: 0, positives: 0 };
91
+ group.total += 1;
92
+ if (record.expected) group.positives += 1;
93
+ map.set(record.score, group);
94
+ }
95
+ return [...map.values()].sort((a, b) => b.score - a.score);
96
+ }
97
+
98
+ function scoreRange(records) {
99
+ if (!records.length) return { min: 0, max: 0 };
100
+ const scores = records.map((record) => record.score);
101
+ return {
102
+ min: round(Math.min(...scores)),
103
+ max: round(Math.max(...scores)),
104
+ };
105
+ }
106
+
107
+ function emptyMetrics() {
108
+ return { tp: 0, fp: 0, fn: 0, tn: 0, total: 0 };
109
+ }
110
+
111
+ function updateMetrics(metrics, predicted, expected) {
112
+ metrics.total += 1;
113
+ if (predicted && expected) metrics.tp += 1;
114
+ else if (predicted && !expected) metrics.fp += 1;
115
+ else if (!predicted && expected) metrics.fn += 1;
116
+ else metrics.tn += 1;
117
+ }
118
+
119
+ function summarize(metrics) {
120
+ const accuracy = metrics.total ? (metrics.tp + metrics.tn) / metrics.total : 0;
121
+ const precision = metrics.tp + metrics.fp ? metrics.tp / (metrics.tp + metrics.fp) : 0;
122
+ const recall = metrics.tp + metrics.fn ? metrics.tp / (metrics.tp + metrics.fn) : 0;
123
+ const f1 = precision + recall ? (2 * precision * recall) / (precision + recall) : 0;
124
+ return {
125
+ ...metrics,
126
+ accuracy: round(accuracy),
127
+ precision: round(precision),
128
+ recall: round(recall),
129
+ f1: round(f1),
130
+ };
131
+ }
132
+
133
+ function round(value, digits = 3) {
134
+ if (value === null || value === undefined) return null;
135
+ return Math.round(value * 10 ** digits) / 10 ** digits;
136
+ }
@@ -0,0 +1,5 @@
1
+ {"sample_id":"en-gpt-blog-ai-001","language":"en","class":"ai-like","register":"blog","model_family":"gpt-family","provider":"fixture","model":"fixture-gpt-family-2026-05","generated_at":"2026-05-21","prompt_id":"rb25-blog-launch-001","decoding":{"temperature":0.7,"top_p":0.9},"postprocess":{"editing_pass":"none"},"redistribution":"repo-ok","text":"This launch guide provides a seamless framework for teams that want to unlock clearer messaging across every touchpoint.","text_hash":"sha256:702cc81afca0e662268a2f4802622a707a2f1fddb10c5d61ef6af8159b0b26b3"}
2
+ {"sample_id":"ko-human-productdoc-001","language":"ko","class":"natural-human","register":"product-doc","model_family":"human-reference","provider":"fixture","model":"maintainer-written-reference","generated_at":"2026-05-21","prompt_id":"rb25-product-doc-001","decoding":"not-applicable","postprocess":{"editing_pass":"maintainer copyedit"},"redistribution":"repo-ok","text":"์„ค์ • ํŒŒ์ผ์„ ๋จผ์ € ์ฝ๊ณ , ๊ฐ’์ด ์—†์„ ๋•Œ๋งŒ ๊ธฐ๋ณธ๊ฐ’์„ ์‚ฌ์šฉํ•œ๋‹ค. ๊ทธ๋ž˜์„œ ํŒ€๋ณ„ ์˜ˆ์™ธ๋ฅผ ๋ฌธ์„œ์— ๋‚จ๊ธฐ๊ธฐ ์‰ฝ๋‹ค.","text_hash":"sha256:7e7edd6e2cfab5a33c0f278ac24f121174d93a92211d150f659429bcba795ddf"}
3
+ {"sample_id":"ja-gemini-technical-lightedit-001","language":"ja","class":"lightly-edited-ai","register":"technical-how-to","model_family":"gemini-family","provider":"manual-vendor-copy","model":"gemini-family-redacted","generated_at":"2026-05-21","prompt_id":"rb25-howto-cache-001","decoding":{"temperature":0.4},"postprocess":{"editing_pass":"light grammar pass"},"redistribution":"metadata-only","text_hash":"sha256:e6ac1a38435c63987b4ad2e15b6acd1e05c09408cb1ae4ec73d0a9bca2d02c6d"}
4
+ {"sample_id":"zh-openweight-academic-heavyedit-001","language":"zh","class":"heavily-edited-ai","register":"academic-summary","model_family":"open-weight","provider":"local-fixture","model":"open-weight-redacted","generated_at":"2026-05-21","prompt_id":"rb25-academic-energy-001","decoding":{"temperature":0.2,"top_p":0.8},"postprocess":{"editing_pass":"heavy human edit"},"redistribution":"no-redistribution","text_hash":"sha256:22b2556861f48baf29f880ac04aedf885b862b5460ce4b24985413f7e94e94ed"}
5
+ {"sample_id":"en-claude-chat-ai-001","language":"en","class":"ai-like","register":"chat-update","model_family":"claude-family","provider":"fixture","model":"fixture-claude-family-2026-05","generated_at":"2026-05-21","prompt_id":"rb25-chat-update-001","decoding":{"temperature":0.5},"postprocess":{"editing_pass":"none"},"redistribution":"repo-ok","text":"Here is a concise update on the initiative: the team aligned on priorities, improved execution, and created a scalable path forward.","text_hash":"sha256:df2076d0f8d458da8bc132ae1d9c86ddae9e92e2f3832a06d405d8dd0d219a4d"}
package/vercel.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "cleanUrls": true,
3
+ "trailingSlash": false,
4
+ "rewrites": [
5
+ {
6
+ "source": "/",
7
+ "destination": "/playground"
8
+ },
9
+ {
10
+ "source": "/app.js",
11
+ "destination": "/playground/app.js"
12
+ },
13
+ {
14
+ "source": "/analytics.js",
15
+ "destination": "/playground/analytics.js"
16
+ },
17
+ {
18
+ "source": "/analyzer.js",
19
+ "destination": "/playground/analyzer.js"
20
+ },
21
+ {
22
+ "source": "/styles.css",
23
+ "destination": "/playground/styles.css"
24
+ },
25
+ {
26
+ "source": "/data/:path*",
27
+ "destination": "/playground/data/:path*"
28
+ }
29
+ ],
30
+ "headers": [
31
+ {
32
+ "source": "/(.*)",
33
+ "headers": [
34
+ {
35
+ "key": "X-Content-Type-Options",
36
+ "value": "nosniff"
37
+ },
38
+ {
39
+ "key": "Referrer-Policy",
40
+ "value": "strict-origin-when-cross-origin"
41
+ },
42
+ {
43
+ "key": "Permissions-Policy",
44
+ "value": "clipboard-write=(self), camera=(), microphone=(), geolocation=()"
45
+ },
46
+ {
47
+ "key": "Content-Security-Policy",
48
+ "value": "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self'; base-uri 'self'; form-action 'none'; frame-ancestors 'none'"
49
+ }
50
+ ]
51
+ }
52
+ ]
53
+ }
package/SKILL-MAX.md DELETED
@@ -1,455 +0,0 @@
1
- ---
2
- name: patina-max
3
- version: 3.11.0
4
- description: |
5
- Multi-model humanization. Runs the same humanization task on multiple
6
- local model CLIs, scores each result, and selects the best
7
- output by lowest AI score among candidates that pass the MPS
8
- meaning-preservation floor. Uses `claude -p` / `gemini -p` for
9
- Claude/Gemini and `codex exec` for Codex.
10
- allowed-tools:
11
- - Read
12
- - Write
13
- - Edit
14
- - Grep
15
- - Glob
16
- - Bash
17
- - AskUserQuestion
18
- ---
19
-
20
- # patina MAX: ๋ฉ€ํ‹ฐ๋ชจ๋ธ Best-of-N Rewriter
21
-
22
- ๋‹น์‹ ์€ ์—ฌ๋Ÿฌ AI ๋ชจ๋ธ์„ ๋™์‹œ์— ์‚ฌ์šฉํ•˜์—ฌ ํ…์ŠคํŠธ๋ฅผ humanizeํ•˜๊ณ , ๊ฐ€์žฅ ์ž์—ฐ์Šค๋Ÿฌ์šด ๊ฒฐ๊ณผ๋ฅผ ์ž๋™ ์„ ํƒํ•˜๋Š” ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ดํ„ฐ์ž…๋‹ˆ๋‹ค. ๊ฐ ๋ชจ๋ธ์ด ๊ฐ™์€ ํ…์ŠคํŠธ๋ฅผ humanizeํ•œ ๋’ค, ์˜๋ฏธ ๋ณด์กด ๊ธฐ์ค€(MPS โ‰ฅ 70)์„ ํ†ต๊ณผํ•œ ๊ฒฐ๊ณผ ์ค‘ AI ์œ ์‚ฌ๋„ ์ ์ˆ˜๊ฐ€ ๊ฐ€์žฅ ๋‚ฎ์€ ๊ฒƒ์„ ์ตœ์ข…๋ณธ์œผ๋กœ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค.
23
-
24
- > **์ „์ œ ์กฐ๊ฑด:**
25
- > - **oh-my-claudecode (OMC)** ๊ฐ€ ์„ค์น˜๋˜์–ด ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค (`dispatch: omc` ๋ณ‘๋ ฌ ๋ชจ๋“œ)
26
- > - **tmux** ์„ธ์…˜ ์•ˆ์—์„œ ์‹คํ–‰ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค (`dispatch: omc` ๋ณ‘๋ ฌ ๋ชจ๋“œ, `$TMUX` ํ™˜๊ฒฝ๋ณ€์ˆ˜ ํ™•์ธ)
27
- > - ์„ ํƒํ•œ ๋ชจ๋ธ์˜ ๋กœ์ปฌ CLI๊ฐ€ ์„ค์น˜๋˜์–ด ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค (`claude`, `gemini`, `codex`)
28
- > - `dispatch: direct` ๋ชจ๋“œ์—์„œ๋Š” OMC/tmux ์—†์ด๋„ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค (์ˆœ์ฐจ ์‹คํ–‰, tmux fallback ๊ฒฝ๊ณ  ์—†์Œ)
29
-
30
- ---
31
-
32
- ## 1๋‹จ๊ณ„: ์„ค์ • ๋กœ๋“œ
33
-
34
- `.patina.default.yaml`์„ ์ฝ์–ด ์„ค์ •์„ ๋กœ๋“œํ•œ๋‹ค.
35
-
36
- ```
37
- Glob .patina.default.yaml โ†’ Read
38
- ```
39
-
40
- ์„ค์ •์—์„œ ๋‹ค์Œ์„ ํ™•์ธ:
41
- - `profile`: ์‚ฌ์šฉํ•  ํ”„๋กœํ•„ (๊ธฐ๋ณธ: `default`)
42
- - `patterns`: ๋กœ๋“œํ•  ํŒจํ„ด ํŒฉ ๋ชฉ๋ก
43
- - `skip-patterns`: ๊ฑด๋„ˆ๋›ธ ํŒจํ„ด ํŒฉ
44
- - `blocklist`: ์ถ”๊ฐ€ ๊ฐ์ง€ ์–ดํœ˜
45
- - `allowlist`: ๊ฐ์ง€ ์ œ์™ธ ์–ดํœ˜
46
- - `max-models`: MAX ๋ชจ๋“œ์—์„œ ์‚ฌ์šฉํ•  ๋ชจ๋ธ ๋ชฉ๋ก (๊ธฐ๋ณธ: `[claude, gemini]`)
47
- - `dispatch`: ๋””์ŠคํŒจ์น˜ ๋ชจ๋“œ (`omc` ๋˜๋Š” `direct`, ๊ธฐ๋ณธ: `omc`)
48
-
49
- `$ARGUMENTS`์—์„œ ์˜ต์…˜์„ ํŒŒ์‹ฑํ•˜์—ฌ ์„ค์ •์„ ์˜ค๋ฒ„๋ผ์ด๋“œ:
50
- - `--profile <name>`: ํ”„๋กœํ•„ ๋ณ€๊ฒฝ
51
- - `--lang <code>`: ์ฒ˜๋ฆฌ ์–ธ์–ด ๋ณ€๊ฒฝ (ko, en, zh, ja)
52
- - `--models <list>`: ์‚ฌ์šฉํ•  ๋ชจ๋ธ ๋ณ€๊ฒฝ (์‰ผํ‘œ ๊ตฌ๋ถ„, ์˜ˆ: `claude,gemini,codex`)
53
- - `--dispatch <mode>`: ๋””์ŠคํŒจ์น˜ ๋ชจ๋“œ ๋ณ€๊ฒฝ (`omc` ๋˜๋Š” `direct`)
54
-
55
- ๋ชจ๋ธ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ: ์š”์ฒญ๋œ ๋ชจ๋“  ๋ชจ๋ธ์ด ์ง€์› ๋ชฉ๋ก (`claude`, `codex`, `gemini`) ์•ˆ์— ์žˆ๋Š”์ง€ ํ™•์ธํ•œ๋‹ค. ์ง€์›ํ•˜์ง€ ์•Š๋Š” ๋ชจ๋ธ์ด ์žˆ์œผ๋ฉด ๊ฒฝ๊ณ ๋ฅผ ํ‘œ์‹œํ•˜๊ณ  ํ•ด๋‹น ๋ชจ๋ธ์„ ๊ฑด๋„ˆ๋›ด๋‹ค.
56
-
57
- ### ์„ค์น˜ ์ธํ„ฐ๋ทฐ (์ตœ์ดˆ ์‹คํ–‰)
58
-
59
- `.patina.yaml` (์‚ฌ์šฉ์ž ์„ค์ • ํŒŒ์ผ)์ด ํ”„๋กœ์ ํŠธ ๋ฃจํŠธ์— ์—†์œผ๋ฉด ์ตœ์ดˆ ์‹คํ–‰์œผ๋กœ ๊ฐ„์ฃผํ•˜๊ณ  `AskUserQuestion`์œผ๋กœ ์ธํ„ฐ๋ทฐ๋ฅผ ์ง„ํ–‰ํ•œ๋‹ค:
60
-
61
- 1. **๋ชจ๋ธ ์„ ํƒ**: "MAX mode์—์„œ ์‚ฌ์šฉํ•  ๋ชจ๋ธ์„ ์„ ํƒํ•˜์„ธ์š” (์‰ผํ‘œ ๊ตฌ๋ถ„): claude, gemini, codex"
62
- - ๊ธฐ๋ณธ๊ฐ’: `claude, gemini`
63
- - ์‘๋‹ต์„ `max-models` ๋ชฉ๋ก์œผ๋กœ ์ €์žฅ
64
-
65
- 2. **๋””์ŠคํŒจ์น˜ ๋ชจ๋“œ**: "๋””์ŠคํŒจ์น˜ ๋ชจ๋“œ๋ฅผ ์„ ํƒํ•˜์„ธ์š”:"
66
- - `omc` โ€” tmux pane ๋ณ‘๋ ฌ ์‹คํ–‰ (์ถ”์ฒœ, OMC + tmux ํ•„์š”)
67
- - `direct` โ€” stdin pipe ์ˆœ์ฐจ ์‹คํ–‰ (์˜์กด์„ฑ ์—†์Œ)
68
- - ๊ธฐ๋ณธ๊ฐ’: `omc`
69
-
70
- 3. **CLI ์„ค์น˜ ํ™•์ธ**: ์„ ํƒ๋œ ๊ฐ ๋ชจ๋ธ์˜ CLI๋ฅผ `which` ๋ช…๋ น์œผ๋กœ ํ™•์ธํ•œ๋‹ค
71
- - ๋ฏธ์„ค์น˜ ๋ชจ๋ธ์ด ์žˆ์œผ๋ฉด ์„ค์น˜ ์•ˆ๋‚ด๋ฅผ ํ‘œ์‹œ:
72
- - `claude`: `npm install -g @anthropic-ai/claude-code`
73
- - `gemini`: `npm install -g @google/gemini-cli`
74
- - `codex`: `npm install -g @openai/codex`
75
- - ๋ฏธ์„ค์น˜ ๋ชจ๋ธ์ด ์žˆ์–ด๋„ ์„ค์ •์€ ์ €์žฅํ•œ๋‹ค (๋‚˜์ค‘์— ์„ค์น˜ ๊ฐ€๋Šฅ)
76
-
77
- 4. **OMC/tmux ํ™•์ธ** (`dispatch: omc` ์„ ํƒ ์‹œ):
78
- - `$TMUX` ํ™˜๊ฒฝ๋ณ€์ˆ˜๋กœ tmux ์„ธ์…˜ ์—ฌ๋ถ€ ํ™•์ธ
79
- - tmux๊ฐ€ ์•„๋‹ˆ๋ฉด ์•„๋ž˜์˜ ์ •ํ™•ํ•œ ๊ฒฝ๊ณ ๋ฅผ stderr์— ์ถœ๋ ฅํ•˜๊ณ  `dispatch: direct`๋กœ ์ž๋™ ์ „ํ™˜:
80
- `[patina-max] tmux not detected. Falling back to sequential dispatch (expect ~Xmin instead of ~Ys). Pass --dispatch direct to silence.`
81
- - ์‚ฌ์šฉ์ž๊ฐ€ ๋ช…์‹œ์ ์œผ๋กœ `--dispatch direct`๋ฅผ ์„ ํƒํ•œ ๊ฒฝ์šฐ์—๋Š” ๊ฒฝ๊ณ ๋ฅผ ์ถœ๋ ฅํ•˜์ง€ ์•Š๋Š”๋‹ค
82
-
83
- ์ธํ„ฐ๋ทฐ ๊ฒฐ๊ณผ๋ฅผ `.patina.yaml`์— ์ €์žฅํ•œ๋‹ค:
84
-
85
- ```yaml
86
- # Generated by patina-max setup interview
87
- max-models:
88
- - claude
89
- - gemini
90
- dispatch: omc
91
- ```
92
-
93
- ์ดํ›„ ์‹คํ–‰์—์„œ๋Š” `.patina.yaml`์ด ์กด์žฌํ•˜๋ฉด ์ธํ„ฐ๋ทฐ๋ฅผ ๊ฑด๋„ˆ๋›ฐ๊ณ  ๋ฐ”๋กœ ์„ค์ •์„ ๋กœ๋“œํ•œ๋‹ค. `.patina.default.yaml`๋ณด๋‹ค `.patina.yaml`์˜ ๊ฐ’์ด ์šฐ์„ ํ•œ๋‹ค.
94
-
95
- ---
96
-
97
- ## 2๋‹จ๊ณ„: ํŒจํ„ด ํŒฉ ๋กœ๋“œ
98
-
99
- 1๋‹จ๊ณ„์—์„œ ๊ฒฐ์ •๋œ ์–ธ์–ด ์ฝ”๋“œ๋ฅผ `{lang}`์œผ๋กœ ์‚ฌ์šฉํ•˜์—ฌ ํŒจํ„ด ํŒฉ์„ ์ž๋™ ํƒ์ƒ‰ํ•œ๋‹ค.
100
-
101
- ```
102
- Glob patterns/{lang}-*.md โ†’ Read ๊ฐ ํŒŒ์ผ
103
- Glob custom/patterns/{lang}-*.md โ†’ Read (์‚ฌ์šฉ์ž ์ปค์Šคํ…€ ํŒจํ„ด ์ถ”๊ฐ€ ๋กœ๋“œ)
104
- ```
105
-
106
- `skip-patterns`์— ํ•ด๋‹นํ•˜๋Š” ํŒฉ์€ ์ œ์™ธํ•œ๋‹ค.
107
-
108
- ๋ชจ๋“  ํŒจํ„ด ํŒฉ์˜ ์ „์ฒด ๋‚ด์šฉ์„ ๋ฉ”๋ชจ๋ฆฌ์— ๋ณด๊ด€ํ•œ๋‹ค โ€” 4๋‹จ๊ณ„์—์„œ ์›Œ์ปค ํ”„๋กฌํ”„ํŠธ์— ์ธ๋ผ์ธํ•ด์•ผ ํ•œ๋‹ค.
109
-
110
- ---
111
-
112
- ## 3๋‹จ๊ณ„: ํ”„๋กœํ•„ + ๋ชฉ์†Œ๋ฆฌ ์ง€์นจ ๋กœ๋“œ
113
-
114
- ```
115
- Read profiles/{profile}.md
116
- Glob custom/profiles/{profile}.md โ†’ Read (์ปค์Šคํ…€ ํ”„๋กœํ•„ ์šฐ์„ )
117
- Read core/voice.md
118
- ```
119
-
120
- ํ”„๋กœํ•„๊ณผ voice.md์˜ ์ „์ฒด ๋‚ด์šฉ์„ ๋ฉ”๋ชจ๋ฆฌ์— ๋ณด๊ด€ํ•œ๋‹ค โ€” 4๋‹จ๊ณ„์—์„œ ์›Œ์ปค ํ”„๋กฌํ”„ํŠธ์— ์ธ๋ผ์ธํ•ด์•ผ ํ•œ๋‹ค.
121
-
122
- ---
123
-
124
- ## 4๋‹จ๊ณ„: ์›Œ์ปค ํ”„๋กฌํ”„ํŠธ ๊ตฌ์„ฑ
125
-
126
- ๋ชจ๋“  ๋ชจ๋ธ์— ๋ณด๋‚ผ **ํ•˜๋‚˜์˜ ์ž๋ฆฝํ˜• ํ”„๋กฌํ”„ํŠธ**๋ฅผ ๊ตฌ์„ฑํ•œ๋‹ค. ์ด ํ”„๋กฌํ”„ํŠธ๋Š” ๋ชจ๋ธ์— ๋ฌด๊ด€ํ•˜๊ฒŒ ๋™์ž‘ํ•ด์•ผ ํ•œ๋‹ค.
127
-
128
- ํ”„๋กฌํ”„ํŠธ ๊ตฌ์กฐ:
129
-
130
- ```
131
- ๋‹น์‹ ์€ AI๊ฐ€ ์ƒ์„ฑํ•œ ํ…์ŠคํŠธ์—์„œ AI ํŠน์œ ์˜ ํŒจํ„ด์„ ์ฐพ์•„ ์ œ๊ฑฐํ•˜์—ฌ, ๊ธ€์„ ์ž์—ฐ์Šค๋Ÿฝ๊ณ  ์‚ฌ๋žŒ์ด ์“ด ๊ฒƒ์ฒ˜๋Ÿผ ๋งŒ๋“œ๋Š” ํŽธ์ง‘์ž์ž…๋‹ˆ๋‹ค.
132
-
133
- ## ํŒจํ„ด ํŒฉ
134
- {2๋‹จ๊ณ„์—์„œ ๋กœ๋“œํ•œ ๋ชจ๋“  ํŒจํ„ด ํŒฉ์˜ ์ „์ฒด ๋‚ด์šฉ์„ ์—ฌ๊ธฐ์— ์ธ๋ผ์ธ}
135
-
136
- ## ํ”„๋กœํ•„ ์ง€์นจ
137
- {3๋‹จ๊ณ„์—์„œ ๋กœ๋“œํ•œ ํ”„๋กœํ•„ ๋‚ด์šฉ์„ ์—ฌ๊ธฐ์— ์ธ๋ผ์ธ}
138
-
139
- ## ๋ชฉ์†Œ๋ฆฌ ์ง€์นจ
140
- {3๋‹จ๊ณ„์—์„œ ๋กœ๋“œํ•œ voice.md ๋‚ด์šฉ์„ ์—ฌ๊ธฐ์— ์ธ๋ผ์ธ}
141
-
142
- ## ์ฒ˜๋ฆฌ ์ง€์นจ
143
- 1. ์œ„์˜ ํŒจํ„ด ํŒฉ์—์„œ ์ •์˜๋œ ๋ชจ๋“  AI ํŒจํ„ด์„ ์ž…๋ ฅ ํ…์ŠคํŠธ์—์„œ ์Šค์บ”ํ•˜๋ผ
144
- 2. ๋ฐœ๊ฒฌ๋œ AI ํŒจํ„ด์„ ์ž์—ฐ์Šค๋Ÿฌ์šด ๋Œ€์•ˆ์œผ๋กœ ๊ต์ฒดํ•˜๋ผ
145
- 3. ํ•ต์‹ฌ ๋ฉ”์‹œ์ง€์™€ ์˜๋ฏธ๋ฅผ ๋ณด์กดํ•˜๋ผ
146
- 4. ํ”„๋กœํ•„์˜ ์–ด์กฐ ์ง€์นจ์— ๋”ฐ๋ผ ํ†ค์„ ์กฐ์ ˆํ•˜๋ผ
147
- 5. ๋ชฉ์†Œ๋ฆฌ ์ง€์นจ์— ๋”ฐ๋ผ ์‹ค์ œ ์‚ฌ๋žŒ์˜ ๊ฐœ์„ฑ์„ ๋ถˆ์–ด๋„ฃ์–ด๋ผ
148
-
149
- ## ์ž…๋ ฅ ํ…์ŠคํŠธ
150
- {์‚ฌ์šฉ์ž๊ฐ€ ์ œ๊ณตํ•œ ์›๋ฌธ ํ…์ŠคํŠธ}
151
-
152
- ## ์ค‘์š” ์ œ์•ฝ
153
- Humanize this text by removing the AI patterns described above.
154
- Output ONLY the humanized text.
155
- Do not use any tools. Do not read or modify any files.
156
- Do not ask questions. Do not include explanations or metadata.
157
- ```
158
-
159
- ์ด ํ”„๋กฌํ”„ํŠธ๋ฅผ ์‹คํ–‰๋งˆ๋‹ค ๊ณ ์œ ํ•œ ์ž„์‹œ ๋””๋ ‰ํ„ฐ๋ฆฌ์— ์ €์žฅํ•œ๋‹ค:
160
-
161
- ```bash
162
- RUN_DIR="$(mktemp -d /tmp/patina-max.XXXXXX)"
163
- PROMPT_FILE="$RUN_DIR/prompt.txt"
164
- TIMEOUT_SECONDS=900
165
- ```
166
-
167
- ์ดํ›„ ์˜ˆ์‹œ์˜ `$RUN_DIR`/`$PROMPT_FILE`์€ ์ด ์‹คํ–‰์—์„œ ์ƒ์„ฑํ•œ ๊ฒฝ๋กœ๋ฅผ ๋œปํ•œ๋‹ค. ์ตœ์ข… ์ถœ๋ ฅ์ด ๋๋‚  ๋•Œ๊นŒ์ง€ ์ด ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ ์œ ์ง€ํ•˜๊ณ , ๋งˆ์ง€๋ง‰์— ์ •๋ฆฌํ•œ๋‹ค.
168
-
169
- ```
170
- Write tool โ†’ $PROMPT_FILE
171
- ```
172
-
173
- ---
174
-
175
- ## 5๋‹จ๊ณ„: ๋ชจ๋ธ ๋””์ŠคํŒจ์น˜ (dispatch-aware)
176
-
177
- ํ”„๋กฌํ”„ํŠธ๋Š” ์ด๋ฏธ `$PROMPT_FILE`์— ์ €์žฅ๋˜์–ด ์žˆ๋‹ค (4๋‹จ๊ณ„).
178
- Claude/Codex๋Š” **stdin pipe**, Gemini๋Š” **`$(cat file)` ์ธ์ž ์น˜ํ™˜**์œผ๋กœ ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ „๋‹ฌํ•œ๋‹ค.
179
- ํ”„๋กฌํ”„ํŠธ๋Š” ํŒŒ์ผ์—์„œ ์ฝ์œผ๋ฏ€๋กœ ์…ธ ๋ณ€์ˆ˜ ํ™•์žฅ ์—†์ด ์•ˆ์ „ํ•˜๊ฒŒ ์ „๋‹ฌ๋œ๋‹ค.
180
-
181
- - Claude: `claude -p`
182
- - Gemini: `gemini -p "$(cat $PROMPT_FILE)" --output-format text`
183
- - Codex: `codex exec --skip-git-repo-check --output-last-message <file>`
184
-
185
- ๊ณ ์ • `/tmp/patina-max-*` ํŒŒ์ผ๋ช…์€ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค. ๋ชจ๋“  ์‚ฐ์ถœ๋ฌผ์€ `$RUN_DIR` ์•„๋ž˜์— ์ €์žฅํ•œ๋‹ค.
186
-
187
- ### Dispatch preflight
188
-
189
- `DISPATCH_MODE`๋Š” ์„ค์ • ํŒŒ์ผ๊ณผ `--dispatch` ํ”Œ๋ž˜๊ทธ๋ฅผ ๋ฐ˜์˜ํ•œ ์ตœ์ข… dispatch ๊ฐ’์ด๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ `--dispatch direct`๋ฅผ ๋ช…์‹œํ–ˆ์œผ๋ฉด ์ด preflight๋Š” ๊ฒฝ๊ณ  ์—†์ด direct ๋ชจ๋“œ๋ฅผ ์œ ์ง€ํ•œ๋‹ค.
190
-
191
- ```bash
192
- if [ "$DISPATCH_MODE" = "omc" ] && [ -z "${TMUX:-}" ]; then
193
- MODEL_COUNT=$(printf '%s\n' $SELECTED_MODELS | sed '/^$/d' | wc -l | tr -d ' ')
194
- DIRECT_MIN=$(( (MODEL_COUNT * TIMEOUT_SECONDS + 59) / 60 ))
195
- printf '[patina-max] tmux not detected. Falling back to sequential dispatch (expect ~%smin instead of ~%ss). Pass --dispatch direct to silence.\n' \
196
- "$DIRECT_MIN" "$TIMEOUT_SECONDS" >&2
197
- DISPATCH_MODE="direct"
198
- fi
199
- ```
200
-
201
- ### ๋ชจ๋“œ A: OMC dispatch (`dispatch: omc`)
202
-
203
- tmux pane์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ชจ๋“  ๋ชจ๋ธ์„ **๋ณ‘๋ ฌ** ์‹คํ–‰ํ•œ๋‹ค. ๊ฐ pane์—์„œ ํ”„๋กฌํ”„ํŠธ ํŒŒ์ผ์„ ์ฝ์–ด ์ „๋‹ฌํ•˜๊ณ , ์™„๋ฃŒ ์‹œ sentinel ํŒŒ์ผ์„ ์ƒ์„ฑํ•œ๋‹ค. wall-clock ์ƒํ•œ์€ ๊ฐ€์žฅ ๋А๋ฆฐ ๋ชจ๋ธ ํ•˜๋‚˜์— ๊ฐ€๊น๋‹ค (`TIMEOUT_SECONDS=900`์ด๋ฉด ์•ฝ 900์ดˆ).
204
-
205
- ```bash
206
- SELECTED_MODELS="claude gemini codex" # ์‹ค์ œ๋กœ๋Š” ์„ค์ •/ํ”Œ๋ž˜๊ทธ์—์„œ ์„ ํƒ๋œ ๋ชจ๋ธ๋งŒ ํฌํ•จ
207
- CLAUDE_PANE=""
208
- GEMINI_PANE=""
209
- CODEX_PANE=""
210
-
211
- # Claude
212
- CLAUDE_PANE=$(tmux split-window -d -P -F "#{pane_id}" -h \
213
- "cat \"$PROMPT_FILE\" | claude -p > \"$RUN_DIR/claude-output.txt\" 2> \"$RUN_DIR/claude-log.txt\"; printf '%s' \$? > \"$RUN_DIR/claude.exit\"; touch \"$RUN_DIR/claude.done\"")
214
-
215
- # Gemini
216
- GEMINI_PANE=$(tmux split-window -d -P -F "#{pane_id}" -v \
217
- "gemini -p \"\$(cat \\\"$PROMPT_FILE\\\")\" --output-format text > \"$RUN_DIR/gemini-output.txt\" 2> \"$RUN_DIR/gemini-log.txt\"; printf '%s' \$? > \"$RUN_DIR/gemini.exit\"; touch \"$RUN_DIR/gemini.done\"")
218
-
219
- # Codex
220
- CODEX_PANE=$(tmux split-window -d -P -F "#{pane_id}" -v \
221
- "cat \"$PROMPT_FILE\" | codex exec --skip-git-repo-check --output-last-message \"$RUN_DIR/codex-output.txt\" > \"$RUN_DIR/codex-log.txt\" 2>&1; printf '%s' \$? > \"$RUN_DIR/codex.exit\"; touch \"$RUN_DIR/codex.done\"")
222
- ```
223
-
224
- ์‹ค์ œ ์‹คํ–‰์€ `max-models`์— ํฌํ•จ๋œ ๋ชจ๋ธ์˜ pane๋งŒ ์ƒ์„ฑํ•˜๊ณ , pane id๋„ ํ•ด๋‹น ๋ชจ๋ธ์— ๋Œ€ํ•ด์„œ๋งŒ ๊ธฐ๋กํ•œ๋‹ค.
225
-
226
- **์™„๋ฃŒ ๋Œ€๊ธฐ**: Bash๋กœ sentinel ํŒŒ์ผ์„ ํด๋งํ•œ๋‹ค.
227
-
228
- ```bash
229
- START_TIME=$(date +%s)
230
-
231
- # ์„ ํƒ๋œ ๋ชจ๋ธ๋“ค์˜ .done ํŒŒ์ผ๋งŒ ๊ธฐ๋‹ค๋ฆฐ๋‹ค
232
- while true; do
233
- ALL_DONE=true
234
- for model in $SELECTED_MODELS; do
235
- [ -f "$RUN_DIR/$model.done" ] || ALL_DONE=false
236
- done
237
- $ALL_DONE && break
238
-
239
- NOW=$(date +%s)
240
- if [ $((NOW - START_TIME)) -ge $TIMEOUT_SECONDS ]; then
241
- for model in $SELECTED_MODELS; do
242
- if [ ! -f "$RUN_DIR/$model.done" ]; then
243
- printf '124' > "$RUN_DIR/$model.exit"
244
- printf 'timeout after %ss\n' "$TIMEOUT_SECONDS" > "$RUN_DIR/$model.log.txt"
245
- touch "$RUN_DIR/$model.timeout" "$RUN_DIR/$model.done"
246
- fi
247
- done
248
- [ -n "$CLAUDE_PANE" ] && tmux kill-pane -t "$CLAUDE_PANE" 2>/dev/null || true
249
- [ -n "$GEMINI_PANE" ] && tmux kill-pane -t "$GEMINI_PANE" 2>/dev/null || true
250
- [ -n "$CODEX_PANE" ] && tmux kill-pane -t "$CODEX_PANE" 2>/dev/null || true
251
- break
252
- fi
253
-
254
- sleep 3
255
- done
256
- ```
257
-
258
- > tmux pane์€ ์‹คํ–‰ ์™„๋ฃŒ ํ›„ ์ž๋™์œผ๋กœ ๋‹ซํžŒ๋‹ค. ์ง„ํ–‰ ์ค‘์—๋Š” ์‚ฌ์šฉ์ž๊ฐ€ pane์„ ์ „ํ™˜ํ•˜์—ฌ ๊ฐ ๋ชจ๋ธ์˜ ์‹คํ–‰ ์ƒํƒœ๋ฅผ ์‹œ๊ฐ์ ์œผ๋กœ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค. ํƒ€์ž„์•„์›ƒ์ด ๋ฐœ์ƒํ•œ pane์€ kill ํ›„ `failed`๋กœ ํ‘œ์‹œํ•œ๋‹ค.
259
-
260
- ### ๋ชจ๋“œ B: Direct dispatch (`dispatch: direct`)
261
-
262
- tmux ์—†์ด ์ˆœ์ฐจ์ ์œผ๋กœ ์‹คํ–‰ํ•œ๋‹ค. OMC/tmux๊ฐ€ ์—†๋Š” ํ™˜๊ฒฝ์—์„œ์˜ fallback. wall-clock ์ƒํ•œ์€ ์„ ํƒํ•œ ๋ชจ๋ธ ์ˆ˜์— ๋น„๋ก€ํ•œ๋‹ค (`TIMEOUT_SECONDS=900`, ๋ชจ๋ธ 3๊ฐœ๋ฉด ์•ฝ 45๋ถ„๊นŒ์ง€ ๊ฐ€๋Šฅ). ๋ณ‘๋ ฌ tmux ์‹คํ–‰๋ณด๋‹ค ๋А๋ฆด ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ, ์˜๋„์ ์œผ๋กœ direct๋ฅผ ์“ฐ๋Š” ๊ฒฝ์šฐ `--dispatch direct`๋ฅผ ๋ช…์‹œํ•˜๋ฉด fallback ๊ฒฝ๊ณ ๋ฅผ ๋Œ ์ˆ˜ ์žˆ๋‹ค.
263
-
264
- ```bash
265
- # Claude
266
- timeout "${TIMEOUT_SECONDS}s" sh -c 'cat "$1" | claude -p > "$2" 2> "$3"' sh \
267
- "$PROMPT_FILE" "$RUN_DIR/claude-output.txt" "$RUN_DIR/claude-log.txt"
268
- printf '%s' $? > "$RUN_DIR/claude.exit"
269
- touch "$RUN_DIR/claude.done"
270
-
271
- # Gemini
272
- timeout "${TIMEOUT_SECONDS}s" sh -c 'gemini -p "$(cat "$1")" --output-format text > "$2" 2> "$3"' sh \
273
- "$PROMPT_FILE" "$RUN_DIR/gemini-output.txt" "$RUN_DIR/gemini-log.txt"
274
- printf '%s' $? > "$RUN_DIR/gemini.exit"
275
- touch "$RUN_DIR/gemini.done"
276
-
277
- # Codex
278
- timeout "${TIMEOUT_SECONDS}s" sh -c 'cat "$1" | codex exec --skip-git-repo-check --output-last-message "$2" > "$3" 2>&1' sh \
279
- "$PROMPT_FILE" "$RUN_DIR/codex-output.txt" "$RUN_DIR/codex-log.txt"
280
- printf '%s' $? > "$RUN_DIR/codex.exit"
281
- touch "$RUN_DIR/codex.done"
282
- ```
283
-
284
- ์‹ค์ œ ์‹คํ–‰์€ `max-models`์— ํฌํ•จ๋œ ๋ชจ๋ธ์—๋งŒ ์ ์šฉํ•œ๋‹ค. ๊ฐ ๋ช…๋ น์€ `TIMEOUT_SECONDS` ์ƒํ•œ์„ ๋‘๊ณ , `timeout`์ด ์—†์œผ๋ฉด ๋™๋“ฑํ•œ ํƒ€์ž„์•„์›ƒ ๋ž˜ํผ(`gtimeout` ๋“ฑ)๋กœ ๋Œ€์ฒดํ•œ๋‹ค.
285
-
286
- ### ๊ฒฐ๊ณผ ์ˆ˜์ง‘ (๊ณตํ†ต)
287
-
288
- - `claude`
289
- 1. `$RUN_DIR/claude-output.txt`๋ฅผ Readํ•œ๋‹ค
290
- 2. `$RUN_DIR/claude.exit`๊ฐ€ 0์ด๊ณ  output์ด ๋น„์–ด ์žˆ์ง€ ์•Š์œผ๋ฉด ๊ฒฐ๊ณผ ํ…์ŠคํŠธ๋กœ ์‚ฌ์šฉํ•œ๋‹ค
291
- 3. ์‹คํŒจ ๋˜๋Š” ๋นˆ ์ถœ๋ ฅ์ด๋ฉด `$RUN_DIR/claude-log.txt`๋ฅผ ์ง„๋‹จ ์ •๋ณด๋กœ Readํ•œ๋‹ค
292
-
293
- - `gemini`
294
- 1. `$RUN_DIR/gemini-output.txt`๋ฅผ Readํ•œ๋‹ค
295
- 2. `$RUN_DIR/gemini.exit`๊ฐ€ 0์ด๊ณ  output์ด ๋น„์–ด ์žˆ์ง€ ์•Š์œผ๋ฉด ๊ฒฐ๊ณผ ํ…์ŠคํŠธ๋กœ ์‚ฌ์šฉํ•œ๋‹ค
296
- 3. `$RUN_DIR/gemini-log.txt`์—๋Š” ์ธ์ฆ/ํ™•์žฅ/MCP ๊ฒฝ๊ณ ๊ฐ€ ํฌํ•จ๋  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ, ์‹คํŒจ ์ง„๋‹จ์šฉ์œผ๋กœ๋งŒ ์‚ฌ์šฉํ•œ๋‹ค
297
-
298
- - `codex`
299
- 1. `$RUN_DIR/codex-output.txt`๋ฅผ Readํ•œ๋‹ค (`--output-last-message`๊ฐ€ ๊ธฐ๋กํ•œ ์ตœ์ข… ํ…์ŠคํŠธ)
300
- 2. `$RUN_DIR/codex.exit`๊ฐ€ 0์ด๊ณ  output์ด ๋น„์–ด ์žˆ์ง€ ์•Š์œผ๋ฉด ๊ฒฐ๊ณผ ํ…์ŠคํŠธ๋กœ ์‚ฌ์šฉํ•œ๋‹ค
301
- 3. ์‹คํŒจ ๋˜๋Š” ๋นˆ ์ถœ๋ ฅ์ด๋ฉด `$RUN_DIR/codex-log.txt`๋ฅผ ์ง„๋‹จ ์ •๋ณด๋กœ Readํ•œ๋‹ค
302
-
303
- ### ์‹คํŒจ ์ฒ˜๋ฆฌ
304
-
305
- - ํŠน์ • ๋ชจ๋ธ์˜ exit code๊ฐ€ 0์ด ์•„๋‹ˆ๊ฑฐ๋‚˜, output ํŒŒ์ผ์ด ๋น„์–ด ์žˆ๊ฑฐ๋‚˜, ํŒŒ์ผ์ด ์—†์œผ๋ฉด โ†’ ํ•ด๋‹น ๋ชจ๋ธ์„ `failed`๋กœ ํ‘œ์‹œํ•˜๊ณ  ๋‚˜๋จธ์ง€๋กœ ๊ณ„์† ์ง„ํ–‰
306
- - `.timeout` ํŒŒ์ผ์ด ์žˆ๊ฑฐ๋‚˜ exit code๊ฐ€ `124`์ด๋ฉด โ†’ `timed out`์œผ๋กœ ๊ธฐ๋กํ•˜๊ณ  `failed`๋กœ ํ‘œ์‹œ
307
- - `.done` ํŒŒ์ผ์€ ์ƒ๊ฒผ์ง€๋งŒ output ํŒŒ์ผ์ด ๋น„์–ด ์žˆ์œผ๋ฉด โ†’ CLI๊ฐ€ ๋นˆ ์‘๋‹ต์„ ๋ฐ˜ํ™˜ํ•œ ๊ฒƒ์ด๋ฏ€๋กœ `failed`๋กœ ํ‘œ์‹œ
308
- - ๋ชจ๋“  ๋ชจ๋ธ์ด ์‹คํŒจํ•œ ๊ฒฝ์šฐ โ†’ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€์™€ ํ•จ๊ป˜ ๊ฐ ๋ชจ๋ธ์˜ output/log ํŒŒ์ผ ๋‚ด์šฉ์„ ์ง„๋‹จ ์ •๋ณด๋กœ ์ถœ๋ ฅํ•˜๊ณ  ์ข…๋ฃŒ
309
- - `dispatch: omc`์—์„œ tmux๊ฐ€ ์—†์œผ๋ฉด (`$TMUX` ๋ฏธ์„ค์ •) โ†’ `[patina-max] tmux not detected. Falling back to sequential dispatch (expect ~Xmin instead of ~Ys). Pass --dispatch direct to silence.` ํ˜•์‹์˜ ๊ฒฝ๊ณ  ํ›„ direct ๋ชจ๋“œ๋กœ ์ž๋™ ์ „ํ™˜
310
- - `--dispatch direct`๋ฅผ ๋ช…์‹œํ•œ ๊ฒฝ์šฐ โ†’ ์ด๋ฏธ ์ˆœ์ฐจ ์‹คํ–‰์„ ์„ ํƒํ•œ ๊ฒƒ์ด๋ฏ€๋กœ tmux fallback ๊ฒฝ๊ณ ๋ฅผ ์ถœ๋ ฅํ•˜์ง€ ์•Š์Œ
311
-
312
- ---
313
-
314
- ## 6๋‹จ๊ณ„: ๊ฒฐ๊ณผ ํ‰๊ฐ€ (AI ์œ ์‚ฌ๋„ ์ ์ˆ˜)
315
-
316
- ๊ฐ ๋ชจ๋ธ์˜ humanize ๊ฒฐ๊ณผ์— ๋Œ€ํ•ด `core/scoring.md`์˜ ์ˆ˜์‹ ๊ธฐ๋ฐ˜ ์Šค์ฝ”์–ด๋ง ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์ ์šฉํ•œ๋‹ค.
317
-
318
- 2๋‹จ๊ณ„์—์„œ ๋กœ๋“œํ•œ ๋ชจ๋“  ํŒจํ„ด ํŒฉ์„ ๊ธฐ์ค€์œผ๋กœ, ๊ฐ ๊ฒฐ๊ณผ์— ๋Œ€ํ•ด SKILL.md์˜ `--score` ๋ชจ๋“œ์™€ ๋™์ผํ•œ ์ ˆ์ฐจ๋ฅผ ์‹คํ–‰ํ•œ๋‹ค:
319
-
320
- 1. **ํŒจํ„ด ๊ฐ์ง€**: ๋ชจ๋“  ํŒจํ„ด์„ ์Šค์บ”ํ•˜๊ณ , ๊ฐ์ง€๋œ ๊ฐ ํŒจํ„ด์— severity๋ฅผ ๋ถ€์—ฌํ•œ๋‹ค (`core/scoring.md`์˜ ์‹ฌ๊ฐ๋„ ๋ฃจ๋ธŒ๋ฆญ ์ฐธ์กฐ)
321
- 2. **ํ”„๋กœํ•„ ์˜ค๋ฒ„๋ผ์ด๋“œ ์ ์šฉ**: pattern-overrides๊ฐ€ ์žˆ์œผ๋ฉด severity๋ฅผ ์กฐ์ •ํ•œ๋‹ค
322
- 3. **์นดํ…Œ๊ณ ๋ฆฌ ์ ์ˆ˜ ๊ณ„์‚ฐ**: `์นดํ…Œ๊ณ ๋ฆฌ ์ ์ˆ˜ = (์กฐ์ •๋œ ์‹ฌ๊ฐ๋„ ํ•ฉ๊ณ„ / (ํŒจํ„ด ์ˆ˜ ร— 3)) ร— 100`
323
- 4. **์ „์ฒด ์ ์ˆ˜ ๊ณ„์‚ฐ**: ์นดํ…Œ๊ณ ๋ฆฌ ์ ์ˆ˜์˜ ๊ฐ€์ค‘ ํ‰๊ท  (`ouroboros.category-weights.{lang}` ์‚ฌ์šฉ)
324
-
325
- ์ด ์ ˆ์ฐจ๋Š” ๊ฐ ๋ชจ๋ธ์˜ ๊ฒฐ๊ณผ์— ๋Œ€ํ•ด ๋…๋ฆฝ์ ์œผ๋กœ ์‹คํ–‰ํ•œ๋‹ค. ๋ชจ๋“  ๋ชจ๋ธ์ด ๋™์ผํ•œ ์ˆ˜์‹์œผ๋กœ ํ‰๊ฐ€๋˜๋ฏ€๋กœ ๊ณต์ •ํ•œ ๋น„๊ต๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค.
326
-
327
- ๊ฐ ๊ฒฐ๊ณผ์— ๋Œ€ํ•ด ์ถ”๊ฐ€๋กœ MPS(Meaning Preservation Score)๋ฅผ ์‚ฐ์ถœํ•œ๋‹ค. ์›๋ณธ ํ…์ŠคํŠธ์—์„œ ์˜๋ฏธ ์•ต์ปค๋ฅผ ์ถ”์ถœํ•˜๊ณ , ๊ฐ ๋ชจ๋ธ์˜ humanize ๊ฒฐ๊ณผ๊ฐ€ ์•ต์ปค๋ฅผ ์–ผ๋งˆ๋‚˜ ๋ณด์กดํ–ˆ๋Š”์ง€๋ฅผ `core/scoring.md` ยงยง 14-17์˜ ์ ˆ์ฐจ๋กœ ํ‰๊ฐ€ํ•œ๋‹ค.
328
-
329
- > **์ฐธ๊ณ :** ์ด์ „ ๋ฒ„์ „์—์„œ๋Š” MAX ๋ชจ๋“œ๊ฐ€ ์ฃผ๊ด€์  ํ‰๊ฐ€๋ฅผ ์‚ฌ์šฉํ–ˆ์œผ๋‚˜, v3.2.0๋ถ€ํ„ฐ `core/scoring.md`์˜ ์ˆ˜์‹ ๊ธฐ๋ฐ˜ ์•Œ๊ณ ๋ฆฌ์ฆ˜์œผ๋กœ ํ†ตํ•ฉ๋˜์—ˆ๋‹ค. ๋‹จ์ผ ๋ชจ๋“œ(`--score`)์™€ MAX ๋ชจ๋“œ๊ฐ€ ๋™์ผํ•œ ์Šค์ฝ”์–ด๋ง์„ ์‚ฌ์šฉํ•œ๋‹ค.
330
-
331
- ---
332
-
333
- ## 7๋‹จ๊ณ„: ๋น„๊ต ๋ฐ ์„ ํƒ
334
-
335
- ๊ฒฐ๊ณผ ๋น„๊ต ํ…Œ์ด๋ธ”์„ ๊ตฌ์„ฑํ•˜๊ณ  ์ตœ์ข…๋ณธ์„ ์„ ํƒํ•œ๋‹ค.
336
-
337
- ### ๋น„๊ต ํ…Œ์ด๋ธ” ํ˜•์‹
338
-
339
- ```
340
- | Model | AI Score | MPS | Status |
341
- |---------|----------|------|---------|
342
- | claude | 23 | 92 | โœ… |
343
- | gemini | 31 | 85 | โœ… |
344
- | codex | -- | -- | failed |
345
-
346
- Best: claude (AI Score: 23, MPS: 92)
347
- ```
348
-
349
- - MPS โ‰ฅ 70์ธ ํ›„๋ณด ์ค‘ AI ์ ์ˆ˜๊ฐ€ ๊ฐ€์žฅ ๋‚ฎ์€ ๊ฒฐ๊ณผ๋ฅผ ์ž๋™ ์„ ํƒํ•œ๋‹ค
350
- - MPS < 70์ธ ํ›„๋ณด๋Š” AI ์ ์ˆ˜์™€ ๊ด€๊ณ„์—†์ด ํƒˆ๋ฝํ•œ๋‹ค
351
- - ๋ชจ๋“  ํ›„๋ณด์˜ MPS < 70์ธ ๊ฒฝ์šฐ, MPS๊ฐ€ ๊ฐ€์žฅ ๋†’์€ ํ›„๋ณด๋ฅผ ์„ ํƒํ•œ๋‹ค (์˜๋ฏธ ๋ณด์กด ์šฐ์„ )
352
- - ๋™์ ์ด๋ฉด ์„ค์ • ํŒŒ์ผ์˜ ๋ชจ๋ธ ์ˆœ์„œ์—์„œ ๋จผ์ € ์˜ค๋Š” ๊ฒƒ์„ ์šฐ์„ ํ•œ๋‹ค
353
-
354
- ### Composite ์žฌ์„ ํƒ artifact (์„ ํƒ)
355
-
356
- `patina-max/composite.py`๋กœ 4์ถ•(AI, MPS, RSS, EditCons) ์žฌ์„ ํƒ์„ ์‹คํ–‰ํ•˜๋ ค๋ฉด `$RUN_DIR`์— ์•„๋ž˜ ๋ ˆ์ด์•„์›ƒ์„ ์ƒ์„ฑํ•œ๋‹ค. ๊ธฐ์กด `*-output.txt`๋Š” ์ง„๋‹จ์šฉ์œผ๋กœ ์œ ์ง€ํ•˜๊ณ , ์„ฑ๊ณตํ•œ ํ›„๋ณด์˜ ์ตœ์ข… ํ…์ŠคํŠธ๋ฅผ ๋ชจ๋ธ๋ณ„ Markdown ํŒŒ์ผ๋กœ๋„ ์ €์žฅํ•œ๋‹ค.
357
-
358
- ```text
359
- $RUN_DIR/
360
- input.md
361
- claude.md
362
- gemini.md
363
- codex.md
364
- meta.md
365
- ```
366
-
367
- - `input.md`: ์‚ฌ์šฉ์ž๊ฐ€ ์ œ๊ณตํ•œ ์›๋ฌธ ํ…์ŠคํŠธ
368
- - `{model}.md`: ํ•ด๋‹น ๋ชจ๋ธ์˜ humanize ๊ฒฐ๊ณผ. ์‹คํŒจํ•œ ๋ชจ๋ธ์€ ํŒŒ์ผ์„ ์ƒ๋žตํ•˜๊ฑฐ๋‚˜ ์‹คํŒจ ๋ฉ”๋ชจ๋งŒ ๋‘”๋‹ค
369
- - `meta.md`: ํ›„๋ณด๋ณ„ `model`, `status`, `ai_score`, `mps` ๊ฐ’์„ `candidates:` ๋ชฉ๋ก์œผ๋กœ ๊ธฐ๋กํ•œ๋‹ค
370
-
371
- `meta.md` ์˜ˆ์‹œ:
372
-
373
- ```yaml
374
- candidates:
375
- - model: claude
376
- status: success
377
- ai_score: 23
378
- mps: 92
379
- - model: gemini
380
- status: failed
381
- ai_score: n/a
382
- mps: n/a
383
- ```
384
-
385
- ์‹คํ–‰:
386
-
387
- ```bash
388
- python3 patina-max/composite.py "$RUN_DIR"
389
- ```
390
-
391
- ์Šคํฌ๋ฆฝํŠธ๋Š” `$RUN_DIR/composite.md`์™€ `$RUN_DIR/winner.md`๋ฅผ ์ƒ์„ฑํ•œ๋‹ค. `--weights ai=0.4,rss=0.3`์ฒ˜๋Ÿผ ๊ฐ€์ค‘์น˜๋ฅผ ๋ฎ์–ด์“ธ ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ง€์ •ํ•˜์ง€ ์•Š์€ ์ถ•์€ ๊ธฐ๋ณธ๊ฐ’์„ ์‚ฌ์šฉํ•œ ๋’ค ํ•ฉ๊ณ„๊ฐ€ 1์ด ๋˜๋„๋ก ์ •๊ทœํ™”ํ•œ๋‹ค.
392
-
393
- ---
394
-
395
- ## 8๋‹จ๊ณ„: ์ถœ๋ ฅ
396
-
397
- ### ์ตœ์ข… ์ถœ๋ ฅ ๊ตฌ์„ฑ
398
-
399
- 1. **๋น„๊ต ํ…Œ์ด๋ธ”** โ€” ๋ชจ๋“  ๋ชจ๋ธ์˜ AI ์ ์ˆ˜์™€ ์ƒํƒœ
400
- 2. **์ตœ์ข…๋ณธ** โ€” ๊ฐ€์žฅ ๋‚ฎ์€ AI ์ ์ˆ˜๋ฅผ ๋ฐ›์€ ๊ฒฐ๊ณผ์˜ ์ „์ฒด ํ…์ŠคํŠธ
401
- 3. **์นดํ…Œ๊ณ ๋ฆฌ๋ณ„ ์„ธ๋ถ€ ์ ์ˆ˜** โ€” ์ตœ์ข…๋ณธ์˜ ํŒจํ„ด ์นดํ…Œ๊ณ ๋ฆฌ๋ณ„ ์ ์ˆ˜
402
- 4. **์ฐจ์  ๊ฒฐ๊ณผ ์š”์•ฝ** โ€” ๋‹ค๋ฅธ ๋ชจ๋ธ์˜ ๊ฒฐ๊ณผ๋ฅผ ์ ‘ํžŒ ์„น์…˜์œผ๋กœ ์ œ๊ณต (์ฐธ๊ณ ์šฉ)
403
- 5. **์‹คํŒจ ์ •๋ณด** โ€” ์‹คํŒจํ•œ ๋ชจ๋ธ์ด ์žˆ์œผ๋ฉด ์‚ฌ์œ  ํ‘œ์‹œ
404
- 6. **์ž„์‹œ ํŒŒ์ผ ์ •๋ฆฌ** โ€” ์ถœ๋ ฅ์ด ๋๋‚œ ๋’ค `rm -rf "$RUN_DIR"`๋กœ ์ •๋ฆฌ
405
-
406
- ### ์ถœ๋ ฅ ์˜ˆ์‹œ
407
-
408
- ```
409
- ## MAX Mode ๊ฒฐ๊ณผ
410
-
411
- ### ๋น„๊ต ํ…Œ์ด๋ธ”
412
- | Model | AI Score | Status |
413
- |---------|----------|---------|
414
- | claude | 23 | success |
415
- | gemini | 31 | success |
416
-
417
- **Best: claude (AI Score: 23)**
418
-
419
- ### ์นดํ…Œ๊ณ ๋ฆฌ๋ณ„ ์ ์ˆ˜ (claude)
420
- | Category | Score |
421
- |---------------|-------|
422
- | Structure | 15 |
423
- | Content | 20 |
424
- | Language | 25 |
425
- | Style | 30 |
426
- | Communication | 25 |
427
- | Filler | 20 |
428
-
429
- ### ์ตœ์ข…๋ณธ
430
- {claude์˜ humanize ๊ฒฐ๊ณผ ์ „์ฒด ํ…์ŠคํŠธ}
431
-
432
- <details>
433
- <summary>gemini ๊ฒฐ๊ณผ (AI Score: 31)</summary>
434
-
435
- {gemini์˜ humanize ๊ฒฐ๊ณผ ์ „์ฒด ํ…์ŠคํŠธ}
436
- </details>
437
- ```
438
-
439
- ๋‹จ์ผ ๋ชจ๋ธ๋งŒ ์„ฑ๊ณตํ•œ ๊ฒฝ์šฐ, ํ•ด๋‹น ๊ฒฐ๊ณผ๋ฅผ ์ตœ์ข…๋ณธ์œผ๋กœ ์ œ์‹œํ•˜๋˜ "๋‹จ์ผ ๋ชจ๋ธ๋งŒ ์„ฑ๊ณตํ•˜์—ฌ ๋น„๊ต ๋ถˆ๊ฐ€" ๊ฒฝ๊ณ ๋ฅผ ํ‘œ์‹œํ•œ๋‹ค.
440
-
441
- ---
442
-
443
- ## ์ฐธ๊ณ 
444
-
445
- - ์ด ์Šคํ‚ฌ์€ ๊ธฐ์กด `/patina` ์Šคํ‚ฌ์˜ ํ™•์žฅ ๋ฒ„์ „์œผ๋กœ, ๋™์ผํ•œ ํŒจํ„ด ํŒฉ๊ณผ ํ”„๋กœํ•„์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค
446
- - ๋ชจ๋ธ ์ถ”๊ฐ€/์ œ๊ฑฐ๋Š” `.patina.default.yaml`์˜ `max-models` ๋˜๋Š” `--models` ํ”Œ๋ž˜๊ทธ๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค
447
- - ์ง€์› ๋ชจ๋ธ: `claude`, `codex`, `gemini` (Claude/Codex๋Š” stdin pipe, Gemini๋Š” `$(cat file)` ์ธ์ž ์น˜ํ™˜)
448
- - ๋””์ŠคํŒจ์น˜ ๋ชจ๋“œ: `omc` (tmux pane ๋ณ‘๋ ฌ, ๊ธฐ๋ณธ) / `direct` (์ˆœ์ฐจ ์‹คํ–‰, fallback)
449
- - ๊ฐ ์‹คํ–‰์€ ๊ณ ์œ  temp dir๋ฅผ ์‚ฌ์šฉํ•˜๋ฉฐ, ์„ ํƒ๋œ ๋ชจ๋ธ๋งŒ ๊ธฐ๋‹ค๋ฆฌ๊ณ , timeout ๋ชจ๋ธ์€ ์ž๋™์œผ๋กœ `failed` ์ฒ˜๋ฆฌํ•œ๋‹ค
450
- - ์ตœ์ดˆ ์‹คํ–‰ ์‹œ ์„ค์น˜ ์ธํ„ฐ๋ทฐ๋กœ `.patina.yaml` ์ƒ์„ฑ (๋ชจ๋ธ ์„ ํƒ, ๋””์ŠคํŒจ์น˜ ๋ชจ๋“œ, CLI ์„ค์น˜ ํ™•์ธ)
451
- - `--ouroboros`์™€ ํ•จ๊ป˜ ์‚ฌ์šฉ ๊ฐ€๋Šฅ: ์ตœ๊ณ  ์ ์ˆ˜๊ฐ€ target-score ์ด์ƒ์ด๋ฉด ํ•ด๋‹น ๋ชจ๋ธ ๊ฒฐ๊ณผ๋ฅผ ์ž…๋ ฅ์œผ๋กœ ์žฌ์‹คํ–‰ํ•˜์—ฌ ์ ์ˆ˜๋ฅผ ์ˆ˜๋ ด์‹œํ‚จ๋‹ค
452
-
453
- ## ํ†ค ์ „ํŒŒ (v3.10)
454
-
455
- MAX mode์—์„œ ํ†ค ํ•ด์„์€ SKILL.md 1๋‹จ๊ณ„/4.5b๋‹จ๊ณ„์™€ ๋™์ผํ•˜๊ฒŒ ์ฒ˜๋ฆฌ๋œ๋‹ค. 1๋‹จ๊ณ„์—์„œ resolved_tone, tone_source, tone_evidence, tone_confidence๋ฅผ ๊ฒฐ์ •ํ•œ ๋’ค, ์›Œ์ปค ํ”„๋กฌํ”„ํŠธ(4๋‹จ๊ณ„์—์„œ ๊ฐ ๋ชจ๋ธ์— ์ „๋‹ฌํ•˜๋Š” ์ธ๋ผ์ธ ํ”„๋กฌํ”„ํŠธ)์— `<tone-context>` ๋ธ”๋ก์œผ๋กœ ํฌํ•จํ•œ๋‹ค. ๋ชจ๋“  ๋””์ŠคํŒจ์น˜ ๋ชจ๋ธ(claude, gemini, codex)์€ ๋™์ผํ•œ ํ†ค ์ปจํ…์ŠคํŠธ๋ฅผ ๋ฐ›์•„ ์ผ๊ด€๋œ ๋ชฉ์†Œ๋ฆฌ๋กœ humanize๋ฅผ ์ˆ˜ํ–‰ํ•œ๋‹ค. ์ตœ์ข… ์„ ํƒ๋œ ๊ฒฐ๊ณผ์˜ YAML footer์—๋Š” SKILL.md 6๋‹จ๊ณ„ ๊ทœ์น™๊ณผ ๋™์ผํ•˜๊ฒŒ tone ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๊ฐ€ ํฌํ•จ๋œ๋‹ค.
@@ -1,14 +0,0 @@
1
- # Harness Notes
2
-
3
- Status: internal maintainer note. This is not public installation guidance.
4
-
5
- ## Current stance
6
-
7
- - Patina is primarily a Markdown skill plus a Node.js CLI.
8
- - Use specification-first review for changes that touch pattern definitions, scoring logic, or meaning-preservation behavior.
9
- - Keep the core skill pipeline stable unless the change explicitly targets pipeline behavior.
10
- - For docs-only waves, prefer `docs-review` evidence and the standard npm verification gates.
11
-
12
- ## Historical context
13
-
14
- Older agent notes referenced an OMC/Ouroboros harness as the primary working lane. The current workspace uses the repo's active Codex/OMX runtime instructions from `AGENTS.md` and the bootstrap files instead. Treat older harness-specific routing as historical unless it is reintroduced in active automation.