Skip to content

Commit 55089d4

Browse files
author
DavidQ
committed
Add advisory changed-file coverage guardrails and finalize batch operation contract - PR_26126_076-coverage-and-batch-guardrails
1 parent e6936e0 commit 55089d4

9 files changed

Lines changed: 231 additions & 11 deletions

docs/dev/PROJECT_INSTRUCTIONS.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -589,8 +589,11 @@ No PR is complete with:
589589
## BATCH OPERATION RULES
590590

591591
- Batch operations must log per item.
592+
- Each item must log `OK`, `WARN`, `FAIL`, or `SKIP`.
592593
- One failed item must not stop the batch unless the failure is global.
593594
- Summary must include written, failed, skipped, and warnings.
595+
- Long-running batches must support a stop or cancel pattern when applicable.
596+
- Batch operations must discover real files and directories and must not assume numeric folder sequences.
594597

595598
## PLAYWRIGHT DEPTH AND COVERAGE REQUIREMENT
596599

@@ -626,6 +629,8 @@ When runtime JavaScript changes, Codex must produce a Playwright V8 coverage rep
626629

627630
The coverage report must list changed runtime JavaScript files.
628631

632+
Missing changed runtime JavaScript files in coverage must be reported as `WARN`, not `FAIL`.
633+
629634
Coverage report lines must start with coverage percentage in this format:
630635

631636
`(xx%) <file-path> - <details>`
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
PR_26126_076 Batch Guardrail Contract
2+
3+
Scope:
4+
- Added Tool Template V2 batch guardrail documentation.
5+
- Linked the guardrail contract from the template README and control/service contracts.
6+
- Updated PROJECT_INSTRUCTIONS.md with the finalized batch guardrail wording.
7+
8+
Documented guardrails:
9+
- Batch operations log every item.
10+
- Each item logs OK, WARN, FAIL, or SKIP.
11+
- One item failure does not stop the batch unless the failure is global.
12+
- Summary includes written, failed, skipped, and warnings.
13+
- Long-running batches support stop/cancel when applicable.
14+
- Batch discovery uses real files/directories and never assumes numeric folder sequences.
15+
16+
Runtime impact:
17+
- No tool runtime behavior changed.
18+
19+
Validation:
20+
- node --check tests/helpers/playwrightV8CoverageReporter.mjs passed.
21+
- git diff --check passed.
22+
- npm run test:workspace-v2 passed.
23+
24+
Manual test notes:
25+
- Review tools/templates-v2/docs/BATCH_GUARDRAIL_CONTRACT.md for the batch contract wording.
26+
- Review docs/dev/reports/coverage_changed_js_guardrail.txt to confirm missing changed runtime JS coverage is advisory WARN-only.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
Changed Runtime JS Coverage Guardrail
2+
3+
Status: advisory only.
4+
Thresholds: none enforced.
5+
Missing changed runtime JS files are WARN, not FAIL.
6+
Source: Playwright/Chromium built-in V8 coverage from npm run test:workspace-v2.
7+
8+
Changed runtime JS files considered:
9+
(100%) none changed - no changed runtime JS files
10+
11+
Guardrail warnings:
12+
(100%) none changed - no changed runtime JS files

docs/dev/reports/playwright_v8_coverage_report.txt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ Coverage scope: all repo-relative browser JavaScript collected by Playwright/Chr
66
Dependencies: no new npm packages.
77
Thresholds: none enforced.
88
Note: coverage is an advisory baseline only for this PR.
9+
Note: missing changed runtime JS coverage is reported as WARN, not FAIL.
910
Note: line counts are V8 range-based and advisory; function counts show partial module exercise where available.
1011
Note: entry percentages use function coverage when available, otherwise line coverage.
1112
Note: coverage entries are aggregated across every page/tool where coverageReporter.start(page) and coverageReporter.stop(page) ran.
@@ -18,7 +19,7 @@ Exercised tool entry points detected:
1819
(0%) Workspace Manager - not exercised by this Playwright run
1920

2021
Changed runtime JS files covered:
21-
(86%) tools/templates-v2/js/controls/StatusLogControl.js - executed lines 24/24; executed functions 6/7
22+
(100%) none changed - no changed runtime JS files
2223

2324
Files with executed line/function counts where available:
2425
(2%) src/engine/input/ActionInputService.js - executed lines 397/397; executed functions 1/51
@@ -156,8 +157,8 @@ Files with executed line/function counts where available:
156157
(100%) tools/templates-v2/js/services/ToolStateSerializer.js - executed lines 13/13; executed functions 3/3
157158

158159
Uncovered or low-coverage changed JS files:
159-
(100%) none - no low-coverage changed runtime JS files
160+
(100%) none changed - no changed runtime JS files
160161

161162
Changed JS files considered:
163+
(0%) tests/helpers/playwrightV8CoverageReporter.mjs - changed JS file not collected as browser runtime coverage
162164
(0%) tests/playwright/PreviewGeneratorV2Baseline.spec.mjs - changed JS file not collected as browser runtime coverage
163-
(86%) tools/templates-v2/js/controls/StatusLogControl.js - changed JS file with browser V8 coverage
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
PR_26126_075-template-form-style-match-palette-manager
2+
3+
Scope:
4+
- Tool Template V2 form/control visual styling only.
5+
- Existing Tool Template V2 runtime behavior, launch-mode NAV behavior, Status Clear placement, and Status Clear behavior were preserved.
6+
7+
Palette Manager V2 patterns reviewed:
8+
- `tools/palette-manager-v2/index.html`
9+
- `tools/palette-manager-v2/paletteManagerV2.css`
10+
- Structural/style patterns used:
11+
- `.palette-manager-v2__field`
12+
- `.palette-manager-v2__field--compact`
13+
- `.palette-manager-v2__controls`
14+
- `.palette-manager-v2__menu-sample`
15+
- `.palette-manager-v2__panel`
16+
- `.accordion-v2__header`
17+
- Palette Manager global surface rules for `button`, `input`, `select`, and `textarea`
18+
- Palette Manager text/log surface rules such as `.palette-manager-v2__json-preview`
19+
20+
Changes:
21+
- Updated `tools/templates-v2/styles/toolStarter.css` to match Palette Manager V2 visual control patterns:
22+
- label/control fields use the compact 74px label column and 8px gap
23+
- inputs/selects/textareas use Palette Manager surface border, 10px radius, surface-inline background, and text color
24+
- buttons use Palette Manager-style 10px radius, compact native button padding, disabled opacity/filter, and focus outline
25+
- textarea/output blocks use Palette Manager-style strong surface, 8px radius, and 12px padding
26+
- panels use Palette Manager-style border, 8px radius, panel background, 14px padding, and 14px spacing
27+
- menu/app shell spacing now follows the Palette Manager V2 shell/menu pattern
28+
- Added Playwright computed-style assertions for Tool Template V2:
29+
- compact field gap and label column
30+
- input radius/background/text color
31+
- button radius and compact padding
32+
- textarea radius/background
33+
- panel radius/padding
34+
35+
Playwright impacted: Yes
36+
- This PR changes visible UI/control styling for Tool Template V2.
37+
- `npm run test:workspace-v2` passed.
38+
39+
Validation:
40+
- `node --check tests/playwright/PreviewGeneratorV2Baseline.spec.mjs` passed.
41+
- `npm run test:workspace-v2` passed: 7 tests passed.
42+
- `git diff --check` passed.
43+
44+
Manual test notes:
45+
- Open `tools/templates-v2/index.html`.
46+
- Confirm default tool-mode NAV is visible and workspace NAV remains hidden.
47+
- Open `tools/templates-v2/index.html?launch=workspace`.
48+
- Confirm workspace-mode NAV is visible and tool NAV remains hidden.
49+
- Confirm template labels, inputs, textarea, buttons, field spacing, panel spacing, and accordion controls visually match Palette Manager V2 patterns.

tests/helpers/playwrightV8CoverageReporter.mjs

Lines changed: 75 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@ export class PlaywrightV8CoverageReporter {
77
constructor({
88
repoRoot = process.cwd(),
99
reportPath = "docs/dev/reports/playwright_v8_coverage_report.txt",
10+
guardrailReportPath = "docs/dev/reports/coverage_changed_js_guardrail.txt",
1011
advisoryLowCoveragePercent = 50
1112
} = {}) {
1213
this.repoRoot = repoRoot;
1314
this.reportPath = path.resolve(repoRoot, reportPath);
15+
this.guardrailReportPath = path.resolve(repoRoot, guardrailReportPath);
1416
this.advisoryLowCoveragePercent = advisoryLowCoveragePercent;
1517
this.activePages = new WeakSet();
1618
this.entries = [];
@@ -49,6 +51,7 @@ export class PlaywrightV8CoverageReporter {
4951
"Dependencies: no new npm packages.",
5052
"Thresholds: none enforced.",
5153
"Note: coverage is an advisory baseline only for this PR.",
54+
"Note: missing changed runtime JS coverage is reported as WARN, not FAIL.",
5255
"Note: line counts are V8 range-based and advisory; function counts show partial module exercise where available.",
5356
"Note: entry percentages use function coverage when available, otherwise line coverage.",
5457
"Note: coverage entries are aggregated across every page/tool where coverageReporter.start(page) and coverageReporter.stop(page) ran.",
@@ -71,6 +74,7 @@ export class PlaywrightV8CoverageReporter {
7174

7275
await fs.mkdir(path.dirname(this.reportPath), { recursive: true });
7376
await fs.writeFile(this.reportPath, `${reportLines.join("\n")}\n`, "utf8");
77+
await this.writeChangedJsGuardrailReport(changedRuntimeJsFiles, coverageByPath);
7478
}
7579

7680
buildCoverageByPath() {
@@ -212,10 +216,14 @@ export class PlaywrightV8CoverageReporter {
212216
}
213217

214218
getChangedJsFiles() {
215-
const statusOutput = execFileSync("git", ["status", "--porcelain"], {
216-
cwd: this.repoRoot,
217-
encoding: "utf8"
218-
});
219+
return [...new Set([
220+
...this.getStatusChangedJsFiles(),
221+
...this.getHeadChangedJsFiles()
222+
])].sort((left, right) => left.localeCompare(right));
223+
}
224+
225+
getStatusChangedJsFiles() {
226+
const statusOutput = this.gitOutput(["status", "--porcelain"]);
219227
return statusOutput
220228
.split(/\r?\n/)
221229
.map((line) => line.trimEnd())
@@ -227,6 +235,28 @@ export class PlaywrightV8CoverageReporter {
227235
.sort((left, right) => left.localeCompare(right));
228236
}
229237

238+
getHeadChangedJsFiles() {
239+
const headOutput = this.gitOutput(["diff-tree", "--no-commit-id", "--name-only", "-r", "HEAD"]);
240+
return headOutput
241+
.split(/\r?\n/)
242+
.map((line) => line.trim().replaceAll("\\", "/"))
243+
.filter(Boolean)
244+
.filter((filePath) => existsSync(path.resolve(this.repoRoot, filePath)))
245+
.filter((filePath) => filePath.endsWith(".js") || filePath.endsWith(".mjs"))
246+
.sort((left, right) => left.localeCompare(right));
247+
}
248+
249+
gitOutput(args) {
250+
try {
251+
return execFileSync("git", args, {
252+
cwd: this.repoRoot,
253+
encoding: "utf8"
254+
});
255+
} catch {
256+
return "";
257+
}
258+
}
259+
230260
isRuntimeJsFile(filePath) {
231261
if (filePath.includes("/tests/")) {
232262
return false;
@@ -258,7 +288,7 @@ export class PlaywrightV8CoverageReporter {
258288
.map(({ filePath, record }) => (
259289
record
260290
? this.formatCoverageEntry(filePath, record, `${this.lineSummary(record)}; ${this.functionSummary(record)}`)
261-
: "(0%) " + filePath + " - not covered"
291+
: `(0%) ${filePath} - WARNING: changed runtime JS file was not collected by Playwright V8 coverage; advisory only`
262292
));
263293
}
264294

@@ -313,8 +343,8 @@ export class PlaywrightV8CoverageReporter {
313343
}
314344
return lowCoverage.map(({ filePath, record }) => (
315345
record
316-
? this.formatCoverageEntry(filePath, record, `advisory low coverage; ${this.lineSummary(record)}`)
317-
: "(0%) " + filePath + " - uncovered"
346+
? this.formatCoverageEntry(filePath, record, `WARNING: advisory low coverage; ${this.lineSummary(record)}`)
347+
: `(0%) ${filePath} - WARNING: uncovered changed runtime JS file; advisory only`
318348
));
319349
}
320350

@@ -336,6 +366,44 @@ export class PlaywrightV8CoverageReporter {
336366
return `(${this.coveragePercent(record)}%) ${filePath} - ${details}`;
337367
}
338368

369+
async writeChangedJsGuardrailReport(changedRuntimeJsFiles, coverageByPath) {
370+
const reportLines = [
371+
"Changed Runtime JS Coverage Guardrail",
372+
"",
373+
"Status: advisory only.",
374+
"Thresholds: none enforced.",
375+
"Missing changed runtime JS files are WARN, not FAIL.",
376+
"Source: Playwright/Chromium built-in V8 coverage from npm run test:workspace-v2.",
377+
"",
378+
"Changed runtime JS files considered:",
379+
...this.formatChangedRuntimeCoverage(changedRuntimeJsFiles, coverageByPath),
380+
"",
381+
"Guardrail warnings:",
382+
...this.formatCoverageGuardrailWarnings(changedRuntimeJsFiles, coverageByPath)
383+
];
384+
385+
await fs.mkdir(path.dirname(this.guardrailReportPath), { recursive: true });
386+
await fs.writeFile(this.guardrailReportPath, `${reportLines.join("\n")}\n`, "utf8");
387+
}
388+
389+
formatCoverageGuardrailWarnings(changedRuntimeJsFiles, coverageByPath) {
390+
if (!changedRuntimeJsFiles.length) {
391+
return ["(100%) none changed - no changed runtime JS files"];
392+
}
393+
const warnings = changedRuntimeJsFiles
394+
.map((filePath) => ({ filePath, record: coverageByPath.get(filePath) }))
395+
.filter(({ record }) => !record || this.coveragePercent(record) < this.advisoryLowCoveragePercent)
396+
.sort((left, right) => this.compareCoverageEntries(left, right));
397+
if (!warnings.length) {
398+
return ["(100%) none - no changed runtime JS coverage warnings"];
399+
}
400+
return warnings.map(({ filePath, record }) => (
401+
record
402+
? this.formatCoverageEntry(filePath, record, `WARNING: advisory low coverage below ${this.advisoryLowCoveragePercent}%; ${this.lineSummary(record)}; ${this.functionSummary(record)}`)
403+
: `(0%) ${filePath} - WARNING: changed runtime JS file missing from coverage; advisory only`
404+
));
405+
}
406+
339407
compareCoverageEntries(left, right) {
340408
const percentDelta = this.coveragePercent(left.record) - this.coveragePercent(right.record);
341409
if (percentDelta !== 0) {

tools/templates-v2/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ tools/templates-v2/
1212
playwright.config.mjs
1313
README.md
1414
docs/
15+
BATCH_GUARDRAIL_CONTRACT.md
1516
CONTROL_SERVICE_CONTRACTS.md
1617
styles/
1718
toolStarter.css
@@ -45,6 +46,7 @@ tools/templates-v2/
4546
- `js/controls/*.js`: one class per UI control or section.
4647
- `js/services/*.js`: focused non-UI helper classes when needed.
4748
- `docs/CONTROL_SERVICE_CONTRACTS.md`: required control, service, app/root, logger, and batch processor contracts.
49+
- `docs/BATCH_GUARDRAIL_CONTRACT.md`: required batch logging, discovery, stop/cancel, and summary guardrails.
4850
- `tests/playwright/*.spec.mjs`: starter behavior coverage to copy and rename with the new First-Class Tool V2.
4951
- `README.md`: tool-specific usage, contracts, and validation notes.
5052

@@ -59,13 +61,16 @@ These folders remain under `tools/templates-v2/` as support/reference template a
5961

6062
Read `docs/CONTROL_SERVICE_CONTRACTS.md` before creating or modifying a First-Class Tool V2 from this starter.
6163

64+
Read `docs/BATCH_GUARDRAIL_CONTRACT.md` before adding any batch operation to a First-Class Tool V2.
65+
6266
The contracts define:
6367

6468
- Control responsibilities and method expectations.
6569
- Service boundaries and result/error return expectations.
6670
- App/root coordinator boundaries.
6771
- Logger level requirements: OK, WARN, FAIL, SKIP, INFO.
6872
- Batch processor discovery, per-item logging, and summary requirements.
73+
- Batch guardrails for per-item outcomes, stop/cancel behavior, and real file/directory discovery.
6974

7075
## Architecture Rules
7176

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# Tool Template V2 Batch Guardrail Contract
2+
3+
Batch operations process repeated work across real discovered inputs. A First-Class Tool V2 batch flow must be explicit, observable, and safe to resume or diagnose.
4+
5+
## Discovery Rules
6+
7+
- Discover real files and directories.
8+
- Never assume numeric folder sequences.
9+
- Missing discovered candidates are logged as `SKIP`, not `FAIL`, unless the missing item is the selected single input.
10+
- Logs must identify the resolved input path or identifier for every item.
11+
12+
## Per-Item Logging
13+
14+
Every item must log exactly one terminal outcome through the tool logger:
15+
16+
- `OK`: item completed and wrote or updated the expected output.
17+
- `WARN`: item completed with a recoverable warning.
18+
- `FAIL`: item failed with an actionable error.
19+
- `SKIP`: item was intentionally skipped.
20+
21+
Per-item logs must include the item identifier and the reason for `WARN`, `FAIL`, or `SKIP`.
22+
23+
## Failure Isolation
24+
25+
- One item failure must not stop the batch unless the failure is global.
26+
- Global failures include missing required configuration, unavailable repo access, invalid destination root, or a cancelled run.
27+
- Batch processors must keep processing remaining discovered items after item-level `FAIL` or `SKIP` outcomes.
28+
29+
## Summary Contract
30+
31+
Every batch run must finish with a summary that includes:
32+
33+
- `written`
34+
- `failed`
35+
- `skipped`
36+
- `warnings`
37+
38+
The summary must match the per-item log outcomes.
39+
40+
## Stop/Cancel Contract
41+
42+
Long-running batches must support a stop or cancel pattern when applicable.
43+
44+
- Stop/cancel requests must prevent new items from starting.
45+
- Already-running item work should finish or fail safely.
46+
- Cancelled remaining items should be logged as `SKIP` or another clearly documented non-success outcome.
47+
- The final summary must make cancellation visible.
48+
49+
## No Silent Fallback
50+
51+
Batch processors must not invent default inputs, substitute fallback targets, or claim success when a fallback or partial result occurred.

tools/templates-v2/docs/CONTROL_SERVICE_CONTRACTS.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,15 +135,18 @@ Recommended logger methods:
135135

136136
Batch processors handle repeated work across discovered inputs.
137137

138+
See `BATCH_GUARDRAIL_CONTRACT.md` for the full batch guardrail contract.
139+
138140
Required rules:
139141

140142
- Process real discovered inputs only.
141143
- Never assume numeric sequences.
142-
- Log every item through the logger.
144+
- Log every item through the logger with `OK`, `WARN`, `FAIL`, or `SKIP`.
143145
- One item failure must not stop the batch unless the failure is global.
144146
- Missing inputs are `SKIP` during batch processing.
145147
- Batch failures identify the exact item and underlying error.
146148
- Batch summaries include written, failed, skipped, and warnings.
149+
- Long-running batches support stop or cancel when applicable.
147150

148151
Recommended summary shape:
149152

0 commit comments

Comments
 (0)