@ps-neko/nekowork 0.1.0-alpha.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/AGENTS.md +112 -0
- package/CLAUDE.md +81 -0
- package/LICENSE +21 -0
- package/README.md +283 -0
- package/REVIEW.md +96 -0
- package/RULES.md +51 -0
- package/SOUL.md +21 -0
- package/WORKING-CONTEXT.md +52 -0
- package/agent.yaml +219 -0
- package/agents/architect.md +57 -0
- package/agents/code-reviewer.md +60 -0
- package/agents/codex-challenger.md +53 -0
- package/agents/codex-reviewer.md +56 -0
- package/agents/debugger.md +33 -0
- package/agents/doc-writer.md +51 -0
- package/agents/executor.md +41 -0
- package/agents/planner.md +49 -0
- package/agents/research.md +50 -0
- package/agents/security-reviewer.md +47 -0
- package/agents/test-engineer.md +41 -0
- package/bridge/mcp-server.js +301 -0
- package/commands/claude-led-codex-review.md +29 -0
- package/docs/ADVANCED.md +321 -0
- package/docs/AI-DEVELOPMENT-LIFECYCLE.md +105 -0
- package/docs/ARCHITECTURE.md +205 -0
- package/docs/AUDIT.md +114 -0
- package/docs/AUTH-MIGRATION.md +282 -0
- package/docs/CHANGELOG.md +97 -0
- package/docs/CLI-STAGES.md +89 -0
- package/docs/CODEMAPS/README.md +15 -0
- package/docs/CODEMAPS/agents.md +22 -0
- package/docs/CODEMAPS/bridge.md +18 -0
- package/docs/CODEMAPS/hooks.md +28 -0
- package/docs/CODEMAPS/manifests.md +14 -0
- package/docs/CODEMAPS/rules.md +22 -0
- package/docs/CODEMAPS/schemas.md +21 -0
- package/docs/CODEMAPS/scripts.md +158 -0
- package/docs/CODEMAPS/skills.md +29 -0
- package/docs/CODEMAPS/tests.md +98 -0
- package/docs/CORE-INVARIANTS.md +38 -0
- package/docs/DEMO.md +110 -0
- package/docs/EXAMPLE-PROJECT.md +92 -0
- package/docs/PORTING.md +154 -0
- package/docs/PRODUCT-PRINCIPLES.md +303 -0
- package/docs/PUBLISH-ALPHA.md +106 -0
- package/docs/QUICKSTART.md +344 -0
- package/docs/RELEASE-READINESS.md +140 -0
- package/docs/RISK-CLASSIFIER.md +50 -0
- package/docs/RUNBOOK.md +146 -0
- package/docs/SECURITY.md +79 -0
- package/docs/SETUP.md +142 -0
- package/docs/WHY-NEKOWORK.md +64 -0
- package/docs/case-studies/README.md +16 -0
- package/docs/case-studies/SINDRESORHUS-IS-PLAIN-OBJ.md +141 -0
- package/docs/dev-log/2026-04-29-p1-recovery.md +142 -0
- package/docs/dev-log/2026-04-29-week1-4.md +81 -0
- package/docs/examples/GITHUB-ACTIONS-HARDENING.md +86 -0
- package/docs/examples/QUALITY-LIFECYCLE-SMOKE.md +32 -0
- package/docs/examples/TRADING-DASHBOARD-MOCK.md +65 -0
- package/docs/workflows-stash/README.md +32 -0
- package/docs/workflows-stash/harness-review.yml +166 -0
- package/docs/workflows-stash/harness-validate.yml +48 -0
- package/examples/github-actions-hardening/.github/workflows/hardened-validate.yml +38 -0
- package/examples/github-actions-hardening/README.md +31 -0
- package/examples/github-actions-hardening/case-study/ASK.md +26 -0
- package/examples/github-actions-hardening/case-study/GATE_STATUS.md +28 -0
- package/examples/github-actions-hardening/case-study/PLAN.md +25 -0
- package/examples/github-actions-hardening/case-study/SHIP_READY.md +21 -0
- package/examples/github-actions-hardening/case-study/TASK.md +30 -0
- package/examples/github-actions-hardening/case-study/TEAM_HANDOFFS.md +37 -0
- package/examples/github-actions-hardening/case-study/VERIFY_SUMMARY.md +35 -0
- package/examples/github-actions-hardening/case-study/WORK_SUMMARY.md +24 -0
- package/examples/github-actions-hardening/package.json +12 -0
- package/examples/github-actions-hardening/scripts/check.mjs +43 -0
- package/examples/quality-lifecycle-smoke/README.md +30 -0
- package/examples/quality-lifecycle-smoke/case-study/ASK.md +24 -0
- package/examples/quality-lifecycle-smoke/case-study/GATE_STATUS.md +10 -0
- package/examples/quality-lifecycle-smoke/case-study/PLAN.md +19 -0
- package/examples/quality-lifecycle-smoke/case-study/SHIP_READY.md +11 -0
- package/examples/quality-lifecycle-smoke/case-study/TASK.md +19 -0
- package/examples/quality-lifecycle-smoke/case-study/TEAM_HANDOFFS.md +21 -0
- package/examples/quality-lifecycle-smoke/case-study/VERIFY_SUMMARY.md +44 -0
- package/examples/quality-lifecycle-smoke/case-study/WORK_SUMMARY.md +19 -0
- package/examples/quality-lifecycle-smoke/package.json +8 -0
- package/examples/quality-lifecycle-smoke/scripts/check.mjs +44 -0
- package/examples/trading-dashboard-mock/README.md +33 -0
- package/examples/trading-dashboard-mock/case-study/ASK.md +24 -0
- package/examples/trading-dashboard-mock/case-study/GATE_STATUS.md +28 -0
- package/examples/trading-dashboard-mock/case-study/PLAN.md +23 -0
- package/examples/trading-dashboard-mock/case-study/SHIP_READY.md +21 -0
- package/examples/trading-dashboard-mock/case-study/TASK.md +29 -0
- package/examples/trading-dashboard-mock/case-study/TEAM_HANDOFFS.md +49 -0
- package/examples/trading-dashboard-mock/case-study/VERIFY_SUMMARY.md +35 -0
- package/examples/trading-dashboard-mock/case-study/WORK_SUMMARY.md +27 -0
- package/examples/trading-dashboard-mock/fixtures/market.json +9 -0
- package/examples/trading-dashboard-mock/index.html +76 -0
- package/examples/trading-dashboard-mock/package.json +9 -0
- package/examples/trading-dashboard-mock/scripts/check.mjs +54 -0
- package/examples/trading-dashboard-mock/src/app.js +83 -0
- package/examples/trading-dashboard-mock/src/styles.css +227 -0
- package/hooks/hooks.json +44 -0
- package/hooks/scripts/config-protection.js +34 -0
- package/hooks/scripts/gateguard-fact-force.js +146 -0
- package/hooks/scripts/persistent-mode.mjs +27 -0
- package/hooks/scripts/pre-bash-dispatcher.js +63 -0
- package/hooks/scripts/quality-gate.js +106 -0
- package/manifests/install-components.json +195 -0
- package/manifests/install-modules.json +101 -0
- package/manifests/install-profiles.json +134 -0
- package/package.json +96 -0
- package/rules/common/coding-style.md +71 -0
- package/rules/common/security.md +69 -0
- package/rules/common/testing.md +58 -0
- package/rules/python/coding-style.md +80 -0
- package/rules/python/testing.md +86 -0
- package/rules/typescript/coding-style.md +97 -0
- package/rules/typescript/security.md +67 -0
- package/rules/typescript/testing.md +78 -0
- package/schemas/agent-yaml.schema.json +168 -0
- package/schemas/agent.schema.json +32 -0
- package/schemas/handoff.schema.json +105 -0
- package/schemas/hooks.schema.json +35 -0
- package/schemas/install-components.schema.json +46 -0
- package/schemas/install-modules.schema.json +39 -0
- package/schemas/install-profiles.schema.json +32 -0
- package/schemas/install-state.schema.json +42 -0
- package/schemas/routing.schema.json +42 -0
- package/schemas/skill.schema.json +19 -0
- package/scripts/agents/dispatch.js +144 -0
- package/scripts/agents/runners/claude.js +214 -0
- package/scripts/agents/runners/codex.js +233 -0
- package/scripts/agents/runners/gemini.js +92 -0
- package/scripts/agents/runners/mock.js +107 -0
- package/scripts/auth/github-import-gh.js +52 -0
- package/scripts/auth/github-login.js +79 -0
- package/scripts/auth/github-logout.js +21 -0
- package/scripts/auth/github-status.js +46 -0
- package/scripts/build-claude.js +101 -0
- package/scripts/build-codemaps.js +286 -0
- package/scripts/build-codex.js +93 -0
- package/scripts/build-cursor.js +132 -0
- package/scripts/build-gemini.js +117 -0
- package/scripts/build-opencode.js +117 -0
- package/scripts/ci/catalog.js +120 -0
- package/scripts/ci/check-markers.js +48 -0
- package/scripts/ci/security-hardening.js +270 -0
- package/scripts/ci/validate-agents.js +88 -0
- package/scripts/ci/validate-hooks.js +99 -0
- package/scripts/ci/validate-manifests.js +128 -0
- package/scripts/ci/validate-skills.js +93 -0
- package/scripts/cli.js +1134 -0
- package/scripts/core/auth-guard.js +22 -0
- package/scripts/core/build-roots.js +11 -0
- package/scripts/core/cli-resolver.js +64 -0
- package/scripts/core/execution-workspace.js +84 -0
- package/scripts/core/git-mutation-guard.js +79 -0
- package/scripts/core/install-state.js +125 -0
- package/scripts/core/json-extractor.js +32 -0
- package/scripts/core/subprocess.js +74 -0
- package/scripts/daemon/wait.js +278 -0
- package/scripts/demo-external-project.js +222 -0
- package/scripts/demo-quick-run.js +193 -0
- package/scripts/demo-review.js +204 -0
- package/scripts/doctor.js +296 -0
- package/scripts/install-apply.js +185 -0
- package/scripts/install-plan.js +411 -0
- package/scripts/lib/acceptance-criteria.js +105 -0
- package/scripts/lib/costs.js +82 -0
- package/scripts/lib/instincts.js +194 -0
- package/scripts/lib/keychain.js +85 -0
- package/scripts/lib/profile-policy.js +134 -0
- package/scripts/lib/profile-safety.js +81 -0
- package/scripts/lib/risk-classifier.js +145 -0
- package/scripts/lib/router.js +138 -0
- package/scripts/lib/severity.js +99 -0
- package/scripts/lib/token-vault.js +136 -0
- package/scripts/orchestrators/apply.js +225 -0
- package/scripts/orchestrators/ask.js +143 -0
- package/scripts/orchestrators/gate.js +179 -0
- package/scripts/orchestrators/ralph.js +179 -0
- package/scripts/orchestrators/review.js +452 -0
- package/scripts/orchestrators/run.js +151 -0
- package/scripts/orchestrators/ship.js +339 -0
- package/scripts/orchestrators/team-lite.js +270 -0
- package/scripts/orchestrators/team.js +244 -0
- package/scripts/orchestrators/verify.js +306 -0
- package/scripts/orchestrators/work.js +207 -0
- package/scripts/portability/simulate-port.js +220 -0
- package/scripts/repair.js +184 -0
- package/scripts/sync-claude-md.js +220 -0
- package/scripts/verify/claude-live.js +30 -0
- package/scripts/verify/codex-live.js +60 -0
- package/scripts/verify/gemini-live.js +48 -0
- package/scripts/verify/runtime.js +105 -0
- package/skills/claude-led-codex-review/SKILL.md +133 -0
- package/skills/plan-eng-review/SKILL.md +51 -0
- package/skills/porting/SKILL.md +69 -0
- package/skills/ralph/SKILL.md +48 -0
- package/skills/release-readiness/SKILL.md +62 -0
- package/skills/review/SKILL.md +42 -0
- package/skills/security-hardening/SKILL.md +59 -0
- package/skills/ship/SKILL.md +44 -0
- package/skills/tdd-workflow/SKILL.md +42 -0
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// CLAUDE.md / .claude/CLAUDE.md 의 HARNESS:START~HARNESS:END 영역을
|
|
3
|
+
// agent.yaml + package.json + manifests 에서 다시 생성해 갈아낀다.
|
|
4
|
+
// 사용자 작성 영역(마커 바깥)은 그대로 보존한다.
|
|
5
|
+
// 멱등(idempotent). 마커가 없으면 "## 자동 갱신 영역" 헤딩 다음에 삽입한다.
|
|
6
|
+
|
|
7
|
+
import fs from 'node:fs';
|
|
8
|
+
import path from 'node:path';
|
|
9
|
+
import { fileURLToPath } from 'node:url';
|
|
10
|
+
import YAML from 'yaml';
|
|
11
|
+
|
|
12
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
const ROOT = path.resolve(__dirname, '..');
|
|
14
|
+
|
|
15
|
+
const TARGETS = [
|
|
16
|
+
path.join(ROOT, 'CLAUDE.md'),
|
|
17
|
+
path.join(ROOT, '.claude', 'CLAUDE.md'),
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
const START_RE = /<!--\s*HARNESS:START(?:\s+version=\S+)?\s*-->/;
|
|
21
|
+
const END_RE = /<!--\s*HARNESS:END\s*-->/;
|
|
22
|
+
const AUTO_HEADING_RE = /^##\s*자동 갱신 영역\s*$/m;
|
|
23
|
+
|
|
24
|
+
function parseArgs(argv) {
|
|
25
|
+
const args = { check: false, dryRun: false, verbose: false };
|
|
26
|
+
for (let i = 2; i < argv.length; i++) {
|
|
27
|
+
const a = argv[i];
|
|
28
|
+
if (a === '--check') args.check = true;
|
|
29
|
+
else if (a === '--dry-run') args.dryRun = true;
|
|
30
|
+
else if (a === '--verbose' || a === '-v') args.verbose = true;
|
|
31
|
+
else if (a === '--help' || a === '-h') { printHelp(); process.exit(0); }
|
|
32
|
+
else { console.error(`알 수 없는 인자: ${a}`); process.exit(2); }
|
|
33
|
+
}
|
|
34
|
+
return args;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function printHelp() {
|
|
38
|
+
console.log(`
|
|
39
|
+
HARNESS sync-claude-md
|
|
40
|
+
|
|
41
|
+
사용법:
|
|
42
|
+
node scripts/sync-claude-md.js [--check] [--dry-run] [--verbose]
|
|
43
|
+
|
|
44
|
+
옵션:
|
|
45
|
+
--check 변경이 필요한지만 검사 (CI 용). 변경 필요하면 exit 1.
|
|
46
|
+
--dry-run 렌더링 결과만 stdout 으로 출력하고 파일은 건드리지 않음.
|
|
47
|
+
--verbose 대상 파일별 상세 로그.
|
|
48
|
+
`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function readJson(rel) { return JSON.parse(fs.readFileSync(path.join(ROOT, rel), 'utf8')); }
|
|
52
|
+
function readYaml(rel) { return YAML.parse(fs.readFileSync(path.join(ROOT, rel), 'utf8')); }
|
|
53
|
+
|
|
54
|
+
function buildAutoSection() {
|
|
55
|
+
const manifest = readYaml('agent.yaml');
|
|
56
|
+
const pkg = readJson('package.json');
|
|
57
|
+
const profilesDoc = readJson('manifests/install-profiles.json');
|
|
58
|
+
|
|
59
|
+
const agentNames = manifest.agents || [];
|
|
60
|
+
const skillNames = manifest.skills || [];
|
|
61
|
+
const commandNames = manifest.commands || [];
|
|
62
|
+
const hookList = manifest.hooks?.active || [];
|
|
63
|
+
const profileNames = Object.keys(profilesDoc.profiles || {});
|
|
64
|
+
const harnessNames = (manifest.harnesses || []).map(h => h.name);
|
|
65
|
+
|
|
66
|
+
// agents/<name>.md frontmatter 에서 provider/model/sandbox 추출
|
|
67
|
+
const rows = [];
|
|
68
|
+
for (const a of agentNames) {
|
|
69
|
+
const file = path.join(ROOT, 'agents', `${a}.md`);
|
|
70
|
+
if (!fs.existsSync(file)) {
|
|
71
|
+
rows.push({ name: a, provider: '?', model: '?', sandbox: '?' });
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
const content = fs.readFileSync(file, 'utf8');
|
|
75
|
+
const fmMatch = content.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
76
|
+
const fm = fmMatch ? YAML.parse(fmMatch[1]) : {};
|
|
77
|
+
rows.push({
|
|
78
|
+
name: a,
|
|
79
|
+
provider: fm.provider || '?',
|
|
80
|
+
model: fm.model || '?',
|
|
81
|
+
sandbox: fm.sandbox || (Array.isArray(fm.disallowedTools) && fm.disallowedTools.length ? 'read-only' : 'full'),
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const lines = [];
|
|
86
|
+
lines.push(`<!-- HARNESS:START version=${pkg.version} -->`);
|
|
87
|
+
lines.push('<!-- 이 영역은 scripts/sync-claude-md.js 가 자동 갱신한다. 직접 편집 금지. -->');
|
|
88
|
+
lines.push('');
|
|
89
|
+
lines.push('## 카탈로그 요약');
|
|
90
|
+
lines.push('');
|
|
91
|
+
lines.push(`- agents: ${agentNames.length}`);
|
|
92
|
+
lines.push(`- skills: ${skillNames.length}`);
|
|
93
|
+
lines.push(`- commands: ${commandNames.length}${commandNames.length ? ' (legacy compat)' : ''}`);
|
|
94
|
+
lines.push(`- hooks: ${hookList.length}${hookList.length ? ' (' + hookList.join(', ') + ')' : ''}`);
|
|
95
|
+
lines.push(`- profiles: ${profileNames.join(', ')}`);
|
|
96
|
+
lines.push(`- harnesses: ${harnessNames.join(', ')}`);
|
|
97
|
+
lines.push('');
|
|
98
|
+
|
|
99
|
+
lines.push('## 에이전트 → 모델 매트릭스');
|
|
100
|
+
lines.push('');
|
|
101
|
+
lines.push('| Agent | Provider | Model | Sandbox |');
|
|
102
|
+
lines.push('|---|---|---|---|');
|
|
103
|
+
for (const r of rows) {
|
|
104
|
+
lines.push(`| ${r.name} | ${r.provider} | ${r.model} | ${r.sandbox} |`);
|
|
105
|
+
}
|
|
106
|
+
lines.push('');
|
|
107
|
+
|
|
108
|
+
lines.push('## 핵심 명령어');
|
|
109
|
+
lines.push('');
|
|
110
|
+
lines.push('```bash');
|
|
111
|
+
lines.push('harness install --plan --profile core # 설치 dry-run');
|
|
112
|
+
lines.push('harness ask "<task>" # question gate, no project mutation');
|
|
113
|
+
lines.push('harness team "<task>" # read-only worker handoffs');
|
|
114
|
+
lines.push('harness work "<task>" # single executor implement handoff');
|
|
115
|
+
lines.push('harness verify "<task>" --session <id> # Codex-only verification');
|
|
116
|
+
lines.push('harness gate status --session <id> # inspect or resolve HUMAN_GATE state');
|
|
117
|
+
lines.push('harness ship "<task>" --session <id> # ship/no-ship readiness handoff');
|
|
118
|
+
lines.push('harness apply --session <id> # apply verified SHIP_READY live-work diff');
|
|
119
|
+
lines.push('harness run "<task>" --session <id> # work -> verify -> ship, optional --apply');
|
|
120
|
+
lines.push('harness review "<task>" [--secure|--fast|--no-ship] # legacy full cycle');
|
|
121
|
+
lines.push('harness review-cycle "<task>" [--secure|--fast|--no-ship] # explicit legacy alias');
|
|
122
|
+
lines.push('harness plan "<task>"');
|
|
123
|
+
lines.push('harness self-review');
|
|
124
|
+
lines.push('harness codex-review # 단계 5 단독');
|
|
125
|
+
lines.push('harness sessions');
|
|
126
|
+
lines.push('harness costs --since=7d');
|
|
127
|
+
lines.push('```');
|
|
128
|
+
lines.push('');
|
|
129
|
+
|
|
130
|
+
lines.push('## State 경로');
|
|
131
|
+
lines.push('');
|
|
132
|
+
const st = manifest.state || {};
|
|
133
|
+
lines.push(`- 세션: \`${st.session_dir || '.harness/state/sessions'}/<id>/{prd.json,progress.txt,notepad.md,handoffs/}\``);
|
|
134
|
+
lines.push(`- 프로젝트: \`${st.project_memory || '.harness/project-memory.json'}\` + \`WORKING-CONTEXT.md\``);
|
|
135
|
+
lines.push(`- 글로벌: \`${st.global_instincts || '~/.harness/instincts'}/\` + \`${st.costs || '.harness/costs.jsonl'}\``);
|
|
136
|
+
lines.push('');
|
|
137
|
+
|
|
138
|
+
lines.push('## 매직 키워드 → 스킬 (명시 옵트인만)');
|
|
139
|
+
lines.push('');
|
|
140
|
+
lines.push('자동 활성 키워드 감지는 **사용**하지 않는다. 사용자 룰("확인 후 실행") 우선. 모든 스킬은 슬래시 명령(`/claude-led-codex-review`) 또는 CLI(`harness review`) 로 명시 호출.');
|
|
141
|
+
lines.push('');
|
|
142
|
+
|
|
143
|
+
lines.push('## 핸드오프 5필드');
|
|
144
|
+
lines.push('');
|
|
145
|
+
lines.push('Decided / Rejected / Risks / Files / Remaining — 10~20줄.');
|
|
146
|
+
lines.push('');
|
|
147
|
+
lines.push('<!-- HARNESS:END -->');
|
|
148
|
+
|
|
149
|
+
return lines.join('\n') + '\n';
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function applyToContent(content, autoSection) {
|
|
153
|
+
const sIdx = content.search(START_RE);
|
|
154
|
+
const eIdx = content.search(END_RE);
|
|
155
|
+
|
|
156
|
+
if (sIdx !== -1 && eIdx !== -1 && sIdx < eIdx) {
|
|
157
|
+
// 두 마커 사이를 통째로 교체. 끝 마커 라인 끝까지 포함.
|
|
158
|
+
const endLineEnd = content.indexOf('\n', eIdx);
|
|
159
|
+
const tail = endLineEnd === -1 ? '' : content.slice(endLineEnd + 1);
|
|
160
|
+
return content.slice(0, sIdx) + autoSection + (tail.length ? tail : '');
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// 마커 없음 → "## 자동 갱신 영역" 다음 줄부터 EOF 까지 자동 컨텐츠로 본다.
|
|
164
|
+
// 그 영역을 통째로 새 마커 블록으로 갈아낀다 (사용자 작성 영역은 헤딩 위쪽이므로 보존).
|
|
165
|
+
const headingMatch = AUTO_HEADING_RE.exec(content);
|
|
166
|
+
if (headingMatch) {
|
|
167
|
+
const headingEnd = content.indexOf('\n', headingMatch.index);
|
|
168
|
+
const before = content.slice(0, headingEnd + 1);
|
|
169
|
+
return before + '\n' + autoSection;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// 헤딩도 없으면 파일 끝에 추가
|
|
173
|
+
return content.replace(/\s*$/, '\n\n## 자동 갱신 영역\n\n') + autoSection;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function processFile(file, autoSection, args) {
|
|
177
|
+
if (!fs.existsSync(file)) {
|
|
178
|
+
if (args.verbose) console.log(`[SKIP] ${path.relative(ROOT, file)} 없음`);
|
|
179
|
+
return { changed: false, skipped: true };
|
|
180
|
+
}
|
|
181
|
+
const before = fs.readFileSync(file, 'utf8');
|
|
182
|
+
const after = applyToContent(before, autoSection);
|
|
183
|
+
const changed = before !== after;
|
|
184
|
+
if (args.verbose || changed) {
|
|
185
|
+
console.log(`[${changed ? 'DIFF' : ' OK '}] ${path.relative(ROOT, file)}`);
|
|
186
|
+
}
|
|
187
|
+
if (changed && !args.dryRun && !args.check) {
|
|
188
|
+
fs.writeFileSync(file, after);
|
|
189
|
+
}
|
|
190
|
+
return { changed, skipped: false };
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function main() {
|
|
194
|
+
const args = parseArgs(process.argv);
|
|
195
|
+
const autoSection = buildAutoSection();
|
|
196
|
+
|
|
197
|
+
if (args.dryRun) {
|
|
198
|
+
process.stdout.write(autoSection);
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
let anyChanged = false;
|
|
203
|
+
for (const f of TARGETS) {
|
|
204
|
+
const r = processFile(f, autoSection, args);
|
|
205
|
+
if (r.changed) anyChanged = true;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (args.check) {
|
|
209
|
+
if (anyChanged) {
|
|
210
|
+
console.error('CLAUDE.md 자동 영역이 카탈로그와 어긋남. `node scripts/sync-claude-md.js` 실행 필요.');
|
|
211
|
+
process.exit(1);
|
|
212
|
+
}
|
|
213
|
+
console.log('CLAUDE.md 자동 영역 동기화 OK.');
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
console.log(anyChanged ? '동기화 완료.' : '변경 없음.');
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
main();
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// Claude Code CLI live smoke.
|
|
2
|
+
// Uses the local Claude subscription/OAuth session by default, not ANTHROPIC_API_KEY.
|
|
3
|
+
|
|
4
|
+
import { runClaude } from '../agents/runners/claude.js';
|
|
5
|
+
|
|
6
|
+
const result = await runClaude({
|
|
7
|
+
agent: 'claude-live-smoke',
|
|
8
|
+
stage: 'plan',
|
|
9
|
+
task: 'Return a minimal HARNESS handoff JSON for a local Claude Code CLI smoke test.',
|
|
10
|
+
model: process.env.HARNESS_CLAUDE_SMOKE_MODEL || 'sonnet',
|
|
11
|
+
sandbox: 'read-only',
|
|
12
|
+
disallowedTools: [],
|
|
13
|
+
promptBody: [
|
|
14
|
+
'Return only one JSON object.',
|
|
15
|
+
'Required shape:',
|
|
16
|
+
'{"decided":"...","rejected":"","risks":"","files":["SMOKE.md"],"remaining":"","issues":[],"verdict":"approve","confidence":0.9}',
|
|
17
|
+
].join('\n'),
|
|
18
|
+
context: {},
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const ok = result?.verdict === 'approve'
|
|
22
|
+
&& Array.isArray(result.files)
|
|
23
|
+
&& result.files.includes('SMOKE.md');
|
|
24
|
+
|
|
25
|
+
if (!ok) {
|
|
26
|
+
console.error(JSON.stringify(result, null, 2));
|
|
27
|
+
throw new Error('Claude live smoke returned an unexpected handoff');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
console.log(`Claude CLI live smoke PASS: verdict=${result.verdict}, files=${result.files.join(', ')}`);
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// codex runner 단독 live 검증 (P2-c).
|
|
2
|
+
//
|
|
3
|
+
// 환경: codex CLI (≥0.124) + ChatGPT 로그인 세션.
|
|
4
|
+
// OPENAI_API_KEY 는 기본 차단되며, 종량제 opt-in 때만 HARNESS_AUTH_ALLOW_ENV_OVERRIDE=1 과 함께 사용.
|
|
5
|
+
// 비용: 1회 호출 약 ~15K 토큰 (ChatGPT 구독 시 무과금).
|
|
6
|
+
//
|
|
7
|
+
// 사용:
|
|
8
|
+
// node scripts/verify/codex-live.js
|
|
9
|
+
//
|
|
10
|
+
// 옵트인 env:
|
|
11
|
+
// HARNESS_CODEX_PROFILE_REVIEW ~/.codex/config.toml 의 review 프로파일 명
|
|
12
|
+
// HARNESS_CODEX_PROFILE 공통 프로파일 (위가 없을 때 fallback)
|
|
13
|
+
// HARNESS_CODEX_TIMEOUT_S codex 호출 timeout (디폴트 180)
|
|
14
|
+
//
|
|
15
|
+
// 종료 코드: 0 = PASS, 1 = FAIL.
|
|
16
|
+
// codex CLI 메이저 업데이트 시 본 스크립트로 회귀 감지 권장.
|
|
17
|
+
|
|
18
|
+
import { runCodex } from '../agents/runners/codex.js';
|
|
19
|
+
|
|
20
|
+
const args = {
|
|
21
|
+
stage: 'codex-review',
|
|
22
|
+
context: {
|
|
23
|
+
diff: "+ console.log('password:', password);\n+ const token = req.headers.authorization;\n+ db.query(`SELECT * FROM users WHERE id = ${userId}`);",
|
|
24
|
+
priorHandoffs: [
|
|
25
|
+
{
|
|
26
|
+
stage: 'self-review',
|
|
27
|
+
decided: 'approve',
|
|
28
|
+
files: ['auth/login.js'],
|
|
29
|
+
verdict: 'approve'
|
|
30
|
+
}
|
|
31
|
+
],
|
|
32
|
+
prd: {
|
|
33
|
+
goal: 'login 디버그 로그 추가',
|
|
34
|
+
acceptanceCriteria: [
|
|
35
|
+
{ id: 'ac-1', text: '로그인 실패 사유 식별 가능', passes: true }
|
|
36
|
+
]
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
const start = Date.now();
|
|
43
|
+
const result = await runCodex(args);
|
|
44
|
+
const ms = Date.now() - start;
|
|
45
|
+
|
|
46
|
+
const issues = result.issues || [];
|
|
47
|
+
const verdict = result.verdict;
|
|
48
|
+
const validVerdicts = ['approve', 'approve_with_fixes', 'block'];
|
|
49
|
+
if (!validVerdicts.includes(verdict)) {
|
|
50
|
+
throw new Error(`unexpected verdict: ${verdict} (expected one of: ${validVerdicts.join(', ')})`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
console.log(JSON.stringify(result, null, 2));
|
|
54
|
+
console.log(`\n[PASS] verdict=${verdict} issues=${issues.length} duration_ms=${ms}`);
|
|
55
|
+
process.exit(0);
|
|
56
|
+
} catch (e) {
|
|
57
|
+
console.error(`[FAIL] ${e.message}`);
|
|
58
|
+
if (e.stack) console.error(e.stack);
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// Gemini CLI live smoke.
|
|
2
|
+
// Uses the local Gemini/gcloud session by default, not GEMINI_API_KEY.
|
|
3
|
+
|
|
4
|
+
import { runGemini } from '../agents/runners/gemini.js';
|
|
5
|
+
|
|
6
|
+
const started = Date.now();
|
|
7
|
+
let result;
|
|
8
|
+
try {
|
|
9
|
+
result = await runGemini({
|
|
10
|
+
agent: 'gemini-live-smoke',
|
|
11
|
+
stage: 'ideate',
|
|
12
|
+
task: [
|
|
13
|
+
'Return a minimal HARNESS handoff JSON for a local Gemini CLI smoke test.',
|
|
14
|
+
'Do not browse, do not call tools, and do not modify files.',
|
|
15
|
+
].join(' '),
|
|
16
|
+
model: process.env.HARNESS_GEMINI_SMOKE_MODEL || 'gemini-2.5-flash',
|
|
17
|
+
sandbox: 'read-only',
|
|
18
|
+
disallowedTools: ['Write', 'Edit', 'Bash'],
|
|
19
|
+
promptBody: [
|
|
20
|
+
'Return only one JSON object.',
|
|
21
|
+
'Required shape:',
|
|
22
|
+
'{"decided":"Gemini CLI smoke passed","rejected":"","risks":"","files":["GEMINI_SMOKE.md"],"remaining":"","issues":[],"verdict":"approve","confidence":0.9}',
|
|
23
|
+
].join('\n'),
|
|
24
|
+
context: {},
|
|
25
|
+
});
|
|
26
|
+
} catch (e) {
|
|
27
|
+
const msg = String(e?.message || e);
|
|
28
|
+
if (/Auth method|GEMINI_API_KEY|GOOGLE_GENAI_USE/i.test(msg)) {
|
|
29
|
+
console.error([
|
|
30
|
+
'Gemini CLI is installed, but headless auth is not configured.',
|
|
31
|
+
'Run interactive `gemini` once and choose Login with Google, or configure Vertex/ADC auth for headless mode.',
|
|
32
|
+
'HARNESS still blocks GEMINI_API_KEY/GOOGLE_API_KEY by default; use delegated Google/Gemini auth unless you explicitly opt into API-key billing.',
|
|
33
|
+
].join('\n'));
|
|
34
|
+
process.exit(2);
|
|
35
|
+
}
|
|
36
|
+
throw e;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const ok = result?.verdict === 'approve'
|
|
40
|
+
&& Array.isArray(result.files)
|
|
41
|
+
&& result.files.includes('GEMINI_SMOKE.md');
|
|
42
|
+
|
|
43
|
+
if (!ok) {
|
|
44
|
+
console.error(JSON.stringify(result, null, 2));
|
|
45
|
+
throw new Error('Gemini live smoke returned an unexpected handoff');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
console.log(`Gemini CLI live smoke PASS: verdict=${result.verdict}, files=${result.files.join(', ')}, duration_ms=${Date.now() - started}`);
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Rust runtime verification smoke: cargo build/test/clippy plus CLI IPC checks.
|
|
3
|
+
|
|
4
|
+
import fs from 'node:fs';
|
|
5
|
+
import os from 'node:os';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import { spawnSync } from 'node:child_process';
|
|
8
|
+
import { fileURLToPath } from 'node:url';
|
|
9
|
+
import { resolveCli } from '../core/cli-resolver.js';
|
|
10
|
+
|
|
11
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
const root = path.resolve(__dirname, '..', '..');
|
|
13
|
+
const runtimeDir = path.join(root, 'runtime');
|
|
14
|
+
|
|
15
|
+
const cargo = resolveCargo();
|
|
16
|
+
const runtimeBin = path.join(
|
|
17
|
+
runtimeDir,
|
|
18
|
+
'target',
|
|
19
|
+
'release',
|
|
20
|
+
process.platform === 'win32' ? 'harness-runtime.exe' : 'harness-runtime',
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
console.log(`cargo: ${cargo}`);
|
|
24
|
+
|
|
25
|
+
run(cargo, ['build', '--release'], { cwd: runtimeDir, inherit: true });
|
|
26
|
+
run(cargo, ['test'], { cwd: runtimeDir, inherit: true });
|
|
27
|
+
run(cargo, ['clippy', '--all-targets', '--', '-D', 'warnings'], { cwd: runtimeDir, inherit: true });
|
|
28
|
+
|
|
29
|
+
if (!fs.existsSync(runtimeBin)) {
|
|
30
|
+
throw new Error(`runtime binary not found after build: ${runtimeBin}`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const smokeRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-runtime-smoke-'));
|
|
34
|
+
try {
|
|
35
|
+
run(runtimeBin, ['--help'], { cwd: root, inherit: true });
|
|
36
|
+
run(runtimeBin, ['--root', smokeRoot, 'init'], { cwd: root, inherit: true });
|
|
37
|
+
run(runtimeBin, ['--root', smokeRoot, 'status'], { cwd: root, inherit: true });
|
|
38
|
+
|
|
39
|
+
const ping = run(runtimeBin, ['--root', smokeRoot, 'ipc'], {
|
|
40
|
+
cwd: root,
|
|
41
|
+
input: '{"id":1,"method":"ping"}\n',
|
|
42
|
+
});
|
|
43
|
+
const response = JSON.parse(ping.trim());
|
|
44
|
+
if (response?.id !== 1 || response?.result?.pong !== true || response.error) {
|
|
45
|
+
throw new Error(`unexpected IPC ping response: ${ping}`);
|
|
46
|
+
}
|
|
47
|
+
console.log(`ipc ping: ${ping.trim()}`);
|
|
48
|
+
} finally {
|
|
49
|
+
fs.rmSync(smokeRoot, { recursive: true, force: true });
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
console.log('Runtime verification PASS');
|
|
53
|
+
|
|
54
|
+
function resolveCargo() {
|
|
55
|
+
if (process.env.HARNESS_CARGO) return process.env.HARNESS_CARGO;
|
|
56
|
+
|
|
57
|
+
const onPath = resolveCli('cargo');
|
|
58
|
+
if (onPath) return onPath;
|
|
59
|
+
|
|
60
|
+
const exe = process.platform === 'win32' ? 'cargo.exe' : 'cargo';
|
|
61
|
+
const candidates = [
|
|
62
|
+
path.join(os.homedir(), '.cargo', 'bin', exe),
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
if (process.platform === 'win32') {
|
|
66
|
+
candidates.push(
|
|
67
|
+
path.join(os.homedir(), '.rustup', 'toolchains', 'stable-x86_64-pc-windows-msvc', 'bin', exe),
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const found = candidates.find((candidate) => fs.existsSync(candidate));
|
|
72
|
+
if (found) return found;
|
|
73
|
+
|
|
74
|
+
throw new Error([
|
|
75
|
+
'cargo was not found on PATH or in the default rustup locations.',
|
|
76
|
+
'Install Rust from https://rustup.rs or set HARNESS_CARGO to the cargo executable path.',
|
|
77
|
+
].join('\n'));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function run(bin, args, options = {}) {
|
|
81
|
+
const command = [quote(bin), ...args.map(quote)].join(' ');
|
|
82
|
+
console.log(`> ${command}`);
|
|
83
|
+
|
|
84
|
+
const result = spawnSync(bin, args, {
|
|
85
|
+
cwd: options.cwd,
|
|
86
|
+
input: options.input,
|
|
87
|
+
encoding: 'utf8',
|
|
88
|
+
stdio: options.inherit ? 'inherit' : ['pipe', 'pipe', 'pipe'],
|
|
89
|
+
windowsHide: true,
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
if (result.error) throw result.error;
|
|
93
|
+
if (result.status !== 0) {
|
|
94
|
+
const stdout = result.stdout ? `\nstdout:\n${result.stdout}` : '';
|
|
95
|
+
const stderr = result.stderr ? `\nstderr:\n${result.stderr}` : '';
|
|
96
|
+
throw new Error(`${command} exited ${result.status}${stdout}${stderr}`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return result.stdout || '';
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function quote(value) {
|
|
103
|
+
const text = String(value);
|
|
104
|
+
return /\s/.test(text) ? `"${text}"` : text;
|
|
105
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: claude-led-codex-review
|
|
3
|
+
description: "Claude 주도 + Codex 위임 7단계 풀사이클 (idea → ship). Week 1 데모 정전(canon)."
|
|
4
|
+
origin: harness-core
|
|
5
|
+
level: 3
|
|
6
|
+
prerequisites: [tdd-workflow, plan-eng-review]
|
|
7
|
+
conflicts: []
|
|
8
|
+
auto_inject_keywords: []
|
|
9
|
+
tags: [workflow, review, codex, primary]
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# claude-led-codex-review
|
|
13
|
+
|
|
14
|
+
Claude 가 구현하고 Codex 가 의심하는 7단계 풀사이클. **HARNESS 의 정전 워크플로우.** 자동 활성 키워드는 비워두고 명시 호출만 받는다 (사용자 환경의 "확인 후 실행" 류 룰을 우회하지 않기 위함).
|
|
15
|
+
|
|
16
|
+
## 호출
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
harness review "<task>" # 전체 사이클
|
|
20
|
+
harness review "<task>" --secure # + 단계 6 codex-challenge 강제
|
|
21
|
+
harness review "<task>" --fast # 단계 1·6 스킵
|
|
22
|
+
harness review "<task>" --no-ship # 단계 7 생략 (리뷰까지만)
|
|
23
|
+
harness review "<task>" --no-codex # 단계 5·6 스킵 (Codex 검증 생략)
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
또는 슬래시: `/claude-led-codex-review <task> [--flags]`
|
|
27
|
+
|
|
28
|
+
## 7단계
|
|
29
|
+
|
|
30
|
+
| # | 단계 | 담당 | 출력 |
|
|
31
|
+
|---|---|---|---|
|
|
32
|
+
| 1 | ideate | research, planner | `handoffs/01-ideate.md` |
|
|
33
|
+
| 2 | plan | planner (opus, ro) | `prd-<id>.md` + `handoffs/02-plan.md` |
|
|
34
|
+
| 3 | implement | executor (sonnet) + test-engineer | git diff, `handoffs/03-implement.md` |
|
|
35
|
+
| 4 | self-review | code-reviewer (opus, ro) | `handoffs/04-self-review.md` (JSON) |
|
|
36
|
+
| 5 | codex-review | codex-reviewer (별도 세션) | `handoffs/05-codex-review.md` (JSON) |
|
|
37
|
+
| 6 | codex-challenge | codex-challenger (--secure) | `handoffs/06-challenge.md` (JSON) |
|
|
38
|
+
| 7 | ship | doc-writer + git-master | PR + CHANGELOG diff + `handoffs/07-ship.md` |
|
|
39
|
+
|
|
40
|
+
## Stage Routing 표 (필수 / 옵션)
|
|
41
|
+
|
|
42
|
+
| Stage | Required | Optional (escalate) | 트리거 |
|
|
43
|
+
|---|---|---|---|
|
|
44
|
+
| 1 ideate | research, planner | architect | 모호한 요구 |
|
|
45
|
+
| 2 plan | planner | architect | 시스템 설계 |
|
|
46
|
+
| 3 implement | executor | debugger, test-engineer | TDD |
|
|
47
|
+
| 4 self-review | code-reviewer | security-reviewer | auth/crypto / >20파일 |
|
|
48
|
+
| 5 codex-review | codex-reviewer | — | --no-codex 가 아닐 때 |
|
|
49
|
+
| 6 codex-challenge | codex-challenger | — | --secure 또는 sensitive 자동 감지 (--fast 와 동시 사용 불가) |
|
|
50
|
+
| 7 ship | doc-writer, git-master | — | 모든 게이트 PASS |
|
|
51
|
+
|
|
52
|
+
## 단계 5+6 병렬 실행
|
|
53
|
+
|
|
54
|
+
`codex-review` 와 `codex-challenge` 는 같은 입력(`prd` / `priorHandoffs` / `diff`)을 받고 컨텍스트가 독립이라
|
|
55
|
+
orchestrator 가 `Promise.all` 로 동시 호출한다. codex CLI 호출 시간(풀사이클의 가장 큰 비용)을
|
|
56
|
+
1회 비용으로 단축.
|
|
57
|
+
|
|
58
|
+
- 직렬 의미 동일: stage 5 critical 시 stage 6 결과는 폐기 (humanGate 즉시 return)
|
|
59
|
+
- `--no-codex` 이면 stage 5·6 전체 스킵
|
|
60
|
+
- `--fast` 또는 sensitive 미감지 + `--secure` 미지정이면 stage 6 자체 스킵 (병렬 안 함)
|
|
61
|
+
- `--fast` 와 `--secure` 는 의미가 충돌하므로 CLI 가 즉시 거절
|
|
62
|
+
- 코드: `scripts/orchestrators/review.js` 의 5+6 블록
|
|
63
|
+
|
|
64
|
+
## Verdict 처리 + Fix Loop
|
|
65
|
+
|
|
66
|
+
각 단계의 핸드오프는 `verdict: block | approve_with_fixes | approve` 와 issues 배열을 갖는다.
|
|
67
|
+
|
|
68
|
+
### Verdict 자동 판정 룰
|
|
69
|
+
|
|
70
|
+
`scripts/lib/severity.js` 의 `deriveVerdict(issues, opts)` 가 정전(canon).
|
|
71
|
+
|
|
72
|
+
**BLOCK 강성 룰** (하나라도 충족):
|
|
73
|
+
- `critical >= 1`
|
|
74
|
+
- `high > 5` — high 다수는 자동 통과 위험, 추가 검토 강제
|
|
75
|
+
- `confidence < 0.6` — codex 가 자신없게 답할 때 보수 안전망
|
|
76
|
+
|
|
77
|
+
**APPROVE_WITH_FIXES**:
|
|
78
|
+
- `high in [1, 5]`
|
|
79
|
+
- `medium >= 1`
|
|
80
|
+
- `blast_radius >= 10` 이면서 `issues.length >= 1` — 큰 변경은 작은 issue 라도 추가 검토
|
|
81
|
+
|
|
82
|
+
**APPROVE**: 그 외.
|
|
83
|
+
|
|
84
|
+
명시 verdict (`block`/`approve_with_fixes`/`approve`) 가 codex 응답에 있으면 그대로 따르되,
|
|
85
|
+
`high > 5` 또는 `confidence < 0.6` 인 경우는 **block 으로 강등**한다 (codex 가 자신감 없이 approve 한 경우의 안전망).
|
|
86
|
+
|
|
87
|
+
### Fix Loop
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
[5 codex-review] ──verdict──┐
|
|
91
|
+
│
|
|
92
|
+
block / w_fixes ─────▶ [3a fix-loop] ──▶ [4 self] ──▶ [5 codex]
|
|
93
|
+
│
|
|
94
|
+
round++; 한도 = 3
|
|
95
|
+
│
|
|
96
|
+
critical 1+ 또는 round ≥ 3 ──▶ HUMAN_GATE
|
|
97
|
+
│
|
|
98
|
+
approve ─────────────▶ [6? challenge | 7 ship]
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## --secure 자동 활성 조건
|
|
102
|
+
|
|
103
|
+
- 사용자 명시 플래그
|
|
104
|
+
- 변경에 다음 보안 카테고리 키워드 포함 (단어 경계 매칭, 대소문자 무시):
|
|
105
|
+
- 인증·세션·시크릿: `auth`, `crypto`, `payment`, `session`, `permission`, `oauth`, `jwt`, `password`, `secret`
|
|
106
|
+
- 자격증명·토큰: `token`, `apikey`, `api-key`, `api_key`
|
|
107
|
+
- 인증서·전송보안: `cert`, `tls`, `ssl`, `mtls`
|
|
108
|
+
- 웹 보안: `csrf`, `cors`, `xss`
|
|
109
|
+
- 외부 검증: `webhook`
|
|
110
|
+
- 단계 5 verdict = block 후 round = 2 진입 시
|
|
111
|
+
|
|
112
|
+
## Fast 모드
|
|
113
|
+
|
|
114
|
+
`--fast`:
|
|
115
|
+
- 단계 1 (office-hours) 스킵 → 사용자 한 줄 → 바로 단계 2
|
|
116
|
+
- 단계 6 (challenge) 스킵
|
|
117
|
+
- `--secure` 와 같이 오면 실패. 보안 검증이 필요하면 `--secure` 만 사용
|
|
118
|
+
|
|
119
|
+
단순 리팩토링·문서 변경에 권장. 보안·인증 코드에는 금지.
|
|
120
|
+
|
|
121
|
+
## 핸드오프 5필드
|
|
122
|
+
|
|
123
|
+
모든 단계의 마크다운 핸드오프는 `Decided / Rejected / Risks / Files / Remaining` 5필드를 지킨다. 자유 산문 금지. JSON 부속 (`schemas/handoff.schema.json`) 은 기계 처리용.
|
|
124
|
+
|
|
125
|
+
## 비용 / 토큰 정책
|
|
126
|
+
|
|
127
|
+
- Opus 에이전트(architect / planner / code-reviewer)는 read-only 강제 → 변경 비용 0.
|
|
128
|
+
- Codex / Gemini 워커는 사용자 ChatGPT Pro / Gemini Pro CLI 를 통해 호출 → Anthropic 토큰 소비 0.
|
|
129
|
+
- eco mode 시 opus → sonnet, sonnet → haiku (단계 4·5 는 sonnet floor).
|
|
130
|
+
|
|
131
|
+
## 단계별 상세
|
|
132
|
+
|
|
133
|
+
본 SKILL.md 가 단계별 상세의 정전(canon). 추가 노트 파일은 단계가 복잡해질 때 점진 추가.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: plan-eng-review
|
|
3
|
+
description: "엔지니어링 매니저 모드 계획 리뷰. 아키텍처 / 데이터 흐름 / 엣지 케이스 / 테스트 / 성능."
|
|
4
|
+
origin: harness-core
|
|
5
|
+
level: 2
|
|
6
|
+
prerequisites: []
|
|
7
|
+
conflicts: []
|
|
8
|
+
tags: [planning, review]
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# plan-eng-review
|
|
12
|
+
|
|
13
|
+
planner / architect 가 산출한 PRD 와 단계 분해를 락인 직전에 한 번 더 본다. 구현 시작 전에 잡을 수 있는 아키텍처 이슈를 잡는다.
|
|
14
|
+
|
|
15
|
+
## 호출
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
harness plan-eng-review <prd-path>
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
claude-led-codex-review 단계 2 의 일부로 자동 호출된다.
|
|
22
|
+
|
|
23
|
+
## 체크리스트
|
|
24
|
+
|
|
25
|
+
### 아키텍처
|
|
26
|
+
- [ ] 모듈 경계 / 책임 명확
|
|
27
|
+
- [ ] 의존 방향 (DI, 인터페이스 분리)
|
|
28
|
+
- [ ] 결합도 / 응집도
|
|
29
|
+
|
|
30
|
+
### 데이터 흐름
|
|
31
|
+
- [ ] 입력 / 출력 / 부수 효과 표
|
|
32
|
+
- [ ] 트랜잭션 경계
|
|
33
|
+
- [ ] 롤백 / 복구
|
|
34
|
+
|
|
35
|
+
### 엣지 케이스
|
|
36
|
+
- [ ] null / empty / 음수 / 매우 큰 값
|
|
37
|
+
- [ ] 동시성 / race condition
|
|
38
|
+
- [ ] 부분 실패 (외부 API timeout, 네트워크 끊김)
|
|
39
|
+
|
|
40
|
+
### 테스트 커버리지
|
|
41
|
+
- [ ] 단위 / 통합 / E2E 분배
|
|
42
|
+
- [ ] 80% 게이트
|
|
43
|
+
- [ ] 회귀 케이스
|
|
44
|
+
|
|
45
|
+
### 성능
|
|
46
|
+
- [ ] N+1 / 메모리 / 알고리즘 복잡도
|
|
47
|
+
- [ ] 벤치 기준선
|
|
48
|
+
|
|
49
|
+
## 출력
|
|
50
|
+
|
|
51
|
+
체크리스트 채워진 마크다운 + 발견 이슈 (severity 분류). 이슈는 PRD 에 다시 반영 (acceptance criteria 추가 또는 비목표 명시).
|