@kodevibe/harness 0.11.4 → 0.11.5
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/README.ko.md +4 -3
- package/README.md +4 -3
- package/package.json +1 -1
- package/src/guard.js +137 -1
- package/src/init.js +21 -0
package/README.ko.md
CHANGED
|
@@ -201,7 +201,7 @@ npm run harness:llm-bench:real
|
|
|
201
201
|
|
|
202
202
|
| IDE | 이럴 때 고르세요 | 디스패처 (always-on) | 스킬 | 에이전트 |
|
|
203
203
|
|-----|--------------------|---------------------|------|----------|
|
|
204
|
-
| **VS Code Copilot** | VS Code를 주로 쓰고 GitHub Copilot Chat 사용. | `.github/copilot-instructions.md` | `.github/skills/*/SKILL.md` | `.github/agents/*.agent.md` |
|
|
204
|
+
| **VS Code Copilot** | VS Code를 주로 쓰고 GitHub Copilot Chat 사용. | `.github/copilot-instructions.md` (+ 짧은 `AGENTS.md` anchor) | `.github/skills/*/SKILL.md` | `.github/agents/*.agent.md` |
|
|
205
205
|
| **Claude Code** | 터미널/Claude Code CLI 선호. | `CLAUDE.md` (+ `.claude/rules/core.md`) | `.claude/skills/*/SKILL.md` | `.claude/agents/*.md` |
|
|
206
206
|
| **Cursor** | Cursor 에디터 사용. | `.cursor/rules/core.mdc` (+ `AGENTS.md`) | `.agents/skills/*/SKILL.md` (cross-tool) | `.cursor/rules/<agent>.mdc` |
|
|
207
207
|
| **Codex** | OpenAI Codex CLI 서브에이전트 사용. | `AGENTS.md` | `.agents/skills/*/SKILL.md` | `.codex/agents/*.toml` |
|
|
@@ -409,7 +409,7 @@ Bootstrap이 `docs/crew/`, `docs/PM/`, `docs/Analyst/`, `docs/ARB/`에서 crew
|
|
|
409
409
|
|
|
410
410
|
## 로드맵
|
|
411
411
|
|
|
412
|
-
kode:harness는 현재 **v0.11.
|
|
412
|
+
kode:harness는 현재 **v0.11.5** — R16 recovery guard 기반 위에 R17 governance hardening(Crew Validation Tracker drift, dependency-map interface log drift, VS Code root instruction anchoring)을 추가했습니다.
|
|
413
413
|
|
|
414
414
|
| 단계 | 버전 | 상태 | 초점 |
|
|
415
415
|
|------|------|------|------|
|
|
@@ -427,7 +427,8 @@ kode:harness는 현재 **v0.11.4** — v0.11 proof-first 기반 위에 R16 recov
|
|
|
427
427
|
| **Uninstall Safety** | v0.11.1 | ✅ 완료 | Manifest 기반 uninstall, state 기본 보존, shared owner 복원, purge cleanup |
|
|
428
428
|
| **Deterministic Release Guard** | v0.11.2 | ✅ 완료 | R1-R10 guard scripts, package-boundary scan, dependency-map scan, R10 manifest-sealed bench workflow |
|
|
429
429
|
| **Experiment Hardening** | v0.11.3 | ✅ 완료 | R15 Recent Changes integrity, Wave Scope boundary drift checks, enum/filter coverage honesty |
|
|
430
|
-
| **Recovery Hardening** | v0.11.4 | ✅
|
|
430
|
+
| **Recovery Hardening** | v0.11.4 | ✅ 완료 | R16 false PASS claim guard, surface-specific Story Contract checks, reviewer dependency evidence, dirty wrap-up guard |
|
|
431
|
+
| **Governance Hardening** | v0.11.5 | ✅ 현재 | R17 Crew Validation Tracker sync, dependency-map interface log guard, VS Code AGENTS.md instruction anchor |
|
|
431
432
|
| **Docs Bridge** | v0.11.1 | 🧪 Experimental | Project Docs Hub Index, docs-bridge 스킬, visibility 경계를 가진 로컬 docs hub 인덱스 |
|
|
432
433
|
| **Safety & Branding** | v0.9.6 | ✅ 완료 | init overwrite 백업, 배포 파일 pm 네이밍 정리, LICENSE 브랜딩 정리 |
|
|
433
434
|
| **Validation** | v1.0 | 🔜 다음 | 실사용 검증, 사용자 피드백 수집 |
|
package/README.md
CHANGED
|
@@ -211,7 +211,7 @@ Not sure which to pick? Use the IDE you already code in — each install path is
|
|
|
211
211
|
|
|
212
212
|
| IDE | Pick this if… | Dispatcher (always-on) | Skills | Agents |
|
|
213
213
|
|-----|---------------|----------------------|--------|--------|
|
|
214
|
-
| **VS Code Copilot** | You use VS Code daily and have GitHub Copilot Chat. | `.github/copilot-instructions.md` | `.github/skills/*/SKILL.md` | `.github/agents/*.agent.md` |
|
|
214
|
+
| **VS Code Copilot** | You use VS Code daily and have GitHub Copilot Chat. | `.github/copilot-instructions.md` (+ short `AGENTS.md` anchor) | `.github/skills/*/SKILL.md` | `.github/agents/*.agent.md` |
|
|
215
215
|
| **Claude Code** | You prefer Claude in the terminal / Claude Code CLI. | `CLAUDE.md` (+ `.claude/rules/core.md`) | `.claude/skills/*/SKILL.md` | `.claude/agents/*.md` |
|
|
216
216
|
| **Cursor** | You use Cursor as your editor. | `.cursor/rules/core.mdc` (+ `AGENTS.md`) | `.agents/skills/*/SKILL.md` (cross-tool) | `.cursor/rules/<agent>.mdc` |
|
|
217
217
|
| **Codex** | You use OpenAI Codex CLI subagents. | `AGENTS.md` | `.agents/skills/*/SKILL.md` | `.codex/agents/*.toml` |
|
|
@@ -389,7 +389,7 @@ It adds a Project Docs Hub Index to `project-brief.md` with each local source, r
|
|
|
389
389
|
|
|
390
390
|
## Roadmap
|
|
391
391
|
|
|
392
|
-
kode:harness is at **v0.11.
|
|
392
|
+
kode:harness is at **v0.11.5** — adds R17 governance hardening for Crew Validation Tracker drift, dependency-map interface log drift, and VS Code root instruction anchoring on top of the R16 recovery guard foundation.
|
|
393
393
|
|
|
394
394
|
| Phase | Version | Status | Focus |
|
|
395
395
|
|---|---|---|---|
|
|
@@ -407,7 +407,8 @@ kode:harness is at **v0.11.4** — adds R16 recovery hardening for false clean s
|
|
|
407
407
|
| **Uninstall Safety** | v0.11.1 | ✅ Complete | Manifest-based uninstall, default state preservation, shared owner restore, purge cleanup |
|
|
408
408
|
| **Deterministic Release Guard** | v0.11.2 | ✅ Complete | R1-R10 guard scripts, package-boundary scan, dependency-map scan, R10 manifest-sealed bench workflow |
|
|
409
409
|
| **Experiment Hardening** | v0.11.3 | ✅ Complete | R15 Recent Changes integrity, Wave Scope boundary drift checks, enum/filter coverage honesty, R15 bench scenarios |
|
|
410
|
-
| **Recovery Hardening** | v0.11.4 | ✅
|
|
410
|
+
| **Recovery Hardening** | v0.11.4 | ✅ Complete | R16 false PASS claim guard, surface-specific Story Contract checks, reviewer dependency evidence, dirty wrap-up guard |
|
|
411
|
+
| **Governance Hardening** | v0.11.5 | ✅ Current | R17 Crew Validation Tracker sync, dependency-map interface log guard, VS Code AGENTS.md instruction anchor |
|
|
411
412
|
| **Docs Bridge** | v0.11.1 | 🧪 Experimental | Project Docs Hub Index, docs-bridge skill, local docs hub index with visibility boundaries |
|
|
412
413
|
| **Safety & Branding** | v0.9.6 | ✅ Done | init overwrite backups, shipped pm naming cleanup, LICENSE branding cleanup |
|
|
413
414
|
| **Validation** | v1.0 | 🔜 Next | Real-world project adoption, user feedback collection |
|
package/package.json
CHANGED
package/src/guard.js
CHANGED
|
@@ -23,6 +23,8 @@
|
|
|
23
23
|
// R15 checkRecentChangesIntegrity — wrap-up must not corrupt state sections
|
|
24
24
|
// R16 checkSelfVerifyClaim — claimed PASS must match deterministic guard
|
|
25
25
|
// R16 checkReviewerAuditEvidence — scope audits must cite real deps/imports
|
|
26
|
+
// R17 checkCrewValidationSync — Crew Validation Tracker follows done work
|
|
27
|
+
// R17 checkDependencyInterfaceLog — interface-affecting features update deps
|
|
26
28
|
//
|
|
27
29
|
// Severity: 'error' blocks the commit (exit 1). 'warn' is informational.
|
|
28
30
|
|
|
@@ -522,7 +524,7 @@ function checkLearnCompletion({ projectState = '', features = '', quiet = false
|
|
|
522
524
|
|
|
523
525
|
function splitPathList(value) {
|
|
524
526
|
return String(value || '')
|
|
525
|
-
.split(/
|
|
527
|
+
.split(/(?:,|;|<br\s*\/?>|`)+|\s{2,}/i)
|
|
526
528
|
.map((v) => v.trim())
|
|
527
529
|
.filter((v) => v && !/^n\/a$/i.test(v) && !/^\(?none\)?$/i.test(v));
|
|
528
530
|
}
|
|
@@ -584,6 +586,124 @@ function checkStateSync({ projectState = '', features = '', dependencyMap = '' }
|
|
|
584
586
|
return violations;
|
|
585
587
|
}
|
|
586
588
|
|
|
589
|
+
// ─── Crew Validation Tracker Sync Gate (R17) ────────────────────────
|
|
590
|
+
|
|
591
|
+
const COMPLETE_STATUS = /✅|done|proven|pass(?:ed)?|reviewed|complete|완료|통과/i;
|
|
592
|
+
const INCOMPLETE_STATUS = /planned|pending|todo|not[_ -]?proven|not[_ -]?verified|⬜|🟡|🔄|대기|계획|미완료/i;
|
|
593
|
+
const REQUIREMENT_ID_RE = /\b(?:FR|KPI|ARB|ARB-FAIL)[-_]?\d+\b/gi;
|
|
594
|
+
const BASELINE_REQUIREMENTS = new Set(['FR-001', 'FR-002']);
|
|
595
|
+
|
|
596
|
+
function normalizedRequirementId(value) {
|
|
597
|
+
return String(value || '').toUpperCase().replace('_', '-');
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
function extractRequirementIds(value) {
|
|
601
|
+
return [...new Set((String(value || '').match(REQUIREMENT_ID_RE) || [])
|
|
602
|
+
.map(normalizedRequirementId))];
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
function trackerRowsFromBrief(projectBrief = '') {
|
|
606
|
+
const visible = stripHtmlComments(projectBrief);
|
|
607
|
+
const section = getSection(visible, 'Validation Tracker') || '';
|
|
608
|
+
return parseMarkdownTable(section);
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
function trackerRequirement(row) {
|
|
612
|
+
return row.Requirement || row.FR || row.KPI || row.ARB || row.Item || row.Control || '';
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
function trackerStory(row) {
|
|
616
|
+
return row.Story || row.Stories || row['Story ID'] || '';
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
function trackerStatus(row) {
|
|
620
|
+
return row.Status || row.status || '';
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
/**
|
|
624
|
+
* Crew mode adds project-brief.md Validation Tracker as the FR/KPI/ARB source
|
|
625
|
+
* of truth. A recurring Qwen failure was marking features/project-state done
|
|
626
|
+
* while leaving tracker rows Planned. This gate makes that drift blocking.
|
|
627
|
+
*
|
|
628
|
+
* @param {{projectState?: string, features?: string, projectBrief?: string}} files
|
|
629
|
+
* @returns {Array}
|
|
630
|
+
*/
|
|
631
|
+
function checkCrewValidationSync({ projectState = '', features = '', projectBrief = '' } = {}) {
|
|
632
|
+
const violations = [];
|
|
633
|
+
const trackerRows = trackerRowsFromBrief(projectBrief);
|
|
634
|
+
if (trackerRows.length === 0) return violations;
|
|
635
|
+
|
|
636
|
+
const doneStoryIds = parseMarkdownTable(getSection(stripHtmlComments(projectState), 'Story Status') || '')
|
|
637
|
+
.filter((row) => /✅\s*done/i.test(rowStatus(row)))
|
|
638
|
+
.map((row) => storyIdFromRow(row))
|
|
639
|
+
.filter(Boolean);
|
|
640
|
+
const doneStorySet = new Set(doneStoryIds);
|
|
641
|
+
|
|
642
|
+
const featureRows = parseMarkdownTable(getSection(stripHtmlComments(features), 'Feature Registry') || stripHtmlComments(features))
|
|
643
|
+
.filter((row) => COMPLETE_STATUS.test(row.Status || row.status || ''));
|
|
644
|
+
const doneRequirements = new Set();
|
|
645
|
+
for (const feature of featureRows) {
|
|
646
|
+
const raw = Object.values(feature).filter((v) => typeof v === 'string').join(' ');
|
|
647
|
+
for (const req of extractRequirementIds(raw)) doneRequirements.add(req);
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
for (const row of trackerRows) {
|
|
651
|
+
const req = normalizedRequirementId(trackerRequirement(row));
|
|
652
|
+
const story = trackerStory(row);
|
|
653
|
+
const status = trackerStatus(row);
|
|
654
|
+
const mappedToDoneStory = [...doneStorySet].some((id) => story.includes(id));
|
|
655
|
+
const doneByFeature = req && doneRequirements.has(req);
|
|
656
|
+
if ((mappedToDoneStory || doneByFeature) && INCOMPLETE_STATUS.test(status)) {
|
|
657
|
+
violations.push({
|
|
658
|
+
check: 'validation-tracker',
|
|
659
|
+
severity: 'error',
|
|
660
|
+
line: 0,
|
|
661
|
+
message: `Validation Tracker row ${req || '(unknown requirement)'} maps to completed work but still has status "${status || 'blank'}" (R17). Update project-brief.md to Proven/Done or keep the Story out of Done.`,
|
|
662
|
+
});
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
return violations;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
// ─── Dependency Interface Log Gate (R17) ────────────────────────────
|
|
670
|
+
|
|
671
|
+
const INTERFACE_FEATURE_TERMS = /\b(FR-00[3-9]|FR-0[1-9]\d|sla|risk|filter|api|interface|contract|auth|login|board|control)\b/i;
|
|
672
|
+
|
|
673
|
+
function checkDependencyInterfaceLog({ features = '', dependencyMap = '' } = {}) {
|
|
674
|
+
const violations = [];
|
|
675
|
+
const depVisible = stripHtmlComments(dependencyMap);
|
|
676
|
+
const interfaceLog = getSection(depVisible, 'Interface Change Log');
|
|
677
|
+
if (interfaceLog === null) return violations;
|
|
678
|
+
|
|
679
|
+
const featureRows = parseMarkdownTable(getSection(stripHtmlComments(features), 'Feature Registry') || stripHtmlComments(features))
|
|
680
|
+
.filter((row) => COMPLETE_STATUS.test(row.Status || row.status || ''));
|
|
681
|
+
|
|
682
|
+
for (const row of featureRows) {
|
|
683
|
+
const raw = Object.values(row).filter((v) => typeof v === 'string').join(' ');
|
|
684
|
+
const requirements = extractRequirementIds(raw).filter((id) => !BASELINE_REQUIREMENTS.has(id));
|
|
685
|
+
const keyFiles = row['Key Files'] || row['Key files'] || row.Files || row.Scope || '';
|
|
686
|
+
const touchesSource = splitPathList(keyFiles).some((file) => /^(src|lib|app|public)\//.test(file) || /^(server|index)\.js$/.test(file));
|
|
687
|
+
const interfaceLike = requirements.length > 0 || INTERFACE_FEATURE_TERMS.test(raw);
|
|
688
|
+
if (!touchesSource || !interfaceLike) continue;
|
|
689
|
+
|
|
690
|
+
const coveredByRequirement = requirements.some((req) => interfaceLog.includes(req));
|
|
691
|
+
const featureName = row.Feature || row.Name || row.Title || '';
|
|
692
|
+
const tokens = String(featureName).toLowerCase().match(/[a-z0-9-]{4,}/g) || [];
|
|
693
|
+
const meaningfulMatches = tokens.filter((token) => interfaceLog.toLowerCase().includes(token));
|
|
694
|
+
if (!coveredByRequirement && meaningfulMatches.length < 2) {
|
|
695
|
+
violations.push({
|
|
696
|
+
check: 'dependency-interface-log',
|
|
697
|
+
severity: 'error',
|
|
698
|
+
line: 0,
|
|
699
|
+
message: `Completed feature "${featureName || '(unnamed feature)'}" changes source/API/UI surfaces but dependency-map.md Interface Change Log has no matching FR/feature entry (R17). Add an interface log row or explicitly record no interface change.`,
|
|
700
|
+
});
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
return violations;
|
|
705
|
+
}
|
|
706
|
+
|
|
587
707
|
// ─── Scope Split Approval Gate (R14) ────────────────────────────────
|
|
588
708
|
|
|
589
709
|
const STORY_ID_RE = /\bS\d+-\d+\b/g;
|
|
@@ -1131,6 +1251,7 @@ function sourceFilesForAudit(cwd) {
|
|
|
1131
1251
|
function runGuard({ files, cwd = process.cwd() }) {
|
|
1132
1252
|
const all = [];
|
|
1133
1253
|
let scanned = 0;
|
|
1254
|
+
const stateContents = {};
|
|
1134
1255
|
|
|
1135
1256
|
for (const file of files) {
|
|
1136
1257
|
const abs = path.isAbsolute(file) ? file : path.join(cwd, file);
|
|
@@ -1139,6 +1260,11 @@ function runGuard({ files, cwd = process.cwd() }) {
|
|
|
1139
1260
|
const rel = path.relative(cwd, abs);
|
|
1140
1261
|
scanned++;
|
|
1141
1262
|
const beforeFile = all.length;
|
|
1263
|
+
const normalizedRel = rel.replace(/\\/g, '/');
|
|
1264
|
+
if (/^(docs|\.harness)\/project-state\.md$/.test(normalizedRel)) stateContents.projectState = content;
|
|
1265
|
+
if (/^(docs|\.harness)\/features\.md$/.test(normalizedRel)) stateContents.features = content;
|
|
1266
|
+
if (/^(docs|\.harness)\/dependency-map\.md$/.test(normalizedRel)) stateContents.dependencyMap = content;
|
|
1267
|
+
if (/^(docs|\.harness)\/project-brief\.md$/.test(normalizedRel)) stateContents.projectBrief = content;
|
|
1142
1268
|
|
|
1143
1269
|
if (isScannableForSecrets(file)) {
|
|
1144
1270
|
all.push(...scanSecrets(content, rel));
|
|
@@ -1179,6 +1305,14 @@ function runGuard({ files, cwd = process.cwd() }) {
|
|
|
1179
1305
|
}
|
|
1180
1306
|
}
|
|
1181
1307
|
|
|
1308
|
+
if (stateContents.projectState && stateContents.features && stateContents.dependencyMap) {
|
|
1309
|
+
all.push(...checkStateSync(stateContents));
|
|
1310
|
+
all.push(...checkDependencyInterfaceLog(stateContents));
|
|
1311
|
+
}
|
|
1312
|
+
if (stateContents.projectState && stateContents.features && stateContents.projectBrief) {
|
|
1313
|
+
all.push(...checkCrewValidationSync(stateContents));
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1182
1316
|
const errorCount = all.filter((v) => v.severity === 'error').length;
|
|
1183
1317
|
const warnCount = all.filter((v) => v.severity === 'warn').length;
|
|
1184
1318
|
return { ok: errorCount === 0, violations: all, errorCount, warnCount, scanned };
|
|
@@ -1191,6 +1325,8 @@ module.exports = {
|
|
|
1191
1325
|
checkStoryContracts,
|
|
1192
1326
|
checkLearnCompletion,
|
|
1193
1327
|
checkStateSync,
|
|
1328
|
+
checkCrewValidationSync,
|
|
1329
|
+
checkDependencyInterfaceLog,
|
|
1194
1330
|
checkScopeSplitApproval,
|
|
1195
1331
|
checkRecentChangesIntegrity,
|
|
1196
1332
|
checkSelfVerifyClaim,
|
package/src/init.js
CHANGED
|
@@ -338,6 +338,24 @@ function writeAgentsAsToml(targetDir, agentsDir, overwrite, mode = 'solo', crew
|
|
|
338
338
|
}
|
|
339
339
|
}
|
|
340
340
|
|
|
341
|
+
function vscodeAgentsMirror() {
|
|
342
|
+
return [
|
|
343
|
+
'# kode:harness VS Code Instruction Anchor',
|
|
344
|
+
'',
|
|
345
|
+
'This project uses kode:harness. The canonical VS Code Copilot dispatcher is `.github/copilot-instructions.md` and must be followed.',
|
|
346
|
+
'',
|
|
347
|
+
'Hard stops:',
|
|
348
|
+
'',
|
|
349
|
+
'- Read `docs/project-state.md` before planning or coding.',
|
|
350
|
+
'- Every response must end with a `🧭 Next Step` block.',
|
|
351
|
+
'- Do not mark a Story Done without Proof Ledger evidence.',
|
|
352
|
+
'- Do not claim state-check/guard PASS without real command output.',
|
|
353
|
+
'- Do not claim clean worktree, commit, push, publish, or policy compliance without checking the actual command result.',
|
|
354
|
+
'- Security, governance, dependency, CI/CD, and release rules are enforced by deterministic guards; if guard output conflicts with prose, guard output wins.',
|
|
355
|
+
'',
|
|
356
|
+
].join('\n');
|
|
357
|
+
}
|
|
358
|
+
|
|
341
359
|
// ─── IDE Generators ──────────────────────────────────────────
|
|
342
360
|
|
|
343
361
|
function generateVscode(targetDir, overwrite, mode = 'solo', crew = false) {
|
|
@@ -345,6 +363,9 @@ function generateVscode(targetDir, overwrite, mode = 'solo', crew = false) {
|
|
|
345
363
|
|
|
346
364
|
// Global instructions (dispatcher only — rules are embedded in skills)
|
|
347
365
|
writeFile(targetDir, '.github/copilot-instructions.md', coreRules, true);
|
|
366
|
+
// Root AGENTS.md mirror — VS Code now supports AGENTS.md as an instruction
|
|
367
|
+
// surface. Keep it short to avoid conflicting with the canonical dispatcher.
|
|
368
|
+
writeFile(targetDir, 'AGENTS.md', vscodeAgentsMirror(), true);
|
|
348
369
|
|
|
349
370
|
// Skills (.github/skills — VS Code default search path, SKILL.md with frontmatter)
|
|
350
371
|
writeSkills(targetDir, '.github/skills', true, mode, crew);
|