qfai 0.7.2 → 0.8.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/LICENSE +21 -0
- package/README.md +1 -1
- package/assets/init/.qfai/README.md +4 -1
- package/assets/init/.qfai/promptpack/steering/compatibility-vs-change.md +34 -0
- package/assets/init/.qfai/prompts.local/README.md +5 -0
- package/dist/cli/index.cjs +122 -51
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.mjs +122 -51
- package/dist/cli/index.mjs.map +1 -1
- package/dist/index.cjs +109 -45
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +109 -45
- package/dist/index.mjs.map +1 -1
- package/package.json +13 -2
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 aganesy
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
このディレクトリは QFAI の成果物を集約する専用領域です。`.qfai` 配下だけを見れば「何を書くか」「どこから始めるか」が分かる構成にしています。
|
|
4
4
|
|
|
5
|
-
## 最短成功(
|
|
5
|
+
## 最短成功(doctor → validate → report)
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
+
npx qfai doctor --fail-on error
|
|
8
9
|
npx qfai validate --fail-on error --format github
|
|
9
10
|
npx qfai report
|
|
10
11
|
```
|
|
@@ -61,6 +62,8 @@ v0.7 以降、プロンプト資産のカスタマイズは `.qfai/prompts.local
|
|
|
61
62
|
- `.qfai/prompts/**` は QFAI 標準資産であり、更新や `qfai init` 再実行で上書きされ得ます
|
|
62
63
|
- 利用者が `.qfai/prompts/**` を直接編集することは非推奨・非サポートです
|
|
63
64
|
- 変更したい場合は同一相対パスで `.qfai/prompts.local/**` に置いて上書きしてください
|
|
65
|
+
- `qfai init` は `.qfai/prompts.local/**` を **保護**します(`--force` でも上書きしません)
|
|
66
|
+
- 現時点の保護対象は `prompts.local` のみです(それ以外は上書きされ得ます)
|
|
64
67
|
|
|
65
68
|
例:
|
|
66
69
|
|
|
@@ -6,3 +6,37 @@
|
|
|
6
6
|
- 仕様変更: 期待値/挙動が変わる
|
|
7
7
|
|
|
8
8
|
必ず `delta.md` に区分と根拠を記録する。
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 最小ルール(迷ったらこれ)
|
|
13
|
+
|
|
14
|
+
- **既存の利用者がそのまま手順・解釈で運用できる**: Compatibility
|
|
15
|
+
- **既存の利用者が手順変更・解釈変更・レビュー基準変更を迫られる**: Change/Improvement
|
|
16
|
+
|
|
17
|
+
「見た目だけ」「文章だけ」でも、運用で機械消費されている可能性がある場合は慎重に扱い、根拠を `delta.md` に残す。
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 具体例(最低10件)
|
|
22
|
+
|
|
23
|
+
| 変更内容 | 区分 | QA/レビュー観点 |
|
|
24
|
+
| ------------------------------------------------------------- | ------------------ | ---------------------------------------------------- |
|
|
25
|
+
| README の誤字修正、リンク切れ修正 | Compatibility | 誤誘導が減る。既存運用は不変。 |
|
|
26
|
+
| report(text/markdown)の表現改善・並び順安定化(意味は不変) | Compatibility | 人間レビューの短縮。出力の“解釈”が変わらないこと。 |
|
|
27
|
+
| report.json のフィールド追加/並び変更(非契約を維持) | Compatibility | 非契約を明記し続ける。機械消費ユーザーへの注意喚起。 |
|
|
28
|
+
| validate の文言改善(issue code/意味/失敗条件は不変) | Compatibility | 次アクションが明確になる。誤爆やノイズ増がないこと。 |
|
|
29
|
+
| validate で新しい issue code を追加(warning/info) | Change/Improvement | CIの表示が変わる。ノイズ/誤検知の受容可否。 |
|
|
30
|
+
| validate の fail 条件を変更(error→warning で落ちる等) | Change/Improvement | Hard Gate が増減する。既存CIが壊れないか。 |
|
|
31
|
+
| init の生成物構成を変更(新規ファイル追加) | Change/Improvement | 既存リポジトリへの導入影響。上書き/衝突/運用導線。 |
|
|
32
|
+
| init が既存ファイルを上書きする/保護対象を変える | Change/Improvement | 事故リスクが高い。保護の回帰テスト必須。 |
|
|
33
|
+
| Spec/Scenario/Contract のID規約を変更 | Change/Improvement | 既存資産が無効化され得る。移行ガイド必須。 |
|
|
34
|
+
| overlay の優先順位/探索規則を変更 | Change/Improvement | 既存の prompts.local 運用が壊れる。回帰テスト必須。 |
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## QA/レビュー時の判断テンプレ
|
|
39
|
+
|
|
40
|
+
- **修正が必要**: Hard Gate を増やしていないか、既存の運用手順を壊していないか、保護領域(prompts.local)が破壊されないか
|
|
41
|
+
- **許容**: 誤誘導削減、説明の具体化、並び順の安定化などで、意味・失敗条件が変わらないもの
|
|
42
|
+
- **要議論**: report.json の変更、validate の新ルール追加、init生成物の変更(CI/運用への波及が読みにくいもの)
|
|
@@ -23,3 +23,8 @@ QFAI v0.7 以降は、プロンプト資産のカスタマイズ手段を **over
|
|
|
23
23
|
- `.qfai/prompts/**` は **QFAI 標準資産**です(更新や `qfai init` の再実行で上書きされ得ます)。
|
|
24
24
|
- 利用者が `.qfai/prompts/**` を直接編集することは **非推奨・非サポート(ほぼ禁止)**です。
|
|
25
25
|
- 変更したい場合は、対象ファイルを `prompts.local` にコピーして上書きしてください。
|
|
26
|
+
|
|
27
|
+
## init 再実行時の保護(契約)
|
|
28
|
+
|
|
29
|
+
- `qfai init` は `.qfai/prompts.local/**` を **保護**します(`--force` を付けても上書きしません)。
|
|
30
|
+
- 現時点でこの保護対象は `prompts.local` のみです。
|
package/dist/cli/index.cjs
CHANGED
|
@@ -889,8 +889,8 @@ var import_promises6 = require("fs/promises");
|
|
|
889
889
|
var import_node_path6 = __toESM(require("path"), 1);
|
|
890
890
|
var import_node_url = require("url");
|
|
891
891
|
async function resolveToolVersion() {
|
|
892
|
-
if ("0.
|
|
893
|
-
return "0.
|
|
892
|
+
if ("0.8.0".length > 0) {
|
|
893
|
+
return "0.8.0";
|
|
894
894
|
}
|
|
895
895
|
try {
|
|
896
896
|
const packagePath = resolvePackageJsonPath();
|
|
@@ -2074,7 +2074,7 @@ async function validateDeltas(root, config) {
|
|
|
2074
2074
|
issues.push(
|
|
2075
2075
|
issue2(
|
|
2076
2076
|
"QFAI-DELTA-002",
|
|
2077
|
-
"delta.md \u306E\u5909\u66F4\u533A\u5206\u304C\u4E0D\u8DB3\u3057\u3066\u3044\u307E\u3059\u3002",
|
|
2077
|
+
"delta.md \u306E\u5909\u66F4\u533A\u5206\u304C\u4E0D\u8DB3\u3057\u3066\u3044\u307E\u3059\u3002`## \u5909\u66F4\u533A\u5206` \u3068\u30C1\u30A7\u30C3\u30AF\u30DC\u30C3\u30AF\u30B9\uFF08Compatibility / Change/Improvement\uFF09\u3092\u8FFD\u52A0\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
|
|
2078
2078
|
"error",
|
|
2079
2079
|
deltaPath,
|
|
2080
2080
|
"delta.section"
|
|
@@ -2617,7 +2617,7 @@ async function validateTraceability(root, config) {
|
|
|
2617
2617
|
issues.push(
|
|
2618
2618
|
issue6(
|
|
2619
2619
|
"QFAI-TRACE-020",
|
|
2620
|
-
"Spec \u306B QFAI-CONTRACT-REF \u304C\u3042\u308A\u307E\u305B\u3093\u3002",
|
|
2620
|
+
"Spec \u306B QFAI-CONTRACT-REF \u304C\u3042\u308A\u307E\u305B\u3093\u3002\u4F8B: `QFAI-CONTRACT-REF: none` \u307E\u305F\u306F `QFAI-CONTRACT-REF: UI-0001`",
|
|
2621
2621
|
"error",
|
|
2622
2622
|
file,
|
|
2623
2623
|
"traceability.specContractRefRequired"
|
|
@@ -2628,7 +2628,7 @@ async function validateTraceability(root, config) {
|
|
|
2628
2628
|
issues.push(
|
|
2629
2629
|
issue6(
|
|
2630
2630
|
"QFAI-TRACE-023",
|
|
2631
|
-
"Spec \u306E QFAI-CONTRACT-REF \u306B none \u3068\u5951\u7D04 ID \u304C\u6DF7\u5728\u3057\u3066\u3044\u307E\u3059\u3002",
|
|
2631
|
+
"Spec \u306E QFAI-CONTRACT-REF \u306B none \u3068\u5951\u7D04 ID \u304C\u6DF7\u5728\u3057\u3066\u3044\u307E\u3059\u3002none \u304B \u5951\u7D04ID \u306E\u3069\u3061\u3089\u304B\u4E00\u65B9\u3060\u3051\u306B\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
|
|
2632
2632
|
"error",
|
|
2633
2633
|
file,
|
|
2634
2634
|
"traceability.specContractRefFormat"
|
|
@@ -2641,7 +2641,7 @@ async function validateTraceability(root, config) {
|
|
|
2641
2641
|
"QFAI-TRACE-021",
|
|
2642
2642
|
`Spec \u306E\u5951\u7D04 ID \u304C\u4E0D\u6B63\u3067\u3059: ${contractRefs.invalidTokens.join(
|
|
2643
2643
|
", "
|
|
2644
|
-
)}`,
|
|
2644
|
+
)} (\u4F8B: UI-0001 / API-0001 / DB-0001)`,
|
|
2645
2645
|
"error",
|
|
2646
2646
|
file,
|
|
2647
2647
|
"traceability.specContractRefFormat",
|
|
@@ -2681,7 +2681,7 @@ async function validateTraceability(root, config) {
|
|
|
2681
2681
|
issues.push(
|
|
2682
2682
|
issue6(
|
|
2683
2683
|
"QFAI-TRACE-031",
|
|
2684
|
-
"Scenario \u306B QFAI-CONTRACT-REF \u304C\u3042\u308A\u307E\u305B\u3093\u3002",
|
|
2684
|
+
"Scenario \u306B QFAI-CONTRACT-REF \u304C\u3042\u308A\u307E\u305B\u3093\u3002\u4F8B: `# QFAI-CONTRACT-REF: none` \u307E\u305F\u306F `# QFAI-CONTRACT-REF: UI-0001`",
|
|
2685
2685
|
"error",
|
|
2686
2686
|
file,
|
|
2687
2687
|
"traceability.scenarioContractRefRequired"
|
|
@@ -2692,7 +2692,7 @@ async function validateTraceability(root, config) {
|
|
|
2692
2692
|
issues.push(
|
|
2693
2693
|
issue6(
|
|
2694
2694
|
"QFAI-TRACE-033",
|
|
2695
|
-
"Scenario \u306E QFAI-CONTRACT-REF \u306B none \u3068\u5951\u7D04 ID \u304C\u6DF7\u5728\u3057\u3066\u3044\u307E\u3059\u3002",
|
|
2695
|
+
"Scenario \u306E QFAI-CONTRACT-REF \u306B none \u3068\u5951\u7D04 ID \u304C\u6DF7\u5728\u3057\u3066\u3044\u307E\u3059\u3002none \u304B \u5951\u7D04ID \u306E\u3069\u3061\u3089\u304B\u4E00\u65B9\u3060\u3051\u306B\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
|
|
2696
2696
|
"error",
|
|
2697
2697
|
file,
|
|
2698
2698
|
"traceability.scenarioContractRefFormat"
|
|
@@ -2705,7 +2705,7 @@ async function validateTraceability(root, config) {
|
|
|
2705
2705
|
"QFAI-TRACE-032",
|
|
2706
2706
|
`Scenario \u306E\u5951\u7D04 ID \u304C\u4E0D\u6B63\u3067\u3059: ${scenarioContractRefs.invalidTokens.join(
|
|
2707
2707
|
", "
|
|
2708
|
-
)}`,
|
|
2708
|
+
)} (\u4F8B: UI-0001 / API-0001 / DB-0001)`,
|
|
2709
2709
|
"error",
|
|
2710
2710
|
file,
|
|
2711
2711
|
"traceability.scenarioContractRefFormat",
|
|
@@ -3203,7 +3203,7 @@ function formatReportMarkdown(data) {
|
|
|
3203
3203
|
lines.push(`- \u8A2D\u5B9A: ${data.configPath}`);
|
|
3204
3204
|
lines.push(`- \u7248: ${data.version}`);
|
|
3205
3205
|
lines.push("");
|
|
3206
|
-
lines.push("##
|
|
3206
|
+
lines.push("## Summary");
|
|
3207
3207
|
lines.push("");
|
|
3208
3208
|
lines.push(`- specs: ${data.summary.specs}`);
|
|
3209
3209
|
lines.push(`- scenarios: ${data.summary.scenarios}`);
|
|
@@ -3213,8 +3213,79 @@ function formatReportMarkdown(data) {
|
|
|
3213
3213
|
lines.push(
|
|
3214
3214
|
`- issues: info ${data.summary.counts.info} / warning ${data.summary.counts.warning} / error ${data.summary.counts.error}`
|
|
3215
3215
|
);
|
|
3216
|
+
lines.push(
|
|
3217
|
+
`- fail-on=error: ${data.summary.counts.error > 0 ? "FAIL" : "PASS"}`
|
|
3218
|
+
);
|
|
3219
|
+
lines.push(
|
|
3220
|
+
`- fail-on=warning: ${data.summary.counts.error + data.summary.counts.warning > 0 ? "FAIL" : "PASS"}`
|
|
3221
|
+
);
|
|
3222
|
+
lines.push("");
|
|
3223
|
+
lines.push("## Findings");
|
|
3224
|
+
lines.push("");
|
|
3225
|
+
lines.push("### Issues (by code)");
|
|
3226
|
+
lines.push("");
|
|
3227
|
+
const severityOrder = {
|
|
3228
|
+
error: 0,
|
|
3229
|
+
warning: 1,
|
|
3230
|
+
info: 2
|
|
3231
|
+
};
|
|
3232
|
+
const issueKeyToCount = /* @__PURE__ */ new Map();
|
|
3233
|
+
for (const issue7 of data.issues) {
|
|
3234
|
+
const key = `${issue7.severity}|${issue7.code}`;
|
|
3235
|
+
const current = issueKeyToCount.get(key);
|
|
3236
|
+
if (current) {
|
|
3237
|
+
current.count += 1;
|
|
3238
|
+
continue;
|
|
3239
|
+
}
|
|
3240
|
+
issueKeyToCount.set(key, {
|
|
3241
|
+
severity: issue7.severity,
|
|
3242
|
+
code: issue7.code,
|
|
3243
|
+
count: 1
|
|
3244
|
+
});
|
|
3245
|
+
}
|
|
3246
|
+
const issueSummaryRows = Array.from(issueKeyToCount.values()).sort((a, b) => {
|
|
3247
|
+
const sa = severityOrder[a.severity] ?? 999;
|
|
3248
|
+
const sb = severityOrder[b.severity] ?? 999;
|
|
3249
|
+
if (sa !== sb) return sa - sb;
|
|
3250
|
+
return a.code.localeCompare(b.code);
|
|
3251
|
+
}).map((x) => [x.severity, x.code, String(x.count)]);
|
|
3252
|
+
if (issueSummaryRows.length === 0) {
|
|
3253
|
+
lines.push("- (none)");
|
|
3254
|
+
} else {
|
|
3255
|
+
lines.push(
|
|
3256
|
+
...formatMarkdownTable(["Severity", "Code", "Count"], issueSummaryRows)
|
|
3257
|
+
);
|
|
3258
|
+
}
|
|
3259
|
+
lines.push("");
|
|
3260
|
+
lines.push("### Issues (list)");
|
|
3261
|
+
lines.push("");
|
|
3262
|
+
if (data.issues.length === 0) {
|
|
3263
|
+
lines.push("- (none)");
|
|
3264
|
+
} else {
|
|
3265
|
+
const sortedIssues = [...data.issues].sort((a, b) => {
|
|
3266
|
+
const sa = severityOrder[a.severity] ?? 999;
|
|
3267
|
+
const sb = severityOrder[b.severity] ?? 999;
|
|
3268
|
+
if (sa !== sb) return sa - sb;
|
|
3269
|
+
const code = a.code.localeCompare(b.code);
|
|
3270
|
+
if (code !== 0) return code;
|
|
3271
|
+
const fileA = a.file ?? "";
|
|
3272
|
+
const fileB = b.file ?? "";
|
|
3273
|
+
const file = fileA.localeCompare(fileB);
|
|
3274
|
+
if (file !== 0) return file;
|
|
3275
|
+
const lineA = a.loc?.line ?? 0;
|
|
3276
|
+
const lineB = b.loc?.line ?? 0;
|
|
3277
|
+
return lineA - lineB;
|
|
3278
|
+
});
|
|
3279
|
+
for (const item of sortedIssues) {
|
|
3280
|
+
const location = item.file ? ` (${item.file})` : "";
|
|
3281
|
+
const refs = item.refs && item.refs.length > 0 ? ` refs=${item.refs.join(",")}` : "";
|
|
3282
|
+
lines.push(
|
|
3283
|
+
`- ${item.severity.toUpperCase()} [${item.code}] ${item.message}${location}${refs}`
|
|
3284
|
+
);
|
|
3285
|
+
}
|
|
3286
|
+
}
|
|
3216
3287
|
lines.push("");
|
|
3217
|
-
lines.push("
|
|
3288
|
+
lines.push("### IDs");
|
|
3218
3289
|
lines.push("");
|
|
3219
3290
|
lines.push(formatIdLine("SPEC", data.ids.spec));
|
|
3220
3291
|
lines.push(formatIdLine("BR", data.ids.br));
|
|
@@ -3223,14 +3294,14 @@ function formatReportMarkdown(data) {
|
|
|
3223
3294
|
lines.push(formatIdLine("API", data.ids.api));
|
|
3224
3295
|
lines.push(formatIdLine("DB", data.ids.db));
|
|
3225
3296
|
lines.push("");
|
|
3226
|
-
lines.push("
|
|
3297
|
+
lines.push("### Traceability");
|
|
3227
3298
|
lines.push("");
|
|
3228
3299
|
lines.push(`- \u4E0A\u6D41ID\u691C\u51FA\u6570: ${data.traceability.upstreamIdsFound}`);
|
|
3229
3300
|
lines.push(
|
|
3230
3301
|
`- \u30B3\u30FC\u30C9/\u30C6\u30B9\u30C8\u53C2\u7167: ${data.traceability.referencedInCodeOrTests ? "\u3042\u308A" : "\u306A\u3057"}`
|
|
3231
3302
|
);
|
|
3232
3303
|
lines.push("");
|
|
3233
|
-
lines.push("
|
|
3304
|
+
lines.push("### Contract Coverage");
|
|
3234
3305
|
lines.push("");
|
|
3235
3306
|
lines.push(`- total: ${data.traceability.contracts.total}`);
|
|
3236
3307
|
lines.push(`- referenced: ${data.traceability.contracts.referenced}`);
|
|
@@ -3239,7 +3310,7 @@ function formatReportMarkdown(data) {
|
|
|
3239
3310
|
`- specContractRefMissing: ${data.traceability.specs.contractRefMissing}`
|
|
3240
3311
|
);
|
|
3241
3312
|
lines.push("");
|
|
3242
|
-
lines.push("
|
|
3313
|
+
lines.push("### Contract \u2192 Spec");
|
|
3243
3314
|
lines.push("");
|
|
3244
3315
|
const contractToSpecs = data.traceability.contracts.idToSpecs;
|
|
3245
3316
|
const contractIds = Object.keys(contractToSpecs).sort(
|
|
@@ -3258,7 +3329,7 @@ function formatReportMarkdown(data) {
|
|
|
3258
3329
|
}
|
|
3259
3330
|
}
|
|
3260
3331
|
lines.push("");
|
|
3261
|
-
lines.push("
|
|
3332
|
+
lines.push("### Spec \u2192 Contracts");
|
|
3262
3333
|
lines.push("");
|
|
3263
3334
|
const specToContracts = data.traceability.specs.specToContracts;
|
|
3264
3335
|
const specIds = Object.keys(specToContracts).sort(
|
|
@@ -3276,7 +3347,7 @@ function formatReportMarkdown(data) {
|
|
|
3276
3347
|
lines.push(...formatMarkdownTable(["Spec", "Status", "Contracts"], rows));
|
|
3277
3348
|
}
|
|
3278
3349
|
lines.push("");
|
|
3279
|
-
lines.push("
|
|
3350
|
+
lines.push("### Specs missing contract-ref");
|
|
3280
3351
|
lines.push("");
|
|
3281
3352
|
const missingRefSpecs = data.traceability.specs.missingRefSpecs;
|
|
3282
3353
|
if (missingRefSpecs.length === 0) {
|
|
@@ -3287,7 +3358,7 @@ function formatReportMarkdown(data) {
|
|
|
3287
3358
|
}
|
|
3288
3359
|
}
|
|
3289
3360
|
lines.push("");
|
|
3290
|
-
lines.push("
|
|
3361
|
+
lines.push("### SC coverage");
|
|
3291
3362
|
lines.push("");
|
|
3292
3363
|
lines.push(`- total: ${data.traceability.sc.total}`);
|
|
3293
3364
|
lines.push(`- covered: ${data.traceability.sc.covered}`);
|
|
@@ -3317,7 +3388,7 @@ function formatReportMarkdown(data) {
|
|
|
3317
3388
|
lines.push(`- missingIds: ${missingWithSources.join(", ")}`);
|
|
3318
3389
|
}
|
|
3319
3390
|
lines.push("");
|
|
3320
|
-
lines.push("
|
|
3391
|
+
lines.push("### SC \u2192 referenced tests");
|
|
3321
3392
|
lines.push("");
|
|
3322
3393
|
const scRefs = data.traceability.sc.refs;
|
|
3323
3394
|
const scIds = Object.keys(scRefs).sort((a, b) => a.localeCompare(b));
|
|
@@ -3334,7 +3405,7 @@ function formatReportMarkdown(data) {
|
|
|
3334
3405
|
}
|
|
3335
3406
|
}
|
|
3336
3407
|
lines.push("");
|
|
3337
|
-
lines.push("
|
|
3408
|
+
lines.push("### Spec:SC=1:1 violations");
|
|
3338
3409
|
lines.push("");
|
|
3339
3410
|
const specScIssues = data.issues.filter(
|
|
3340
3411
|
(item) => item.code === "QFAI-TRACE-012"
|
|
@@ -3349,7 +3420,7 @@ function formatReportMarkdown(data) {
|
|
|
3349
3420
|
}
|
|
3350
3421
|
}
|
|
3351
3422
|
lines.push("");
|
|
3352
|
-
lines.push("
|
|
3423
|
+
lines.push("### Hotspots");
|
|
3353
3424
|
lines.push("");
|
|
3354
3425
|
const hotspots = buildHotspots(data.issues);
|
|
3355
3426
|
if (hotspots.length === 0) {
|
|
@@ -3362,35 +3433,28 @@ function formatReportMarkdown(data) {
|
|
|
3362
3433
|
}
|
|
3363
3434
|
}
|
|
3364
3435
|
lines.push("");
|
|
3365
|
-
lines.push("##
|
|
3436
|
+
lines.push("## Guidance");
|
|
3366
3437
|
lines.push("");
|
|
3367
|
-
|
|
3368
|
-
|
|
3438
|
+
lines.push(
|
|
3439
|
+
"- \u6B21\u306E\u624B\u9806: `qfai doctor --fail-on error` \u2192 `qfai validate --fail-on error` \u2192 `qfai report`"
|
|
3369
3440
|
);
|
|
3370
|
-
if (
|
|
3371
|
-
lines.push("-
|
|
3372
|
-
} else {
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
`- ${item.severity.toUpperCase()} [${item.code}] ${item.message}${location}`
|
|
3377
|
-
);
|
|
3378
|
-
}
|
|
3379
|
-
}
|
|
3380
|
-
lines.push("");
|
|
3381
|
-
lines.push("## \u691C\u8A3C\u7D50\u679C");
|
|
3382
|
-
lines.push("");
|
|
3383
|
-
if (data.issues.length === 0) {
|
|
3384
|
-
lines.push("- (none)");
|
|
3441
|
+
if (data.summary.counts.error > 0) {
|
|
3442
|
+
lines.push("- error \u304C\u3042\u308B\u305F\u3081\u3001\u307E\u305A error \u304B\u3089\u4FEE\u6B63\u3057\u3066\u304F\u3060\u3055\u3044\u3002");
|
|
3443
|
+
} else if (data.summary.counts.warning > 0) {
|
|
3444
|
+
lines.push(
|
|
3445
|
+
"- warning \u306E\u6271\u3044\uFF08Hard Gate \u306B\u3059\u308B\u304B\uFF09\u306F\u904B\u7528\u3067\u6C7A\u3081\u3066\u304F\u3060\u3055\u3044\u3002"
|
|
3446
|
+
);
|
|
3385
3447
|
} else {
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
lines.push(
|
|
3390
|
-
`- ${item.severity.toUpperCase()} [${item.code}] ${item.message}${location}${refs}`
|
|
3391
|
-
);
|
|
3392
|
-
}
|
|
3448
|
+
lines.push(
|
|
3449
|
+
"- issue \u306F\u691C\u51FA\u3055\u308C\u307E\u305B\u3093\u3067\u3057\u305F\u3002\u904B\u7528\u30C6\u30F3\u30D7\u30EC\u306B\u6CBF\u3063\u3066\u7D99\u7D9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002"
|
|
3450
|
+
);
|
|
3393
3451
|
}
|
|
3452
|
+
lines.push(
|
|
3453
|
+
"- \u5909\u66F4\u533A\u5206\uFF08Compatibility / Change/Improvement\uFF09\u306F `.qfai/specs/*/delta.md` \u306B\u8A18\u9332\u3057\u307E\u3059\u3002"
|
|
3454
|
+
);
|
|
3455
|
+
lines.push(
|
|
3456
|
+
"- \u53C2\u7167\u30EB\u30FC\u30EB\u306E\u6B63\u672C: `.qfai/promptpack/steering/traceability.md` / `.qfai/promptpack/steering/compatibility-vs-change.md`"
|
|
3457
|
+
);
|
|
3394
3458
|
return lines.join("\n");
|
|
3395
3459
|
}
|
|
3396
3460
|
function formatReportJson(data) {
|
|
@@ -3714,6 +3778,8 @@ async function runValidate(options) {
|
|
|
3714
3778
|
const configResult = await loadConfig(root);
|
|
3715
3779
|
const result = await validateProject(root, configResult);
|
|
3716
3780
|
const normalized = normalizeValidationResult(root, result);
|
|
3781
|
+
const failOn = resolveFailOn(options, configResult.config.validation.failOn);
|
|
3782
|
+
const willFail = shouldFail(normalized, failOn);
|
|
3717
3783
|
const format = options.format ?? "text";
|
|
3718
3784
|
if (format === "text") {
|
|
3719
3785
|
emitText(normalized);
|
|
@@ -3723,11 +3789,10 @@ async function runValidate(options) {
|
|
|
3723
3789
|
root,
|
|
3724
3790
|
configResult.config.output.validateJsonPath
|
|
3725
3791
|
);
|
|
3726
|
-
emitGitHubOutput(normalized, root, jsonPath);
|
|
3792
|
+
emitGitHubOutput(normalized, root, jsonPath, { failOn, willFail });
|
|
3727
3793
|
}
|
|
3728
3794
|
await emitJson(normalized, root, configResult.config.output.validateJsonPath);
|
|
3729
|
-
|
|
3730
|
-
return shouldFail(normalized, failOn) ? 1 : 0;
|
|
3795
|
+
return willFail ? 1 : 0;
|
|
3731
3796
|
}
|
|
3732
3797
|
function resolveFailOn(options, fallback) {
|
|
3733
3798
|
if (options.failOn) {
|
|
@@ -3752,7 +3817,7 @@ function emitText(result) {
|
|
|
3752
3817
|
`
|
|
3753
3818
|
);
|
|
3754
3819
|
}
|
|
3755
|
-
function emitGitHubOutput(result, root, jsonPath) {
|
|
3820
|
+
function emitGitHubOutput(result, root, jsonPath, status) {
|
|
3756
3821
|
const deduped = dedupeIssues(result.issues);
|
|
3757
3822
|
const omitted = Math.max(deduped.length - GITHUB_ANNOTATION_LIMIT, 0);
|
|
3758
3823
|
const dropped = Math.max(result.issues.length - deduped.length, 0);
|
|
@@ -3761,7 +3826,8 @@ function emitGitHubOutput(result, root, jsonPath) {
|
|
|
3761
3826
|
omitted,
|
|
3762
3827
|
dropped,
|
|
3763
3828
|
jsonPath,
|
|
3764
|
-
root
|
|
3829
|
+
root,
|
|
3830
|
+
...status
|
|
3765
3831
|
});
|
|
3766
3832
|
const issues = deduped.slice(0, GITHUB_ANNOTATION_LIMIT);
|
|
3767
3833
|
for (const issue7 of issues) {
|
|
@@ -3785,7 +3851,9 @@ function emitGitHubSummary(result, options) {
|
|
|
3785
3851
|
`error=${result.counts.error}`,
|
|
3786
3852
|
`warning=${result.counts.warning}`,
|
|
3787
3853
|
`info=${result.counts.info}`,
|
|
3788
|
-
`annotations=${Math.min(options.total, GITHUB_ANNOTATION_LIMIT)}/${options.total}
|
|
3854
|
+
`annotations=${Math.min(options.total, GITHUB_ANNOTATION_LIMIT)}/${options.total}`,
|
|
3855
|
+
`failOn=${options.failOn}`,
|
|
3856
|
+
`result=${options.willFail ? "FAIL" : "PASS"}`
|
|
3789
3857
|
].join(" ");
|
|
3790
3858
|
process.stdout.write(`${summary}
|
|
3791
3859
|
`);
|
|
@@ -3803,6 +3871,9 @@ function emitGitHubSummary(result, options) {
|
|
|
3803
3871
|
`qfai validate note: \u8A73\u7D30\u306F ${relative} \u307E\u305F\u306F --format text \u3092\u53C2\u7167\u3057\u3066\u304F\u3060\u3055\u3044\u3002
|
|
3804
3872
|
`
|
|
3805
3873
|
);
|
|
3874
|
+
process.stdout.write(
|
|
3875
|
+
"qfai validate note: \u6B21\u306F qfai report \u3067 report.md \u3092\u751F\u6210\u3067\u304D\u307E\u3059\uFF08\u4F8B: qfai report\uFF09\u3002\n"
|
|
3876
|
+
);
|
|
3806
3877
|
}
|
|
3807
3878
|
function dedupeIssues(issues) {
|
|
3808
3879
|
const seen = /* @__PURE__ */ new Set();
|