phasegate 0.335.0 → 0.341.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/CHANGELOG.md +16 -0
- package/README.ja.md +22 -9
- package/README.md +28 -17
- package/docs/ADR/038-config-state-operation-permission-policy.md +5 -2
- package/docs/ADR/041-non-excludable-agent-trust-roots.md +68 -0
- package/docs/folder_management_rules.md +10 -1
- package/docs/guide/antigravity-integration.md +26 -0
- package/docs/guide/cli-reference.md +27 -7
- package/docs/guide/codex-integration.md +44 -107
- package/docs/guide/configuration.md +28 -2
- package/docs/guide/grok-integration.md +26 -0
- package/docs/guide/hooks-integration.md +34 -5
- package/docs/guide/installation.md +4 -2
- package/docs/guide/setup-artifacts.md +4 -4
- package/docs/guide/skills-overview.md +4 -1
- package/package.json +2 -1
- package/scripts/harness/agent-integration/application/dto/handle-pre-tool-use-dto.ts +1 -1
- package/scripts/harness/agent-integration/application/dto/normalized-pre-tool-use-request.ts +32 -0
- package/scripts/harness/agent-integration/application/usecases/handle-post-tool-use-usecase.ts +4 -0
- package/scripts/harness/agent-integration/application/usecases/handle-pre-tool-use-usecase.ts +38 -3
- package/scripts/harness/agent-integration/application/usecases/handle-stop-usecase.ts +3 -9
- package/scripts/harness/agent-integration/domain/ports/full-mode-requirement-query-port.ts +3 -1
- package/scripts/harness/agent-integration/domain/ports/story-reflection-query-port.ts +2 -0
- package/scripts/harness/agent-integration/domain/services/apply-patch-write-target-extractor.ts +98 -0
- package/scripts/harness/agent-integration/domain/services/bash-write-target-extractor.ts +6 -52
- package/scripts/harness/agent-integration/domain/services/hook-to-cli-translator.ts +8 -5
- package/scripts/harness/agent-integration/domain/value-objects/hook-translation-result.ts +2 -2
- package/scripts/harness/agent-integration/domain/value-objects/protected-file-list.ts +35 -18
- package/scripts/harness/agent-integration/domain/value-objects/story-reflection-query-result.ts +10 -2
- package/scripts/harness/agent-integration/domain/value-objects/write-target-scope.ts +12 -0
- package/scripts/harness/agent-integration/infrastructure/adapters/child-process-cli-executor-adapter.ts +64 -13
- package/scripts/harness/agent-integration/infrastructure/adapters/file-system-story-reflection-query-adapter.ts +108 -2
- package/scripts/harness/agent-integration/infrastructure/adapters/harness-config-config-query-adapter.ts +8 -6
- package/scripts/harness/agent-integration/presentation/hook-skip-event-recorder.ts +3 -0
- package/scripts/harness/agent-integration/presentation/phasegate-status-context.ts +1 -1
- package/scripts/harness/agent-integration/presentation/post-tool-use-feedback.ts +39 -0
- package/scripts/harness/agent-integration/presentation/post-tool-use-hook.ts +21 -27
- package/scripts/harness/agent-integration/presentation/pre-tool-use-hook.ts +126 -70
- package/scripts/harness/agent-integration/presentation/pre-tool-use-payload-normalizer.ts +228 -0
- package/scripts/harness/agent-integration/presentation/pre-tool-use-response-renderer.ts +38 -0
- package/scripts/harness/agent-integration/presentation/stop-hook.ts +1 -3
- package/scripts/harness/biome-ast-engine/infrastructure/adapters/biome-cli-executor-adapter.ts +21 -1
- package/scripts/harness/biome-ast-engine/infrastructure/adapters/typescript-source-module-analyzer-adapter.ts +5 -0
- package/scripts/harness/ci-governance/domain/value-objects/integrity-target.ts +2 -0
- package/scripts/harness/config-foundation/infrastructure/schemas/harness-config-v3.schema.json +15 -0
- package/scripts/harness/config-foundation/infrastructure/validators/ajv-config-schema-validator.ts +6 -3
- package/scripts/harness/harness-api/domain/ports/biome-lint-port.ts +2 -1
- package/scripts/harness/harness-api/domain/services/command-dispatch-service.ts +12 -1
- package/scripts/harness/harness-api/infrastructure/adapters/biome-ast-engine-lint-adapter.ts +10 -2
- package/scripts/harness/installation/application/bundled-skill-selection.ts +28 -2
- package/scripts/harness/installation/application/checks/antigravity-hook-missing-check.ts +79 -0
- package/scripts/harness/installation/application/checks/check-utils.ts +2 -2
- package/scripts/harness/installation/application/checks/codex-hook-missing-check.ts +70 -6
- package/scripts/harness/installation/application/checks/grok-hook-missing-check.ts +79 -0
- package/scripts/harness/installation/application/checks/husky-runtime-inactive-check.ts +33 -0
- package/scripts/harness/installation/application/named-hook-json.ts +17 -0
- package/scripts/harness/installation/application/operator-notice.ts +42 -0
- package/scripts/harness/installation/application/usecases/run-doctor-diagnostics.ts +18 -7
- package/scripts/harness/installation/application/usecases/run-install.ts +157 -57
- package/scripts/harness/installation/application/usecases/run-reconcile.ts +148 -31
- package/scripts/harness/installation/application/usecases/run-uninstall.ts +149 -25
- package/scripts/harness/installation/composition-root.ts +10 -0
- package/scripts/harness/installation/domain/agent-target.ts +39 -0
- package/scripts/harness/installation/domain/check-id.ts +5 -0
- package/scripts/harness/installation/domain/husky-runtime-state.ts +34 -0
- package/scripts/harness/installation/domain/ports/git-hooks-runtime-probe.ts +9 -0
- package/scripts/harness/installation/domain/repair-table.ts +5 -0
- package/scripts/harness/installation/infrastructure/adapters/git-hooks-runtime-probe-adapter.ts +62 -0
- package/scripts/harness/installation/presentation/cli/install-handler.ts +25 -5
- package/scripts/harness/installation/presentation/cli/reconcile-handler.ts +7 -1
- package/scripts/harness/installation/presentation/formatters/diagnostic-report-formatter.ts +27 -0
- package/scripts/harness/main.ts +227 -128
- package/scripts/harness/phase-dependency-model/domain/services/story-reflection-checker.ts +48 -5
- package/scripts/harness/phase-dependency-model/domain/services/work-item-reflection-scope-resolver.ts +64 -0
- package/scripts/harness/phase-dependency-model/domain/values/story-reflection-mapping.ts +8 -2
- package/scripts/harness/phase-dependency-model/infrastructure/filesystem/file-system-story-reflection-adapter.ts +6 -1
- package/scripts/harness/phase-dependency-model/infrastructure/filesystem/file-system-work-item-dependency-catalog.ts +73 -0
- package/scripts/harness/quick-mode/application/dto/change-category-classification-contract.ts +5 -1
- package/scripts/harness/quick-mode/application/dto/quick-mode-eligibility-contract.ts +1 -1
- package/scripts/harness/quick-mode/application/ports/change-risk-advisory-port.ts +16 -0
- package/scripts/harness/quick-mode/application/usecases/classify-change-category-usecase.ts +3 -1
- package/scripts/harness/quick-mode/composition-root.ts +5 -1
- package/scripts/harness/quick-mode/domain/services/quick-mode-judgment-engine.ts +13 -2
- package/scripts/harness/quick-mode/domain/types/rejection-rule.ts +1 -1
- package/scripts/harness/quick-mode/infrastructure/adapters/snapshot-risk-advisory-adapter.ts +118 -0
- package/scripts/harness/quick-mode/presentation/formatters/change-category-formatter.ts +7 -0
- package/scripts/harness/quick-mode/presentation/handlers/check-change-category-handler.ts +12 -1
- package/scripts/harness/regression-suite/infrastructure/adapters/vitest-test-runner-adapter.ts +13 -3
- package/scripts/harness/setup/skill-deployer.ts +3 -45
- package/scripts/harness/skill-quality/application/dto/run-plan-checker-loop-output.ts +1 -0
- package/scripts/harness/skill-quality/application/usecases/apply-cascade-update-usecase.ts +9 -4
- package/scripts/harness/skill-quality/application/usecases/execute-tdd-cycle-usecase.ts +2 -1
- package/scripts/harness/skill-quality/application/usecases/run-plan-checker-loop-usecase.ts +8 -0
- package/scripts/harness/skill-quality/composition-root.ts +14 -7
- package/scripts/harness/skill-quality/domain/ports/plan-check-executor-port.ts +2 -0
- package/scripts/harness/skill-quality/domain/value-objects/cascade-update-target.ts +15 -1
- package/scripts/harness/skill-quality/infrastructure/adapters/l1-biome-validator-adapter.ts +9 -1
- package/scripts/harness/skill-quality/infrastructure/adapters/l2-validator-system-adapter.ts +18 -2
- package/scripts/harness/skill-quality/presentation/handlers/apply-cascade-update-handler.ts +8 -2
- package/scripts/harness/skill-quality/presentation/handlers/execute-tdd-cycle-handler.ts +4 -3
- package/scripts/harness/skill-quality/presentation/handlers/run-plan-checker-loop-handler.ts +11 -4
- package/scripts/harness/traceability-model/infrastructure/parsers/work-item-frontmatter-parser.ts +41 -0
- package/skills/README.md +7 -0
- package/skills/cascade-updater/SKILL.md +37 -109
- package/skills/cascade-updater/references//345/261/244/345/210/245/345/210/244/345/256/232/343/202/254/343/202/244/343/203/211.md +5 -3
- package/skills/release-publisher/SKILL.md +3 -1
- package/skills/skill-creator/SKILL.md +8 -9
- package/templates/.agents/hooks.json +16 -0
- package/templates/.claude/scripts/analyze-errors-hook.sh +2 -2
- package/templates/.claude/settings.json +5 -3
- package/templates/.codex/hooks.json +2 -2
package/scripts/harness/regression-suite/infrastructure/adapters/vitest-test-runner-adapter.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
// @layer infrastructure
|
|
2
|
-
|
|
2
|
+
// @unit regression-suite
|
|
3
|
+
// @work-item-id WI-384
|
|
4
|
+
import { execFileSync } from 'node:child_process';
|
|
3
5
|
import { existsSync } from 'node:fs';
|
|
4
6
|
import { join } from 'node:path';
|
|
7
|
+
import { execPath } from 'node:process';
|
|
5
8
|
import { KRequirementTest } from '../../domain/value-objects/k-requirement-test.js';
|
|
6
9
|
import { GngConditionTest } from '../../domain/value-objects/gng-condition-test.js';
|
|
7
10
|
import { AgentIndependenceTest } from '../../domain/value-objects/agent-independence-test.js';
|
|
@@ -65,8 +68,15 @@ export class VitestTestRunnerAdapter implements TestRunnerPort {
|
|
|
65
68
|
|
|
66
69
|
const testPattern = hasUnitTests ? unitTestDir : unitIntegrationDir;
|
|
67
70
|
try {
|
|
68
|
-
|
|
69
|
-
|
|
71
|
+
execFileSync(
|
|
72
|
+
execPath,
|
|
73
|
+
[
|
|
74
|
+
'node_modules/vitest/vitest.mjs',
|
|
75
|
+
'run',
|
|
76
|
+
'--config',
|
|
77
|
+
'scripts/harness/__tests__/vitest.config.ts',
|
|
78
|
+
testPattern,
|
|
79
|
+
],
|
|
70
80
|
{ cwd: this.rootDir, stdio: 'pipe', timeout: 60_000 },
|
|
71
81
|
);
|
|
72
82
|
unitResults.set(unit, true);
|
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
|
|
11
11
|
import { promises as fs } from "node:fs";
|
|
12
12
|
import { dirname, join } from "node:path";
|
|
13
|
+
import { getBundledSkillsForSet, SKILL_CATEGORIES, type SkillCategory, type SkillSet } from "../installation/application/bundled-skill-selection.js";
|
|
14
|
+
export { SKILL_CATEGORIES, type SkillCategory, type SkillSet } from "../installation/application/bundled-skill-selection.js";
|
|
13
15
|
|
|
14
16
|
const HARNESS_VERSION_FILE = ".harness-version";
|
|
15
17
|
const SKILLS_SOURCE_DIR = "skills";
|
|
@@ -25,52 +27,8 @@ const DEFAULT_FOLDER_RULES_DOC = join("docs", "folder_management_rules.md");
|
|
|
25
27
|
|
|
26
28
|
// ── Skill Category Map ──
|
|
27
29
|
|
|
28
|
-
export type SkillCategory = "core" | "aidlc" | "utility" | "guidance";
|
|
29
|
-
export type SkillSet = "core" | "all";
|
|
30
|
-
|
|
31
|
-
export const SKILL_CATEGORIES: Record<SkillCategory, readonly string[]> = {
|
|
32
|
-
core: [
|
|
33
|
-
"cascade-updater",
|
|
34
|
-
"codebase-mapper",
|
|
35
|
-
"consistency-checker",
|
|
36
|
-
"doc-health-checker",
|
|
37
|
-
"engineering-perspective",
|
|
38
|
-
"implementation-readiness-checker",
|
|
39
|
-
"test-coverage-checker",
|
|
40
|
-
],
|
|
41
|
-
aidlc: [
|
|
42
|
-
"domain-designer",
|
|
43
|
-
"environment-designer",
|
|
44
|
-
"it-test-designer",
|
|
45
|
-
"it-test-logic-designer",
|
|
46
|
-
"logical-designer",
|
|
47
|
-
"mock-designer",
|
|
48
|
-
"product-architect",
|
|
49
|
-
"quick-implementor",
|
|
50
|
-
"scenario-test-designer",
|
|
51
|
-
"scenario-test-logic-designer",
|
|
52
|
-
"story-implementor",
|
|
53
|
-
"story-mapper",
|
|
54
|
-
"story-writer",
|
|
55
|
-
"uiux-designer",
|
|
56
|
-
"unit-designer",
|
|
57
|
-
"unit-test-designer",
|
|
58
|
-
"unit-test-logic-designer",
|
|
59
|
-
],
|
|
60
|
-
utility: ["codex-delegator", "skill-creator"],
|
|
61
|
-
guidance: ["phasegate-toolkit-guide", "phasegate-config-doctor", "release-publisher"],
|
|
62
|
-
} as const;
|
|
63
|
-
|
|
64
30
|
export function getSkillsForSet(skillSet: SkillSet): string[] {
|
|
65
|
-
|
|
66
|
-
return [...SKILL_CATEGORIES.core];
|
|
67
|
-
}
|
|
68
|
-
return [
|
|
69
|
-
...SKILL_CATEGORIES.core,
|
|
70
|
-
...SKILL_CATEGORIES.aidlc,
|
|
71
|
-
...SKILL_CATEGORIES.utility,
|
|
72
|
-
...SKILL_CATEGORIES.guidance,
|
|
73
|
-
];
|
|
31
|
+
return getBundledSkillsForSet(skillSet);
|
|
74
32
|
}
|
|
75
33
|
|
|
76
34
|
export function getCategoryForSkill(skillName: string): SkillCategory | null {
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* @layer application
|
|
3
3
|
* @unit skill-quality
|
|
4
4
|
* @work-item-id WI-192
|
|
5
|
+
* @work-item-id WI-220
|
|
5
6
|
*/
|
|
6
7
|
import { CascadeUpdateResult } from '../../domain/value-objects/cascade-update-result.js';
|
|
7
8
|
import type { CascadeUpdateService } from '../../domain/services/cascade-update-service.js';
|
|
@@ -21,6 +22,7 @@ export class ApplyCascadeUpdateUseCase {
|
|
|
21
22
|
let updatedCount = 0;
|
|
22
23
|
const appliedStoryIds: string[] = [];
|
|
23
24
|
const errors: string[] = [];
|
|
25
|
+
const visitedFiles = new Set<string>();
|
|
24
26
|
|
|
25
27
|
for (const target of targets) {
|
|
26
28
|
try {
|
|
@@ -30,13 +32,16 @@ export class ApplyCascadeUpdateUseCase {
|
|
|
30
32
|
: [target.filePath];
|
|
31
33
|
|
|
32
34
|
for (const filePath of filePaths) {
|
|
35
|
+
if (visitedFiles.has(filePath)) continue;
|
|
36
|
+
visitedFiles.add(filePath);
|
|
33
37
|
try {
|
|
34
38
|
const content = await this.fileSystemPort.read(filePath);
|
|
35
|
-
//
|
|
36
|
-
const updatedContent =
|
|
39
|
+
// This is exact tag presence, not proof of semantic reflection.
|
|
40
|
+
const updatedContent = target.hasAnnotationIn(content)
|
|
37
41
|
? content
|
|
38
|
-
: `${content}\n${target.
|
|
39
|
-
if (
|
|
42
|
+
: `${content}\n${target.renderAnnotation(filePath)}`;
|
|
43
|
+
if (updatedContent === content) continue;
|
|
44
|
+
if (!input.dryRun) {
|
|
40
45
|
await this.fileSystemPort.write(filePath, updatedContent);
|
|
41
46
|
}
|
|
42
47
|
updatedCount++;
|
|
@@ -13,7 +13,8 @@ export class ExecuteTddCycleUseCase {
|
|
|
13
13
|
|
|
14
14
|
async execute(input: ExecuteTddCycleInput): Promise<ExecuteTddCycleOutput> {
|
|
15
15
|
const tddCycle = TddCycle.create(input.phase, input.passed);
|
|
16
|
-
const
|
|
16
|
+
const workItemId = /^WI-\d+$/.test(input.storyId) ? input.storyId : undefined;
|
|
17
|
+
const commitMessage = CommitMessage.create(input.unit, input.storyId, input.description, workItemId);
|
|
17
18
|
|
|
18
19
|
const readiness = await this.atomicCommitService.execute(tddCycle, commitMessage);
|
|
19
20
|
|
|
@@ -26,6 +26,14 @@ export class RunPlanCheckerLoopUseCase {
|
|
|
26
26
|
revision: result.revision,
|
|
27
27
|
});
|
|
28
28
|
loop.addAttempt(attempt);
|
|
29
|
+
if (loop.status === 'RUNNING' && this.planCheckExecutorPort.supportsRetry === false) {
|
|
30
|
+
return {
|
|
31
|
+
status: 'FAILED_EXCEEDED',
|
|
32
|
+
loopHistory: loop.loopHistory,
|
|
33
|
+
escalationRequired: true,
|
|
34
|
+
stopReason: 'UNCHANGED_INPUT',
|
|
35
|
+
};
|
|
36
|
+
}
|
|
29
37
|
attemptNumber++;
|
|
30
38
|
}
|
|
31
39
|
|
|
@@ -14,7 +14,7 @@ import { SkillStructureValidator } from './domain/services/skill-structure-valid
|
|
|
14
14
|
|
|
15
15
|
// Infrastructure Adapters
|
|
16
16
|
import { GitCommitExecutorAdapter } from './infrastructure/adapters/git-commit-executor-adapter.js';
|
|
17
|
-
import { L1BiomeValidatorAdapter } from './infrastructure/adapters/l1-biome-validator-adapter.js';
|
|
17
|
+
import { L1BiomeValidatorAdapter, type L1BiomeValidatorOptions } from './infrastructure/adapters/l1-biome-validator-adapter.js';
|
|
18
18
|
import { L2ValidatorSystemAdapter } from './infrastructure/adapters/l2-validator-system-adapter.js';
|
|
19
19
|
import { FileSystemLessonSourceReaderAdapter } from './infrastructure/adapters/file-system-lesson-source-reader-adapter.js';
|
|
20
20
|
import { FileSystemLessonArtifactWriterAdapter } from './infrastructure/adapters/file-system-lesson-artifact-writer-adapter.js';
|
|
@@ -60,11 +60,18 @@ class NodeFileSystemAdapter implements FileSystemPort {
|
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
export
|
|
63
|
+
export interface SkillQualityHandlerOptions {
|
|
64
|
+
rootDir?: string;
|
|
65
|
+
l1Config?: L1BiomeValidatorOptions['config'];
|
|
66
|
+
validatorSystemConfig?: object;
|
|
67
|
+
failOnWarning?: boolean;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function createSkillQualityHandlers(options: SkillQualityHandlerOptions = {}) {
|
|
64
71
|
// Infrastructure
|
|
65
72
|
const commitExecutorPort = new GitCommitExecutorAdapter();
|
|
66
|
-
const l1ValidatorPort = new L1BiomeValidatorAdapter();
|
|
67
|
-
const l2ValidatorPort = new L2ValidatorSystemAdapter();
|
|
73
|
+
const l1ValidatorPort = new L1BiomeValidatorAdapter({ rootDir: options.rootDir, config: options.l1Config });
|
|
74
|
+
const l2ValidatorPort = new L2ValidatorSystemAdapter({ config: options.validatorSystemConfig, failOnWarning: options.failOnWarning });
|
|
68
75
|
const lessonSourceReaderPort = new FileSystemLessonSourceReaderAdapter();
|
|
69
76
|
const lessonArtifactWriterPort = new FileSystemLessonArtifactWriterAdapter();
|
|
70
77
|
const lessonArtifactSchemaPort = new AjvLessonArtifactSchemaAdapter();
|
|
@@ -87,6 +94,7 @@ export function createSkillQualityHandlers() {
|
|
|
87
94
|
const checkCoverageUseCase = new CheckCoverageUseCase(requirementTestMatrixPort, coverageRunnerPort, configQueryPort);
|
|
88
95
|
const runPlanCheckerLoopUseCase = new RunPlanCheckerLoopUseCase(
|
|
89
96
|
{
|
|
97
|
+
supportsRetry: false,
|
|
90
98
|
evaluate: async (planDocument: string) => {
|
|
91
99
|
// チェックボックス形式(- [x] / - [ ])でカバレッジを評価する
|
|
92
100
|
const checked = (planDocument.match(/- \[x\]/gi) ?? []).length;
|
|
@@ -99,8 +107,7 @@ export function createSkillQualityHandlers() {
|
|
|
99
107
|
const gaps = planDocument
|
|
100
108
|
.split('\n')
|
|
101
109
|
.filter((line) => /- \[ \]/.test(line))
|
|
102
|
-
.map((line) => line.replace(/^.*- \[ \]\s*/, '').trim())
|
|
103
|
-
.filter(Boolean);
|
|
110
|
+
.map((line) => line.replace(/^.*- \[ \]\s*/, '').trim() || '未記入のチェック項目');
|
|
104
111
|
return { coverageRate, gaps, revision: `${checked}/${total}` };
|
|
105
112
|
},
|
|
106
113
|
}
|
|
@@ -114,7 +121,7 @@ export function createSkillQualityHandlers() {
|
|
|
114
121
|
return {
|
|
115
122
|
executeTddCycleHandler: new ExecuteTddCycleHandler(executeTddCycleUseCase),
|
|
116
123
|
checkCoverageHandler: new CheckCoverageHandler(checkCoverageUseCase),
|
|
117
|
-
runPlanCheckerLoopHandler: new RunPlanCheckerLoopHandler(runPlanCheckerLoopUseCase),
|
|
124
|
+
runPlanCheckerLoopHandler: new RunPlanCheckerLoopHandler(runPlanCheckerLoopUseCase, fileSystemPort),
|
|
118
125
|
collectLessonsHandler: new CollectLessonsHandler(collectLessonsUseCase, writeLessonArtifactUseCase),
|
|
119
126
|
applyCascadeUpdateHandler: new ApplyCascadeUpdateHandler(applyCascadeUpdateUseCase),
|
|
120
127
|
validateSkillStructureHandler: new ValidateSkillStructureHandler(validateSkillStructureUseCase),
|
|
@@ -11,5 +11,7 @@ export interface PlanCheckResult {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
export interface PlanCheckExecutorPort {
|
|
14
|
+
/** False for deterministic evaluators; omitted preserves legacy retry behavior. */
|
|
15
|
+
readonly supportsRetry?: boolean;
|
|
14
16
|
evaluate(planDocument: string, previousAttempts: readonly LoopAttempt[]): Promise<PlanCheckResult>;
|
|
15
17
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @layer domain
|
|
3
3
|
* @unit skill-quality
|
|
4
|
+
* @work-item-id WI-220
|
|
4
5
|
*/
|
|
5
6
|
import { SkillQualityError } from '../errors/skill-quality-error.js';
|
|
6
7
|
|
|
@@ -18,11 +19,24 @@ export class CascadeUpdateTarget {
|
|
|
18
19
|
if (!filePath) {
|
|
19
20
|
throw new SkillQualityError('EMPTY_FILE_PATH', 'filePath must be non-empty');
|
|
20
21
|
}
|
|
21
|
-
const storyIdTag =
|
|
22
|
+
const storyIdTag = `@${/^WI-\d+$/.test(storyId) ? 'work-item-id' : 'story-id'} ${storyId}`;
|
|
22
23
|
return new CascadeUpdateTarget(filePath, storyIdTag);
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
equals(other: CascadeUpdateTarget): boolean {
|
|
26
27
|
return this.filePath === other.filePath && this.storyIdTag === other.storyIdTag;
|
|
27
28
|
}
|
|
29
|
+
|
|
30
|
+
renderAnnotation(filePath: string): string {
|
|
31
|
+
return /\.(?:[cm]?[jt]s|[jt]sx)$/i.test(filePath) ? `// ${this.storyIdTag}` : this.storyIdTag;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
hasAnnotationIn(content: string): boolean {
|
|
35
|
+
const storyId = this.storyIdTag.slice(this.storyIdTag.indexOf(' ') + 1);
|
|
36
|
+
for (const match of content.matchAll(/@(?:story-id|issue-id|work-item-id)[ \t]+([^\r\n]+)/g)) {
|
|
37
|
+
const ids = match[1].replace(/-->.*$/, '').split(/[\s,]+/);
|
|
38
|
+
if (ids.includes(storyId)) return true;
|
|
39
|
+
}
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
28
42
|
}
|
|
@@ -5,12 +5,20 @@
|
|
|
5
5
|
import type { L1ValidatorPort } from '../../domain/ports/l1-validator-port.js';
|
|
6
6
|
import type { CommitMessage } from '../../domain/value-objects/commit-message.js';
|
|
7
7
|
import type { ValidationViolation } from '../../domain/types/validation-violation.js';
|
|
8
|
+
import type { BiomeAstEngineModuleOptions } from '../../../biome-ast-engine/composition-root.js';
|
|
9
|
+
|
|
10
|
+
export interface L1BiomeValidatorOptions {
|
|
11
|
+
rootDir?: string;
|
|
12
|
+
config?: BiomeAstEngineModuleOptions;
|
|
13
|
+
}
|
|
8
14
|
|
|
9
15
|
export class L1BiomeValidatorAdapter implements L1ValidatorPort {
|
|
16
|
+
constructor(private readonly options: L1BiomeValidatorOptions = {}) {}
|
|
17
|
+
|
|
10
18
|
async validate(_commitMessage: CommitMessage): Promise<readonly ValidationViolation[]> {
|
|
11
19
|
try {
|
|
12
20
|
const { createBiomeAstEngineModule } = await import('../../../biome-ast-engine/composition-root.js');
|
|
13
|
-
const mod = createBiomeAstEngineModule(process.cwd());
|
|
21
|
+
const mod = createBiomeAstEngineModule(this.options.rootDir ?? process.cwd(), this.options.config);
|
|
14
22
|
const output = await mod.executeLintUseCase.execute({ targets: [] });
|
|
15
23
|
|
|
16
24
|
return output.report.violations.map((violation) => ({
|
package/scripts/harness/skill-quality/infrastructure/adapters/l2-validator-system-adapter.ts
CHANGED
|
@@ -5,20 +5,36 @@
|
|
|
5
5
|
import type { L2ValidatorPort } from '../../domain/ports/l2-validator-port.js';
|
|
6
6
|
import type { CommitMessage } from '../../domain/value-objects/commit-message.js';
|
|
7
7
|
import type { ValidationViolation } from '../../domain/types/validation-violation.js';
|
|
8
|
+
import { isEffectivelyPassed } from '../../../validator-system/domain/services/effective-severity-policy.js';
|
|
9
|
+
|
|
10
|
+
export interface L2ValidatorSystemOptions {
|
|
11
|
+
config?: object;
|
|
12
|
+
failOnWarning?: boolean;
|
|
13
|
+
}
|
|
8
14
|
|
|
9
15
|
export class L2ValidatorSystemAdapter implements L2ValidatorPort {
|
|
16
|
+
constructor(private readonly options: L2ValidatorSystemOptions = {}) {}
|
|
17
|
+
|
|
10
18
|
async validate(_commitMessage: CommitMessage): Promise<readonly ValidationViolation[]> {
|
|
11
19
|
try {
|
|
12
20
|
const { createValidatorSystemModule } = await import('../../../validator-system/composition-root.js');
|
|
13
|
-
const mod = createValidatorSystemModule();
|
|
21
|
+
const mod = createValidatorSystemModule(this.options.config);
|
|
22
|
+
// Direct legacy callers keep warning-as-blocking unless explicitly configured.
|
|
23
|
+
const failOnWarning = this.options.failOnWarning ?? true;
|
|
14
24
|
const report = await mod.runFullValidationUseCase.execute({
|
|
15
25
|
targetPaths: [],
|
|
16
26
|
unitName: '',
|
|
17
27
|
currentPhase: '',
|
|
18
28
|
includeL4: false,
|
|
29
|
+
failOnWarning,
|
|
19
30
|
});
|
|
20
31
|
|
|
21
|
-
|
|
32
|
+
const blockingErrors = report.allErrors.filter((error) => {
|
|
33
|
+
const advisory = isEffectivelyPassed({ passed: false, errors: [error] }, failOnWarning);
|
|
34
|
+
if (advisory) console.warn(`[skill-quality] [${error.code}] ${error.message}`);
|
|
35
|
+
return !advisory;
|
|
36
|
+
});
|
|
37
|
+
return blockingErrors.map((error) => ({
|
|
22
38
|
ruleId: error.code.toString(),
|
|
23
39
|
message: error.message,
|
|
24
40
|
location: '',
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* @layer presentation
|
|
3
3
|
* @unit skill-quality
|
|
4
4
|
* @work-item-id WI-192
|
|
5
|
+
* @work-item-id WI-220
|
|
5
6
|
*/
|
|
6
7
|
import type { ApplyCascadeUpdateUseCase } from '../../application/usecases/apply-cascade-update-usecase.js';
|
|
7
8
|
|
|
@@ -20,12 +21,17 @@ export class ApplyCascadeUpdateHandler {
|
|
|
20
21
|
if (args.format === 'json') {
|
|
21
22
|
return {
|
|
22
23
|
exitCode: output.errors.length > 0 ? 1 : 0,
|
|
23
|
-
message: JSON.stringify({
|
|
24
|
+
message: JSON.stringify({
|
|
25
|
+
dryRun: args.dryRun === true,
|
|
26
|
+
...output,
|
|
27
|
+
operation: 'traceability-tag-update',
|
|
28
|
+
semanticReviewPerformed: false,
|
|
29
|
+
}, null, 2),
|
|
24
30
|
};
|
|
25
31
|
}
|
|
26
32
|
const tagsLine = output.appliedStoryIds.join(', ');
|
|
27
33
|
const verb = args.dryRun ? 'Would update' : 'Updated';
|
|
28
|
-
let msg = `${verb} ${output.updatedCount} files with tags: ${tagsLine}
|
|
34
|
+
let msg = `${verb} ${output.updatedCount} files with tags: ${tagsLine}\nタグ追記のみです。意味レビューは未実施であり、設計内容の反映・承認を保証しません。`;
|
|
29
35
|
|
|
30
36
|
if (output.errors.length > 0) {
|
|
31
37
|
const errLines = output.errors.map((e) => ` - ${e}`).join('\n');
|
|
@@ -16,6 +16,7 @@ export class ExecuteTddCycleHandler {
|
|
|
16
16
|
constructor(private readonly useCase: ExecuteTddCycleUseCase) {}
|
|
17
17
|
|
|
18
18
|
async handle(args: ExecuteTddCycleArgs): Promise<{ exitCode: number; message: string }> {
|
|
19
|
+
const evidenceNotice = '\n注意: --passed は呼出元の申告です。このコマンドはテストを実行しません。';
|
|
19
20
|
try {
|
|
20
21
|
const output = await this.useCase.execute({
|
|
21
22
|
unit: args.unit,
|
|
@@ -26,12 +27,12 @@ export class ExecuteTddCycleHandler {
|
|
|
26
27
|
});
|
|
27
28
|
|
|
28
29
|
if (output.ready) {
|
|
29
|
-
return { exitCode: 0, message: `Commit successful: ${output.committedMessage}` };
|
|
30
|
+
return { exitCode: 0, message: `Commit successful: ${output.committedMessage}${evidenceNotice}` };
|
|
30
31
|
}
|
|
31
32
|
const violationMessages = output.violations.map((v) => ` - [${v.ruleId}] ${v.message}`).join('\n');
|
|
32
|
-
return { exitCode: 1, message: `Validation failed:\n${violationMessages}` };
|
|
33
|
+
return { exitCode: 1, message: `Validation failed:\n${violationMessages}${evidenceNotice}` };
|
|
33
34
|
} catch (err) {
|
|
34
|
-
return { exitCode: 2, message: `Error: ${err instanceof Error ? err.message : String(err)}` };
|
|
35
|
+
return { exitCode: 2, message: `Error: ${err instanceof Error ? err.message : String(err)}${evidenceNotice}` };
|
|
35
36
|
}
|
|
36
37
|
}
|
|
37
38
|
}
|
package/scripts/harness/skill-quality/presentation/handlers/run-plan-checker-loop-handler.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* @unit skill-quality
|
|
4
4
|
*/
|
|
5
5
|
import type { RunPlanCheckerLoopUseCase } from '../../application/usecases/run-plan-checker-loop-usecase.js';
|
|
6
|
+
import type { FileSystemPort } from '../../domain/ports/file-system-port.js';
|
|
6
7
|
|
|
7
8
|
export interface RunPlanCheckerLoopArgs {
|
|
8
9
|
planFile: string;
|
|
@@ -10,12 +11,15 @@ export interface RunPlanCheckerLoopArgs {
|
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
export class RunPlanCheckerLoopHandler {
|
|
13
|
-
constructor(
|
|
14
|
+
constructor(
|
|
15
|
+
private readonly useCase: RunPlanCheckerLoopUseCase,
|
|
16
|
+
private readonly planReader?: Pick<FileSystemPort, 'read'>,
|
|
17
|
+
) {}
|
|
14
18
|
|
|
15
19
|
async handle(args: RunPlanCheckerLoopArgs): Promise<{ exitCode: number; message: string }> {
|
|
16
20
|
try {
|
|
17
21
|
const output = await this.useCase.execute({
|
|
18
|
-
planDocument: args.planFile,
|
|
22
|
+
planDocument: this.planReader ? await this.planReader.read(args.planFile) : args.planFile,
|
|
19
23
|
storyId: args.storyId,
|
|
20
24
|
});
|
|
21
25
|
|
|
@@ -24,9 +28,12 @@ export class RunPlanCheckerLoopHandler {
|
|
|
24
28
|
).join('\n');
|
|
25
29
|
|
|
26
30
|
if (output.escalationRequired) {
|
|
27
|
-
|
|
31
|
+
const guidance = output.stopReason === 'UNCHANGED_INPUT'
|
|
32
|
+
? '\n同じ入力の再試行では改善しません。文書を修正するか判断を求めてください。'
|
|
33
|
+
: '';
|
|
34
|
+
return { exitCode: 1, message: `Plan check FAILED_EXCEEDED - escalation required\n${historyLines}${guidance}\nこの結果は設計の意味的承認ではありません。` };
|
|
28
35
|
}
|
|
29
|
-
return { exitCode: 0, message: `Plan check PASSED\n${historyLines}
|
|
36
|
+
return { exitCode: 0, message: `Plan check PASSED\n${historyLines}\nこの結果は設計の意味的承認ではありません。` };
|
|
30
37
|
} catch (err) {
|
|
31
38
|
return { exitCode: 2, message: `Error: ${err instanceof Error ? err.message : String(err)}` };
|
|
32
39
|
}
|
package/scripts/harness/traceability-model/infrastructure/parsers/work-item-frontmatter-parser.ts
CHANGED
|
@@ -31,6 +31,47 @@ const flowArrayPattern = (key: string): RegExp => new RegExp(`^\\s*${key}\\s*:\\
|
|
|
31
31
|
const blockArrayPattern = (key: string): RegExp =>
|
|
32
32
|
new RegExp(`^\\s*${key}\\s*:\\s*\\r?\\n((?:[ \\t]+-[ \\t]+.+\\r?\\n?)+)`, "m");
|
|
33
33
|
|
|
34
|
+
/** Opt-in dependency diagnostics; existing metadata readers keep their old contract. */
|
|
35
|
+
export function parseWorkItemDependencies(content: string): readonly string[] | undefined {
|
|
36
|
+
const body = FRONTMATTER_PATTERN.exec(content)?.[1];
|
|
37
|
+
if (body === undefined) return undefined;
|
|
38
|
+
const lines = body.split(/\r?\n/);
|
|
39
|
+
const headers = lines.flatMap((line, index) => /^[ \t]*depends_on[ \t]*:/.test(line) ? [index] : []);
|
|
40
|
+
if (headers.length === 0) return undefined;
|
|
41
|
+
const invalid = (reason: string): never => {
|
|
42
|
+
throw new WorkItemFrontmatterValidationError(`depends_on ${reason}`);
|
|
43
|
+
};
|
|
44
|
+
if (headers.length !== 1) return invalid('が重複しています');
|
|
45
|
+
if (!lines[headers[0]].startsWith('depends_on')) return invalid('はfrontmatter直下に指定してください');
|
|
46
|
+
const withoutComment = (value: string) => value.replace(/[ \t]+#.*$/, '').trim();
|
|
47
|
+
const declaration = withoutComment(lines[headers[0]].replace(/^[ \t]*depends_on[ \t]*:/, ''));
|
|
48
|
+
const continuation: string[] = [];
|
|
49
|
+
for (const line of lines.slice(headers[0] + 1)) {
|
|
50
|
+
if (line.trim() === '' || line.trimStart().startsWith('#')) continue;
|
|
51
|
+
if (/^[^\s:#][^:]*:/.test(line)) break;
|
|
52
|
+
continuation.push(line);
|
|
53
|
+
}
|
|
54
|
+
let values: string[];
|
|
55
|
+
if (declaration.startsWith('[') && declaration.endsWith(']')) {
|
|
56
|
+
if (continuation.length > 0) return invalid('のflow配列に続く行が不正です');
|
|
57
|
+
const inner = declaration.slice(1, -1).trim();
|
|
58
|
+
values = inner === '' ? [] : inner.split(',');
|
|
59
|
+
} else if (declaration === '') {
|
|
60
|
+
values = [];
|
|
61
|
+
for (const line of continuation) {
|
|
62
|
+
const item = /^[ \t]+-[ \t]+(.+)$/.exec(line);
|
|
63
|
+
if (!item) return invalid('はWI IDの配列で指定してください');
|
|
64
|
+
values.push(withoutComment(item[1]));
|
|
65
|
+
}
|
|
66
|
+
if (values.length === 0) return invalid('の空宣言には [] を指定してください');
|
|
67
|
+
} else {
|
|
68
|
+
return invalid('はWI IDの配列で指定してください');
|
|
69
|
+
}
|
|
70
|
+
const ids = values.map((value) => stripYamlQuotes(value.trim()));
|
|
71
|
+
if (ids.some((id) => !/^WI-\d+$/.test(id))) return invalid('に不正なWI IDまたは空項目があります');
|
|
72
|
+
return Object.freeze(ids);
|
|
73
|
+
}
|
|
74
|
+
|
|
34
75
|
export function parseWorkItemFrontmatter(content: string): WorkItemFrontmatter | null {
|
|
35
76
|
const match = FRONTMATTER_PATTERN.exec(content);
|
|
36
77
|
if (!match) return null;
|
package/skills/README.md
CHANGED
|
@@ -20,4 +20,11 @@
|
|
|
20
20
|
|
|
21
21
|
## 新しいスキルの追加
|
|
22
22
|
|
|
23
|
+
### 利用者向けの選択
|
|
24
|
+
|
|
25
|
+
<!-- @work-item-id WI-223 -->
|
|
26
|
+
`phasegate install --skills consumer --dry-run`で本体メンテナー用の`release-publisher`と`skill-creator`を除く27件を確認できる。適用は`--apply`を明示する。既存の`core`は7件、`all`と省略時は29件のまま。reconcileは各配置先の`.harness-version`に記録したセットを維持する。セット変更だけで以前のスキルやユーザー作成スキルを削除しないため、既存allからの切替は削除移行ではない。
|
|
27
|
+
|
|
28
|
+
配布カタログの正本は`installation/application/bundled-skill-selection.ts`の一箇所。consumerは言語非依存化済みという意味ではなく、Vitest/Playwright等の技術固有スキルは引き続き含まれる。
|
|
29
|
+
|
|
23
30
|
新しいスキルを追加する場合は、この `skills` ディレクトリに直接追加してください。あわせて `docs/guide/skills-overview.md`、README の skill 数、必要なら `skills/phasegate-toolkit-guide/SKILL.md` の参照先を更新します。シンボリックリンクを通じて、対応エージェントから利用可能になります。<!-- @work-item-id WI-154 -->
|