Skip to content

Commit 1120a18

Browse files
author
DavidQ
committed
Close Level 24 with a real execution-backed guard.
Add one runnable validation entry point that enforces: - roadmap lock rules - fullscreen restriction to sample 0713 No roadmap edits. No engine changes. No gameplay changes. PR: BUILD_PR_LEVEL_24_5_PHASE_24_CLOSEOUT_EXECUTION_GUARD
1 parent 682cf8b commit 1120a18

7 files changed

Lines changed: 254 additions & 6 deletions
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
BUILD_PR_LEVEL_24_5 Phase 24 Closeout Execution Guard Report
2+
3+
Command
4+
- npm run check:phase24-closeout-guard
5+
6+
Result
7+
- PASS
8+
- PHASE24_CLOSEOUT_EXECUTION_GUARD_PASS
9+
- Roadmap lock path: docs/dev/roadmaps/MASTER_ROADMAP_ENGINE.md
10+
- Fullscreen sample hits: 2
11+
- Fullscreen outside allowed root: 0
12+
13+
Guard entry point
14+
- package.json script: check:phase24-closeout-guard
15+
- runtime: tools/dev/checkPhase24CloseoutExecutionGuard.mjs
16+
- baseline: tools/dev/checkPhase24CloseoutExecutionGuard.baseline.json
17+
18+
Scope notes
19+
- No roadmap text/status edits
20+
- No engine changes
21+
- No gameplay changes
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
Phase 24 Closeout Execution Guard Spec
2+
3+
Goal:
4+
End Level 24 with a real runnable guard, not a commit-only or status-only tail.
5+
6+
Required checks:
7+
1. Roadmap file path remains:
8+
docs/dev/roadmaps/MASTER_ROADMAP_ENGINE.md
9+
2. Roadmap lock policy remains enforced:
10+
- allow only [ ] -> [.]
11+
- allow only [.] -> [x]
12+
- reject all non-status edits
13+
- reject file replacement/rename
14+
3. Fullscreen policy remains enforced:
15+
- allow fullscreen only in samples/phase-07/0713
16+
- fail on any hit outside 0713
17+
18+
Suggested outputs:
19+
- PASS/FAIL summary
20+
- offending file list when failing
21+
- non-zero exit on failure
Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
Validation Checklist
22

3-
[x] API lock validated
4-
[x] Boundaries validated
5-
[x] Contracts documented
6-
[x] Docs cleanup verified
7-
[x] Status-only roadmap update applied
3+
[x] Runnable guard added
4+
[x] Guard executes in one pass from repo root
5+
[x] Roadmap file path lock checked
6+
[x] Roadmap non-status edit lock checked
7+
[x] Fullscreen outside 0713 checked
8+
[x] Clear PASS/FAIL output produced
9+
[x] No roadmap text edits made
10+
[x] No engine changes made
11+
[x] No gameplay changes made
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# BUILD_PR_LEVEL_24_5_PHASE_24_CLOSEOUT_EXECUTION_GUARD
2+
3+
## Purpose
4+
Close Level 24 with one small, testable, execution-backed PR.
5+
6+
This replaces the prior non-executable `24_5` planning stub.
7+
No roadmap advancement happens in this PR.
8+
No commit-only packaging.
9+
No roadmap text edits.
10+
11+
## Why this PR is needed
12+
Phase 24 roadmap items are already complete.
13+
What is missing is a final executable guard so Phase 24 ends on a testable artifact rather than a docs-only/status-only tail.
14+
15+
## Single PR Purpose
16+
Add one runnable guard entry point that checks both of these locked rules:
17+
1. Roadmap file is locked against non-status edits.
18+
2. Fullscreen remains restricted to sample `0713`.
19+
20+
## In Scope
21+
- add one guard script or one guard test entry point
22+
- make it runnable in one pass from the repo root
23+
- validate:
24+
- roadmap file path is unchanged
25+
- roadmap content is not replaced/renamed
26+
- fullscreen usage outside `samples/phase-07/0713` fails
27+
- add minimal documentation for how to run the guard
28+
- add test/report output under docs/dev/reports if useful
29+
30+
## Out of Scope
31+
- no roadmap text edits
32+
- no roadmap status changes
33+
- no engine changes
34+
- no gameplay changes
35+
- no sample feature work
36+
- no broad repo cleanup
37+
- no template-driven expansion
38+
39+
## Preferred Implementation Shape
40+
Choose the smallest executable option already aligned with repo tooling:
41+
- either a focused Node test
42+
- or a focused PowerShell validator
43+
- or both only if one invokes the other and remains one-pass executable
44+
45+
Prefer PowerShell if it reduces Codex token usage.
46+
47+
## Required Behavior
48+
The guard must fail if any of the following are true:
49+
- `docs/dev/roadmaps/MASTER_ROADMAP_ENGINE.md` is renamed or replaced
50+
- roadmap text is rewritten, reordered, added to, deleted from, or reflowed
51+
- any non-status roadmap edit is detected
52+
- fullscreen usage exists outside sample `0713`
53+
54+
The guard must pass when:
55+
- roadmap remains locked
56+
- only status-marker transitions are allowed in principle
57+
- fullscreen hits remain limited to the known `0713` files
58+
59+
## Run
60+
- `npm run check:phase24-closeout-guard`
61+
62+
## Acceptance
63+
- one-pass executable from repo root
64+
- testable and repeatable
65+
- no roadmap edits required
66+
- no engine/gameplay changes
67+
- guard clearly reports pass/fail
68+
- package output to:
69+
`<project folder>/tmp/BUILD_PR_LEVEL_24_5_PHASE_24_CLOSEOUT_EXECUTION_GUARD.zip`

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@
55
"test": "node ./scripts/run-node-tests.mjs",
66
"build:manifest": "node ./scripts/generate-sample-manifest.mjs",
77
"check:shared-extraction-guard": "node tools/dev/checkSharedExtractionGuard.mjs",
8+
"check:phase24-closeout-guard": "node tools/dev/checkPhase24CloseoutExecutionGuard.mjs",
89
"check:games-template-contract": "node ./scripts/validate-games-template-contract.mjs",
910
"test:launch-smoke": "node ./tests/runtime/LaunchSmokeAllEntries.test.mjs"
1011
},
1112
"dependencies": {
1213
"ws": "^8.20.0"
1314
}
14-
}
15+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"roadmapPath": "docs/dev/roadmaps/MASTER_ROADMAP_ENGINE.md",
3+
"statusInsensitiveSha256": "1fb2ead588a71e63291463c04fb012c76749ff929a7ed1b4b3aac015b94dcc16",
4+
"fullscreenAllowedRoot": "samples/phase-07/0713",
5+
"fullscreenPatterns": [
6+
"requestFullscreen",
7+
"webkitRequestFullscreen",
8+
"engine.fullscreen",
9+
"fullscreenPreferred",
10+
"settings-fullscreen"
11+
]
12+
}
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import fs from "node:fs";
2+
import path from "node:path";
3+
import crypto from "node:crypto";
4+
import { fileURLToPath } from "node:url";
5+
6+
const __filename = fileURLToPath(import.meta.url);
7+
const __dirname = path.dirname(__filename);
8+
const repoRoot = path.resolve(__dirname, "../..");
9+
const baselinePath = path.join(__dirname, "checkPhase24CloseoutExecutionGuard.baseline.json");
10+
11+
const SOURCE_EXTENSIONS = new Set([".js", ".mjs", ".cjs", ".html", ".json"]);
12+
const SKIP_DIRS = new Set(["metadata", "shared", "_shared"]);
13+
14+
function toPosix(value) {
15+
return value.replace(/\\/g, "/");
16+
}
17+
18+
function readJson(filePath) {
19+
return JSON.parse(fs.readFileSync(filePath, "utf8"));
20+
}
21+
22+
function sha256(text) {
23+
return crypto.createHash("sha256").update(text, "utf8").digest("hex");
24+
}
25+
26+
function normalizeRoadmapStatusMarkers(roadmapText) {
27+
const lf = roadmapText.replace(/\r\n/g, "\n");
28+
return lf.replace(/^([ \t]*[-*]?\s*\[)(?: |\.|x)(\])/gm, "$1#$2");
29+
}
30+
31+
function collectSampleFiles(samplesRoot) {
32+
const out = [];
33+
const stack = [samplesRoot];
34+
while (stack.length > 0) {
35+
const current = stack.pop();
36+
const entries = fs.readdirSync(current, { withFileTypes: true });
37+
for (const entry of entries) {
38+
const full = path.join(current, entry.name);
39+
if (entry.isDirectory()) {
40+
const isSamplesRootChild = path.resolve(current) === path.resolve(samplesRoot);
41+
if (isSamplesRootChild && SKIP_DIRS.has(entry.name)) {
42+
continue;
43+
}
44+
stack.push(full);
45+
continue;
46+
}
47+
if (SOURCE_EXTENSIONS.has(path.extname(entry.name).toLowerCase())) {
48+
out.push(full);
49+
}
50+
}
51+
}
52+
return out;
53+
}
54+
55+
function runGuard() {
56+
if (!fs.existsSync(baselinePath)) {
57+
throw new Error(`Missing baseline: ${toPosix(path.relative(repoRoot, baselinePath))}`);
58+
}
59+
60+
const baseline = readJson(baselinePath);
61+
const roadmapPath = path.join(repoRoot, baseline.roadmapPath);
62+
const errors = [];
63+
64+
if (!fs.existsSync(roadmapPath)) {
65+
errors.push(`Roadmap path missing: ${baseline.roadmapPath}`);
66+
} else {
67+
const roadmapText = fs.readFileSync(roadmapPath, "utf8");
68+
const normalized = normalizeRoadmapStatusMarkers(roadmapText);
69+
const hash = sha256(normalized);
70+
if (hash !== baseline.statusInsensitiveSha256) {
71+
errors.push(
72+
`Roadmap non-status drift detected at ${baseline.roadmapPath}. Expected ${baseline.statusInsensitiveSha256}, got ${hash}.`
73+
);
74+
}
75+
}
76+
77+
const samplesRoot = path.join(repoRoot, "samples");
78+
const allowedRoot = path.join(repoRoot, baseline.fullscreenAllowedRoot);
79+
const patterns = (baseline.fullscreenPatterns || []).map((token) => ({ token, re: new RegExp(token.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g") }));
80+
const hitsOutsideAllowed = [];
81+
let totalHits = 0;
82+
83+
for (const filePath of collectSampleFiles(samplesRoot)) {
84+
const source = fs.readFileSync(filePath, "utf8");
85+
let matched = false;
86+
for (const { re } of patterns) {
87+
if (re.test(source)) {
88+
matched = true;
89+
}
90+
re.lastIndex = 0;
91+
}
92+
if (!matched) {
93+
continue;
94+
}
95+
totalHits += 1;
96+
if (!path.resolve(filePath).startsWith(path.resolve(allowedRoot))) {
97+
hitsOutsideAllowed.push(toPosix(path.relative(repoRoot, filePath)));
98+
}
99+
}
100+
101+
if (hitsOutsideAllowed.length > 0) {
102+
errors.push(
103+
`Fullscreen policy violation outside ${baseline.fullscreenAllowedRoot}:\n- ${hitsOutsideAllowed.join("\n- ")}`
104+
);
105+
}
106+
107+
if (errors.length > 0) {
108+
console.error("PHASE24_CLOSEOUT_EXECUTION_GUARD_FAIL");
109+
errors.forEach((item) => console.error(item));
110+
process.exitCode = 1;
111+
return;
112+
}
113+
114+
console.log("PHASE24_CLOSEOUT_EXECUTION_GUARD_PASS");
115+
console.log(`Roadmap lock path: ${baseline.roadmapPath}`);
116+
console.log(`Fullscreen sample hits: ${totalHits}`);
117+
console.log(`Fullscreen outside allowed root: 0`);
118+
}
119+
120+
runGuard();

0 commit comments

Comments
 (0)