ccqa 1.33.0 → 1.34.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/dist/bin/ccqa.mjs +285 -179
- package/dist/package.json +1 -1
- package/package.json +1 -1
package/dist/bin/ccqa.mjs
CHANGED
|
@@ -1524,16 +1524,21 @@ const ENDPOINT_ENV_KEYS = [
|
|
|
1524
1524
|
"CLAUDE_CODE_OAUTH_TOKEN"
|
|
1525
1525
|
];
|
|
1526
1526
|
/**
|
|
1527
|
+
* When both credentials are present the OAuth token wins and the API key is
|
|
1528
|
+
* dropped. Left to the CLI the API key would win, which makes "switch a CI
|
|
1529
|
+
* job to the subscription token" require unwiring the key everywhere; with
|
|
1530
|
+
* this rule, adding the one variable is the whole switch, and removing it is
|
|
1531
|
+
* the whole rollback. The one place the rule lives — both the resolved view
|
|
1532
|
+
* and the env the SDK receives apply it through here.
|
|
1533
|
+
*/
|
|
1534
|
+
function preferOauthToken(env) {
|
|
1535
|
+
if (env["CLAUDE_CODE_OAUTH_TOKEN"]) delete env["ANTHROPIC_API_KEY"];
|
|
1536
|
+
}
|
|
1537
|
+
/**
|
|
1527
1538
|
* Collects the endpoint/auth variables set in the current process environment
|
|
1528
1539
|
* so they can be forwarded, verbatim, to every Claude Code invocation. Returns
|
|
1529
1540
|
* only the keys that are actually set (non-empty), so unset variables never
|
|
1530
|
-
* override the SDK's own defaults.
|
|
1531
|
-
*
|
|
1532
|
-
* When both credentials are present the OAuth token wins and the API key is
|
|
1533
|
-
* not forwarded. Left to the CLI the API key would win, which makes "switch a
|
|
1534
|
-
* CI job to the subscription token" require unwiring the key everywhere; with
|
|
1535
|
-
* the precedence here, adding the one variable is the whole switch, and
|
|
1536
|
-
* removing it is the whole rollback.
|
|
1541
|
+
* override the SDK's own defaults. Credential precedence per preferOauthToken.
|
|
1537
1542
|
*/
|
|
1538
1543
|
function resolveEndpointEnv() {
|
|
1539
1544
|
const endpointEnv = {};
|
|
@@ -1541,7 +1546,7 @@ function resolveEndpointEnv() {
|
|
|
1541
1546
|
const value = process.env[key];
|
|
1542
1547
|
if (value && value.length > 0) endpointEnv[key] = value;
|
|
1543
1548
|
}
|
|
1544
|
-
|
|
1549
|
+
preferOauthToken(endpointEnv);
|
|
1545
1550
|
return endpointEnv;
|
|
1546
1551
|
}
|
|
1547
1552
|
/**
|
|
@@ -1555,6 +1560,30 @@ function withoutEmptyEndpointVars(env) {
|
|
|
1555
1560
|
for (const key of ENDPOINT_ENV_KEYS) if (out[key] === "") delete out[key];
|
|
1556
1561
|
return out;
|
|
1557
1562
|
}
|
|
1563
|
+
/**
|
|
1564
|
+
* The environment actually handed to the Claude Code process: the full process
|
|
1565
|
+
* environment with the caller's overrides on top, empty endpoint variables
|
|
1566
|
+
* dropped, and — when both credentials survive the merge — the API key removed
|
|
1567
|
+
* so the OAuth token wins.
|
|
1568
|
+
*
|
|
1569
|
+
* That removal MUST happen on the env the SDK receives, not only on the
|
|
1570
|
+
* resolved view: left to the CLI the API key would win, silently moving every
|
|
1571
|
+
* call from the subscription to metered billing when a CI job wires both
|
|
1572
|
+
* (which is exactly what happened before this function existed).
|
|
1573
|
+
*
|
|
1574
|
+
* Returns undefined when no endpoint variable is set and the caller passes no
|
|
1575
|
+
* env, so the SDK keeps its own default environment.
|
|
1576
|
+
*/
|
|
1577
|
+
function buildInvocationEnv(env) {
|
|
1578
|
+
const hasEndpointEnv = Object.keys(resolveEndpointEnv()).length > 0;
|
|
1579
|
+
if (!env && !hasEndpointEnv) return void 0;
|
|
1580
|
+
const merged = withoutEmptyEndpointVars({
|
|
1581
|
+
...process.env,
|
|
1582
|
+
...env
|
|
1583
|
+
});
|
|
1584
|
+
preferOauthToken(merged);
|
|
1585
|
+
return merged;
|
|
1586
|
+
}
|
|
1558
1587
|
let nativeBinaryWarned = false;
|
|
1559
1588
|
/**
|
|
1560
1589
|
* Warn once per process when the SDK's per-platform native binary is missing:
|
|
@@ -1570,11 +1599,7 @@ function warnOnceIfNativeBinaryMissing() {
|
|
|
1570
1599
|
async function invokeClaudeStreaming(options, onEvent) {
|
|
1571
1600
|
const { prompt, systemPrompt, allowedTools, disableBuiltinTools = false, disableThinking = false, mcpServers, maxTurns, env, model, cwd, onAbAction, onAbActionFailed, silenceBashLog = false, envScrubMap = [], relaxAbConstraints = false } = options;
|
|
1572
1601
|
const resolvedModel = resolveModel(model);
|
|
1573
|
-
const
|
|
1574
|
-
const mergedEnv = env || hasEndpointEnv ? withoutEmptyEndpointVars({
|
|
1575
|
-
...process.env,
|
|
1576
|
-
...env
|
|
1577
|
-
}) : void 0;
|
|
1602
|
+
const mergedEnv = buildInvocationEnv(env);
|
|
1578
1603
|
let lastAbToolUseId = null;
|
|
1579
1604
|
const claimAbToolUse = (toolUseId) => {
|
|
1580
1605
|
if (toolUseId !== lastAbToolUseId) return false;
|
|
@@ -21079,7 +21104,7 @@ const CSS = `
|
|
|
21079
21104
|
.d-grid dt { color: var(--muted); font-size: 12px; padding-top: 1px; }
|
|
21080
21105
|
.d-grid dd { color: var(--fg-dim); }
|
|
21081
21106
|
.d-grid dd ul { list-style: none; display: flex; flex-direction: column; gap: 3px; margin: 0; padding: 0; }
|
|
21082
|
-
.d-grid dd li::before { content: "\\2022 "; color: var(--muted-2); }
|
|
21107
|
+
.d-grid dd ul li::before { content: "\\2022 "; color: var(--muted-2); }
|
|
21083
21108
|
.d-grid code { font-size: 12px; background: var(--surface-2); border: 1px solid var(--border); border-radius: 4px; padding: 1px 5px; }
|
|
21084
21109
|
/* Prose gets a measure so it stops wrapping mid-phrase in a narrow column;
|
|
21085
21110
|
paths wrap as whole chips, never inside a path. */
|
|
@@ -21088,17 +21113,20 @@ const CSS = `
|
|
|
21088
21113
|
.d-paths code { white-space: nowrap; }
|
|
21089
21114
|
.d-prose + .d-paths { margin-top: 6px; }
|
|
21090
21115
|
.manual-attest { margin-top: 14px; }
|
|
21091
|
-
.steps-box { margin-top: 14px; max-width: 900px; }
|
|
21092
|
-
.steps-box .slabel { font-size: 12px; color: var(--muted); margin-bottom: 4px; }
|
|
21093
21116
|
.d-steps { margin: 4px 0 0; padding-left: 18px; display: flex; flex-direction: column; gap: 10px; font-size: 13px; color: var(--fg-dim); white-space: pre-line; }
|
|
21094
21117
|
.d-steps .step-expected { display: block; margin-top: 2px; font-size: 12.5px; }
|
|
21095
|
-
.notebox { margin-top: 14px; max-width: 900px; }
|
|
21096
|
-
.notebox .nlabel { font-size: 12px; color: var(--muted); margin-bottom: 4px; }
|
|
21097
21118
|
.notebox textarea { width: 100%; min-height: 54px; resize: vertical; font: inherit; font-size: 13px; color: var(--fg-dim); background: var(--surface); border: 1px solid var(--border-strong); border-radius: var(--radius-sm); padding: 8px 10px; }
|
|
21098
|
-
.notebox .nact { margin-top: 6px; display: flex; align-items: center; gap: 8px; }
|
|
21119
|
+
.notebox .nact { margin-top: 6px; display: flex; align-items: center; justify-content: flex-end; gap: 8px; }
|
|
21099
21120
|
.notebox .nstatus { font-size: 12px; color: var(--muted); }
|
|
21100
21121
|
.notebox .nstatus.ok { color: var(--pass); }
|
|
21101
21122
|
.notebox .nstatus.err { color: var(--fail); }
|
|
21123
|
+
/* ── detail panel: one card stack — reason, contents, note ── */
|
|
21124
|
+
.c-title .c-id { display: block; font-family: var(--mono); font-size: 11px; color: var(--muted-2); margin-top: 2px; }
|
|
21125
|
+
.p-sect { margin-top: 16px; max-width: 900px; }
|
|
21126
|
+
.p-sect:first-child { margin-top: 12px; }
|
|
21127
|
+
.p-slabel { font-size: 13px; font-weight: 600; color: var(--fg); margin-bottom: 6px; }
|
|
21128
|
+
/* The one-line state note beside the finding chip: what happens next. */
|
|
21129
|
+
.p-head-note { margin-left: auto; font-size: 12.5px; color: var(--muted); }
|
|
21102
21130
|
/* The inline form an audit-dismissal or environment-attestation button
|
|
21103
21131
|
expands into, in place of the two window.prompt() calls this replaces. */
|
|
21104
21132
|
.override-form { margin-top: 10px; max-width: 480px; display: flex; flex-direction: column; gap: 10px; }
|
|
@@ -21226,16 +21254,15 @@ const CLIENT_JS = `
|
|
|
21226
21254
|
"perspectives.mode.deterministic": "deterministic", "perspectives.mode.live": "live",
|
|
21227
21255
|
"perspectives.ov.cases": "cases", "perspectives.ov.features": "features",
|
|
21228
21256
|
"perspectives.d.preconditions": "Preconditions", "perspectives.d.startScreen": "Start screen",
|
|
21229
|
-
"perspectives.d.testCondition": "Condition", "perspectives.d.
|
|
21230
|
-
"perspectives.d.steps": "Steps", "perspectives.d.stepInclude": "Include: {name}",
|
|
21257
|
+
"perspectives.d.testCondition": "Condition", "perspectives.d.steps": "Steps", "perspectives.d.stepInclude": "Include: {name}",
|
|
21231
21258
|
"perspectives.d.stepExpected": "Expected:",
|
|
21232
21259
|
"perspectives.note.label": "Note",
|
|
21233
21260
|
"perspectives.note.placeholder": "Notes about this case…",
|
|
21234
21261
|
"perspectives.note.saved": "Saved",
|
|
21235
21262
|
"perspectives.note.error": "Could not save — retry",
|
|
21236
|
-
"perspectives.d.lastRed": "Most recent failure",
|
|
21237
|
-
"perspectives.d.changedSince": "Changes since the last run",
|
|
21238
21263
|
"perspectives.d.whyVerdict": "Why this verdict",
|
|
21264
|
+
"perspectives.d.contents": "What this case does",
|
|
21265
|
+
"perspectives.finding.loadFailed": "Could not load the finding's detail \u2014 open the run to read it",
|
|
21239
21266
|
"perspectives.result.openRun": "Open this run in the hub",
|
|
21240
21267
|
"perspectives.result.ci": "CI",
|
|
21241
21268
|
"perspectives.rerun.state.needsRepair": "Needs repair",
|
|
@@ -21246,26 +21273,26 @@ const CLIENT_JS = `
|
|
|
21246
21273
|
"perspectives.rerun.vsDeploy": "judged against deploy",
|
|
21247
21274
|
"perspectives.rerun.noDeployHead": "no deploy recorded for this profile",
|
|
21248
21275
|
"perspectives.rerun.changedByDeploy": "deploy {sha} changed files matched to this case",
|
|
21249
|
-
"perspectives.rerun.changesSome": "
|
|
21250
|
-
"perspectives.rerun.changesNone": "
|
|
21276
|
+
"perspectives.rerun.changesSome": "changes matched to this case since the last run (as of deploy {sha})",
|
|
21277
|
+
"perspectives.rerun.changesNone": "no changes matched to this case since the last run (as of deploy {sha})",
|
|
21251
21278
|
"perspectives.rerun.touchedCount": "{n} deployed path(s) matched this case",
|
|
21252
21279
|
"perspectives.rerun.touchedUnknown": "a deploy since the last run matched this case",
|
|
21253
21280
|
"perspectives.rerun.inProgressHint": "an audit or a run is still going, or the audit has not caught up with the deploy",
|
|
21254
|
-
"perspectives.rerun.heldHint": "
|
|
21255
|
-
"perspectives.rerun.repair.testDrift": "the generated test
|
|
21256
|
-
"perspectives.rerun.repair.specChange": "the
|
|
21257
|
-
"perspectives.rerun.repair.auditUndecided": "the audit
|
|
21258
|
-
"perspectives.rerun.repair.runFailed": "the
|
|
21259
|
-
"perspectives.rerun.why.noSelectionInRange": "a deploy in range
|
|
21260
|
-
"perspectives.rerun.why.selectionUnknown": "the
|
|
21281
|
+
"perspectives.rerun.heldHint": "an audit, auto-fix or run job is working on this case — dismissing or attesting is unavailable until it finishes",
|
|
21282
|
+
"perspectives.rerun.repair.testDrift": "the audit judged the generated test code older than the implementation — auto-fix re-records it, so nothing is needed yet",
|
|
21283
|
+
"perspectives.rerun.repair.specChange": "the audit judged that the behaviour this test assumes is gone from the code — fix or delete the test, or dismiss the finding via “{dismissButton}” if it is wrong",
|
|
21284
|
+
"perspectives.rerun.repair.auditUndecided": "the audit could not tell whether the test has drifted — review the finding, then fix the test or dismiss it via “{dismissButton}”",
|
|
21285
|
+
"perspectives.rerun.repair.runFailed": "the latest run failed — this verdict will not change until the failure's cause is addressed",
|
|
21286
|
+
"perspectives.rerun.why.noSelectionInRange": "a deploy in range carries no impact judgement, so this case runs to be safe",
|
|
21287
|
+
"perspectives.rerun.why.selectionUnknown": "the latest deploy could not be judged as affecting this case or not, so it runs to be safe",
|
|
21261
21288
|
"perspectives.rerun.why.noDeployLog": "no deploy log for this profile",
|
|
21262
21289
|
"perspectives.rerun.why.unknownDeployedSha": "the last run's deployed commit is unknown",
|
|
21263
21290
|
"perspectives.rerun.why.ambiguousDeployedSha": "a deploy landed while the last run was executing",
|
|
21264
21291
|
"perspectives.rerun.why.deployedShaNotInLog": "the last run's commit predates the retained deploy log",
|
|
21265
21292
|
"perspectives.rerun.why.gapInRange": "deploys are missing from the range",
|
|
21266
21293
|
"perspectives.rerun.why.unrecognized": "this hub reported a reason this UI does not recognise",
|
|
21267
|
-
"perspectives.rerun.fix.noSelectionInRange": "A deploy
|
|
21268
|
-
"perspectives.rerun.fix.selectionUnknown": "A deploy
|
|
21294
|
+
"perspectives.rerun.fix.noSelectionInRange": "A deploy was recorded without an impact judgement, so whether it affected this case is unknown. This happens when the judgement did not finish in time, or was disabled, at record. The next run and audit retake the result.",
|
|
21295
|
+
"perspectives.rerun.fix.selectionUnknown": "A deploy could not be judged as affecting this case or not, so the next run retakes the result.",
|
|
21269
21296
|
"perspectives.rerun.fix.noDeployLog": "Nothing has been recorded in this profile's deploy log. Wire ccqa hub deploy record into the deploy job for this environment so ccqa knows what shipped.",
|
|
21270
21297
|
"perspectives.rerun.fix.unknownDeployedSha": "The last run did not record which commit the environment was running, so it cannot be positioned in the deploy log. Runs record it once this profile has a deploy log.",
|
|
21271
21298
|
"perspectives.rerun.fix.ambiguousDeployedSha": "A deploy landed while the last run was executing, so which commit it exercised is not knowable. Re-run this case to get a clean baseline.",
|
|
@@ -21298,8 +21325,8 @@ const CLIENT_JS = `
|
|
|
21298
21325
|
"perspectives.override.noteLabel": "What was resolved, and how you checked (required)",
|
|
21299
21326
|
"perspectives.override.submit": "Record",
|
|
21300
21327
|
"perspectives.override.cancel": "Never mind",
|
|
21301
|
-
"perspectives.override.dismissHint": "
|
|
21302
|
-
"perspectives.override.envHint": "The failure stays on record
|
|
21328
|
+
"perspectives.override.dismissHint": "Withdraws the audit finding and puts this case back among the runnable. The verdict moves to “re-run needed”, and the next run's result settles it.",
|
|
21329
|
+
"perspectives.override.envHint": "The failure stays on record; only the verdict becomes “manually verified”. It lapses once a deploy reaches this case, and normal runs resume.",
|
|
21303
21330
|
"prompt.card.record": "Recording browser actions",
|
|
21304
21331
|
"prompt.card.live": "Live run (AI-driven)",
|
|
21305
21332
|
"prompt.card.playwright": "Playwright test generation",
|
|
@@ -21416,16 +21443,15 @@ const CLIENT_JS = `
|
|
|
21416
21443
|
"perspectives.mode.deterministic": "決定的", "perspectives.mode.live": "ライブ",
|
|
21417
21444
|
"perspectives.ov.cases": "ケース", "perspectives.ov.features": "機能",
|
|
21418
21445
|
"perspectives.d.preconditions": "前提条件", "perspectives.d.startScreen": "開始画面",
|
|
21419
|
-
"perspectives.d.testCondition": "実行条件", "perspectives.d.
|
|
21420
|
-
"perspectives.d.steps": "手順", "perspectives.d.stepInclude": "ブロック: {name}",
|
|
21446
|
+
"perspectives.d.testCondition": "実行条件", "perspectives.d.steps": "手順", "perspectives.d.stepInclude": "ブロック: {name}",
|
|
21421
21447
|
"perspectives.d.stepExpected": "期待結果:",
|
|
21422
|
-
"perspectives.note.label": "
|
|
21448
|
+
"perspectives.note.label": "メモ",
|
|
21423
21449
|
"perspectives.note.placeholder": "このケースについてのメモ…",
|
|
21424
21450
|
"perspectives.note.saved": "保存しました",
|
|
21425
21451
|
"perspectives.note.error": "保存に失敗しました — 再試行してください",
|
|
21426
|
-
"perspectives.d.lastRed": "直近の失敗",
|
|
21427
|
-
"perspectives.d.changedSince": "前回実行以降の変更",
|
|
21428
21452
|
"perspectives.d.whyVerdict": "この判定の理由",
|
|
21453
|
+
"perspectives.d.contents": "テストの内容",
|
|
21454
|
+
"perspectives.finding.loadFailed": "詳細を読み込めませんでした。実行のページで確認してください",
|
|
21429
21455
|
"perspectives.result.openRun": "ハブでこの実行を開く",
|
|
21430
21456
|
"perspectives.result.ci": "CI",
|
|
21431
21457
|
"perspectives.rerun.state.needsRepair": "修正待ち",
|
|
@@ -21435,27 +21461,27 @@ const CLIENT_JS = `
|
|
|
21435
21461
|
"perspectives.rerun.state.verified": "検証済み",
|
|
21436
21462
|
"perspectives.rerun.vsDeploy": "判定基準: デプロイ",
|
|
21437
21463
|
"perspectives.rerun.noDeployHead": "このプロファイルにはデプロイの記録がありません",
|
|
21438
|
-
"perspectives.rerun.changedByDeploy": "デプロイ {sha}
|
|
21439
|
-
"perspectives.rerun.changesSome": "
|
|
21440
|
-
"perspectives.rerun.changesNone": "
|
|
21441
|
-
"perspectives.rerun.touchedCount": "
|
|
21442
|
-
"perspectives.rerun.touchedUnknown": "
|
|
21443
|
-
"perspectives.rerun.inProgressHint": "
|
|
21444
|
-
"perspectives.rerun.heldHint": "
|
|
21445
|
-
"perspectives.rerun.repair.testDrift": "
|
|
21446
|
-
"perspectives.rerun.repair.specChange": "
|
|
21447
|
-
"perspectives.rerun.repair.auditUndecided": "
|
|
21448
|
-
"perspectives.rerun.repair.runFailed": "
|
|
21449
|
-
"perspectives.rerun.why.noSelectionInRange": "
|
|
21450
|
-
"perspectives.rerun.why.selectionUnknown": "
|
|
21464
|
+
"perspectives.rerun.changedByDeploy": "デプロイ {sha} が、このケースに関係するファイルを変更しています",
|
|
21465
|
+
"perspectives.rerun.changesSome": "前回の実行より後に、このケースに関係する変更があります(デプロイ {sha} 時点)",
|
|
21466
|
+
"perspectives.rerun.changesNone": "前回の実行より後に、このケースに関係する変更はありません(デプロイ {sha} 時点)",
|
|
21467
|
+
"perspectives.rerun.touchedCount": "前回の実行より後のデプロイが、このケースに関係する変更を {n} 件含んでいます",
|
|
21468
|
+
"perspectives.rerun.touchedUnknown": "前回の実行より後のデプロイが、このケースに関係する変更を含んでいます",
|
|
21469
|
+
"perspectives.rerun.inProgressHint": "監査または実行のジョブが作業中か、最新デプロイに対する監査がまだ走っていません",
|
|
21470
|
+
"perspectives.rerun.heldHint": "監査・自動修正・実行のいずれかのジョブが、このケースを処理中です。終わるまで、このケースへの操作(棄却・手動確認)はできません",
|
|
21471
|
+
"perspectives.rerun.repair.testDrift": "監査が、生成済みのテストコードは実装より古いと判定しました。自動修正が録り直すので、まず対応は不要です",
|
|
21472
|
+
"perspectives.rerun.repair.specChange": "監査が、このテストの前提とする振る舞いは実装から無くなったと判定しました。テストを直すか削除するかを決めてください。指摘のほうが誤りなら、「{dismissButton}」から棄却できます",
|
|
21473
|
+
"perspectives.rerun.repair.auditUndecided": "テストが実装とズレているかどうか、監査では判断できませんでした。指摘の内容を確認して、テストを直すか、「{dismissButton}」から棄却してください",
|
|
21474
|
+
"perspectives.rerun.repair.runFailed": "直近の実行が失敗しています。失敗の原因に対処するまで、この判定は変わりません",
|
|
21475
|
+
"perspectives.rerun.why.noSelectionInRange": "影響判定なしで記録されたデプロイがあるため、念のため実行対象になっています",
|
|
21476
|
+
"perspectives.rerun.why.selectionUnknown": "直近のデプロイがこのケースに影響するかどうか判断できなかったため、念のため実行対象になっています",
|
|
21451
21477
|
"perspectives.rerun.why.noDeployLog": "このプロファイルのデプロイ記録がありません",
|
|
21452
21478
|
"perspectives.rerun.why.unknownDeployedSha": "前回実行時にデプロイされていたcommitが不明です",
|
|
21453
21479
|
"perspectives.rerun.why.ambiguousDeployedSha": "前回実行の途中でデプロイが発生しました",
|
|
21454
|
-
"perspectives.rerun.why.deployedShaNotInLog": "
|
|
21480
|
+
"perspectives.rerun.why.deployedShaNotInLog": "前回の実行が古く、どのデプロイに対して実行した結果か特定できません",
|
|
21455
21481
|
"perspectives.rerun.why.gapInRange": "対象範囲のデプロイ記録が欠けています",
|
|
21456
21482
|
"perspectives.rerun.why.unrecognized": "このUIが認識できない理由がハブから返されました",
|
|
21457
|
-
"perspectives.rerun.fix.noSelectionInRange": "
|
|
21458
|
-
"perspectives.rerun.fix.selectionUnknown": "
|
|
21483
|
+
"perspectives.rerun.fix.noSelectionInRange": "影響判定なしで記録されたデプロイがあり、このケースに影響したかどうか分かりません。デプロイの記録時に影響判定が時間内に終わらなかったか、無効化されていたときに起きます。次の実行と監査で結果を取り直します。",
|
|
21484
|
+
"perspectives.rerun.fix.selectionUnknown": "デプロイがこのケースに影響するかどうか判断できなかったため、次の実行で結果を取り直します。",
|
|
21459
21485
|
"perspectives.rerun.fix.noDeployLog": "このプロファイルのデプロイログに記録がありません。何がデプロイされたかをccqaに伝えるため、この環境のデプロイジョブに ccqa hub deploy record を組み込んでください。",
|
|
21460
21486
|
"perspectives.rerun.fix.unknownDeployedSha": "前回実行は環境で動いていたcommitを記録していないため、デプロイログ上の位置を決められません。このプロファイルにデプロイログができれば、以降の実行では記録されます。",
|
|
21461
21487
|
"perspectives.rerun.fix.ambiguousDeployedSha": "前回実行の途中でデプロイが発生したため、どのcommitを検証したのか確定できません。基準を取り直すには再実行してください。",
|
|
@@ -21471,25 +21497,25 @@ const CLIENT_JS = `
|
|
|
21471
21497
|
"perspectives.manual.envButton": "環境要因が解消した場合はこちら",
|
|
21472
21498
|
"perspectives.manual.confirmRevoke": "このケースの手動確認を取り消しますか?",
|
|
21473
21499
|
"perspectives.manual.error": "保存に失敗しました — 再試行してください",
|
|
21474
|
-
"perspectives.manual.verifiedBy": "{by}
|
|
21475
|
-
"perspectives.manual.lapsed.deployReached": "
|
|
21476
|
-
"perspectives.manual.lapsed.deployReachedNamed": "
|
|
21477
|
-
"perspectives.manual.lapsed.cannotPlace": "
|
|
21478
|
-
"perspectives.manual.lapsed.specEdited": "
|
|
21479
|
-
"perspectives.manual.lapsed.newerRed": "
|
|
21480
|
-
"perspectives.manual.lapsed.unrecognized": "このUI
|
|
21500
|
+
"perspectives.manual.verifiedBy": "{by} さんが手動で動作確認しました({at})",
|
|
21501
|
+
"perspectives.manual.lapsed.deployReached": "デプロイがこのケースに届いたため、手動確認は失効しました",
|
|
21502
|
+
"perspectives.manual.lapsed.deployReachedNamed": "デプロイ {sha}({at})がこのケースに届いたため、手動確認は失効しました",
|
|
21503
|
+
"perspectives.manual.lapsed.cannotPlace": "基準のデプロイをログで特定できなくなったため、手動確認は失効しました",
|
|
21504
|
+
"perspectives.manual.lapsed.specEdited": "spec が編集されたため、手動確認は失効しました",
|
|
21505
|
+
"perspectives.manual.lapsed.newerRed": "手動確認より後の実行が失敗したため、手動確認は失効しました",
|
|
21506
|
+
"perspectives.manual.lapsed.unrecognized": "この UI が認識できない理由により、手動確認は失効しました",
|
|
21481
21507
|
"perspectives.dismiss.offerButton": "テスト仕様に問題がなかった場合はこちら",
|
|
21482
21508
|
"perspectives.dismiss.revokeButton": "棄却を取り消す",
|
|
21483
21509
|
"perspectives.dismiss.confirmRevoke": "このケースの棄却を取り消しますか?",
|
|
21484
|
-
"perspectives.dismiss.activeNote": "
|
|
21485
|
-
"perspectives.dismiss.priorNote": "
|
|
21510
|
+
"perspectives.dismiss.activeNote": "{by} さんが監査指摘「{headline}」を誤検知として棄却しました({at}・理由: {note})。次の実行結果が正否を決めます。",
|
|
21511
|
+
"perspectives.dismiss.priorNote": "この指摘は、以前 {by} さんが棄却しています({at}・理由: {note})",
|
|
21486
21512
|
"perspectives.override.byLabel": "確認した人",
|
|
21487
21513
|
"perspectives.override.reasonLabel": "理由(必須)",
|
|
21488
21514
|
"perspectives.override.noteLabel": "解消と確認の内容(必須)",
|
|
21489
21515
|
"perspectives.override.submit": "記録する",
|
|
21490
21516
|
"perspectives.override.cancel": "やめる",
|
|
21491
|
-
"perspectives.override.dismissHint": "
|
|
21492
|
-
"perspectives.override.envHint": "
|
|
21517
|
+
"perspectives.override.dismissHint": "監査の指摘を取り下げて、このケースを実行対象に戻します。判定は「要再実行」になり、次の実行結果が正否を決めます。",
|
|
21518
|
+
"perspectives.override.envHint": "失敗の記録は残したまま、判定だけが「手動確認済み」になります。次のデプロイがこのケースに届くと失効し、通常の実行に戻ります。",
|
|
21493
21519
|
"prompt.card.record": "ブラウザ操作の記録",
|
|
21494
21520
|
"prompt.card.live": "ライブ実行(AI操作)",
|
|
21495
21521
|
"prompt.card.playwright": "Playwrightテスト生成",
|
|
@@ -21786,7 +21812,6 @@ const CLIENT_JS = `
|
|
|
21786
21812
|
return span;
|
|
21787
21813
|
}
|
|
21788
21814
|
|
|
21789
|
-
|
|
21790
21815
|
// Shared by the runs-list row and the run-detail header — the one place
|
|
21791
21816
|
// both decide whether a run's own status badge speaks drift's vocabulary.
|
|
21792
21817
|
function runStatusBadge(run) {
|
|
@@ -22396,15 +22421,13 @@ const CLIENT_JS = `
|
|
|
22396
22421
|
|
|
22397
22422
|
// ── run detail: spec cards ──────────────────────────────────────────
|
|
22398
22423
|
|
|
22399
|
-
// The diagnosis card
|
|
22400
|
-
//
|
|
22401
|
-
//
|
|
22402
|
-
//
|
|
22403
|
-
//
|
|
22404
|
-
//
|
|
22405
|
-
function
|
|
22406
|
-
var wrap = el("div", "analysis-box");
|
|
22407
|
-
var a = r.analysis;
|
|
22424
|
+
// The diagnosis card's two halves, shared by the run view (analysisSection)
|
|
22425
|
+
// and the perspectives reason card so the two renderings cannot drift:
|
|
22426
|
+
// verdict head (label chip + confidence), then the cause→fix pair as
|
|
22427
|
+
// labelled rows — headline and recommendation are one causal unit, so they
|
|
22428
|
+
// read as one. subDiagnosis is deliberately NOT shown: it is a machine
|
|
22429
|
+
// vocabulary for accuracy stratification and learning, not for humans.
|
|
22430
|
+
function diagnosisHead(a) {
|
|
22408
22431
|
var head = el("div", "analysis-head");
|
|
22409
22432
|
head.appendChild(labelChip(a.label));
|
|
22410
22433
|
// Which repair a SPEC_CHANGE needs — delete the spec, or rewrite it. The
|
|
@@ -22414,7 +22437,10 @@ const CLIENT_JS = `
|
|
|
22414
22437
|
head.appendChild(el("span", "chip spec-change-chip", t("diag.specChangeKind." + a.specChangeKind)));
|
|
22415
22438
|
}
|
|
22416
22439
|
head.appendChild(el("span", "conf", Math.round(a.confidence * 100) + "%"));
|
|
22417
|
-
|
|
22440
|
+
return head;
|
|
22441
|
+
}
|
|
22442
|
+
|
|
22443
|
+
function diagnosisKv(a) {
|
|
22418
22444
|
var kv = el("div", "analysis-kv");
|
|
22419
22445
|
// Set only when the verdict blames the test case (TEST_DRIFT/SPEC_CHANGE),
|
|
22420
22446
|
// on both kinds of row — it names the half that has to be repaired.
|
|
@@ -22430,7 +22456,14 @@ const CLIENT_JS = `
|
|
|
22430
22456
|
kv.appendChild(el("div", "k", t("diag.fix")));
|
|
22431
22457
|
kv.appendChild(el("div", "v", a.recommendation));
|
|
22432
22458
|
}
|
|
22433
|
-
|
|
22459
|
+
return kv.childNodes.length > 0 ? kv : null;
|
|
22460
|
+
}
|
|
22461
|
+
|
|
22462
|
+
function analysisSection(runId, r) {
|
|
22463
|
+
var wrap = el("div", "analysis-box");
|
|
22464
|
+
wrap.appendChild(diagnosisHead(r.analysis));
|
|
22465
|
+
var kv = diagnosisKv(r.analysis);
|
|
22466
|
+
if (kv) wrap.appendChild(kv);
|
|
22434
22467
|
return wrap;
|
|
22435
22468
|
}
|
|
22436
22469
|
|
|
@@ -23543,9 +23576,10 @@ const CLIENT_JS = `
|
|
|
23543
23576
|
// this hub answers at all. Chip visibility follows it rather than the report,
|
|
23544
23577
|
// so switching profile doesn't drop the filter while the next one loads.
|
|
23545
23578
|
// "drift" is the DriftLedgerResponse, or null when unanswered (older hub, or
|
|
23546
|
-
// a failed fetch) — not profile-scoped
|
|
23547
|
-
//
|
|
23548
|
-
//
|
|
23579
|
+
// a failed fetch) — not profile-scoped. It backs the audit column's
|
|
23580
|
+
// "audited at" line and the reason card's finding (runId/label/headline),
|
|
23581
|
+
// so reloadRerun refetches it alongside the rerun report to keep the two
|
|
23582
|
+
// reports of one card equally fresh.
|
|
23549
23583
|
var perspState = {
|
|
23550
23584
|
doc: null, q: "", f: "all",
|
|
23551
23585
|
rerun: null, rerunSupported: null, runUrls: {}, rerunProfiles: [],
|
|
@@ -23613,10 +23647,10 @@ const CLIENT_JS = `
|
|
|
23613
23647
|
|
|
23614
23648
|
// ── perspectives: drift ledger ────────────────────────────────────────
|
|
23615
23649
|
// Not profile-scoped (see perspState.drift above), so unlike rerunPath this
|
|
23616
|
-
// takes no ?profile=.
|
|
23617
|
-
//
|
|
23618
|
-
//
|
|
23619
|
-
// column's evidence line
|
|
23650
|
+
// takes no ?profile=. The audit AXIS is answered by the /rerun report
|
|
23651
|
+
// (ADR-0014); this ledger supplies what that report does not carry — the
|
|
23652
|
+
// finding's own coordinate and words (runId/at/label/headline), shown in
|
|
23653
|
+
// the audit column's evidence line and the reason card.
|
|
23620
23654
|
|
|
23621
23655
|
function driftPath() {
|
|
23622
23656
|
return "/api/v1/projects/" + encodeURIComponent(state.project) + "/drift";
|
|
@@ -23669,7 +23703,10 @@ const CLIENT_JS = `
|
|
|
23669
23703
|
// when it has no entry).
|
|
23670
23704
|
function rerunReasonText(prefix, reason) {
|
|
23671
23705
|
var text = t(prefix + reason);
|
|
23672
|
-
|
|
23706
|
+
if (text === prefix + reason) text = t(prefix + "unrecognized");
|
|
23707
|
+
// Wordings that point at the dismiss control name it by its own label, so
|
|
23708
|
+
// renaming the button cannot silently strand four strings.
|
|
23709
|
+
return text.replace("{dismissButton}", t("perspectives.dismiss.offerButton"));
|
|
23673
23710
|
}
|
|
23674
23711
|
|
|
23675
23712
|
// Who attested and when, plus their note if they left one — the whole
|
|
@@ -23760,7 +23797,6 @@ const CLIENT_JS = `
|
|
|
23760
23797
|
return lapse ? why + " · " + lapse : why;
|
|
23761
23798
|
}
|
|
23762
23799
|
|
|
23763
|
-
|
|
23764
23800
|
// --- pure: rerun composition ---------------------------------------------
|
|
23765
23801
|
// Self-contained on purpose: no DOM, no closures. rerun-view.test.ts lifts
|
|
23766
23802
|
// this region out of the rendered page and runs it, because the summary bar
|
|
@@ -24202,8 +24238,8 @@ const CLIENT_JS = `
|
|
|
24202
24238
|
|
|
24203
24239
|
// --- pure: rerun detail labels -------------------------------------------
|
|
24204
24240
|
// Self-contained on purpose (no DOM, no closures) so rerun-view.test.ts can
|
|
24205
|
-
// lift this region out of the rendered page and run it:
|
|
24206
|
-
//
|
|
24241
|
+
// lift this region out of the rendered page and run it: whether the deploy
|
|
24242
|
+
// log answered for this case, and which deploy the reason line names.
|
|
24207
24243
|
|
|
24208
24244
|
// The deploy log answered for this case: the row can show what it holds.
|
|
24209
24245
|
// A case the log could not place has no evidence to show even though its
|
|
@@ -24213,20 +24249,6 @@ const CLIENT_JS = `
|
|
|
24213
24249
|
return rr.verdict === "rerunNeeded" && !rr.executionAssumedReached;
|
|
24214
24250
|
}
|
|
24215
24251
|
|
|
24216
|
-
// Evidence is labelled by the timeframe it covers; everything else names why
|
|
24217
|
-
// the verdict landed — a different kind of content, and forcing one label
|
|
24218
|
-
// over both would make one of the two read as a lie.
|
|
24219
|
-
function rerunEvidenceLabelKey(rr) {
|
|
24220
|
-
return rerunHasEvidence(rr) ? "perspectives.d.changedSince" : "perspectives.d.whyVerdict";
|
|
24221
|
-
}
|
|
24222
|
-
|
|
24223
|
-
// The failure row points at a run. With no failure there is nothing to point
|
|
24224
|
-
// at, so the row is omitted rather than filled with "never failed" — the row
|
|
24225
|
-
// above already carries the last result.
|
|
24226
|
-
function rerunHasFailure(rr) {
|
|
24227
|
-
return !!(rr && rr.lastRed);
|
|
24228
|
-
}
|
|
24229
|
-
|
|
24230
24252
|
// Which deploy the evidence line names, and how. A "needed" verdict carries
|
|
24231
24253
|
// the deploy that caused it (touchedByDeploy) when the hub could confirm one,
|
|
24232
24254
|
// and that is the deploy a reader wants — so it is named, with when it
|
|
@@ -24293,13 +24315,10 @@ const CLIENT_JS = `
|
|
|
24293
24315
|
return null;
|
|
24294
24316
|
}
|
|
24295
24317
|
|
|
24296
|
-
// The
|
|
24297
|
-
//
|
|
24298
|
-
//
|
|
24299
|
-
//
|
|
24300
|
-
// A dismissal (active or superseded by a later finding) is appended below
|
|
24301
|
-
// whichever of those this case has, rather than replacing it — see
|
|
24302
|
-
// rerunDismissalLine.
|
|
24318
|
+
// The reason card's body when no run-recorded finding is shown: what the
|
|
24319
|
+
// deploy log holds since this case last ran (rerunChangeLine), or why the
|
|
24320
|
+
// verdict landed. The dismissal and lapse notes are the card's own to
|
|
24321
|
+
// append (perspReasonCard), not this value's.
|
|
24303
24322
|
function rerunEvidenceValue(rr) {
|
|
24304
24323
|
var wrap = el("div");
|
|
24305
24324
|
if (!rerunHasEvidence(rr)) {
|
|
@@ -24319,29 +24338,12 @@ const CLIENT_JS = `
|
|
|
24319
24338
|
wrap.appendChild(pathCodes(rr.touchedBy));
|
|
24320
24339
|
}
|
|
24321
24340
|
}
|
|
24322
|
-
var dismissLine = rerunDismissalLine(rr);
|
|
24323
|
-
if (dismissLine) wrap.appendChild(el("div", "d-prose" + (dismissLine.muted ? " muted" : ""), dismissLine.text));
|
|
24324
|
-
return wrap;
|
|
24325
|
-
}
|
|
24326
|
-
|
|
24327
|
-
// The failure: which run it was, then what the analysis concluded. The
|
|
24328
|
-
// headline is model output, already localized server-side, so it is shown as
|
|
24329
|
-
// written. A run made without failure analysis carries neither field, and the
|
|
24330
|
-
// row is then the coordinate alone — what it has always been.
|
|
24331
|
-
function rerunFailureValue(entry) {
|
|
24332
|
-
var wrap = el("div");
|
|
24333
|
-
wrap.appendChild(ledgerLine(entry));
|
|
24334
|
-
if (entry.label) {
|
|
24335
|
-
var line = el("div", "d-prose", labelText(entry.label));
|
|
24336
|
-
if (entry.headline) line.appendChild(document.createTextNode(" · " + entry.headline));
|
|
24337
|
-
wrap.appendChild(line);
|
|
24338
|
-
}
|
|
24339
24341
|
return wrap;
|
|
24340
24342
|
}
|
|
24341
24343
|
|
|
24342
24344
|
// Lets a person's own check stand in for the machine's verdict. reloadRerun()
|
|
24343
|
-
// is the same
|
|
24344
|
-
//
|
|
24345
|
+
// is the same refresh a profile switch uses — it re-renders the whole
|
|
24346
|
+
// table, so an open detail panel closes along with it.
|
|
24345
24347
|
function submitAttestation(method, body) {
|
|
24346
24348
|
apiFetch(attestationsPath(), {
|
|
24347
24349
|
method: method,
|
|
@@ -24458,7 +24460,10 @@ const CLIENT_JS = `
|
|
|
24458
24460
|
// dismissal that is currently the reason the axis reads clean. At most one
|
|
24459
24461
|
// of the two ever shows — a finding the axis
|
|
24460
24462
|
// itself has cleared, dismissed or not, offers nothing here.
|
|
24463
|
+
// While a job holds the spec, no control shows at all: heldHint promises
|
|
24464
|
+
// the reader that overrides wait for the job, so the buttons must too.
|
|
24461
24465
|
function auditOverrideBox(feature, spec, rr) {
|
|
24466
|
+
if (rr.heldBy) return null;
|
|
24462
24467
|
if (auditDismissalActive(rr)) {
|
|
24463
24468
|
var box = el("div", "manual-attest");
|
|
24464
24469
|
box.appendChild(auditDismissalRevokeButton(feature, spec));
|
|
@@ -24473,6 +24478,7 @@ const CLIENT_JS = `
|
|
|
24473
24478
|
// no open finding of its own, which is auditOverrideBox's problem to answer,
|
|
24474
24479
|
// not this one's.
|
|
24475
24480
|
function executionOverrideBox(feature, spec, rr) {
|
|
24481
|
+
if (rr.heldBy) return null;
|
|
24476
24482
|
if (rr.manual) {
|
|
24477
24483
|
var box = el("div", "manual-attest");
|
|
24478
24484
|
if (rr.verdict !== "manuallyVerified") box.appendChild(el("div", "d-prose", manualAttestationText(rr.manual)));
|
|
@@ -24487,16 +24493,133 @@ const CLIENT_JS = `
|
|
|
24487
24493
|
return null;
|
|
24488
24494
|
}
|
|
24489
24495
|
|
|
24490
|
-
//
|
|
24491
|
-
//
|
|
24492
|
-
//
|
|
24493
|
-
//
|
|
24494
|
-
//
|
|
24495
|
-
//
|
|
24496
|
-
//
|
|
24497
|
-
|
|
24496
|
+
// ── the reason card ──────────────────────────────────────────────────────
|
|
24497
|
+
// One card that answers "why is the verdict what it is". When an axis
|
|
24498
|
+
// stands on a run-recorded finding (an open audit finding, or the failure
|
|
24499
|
+
// the execution axis reports), the card carries that finding in full,
|
|
24500
|
+
// fetched from the run's own report — where cause, fix and evidence
|
|
24501
|
+
// already live. Everything a person may do about the state (dismiss,
|
|
24502
|
+
// attest, revoke) sits in the same card, beside the reason it answers.
|
|
24503
|
+
|
|
24504
|
+
var runReportCache = {};
|
|
24505
|
+
function fetchRunReport(runId) {
|
|
24506
|
+
if (!runReportCache[runId]) {
|
|
24507
|
+
// Same endpoint (and so the same HTTP cache entry) the run view reads.
|
|
24508
|
+
// A failure is not cached: a transient 502 costs one refetch on the
|
|
24509
|
+
// next expand instead of pinning the fallback for the session.
|
|
24510
|
+
runReportCache[runId] = apiFetch("/api/v1/runs/" + encodeURIComponent(runId) + "/report")
|
|
24511
|
+
.catch(function () { delete runReportCache[runId]; return null; });
|
|
24512
|
+
}
|
|
24513
|
+
return runReportCache[runId];
|
|
24514
|
+
}
|
|
24515
|
+
|
|
24516
|
+
function reportRowFor(report, key) {
|
|
24517
|
+
var rows = (report && report.results) || [];
|
|
24518
|
+
for (var i = 0; i < rows.length; i++) {
|
|
24519
|
+
if (rows[i].feature + "/" + rows[i].spec === key) return rows[i];
|
|
24520
|
+
}
|
|
24521
|
+
return null;
|
|
24522
|
+
}
|
|
24523
|
+
|
|
24524
|
+
// Which run-recorded finding the card shows, if any: an open audit finding
|
|
24525
|
+
// wins (it is why nothing runs), else the failure the execution axis stands
|
|
24526
|
+
// on. The ledger's own label/headline are the instant fallback while the
|
|
24527
|
+
// report loads — and the whole content if it never arrives.
|
|
24528
|
+
function reasonFindingSource(rr, driftEntry) {
|
|
24529
|
+
if (auditOpen(rr) && driftEntry && driftEntry.runId && driftEntry.label) return driftEntry;
|
|
24530
|
+
if (rr.execution === "failed" && rr.lastRed && rr.lastRed.runId && rr.lastRed.label) return rr.lastRed;
|
|
24531
|
+
return null;
|
|
24532
|
+
}
|
|
24533
|
+
|
|
24534
|
+
function perspReasonCard(feature, spec, rr, driftEntry) {
|
|
24535
|
+
var card = el("div", "analysis-box");
|
|
24536
|
+
var source = reasonFindingSource(rr, driftEntry);
|
|
24537
|
+
|
|
24538
|
+
if (source) {
|
|
24539
|
+
// The ledger's label/headline render at once; the run's own report
|
|
24540
|
+
// replaces them with the full diagnosis (the same head/kv the run view
|
|
24541
|
+
// builds) when — and if — it arrives.
|
|
24542
|
+
var note = el("span", "p-head-note", rerunWhyVerdict(rr));
|
|
24543
|
+
var slot = el("div");
|
|
24544
|
+
var head = el("div", "analysis-head");
|
|
24545
|
+
head.appendChild(labelChip(source.label));
|
|
24546
|
+
head.appendChild(note);
|
|
24547
|
+
slot.appendChild(head);
|
|
24548
|
+
if (source.headline) {
|
|
24549
|
+
var kv = diagnosisKv({ headline: source.headline });
|
|
24550
|
+
if (kv) slot.appendChild(kv);
|
|
24551
|
+
}
|
|
24552
|
+
card.appendChild(slot);
|
|
24553
|
+
|
|
24554
|
+
// A graded finding is the human's word, not the model's: the ledger
|
|
24555
|
+
// carries the corrected label/headline, while the run's report still
|
|
24556
|
+
// holds the original prediction. Upgrading would show the guess the
|
|
24557
|
+
// person explicitly overwrote, so the card keeps the ledger's version.
|
|
24558
|
+
if (source.graded) return finishReasonCard(card, feature, spec, rr);
|
|
24559
|
+
|
|
24560
|
+
fetchRunReport(source.runId).then(function (report) {
|
|
24561
|
+
var reportRow = reportRowFor(report, perspSpecKey(feature, spec));
|
|
24562
|
+
var a = reportRow && reportRow.analysis;
|
|
24563
|
+
if (!a) {
|
|
24564
|
+
if (!source.headline) slot.appendChild(el("div", "d-prose muted", t("perspectives.finding.loadFailed")));
|
|
24565
|
+
return;
|
|
24566
|
+
}
|
|
24567
|
+
// The ledger's one-liner stands in when the report row lost its own.
|
|
24568
|
+
if (!a.headline) a.headline = source.headline || "";
|
|
24569
|
+
clear(slot);
|
|
24570
|
+
var fullHead = diagnosisHead(a);
|
|
24571
|
+
fullHead.appendChild(note);
|
|
24572
|
+
slot.appendChild(fullHead);
|
|
24573
|
+
var fullKv = diagnosisKv(a);
|
|
24574
|
+
if (fullKv) slot.appendChild(fullKv);
|
|
24575
|
+
var evi = analysisEvidenceSection(reportRow);
|
|
24576
|
+
if (evi.count) slot.appendChild(detailsBlock(t("acc.evidence"), evi.count, evi.node));
|
|
24577
|
+
});
|
|
24578
|
+
} else {
|
|
24579
|
+
// No finding to show: the reason is the deploy-log answer, or the
|
|
24580
|
+
// verdict's own wording.
|
|
24581
|
+
card.appendChild(rerunEvidenceValue(rr));
|
|
24582
|
+
}
|
|
24583
|
+
|
|
24584
|
+
return finishReasonCard(card, feature, spec, rr);
|
|
24585
|
+
}
|
|
24586
|
+
|
|
24587
|
+
// The card's shared tail: the dismissal/lapse notes and the person's
|
|
24588
|
+
// controls, appended after whichever body the card ended up with.
|
|
24589
|
+
function finishReasonCard(card, feature, spec, rr) {
|
|
24590
|
+
var dline = rerunDismissalLine(rr);
|
|
24591
|
+
if (dline) card.appendChild(el("div", "d-prose" + (dline.muted ? " muted" : ""), dline.text));
|
|
24592
|
+
var lapse = rerunManualLapseText(rr);
|
|
24593
|
+
if (lapse) card.appendChild(el("div", "d-prose muted", lapse));
|
|
24594
|
+
var auditBox = auditOverrideBox(feature, spec, rr);
|
|
24595
|
+
if (auditBox) card.appendChild(auditBox);
|
|
24596
|
+
var execBox = executionOverrideBox(feature, spec, rr);
|
|
24597
|
+
if (execBox) card.appendChild(execBox);
|
|
24598
|
+
return card;
|
|
24599
|
+
}
|
|
24600
|
+
|
|
24601
|
+
// Detail row: the case's current state first, then why the verdict is what
|
|
24602
|
+
// it is, then what the case does, then the note — a stack of cards in the
|
|
24603
|
+
// order a reader asks the questions. Built with createElement/textContent
|
|
24604
|
+
// throughout — every field here is API-derived, so none of it may go
|
|
24605
|
+
// through innerHTML.
|
|
24498
24606
|
function perspDetailContent(feature, spec) {
|
|
24499
24607
|
var frag = document.createDocumentFragment();
|
|
24608
|
+
var rr = ledgerEntryFor(perspState.rerun, feature, spec);
|
|
24609
|
+
var driftEntry = ledgerEntryFor(perspState.drift, feature, spec);
|
|
24610
|
+
|
|
24611
|
+
// The two axis states stay in the table row only — the panel answers why,
|
|
24612
|
+
// not what, so it opens straight on the reason.
|
|
24613
|
+
if (rr) {
|
|
24614
|
+
var reason = el("div", "p-sect");
|
|
24615
|
+
reason.appendChild(el("div", "p-slabel", t("perspectives.d.whyVerdict")));
|
|
24616
|
+
reason.appendChild(perspReasonCard(feature, spec, rr, driftEntry));
|
|
24617
|
+
frag.appendChild(reason);
|
|
24618
|
+
}
|
|
24619
|
+
|
|
24620
|
+
var contents = el("div", "p-sect");
|
|
24621
|
+
contents.appendChild(el("div", "p-slabel", t("perspectives.d.contents")));
|
|
24622
|
+
var ccard = el("div", "analysis-box");
|
|
24500
24623
|
var dl = el("dl", "d-grid");
|
|
24501
24624
|
function row(labelKey, valueNode) {
|
|
24502
24625
|
dl.appendChild(el("dt", null, t(labelKey)));
|
|
@@ -24513,20 +24636,7 @@ const CLIENT_JS = `
|
|
|
24513
24636
|
}
|
|
24514
24637
|
if (spec.startScreen) row("perspectives.d.startScreen", spec.startScreen);
|
|
24515
24638
|
if (spec.testCondition) row("perspectives.d.testCondition", spec.testCondition);
|
|
24516
|
-
// The spec id stays: it is what a user types to re-run this case, and the
|
|
24517
|
-
// table shows the title, never the id.
|
|
24518
|
-
row("perspectives.d.spec", el("code", null, spec.specName));
|
|
24519
|
-
|
|
24520
|
-
var rr = ledgerEntryFor(perspState.rerun, feature, spec);
|
|
24521
|
-
if (rr) {
|
|
24522
|
-
row(rerunEvidenceLabelKey(rr), rerunEvidenceValue(rr));
|
|
24523
|
-
if (rerunHasFailure(rr)) row("perspectives.d.lastRed", rerunFailureValue(rr.lastRed));
|
|
24524
|
-
}
|
|
24525
|
-
frag.appendChild(dl);
|
|
24526
|
-
|
|
24527
24639
|
if (spec.steps && spec.steps.length) {
|
|
24528
|
-
var stepsBox = el("div", "steps-box");
|
|
24529
|
-
stepsBox.appendChild(el("div", "slabel", t("perspectives.d.steps")));
|
|
24530
24640
|
var stepsList = el("ol", "d-steps");
|
|
24531
24641
|
spec.steps.forEach(function (step) {
|
|
24532
24642
|
var li = el("li");
|
|
@@ -24540,22 +24650,14 @@ const CLIENT_JS = `
|
|
|
24540
24650
|
}
|
|
24541
24651
|
stepsList.appendChild(li);
|
|
24542
24652
|
});
|
|
24543
|
-
|
|
24544
|
-
frag.appendChild(stepsBox);
|
|
24653
|
+
row("perspectives.d.steps", stepsList);
|
|
24545
24654
|
}
|
|
24655
|
+
ccard.appendChild(dl);
|
|
24656
|
+
contents.appendChild(ccard);
|
|
24657
|
+
frag.appendChild(contents);
|
|
24546
24658
|
|
|
24547
|
-
|
|
24548
|
-
|
|
24549
|
-
// standing one (auditOverrideBox / executionOverrideBox).
|
|
24550
|
-
if (rr) {
|
|
24551
|
-
var auditBox = auditOverrideBox(feature, spec, rr);
|
|
24552
|
-
if (auditBox) frag.appendChild(auditBox);
|
|
24553
|
-
var execBox = executionOverrideBox(feature, spec, rr);
|
|
24554
|
-
if (execBox) frag.appendChild(execBox);
|
|
24555
|
-
}
|
|
24556
|
-
|
|
24557
|
-
var notebox = el("div", "notebox");
|
|
24558
|
-
notebox.appendChild(el("div", "nlabel", t("perspectives.note.label")));
|
|
24659
|
+
var notebox = el("div", "notebox p-sect");
|
|
24660
|
+
notebox.appendChild(el("div", "p-slabel", t("perspectives.note.label")));
|
|
24559
24661
|
var ta = el("textarea");
|
|
24560
24662
|
ta.placeholder = t("perspectives.note.placeholder");
|
|
24561
24663
|
ta.value = spec.note || "";
|
|
@@ -24564,8 +24666,8 @@ const CLIENT_JS = `
|
|
|
24564
24666
|
var saveBtn = el("button", "btn primary", t("common.save"));
|
|
24565
24667
|
saveBtn.type = "button";
|
|
24566
24668
|
var statusEl = el("span", "nstatus");
|
|
24567
|
-
nact.appendChild(saveBtn);
|
|
24568
24669
|
nact.appendChild(statusEl);
|
|
24670
|
+
nact.appendChild(saveBtn);
|
|
24569
24671
|
notebox.appendChild(nact);
|
|
24570
24672
|
frag.appendChild(notebox);
|
|
24571
24673
|
|
|
@@ -24621,6 +24723,7 @@ const CLIENT_JS = `
|
|
|
24621
24723
|
|
|
24622
24724
|
var titleTd = el("td", "c-title");
|
|
24623
24725
|
titleTd.appendChild(document.createTextNode(spec.title));
|
|
24726
|
+
titleTd.appendChild(el("span", "c-id", perspSpecKey(feature, spec)));
|
|
24624
24727
|
if (spec.summary) titleTd.appendChild(el("span", "csum", spec.summary));
|
|
24625
24728
|
row.appendChild(titleTd);
|
|
24626
24729
|
|
|
@@ -24753,8 +24856,8 @@ const CLIENT_JS = `
|
|
|
24753
24856
|
loadRerun().catch(function (err) {
|
|
24754
24857
|
setPerspNote("persp-rerun-note", t("perspectives.rerun.loadFailed") + ": " + err.message, "warn");
|
|
24755
24858
|
}),
|
|
24756
|
-
//
|
|
24757
|
-
//
|
|
24859
|
+
// A failed or unsupported fetch just omits the "audited at" line
|
|
24860
|
+
// and the reason card's finding detail, no banner.
|
|
24758
24861
|
loadDrift().catch(function () {}),
|
|
24759
24862
|
]);
|
|
24760
24863
|
})
|
|
@@ -24893,29 +24996,32 @@ const CLIENT_JS = `
|
|
|
24893
24996
|
});
|
|
24894
24997
|
}
|
|
24895
24998
|
|
|
24896
|
-
// Loaded
|
|
24897
|
-
//
|
|
24898
|
-
//
|
|
24899
|
-
//
|
|
24900
|
-
// axis in the /rerun report — so there is nothing here worth a banner on
|
|
24901
|
-
// an older or unreachable hub.
|
|
24999
|
+
// Loaded on project open and again on every reloadRerun: the reason card
|
|
25000
|
+
// joins rr.audit against this ledger's entry (runId/headline), so the two
|
|
25001
|
+
// must not drift apart after a dismissal or attestation. An older or
|
|
25002
|
+
// unreachable hub degrades to the axis alone — not worth a banner.
|
|
24902
25003
|
function loadDrift() {
|
|
24903
25004
|
return fetch(driftPath(), { headers: { Authorization: "Bearer " + state.token } })
|
|
24904
25005
|
.then(function (res) { return res.ok ? res.json() : null; }, function () { return null; })
|
|
24905
25006
|
.then(function (report) {
|
|
24906
|
-
|
|
25007
|
+
// A transient failure keeps the copy already loaded — blanking it
|
|
25008
|
+
// would drop the audit coordinates and the reason card's finding
|
|
25009
|
+
// for the rest of the session.
|
|
25010
|
+
if (report) perspState.drift = report;
|
|
24907
25011
|
renderPerspectives();
|
|
24908
25012
|
});
|
|
24909
25013
|
}
|
|
24910
25014
|
|
|
24911
|
-
//
|
|
24912
|
-
//
|
|
24913
|
-
//
|
|
25015
|
+
// Re-asks the rerun question and refreshes the drift ledger beside it (the
|
|
25016
|
+
// reason card reads both; see perspState.drift). The perspectives document
|
|
25017
|
+
// itself is project-scoped and does not change, and neither does the run
|
|
25018
|
+
// index loadRerun used to (wastefully) re-fetch.
|
|
24914
25019
|
function reloadRerun() {
|
|
24915
25020
|
perspState.rerun = null;
|
|
24916
25021
|
setPerspNote("persp-rerun-note", "");
|
|
24917
25022
|
setPerspDeployHead(null);
|
|
24918
25023
|
renderPerspectives();
|
|
25024
|
+
loadDrift().catch(function () {});
|
|
24919
25025
|
return loadRerun();
|
|
24920
25026
|
}
|
|
24921
25027
|
|
package/dist/package.json
CHANGED