Skip to content

Commit be2ceeb

Browse files
author
DavidQ
committed
Level 19.1 overlay system expansion framework.
Introduce extension points for future overlay types.
1 parent f1fc599 commit be2ceeb

8 files changed

Lines changed: 299 additions & 39 deletions

docs/dev/CODEX_COMMANDS.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
MODEL: GPT-5.4-codex
2-
REASONING: low
2+
REASONING: medium
33

44
COMMAND:
5-
Promote Level 18 overlay system to baseline.
6-
- Update status markers only
7-
- Confirm no regressions
8-
- Do not change runtime behavior
5+
Implement overlay expansion framework:
6+
- Define extension interfaces/contracts
7+
- Ensure compatibility with config-driven overlays
8+
- Do not change existing behavior
99

1010
Package ZIP to <project folder>/tmp/

docs/dev/COMMIT_COMMENT.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
Promote Level 18 overlay system to baseline after final validation.
1+
Level 19.1 overlay system expansion framework.
2+
Introduce extension points for future overlay types.
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# BUILD_PR_LEVEL_19_1_OVERLAY_SYSTEM_EXPANSION_FRAMEWORK Report
2+
3+
## Purpose
4+
Establish a reusable overlay expansion framework with explicit contracts while keeping current runtime behavior unchanged.
5+
6+
## Extension Contracts
7+
- `samples/phase-17/shared/overlayExpansionContracts.js`
8+
- `defineOverlayExtensionContract(...)`
9+
- `createOverlayExtensionContractMap(...)`
10+
- `getOverlayControllerConfigFromContract(...)`
11+
12+
Contract fields:
13+
- `id`
14+
- `channel`
15+
- `overlays` (`{ id, label }[]`)
16+
- `initialOverlayId`
17+
- `cycleKey`
18+
- `persistenceKey`
19+
- `metadata`
20+
21+
## Integration Pattern (Config-Driven Compatibility)
22+
1. Define overlay contracts with `defineOverlayExtensionContract(...)`.
23+
2. Group contracts in a map with `createOverlayExtensionContractMap(...)`.
24+
3. Derive controller-ready configs through `getOverlayControllerConfigFromContract(...)`.
25+
4. Keep scene wiring unchanged by continuing to consume the existing stack-config shape:
26+
- `overlays`
27+
- `initialOverlayId`
28+
- `cycleKey`
29+
- `persistenceKey`
30+
31+
## Level 17 Adoption
32+
- `samples/phase-17/shared/overlayStackBySampleConfig.js`
33+
- now sources sample stack configs from the new contract definitions
34+
- exports extension accessors:
35+
- `getLevel17OverlayExtensionContract(sampleId)`
36+
- `listLevel17OverlayExtensionContracts()`
37+
38+
## Behavior Impact
39+
- No runtime behavior changes intended.
40+
- Overlay ordering, cycle key behavior, persistence, and panel rendering remain unchanged.
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
[ ] Smoke test complete
2-
[ ] All integrations stable
3-
[ ] No regressions
4-
[ ] Baseline confirmed
1+
[ ] Existing overlays load
2+
[ ] Extension points defined
3+
[ ] No regression
4+
[ ] Config compatibility maintained

docs/pr/BUILD_PR.md

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,30 @@
1-
# BUILD_PR_LEVEL_18_10_OVERLAY_PROMOTE_BASELINE
2-
3-
## Purpose
4-
Promote Level 18 overlay system to baseline after successful validation.
5-
6-
## Scope
7-
- Mark Level 18 as complete
8-
- Confirm baseline:
9-
- Config-driven stacks
10-
- Non-Tab cycle key
11-
- Bottom-right placement
12-
- Mission + telemetry integration
13-
- Diagnostics and persistence
14-
15-
## Test Steps
16-
1. Smoke test all samples
17-
2. Verify integrations remain stable
18-
3. Confirm no regressions
19-
20-
## Expected
21-
- Stable production-ready baseline
1+
# BUILD_PR_LEVEL_19_1_OVERLAY_SYSTEM_EXPANSION_FRAMEWORK
2+
3+
## PLAN
4+
5+
### Purpose
6+
Establish a framework for expanding the overlay system beyond debug use into reusable gameplay and tool overlays.
7+
8+
### Goals
9+
- Define extension points for new overlays
10+
- Ensure compatibility with existing config-driven system
11+
- Maintain strict separation between debug and gameplay overlays
12+
13+
---
14+
15+
## BUILD
16+
17+
### Scope
18+
- Define overlay extension interface/contracts
19+
- Document integration pattern for new overlays
20+
- Ensure existing overlays conform to framework
21+
- No behavior change
22+
23+
### Test Steps
24+
1. Validate existing overlays still load
25+
2. Confirm extension points available
26+
3. Verify no regression
27+
28+
### Expected
29+
- Overlay system ready for expansion
30+
- No impact to existing behavior
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/*
2+
Toolbox Aid
3+
David Quesenberry
4+
04/16/2026
5+
overlayExpansionContracts.js
6+
*/
7+
8+
const DEFAULT_CHANNEL = 'debug';
9+
10+
function normalizeOverlayEntry(entry) {
11+
const id = String(entry?.id ?? '').trim();
12+
if (!id) {
13+
return null;
14+
}
15+
const label = String(entry?.label ?? id).trim() || id;
16+
return { id, label };
17+
}
18+
19+
function normalizeOverlayStack(overlays) {
20+
const normalized = [];
21+
for (let i = 0; i < overlays.length; i += 1) {
22+
const candidate = normalizeOverlayEntry(overlays[i]);
23+
if (!candidate) {
24+
continue;
25+
}
26+
if (normalized.some((overlay) => overlay.id === candidate.id)) {
27+
continue;
28+
}
29+
normalized.push(candidate);
30+
}
31+
return normalized;
32+
}
33+
34+
function resolveInitialOverlayId(overlays, initialOverlayId) {
35+
if (overlays.length === 0) {
36+
return '';
37+
}
38+
39+
const requestedId = String(initialOverlayId || '').trim();
40+
if (!requestedId) {
41+
return overlays[0].id;
42+
}
43+
44+
const exists = overlays.some((overlay) => overlay.id === requestedId);
45+
return exists ? requestedId : overlays[0].id;
46+
}
47+
48+
export function defineOverlayExtensionContract({
49+
id = '',
50+
overlays = [],
51+
initialOverlayId = '',
52+
cycleKey = '',
53+
persistenceKey = '',
54+
channel = DEFAULT_CHANNEL,
55+
metadata = {},
56+
} = {}) {
57+
const normalizedId = String(id || '').trim();
58+
if (!normalizedId) {
59+
throw new Error('Overlay extension contract requires a non-empty id.');
60+
}
61+
62+
const normalizedOverlays = normalizeOverlayStack(Array.isArray(overlays) ? overlays : []);
63+
if (normalizedOverlays.length === 0) {
64+
throw new Error(`Overlay extension contract "${normalizedId}" requires at least one overlay entry.`);
65+
}
66+
67+
const normalizedCycleKey = String(cycleKey || '').trim();
68+
const normalizedPersistenceKey = String(persistenceKey || '').trim();
69+
const normalizedChannel = String(channel || DEFAULT_CHANNEL).trim() || DEFAULT_CHANNEL;
70+
const normalizedInitialOverlayId = resolveInitialOverlayId(normalizedOverlays, initialOverlayId);
71+
72+
return Object.freeze({
73+
id: normalizedId,
74+
channel: normalizedChannel,
75+
overlays: Object.freeze(normalizedOverlays),
76+
initialOverlayId: normalizedInitialOverlayId,
77+
cycleKey: normalizedCycleKey,
78+
persistenceKey: normalizedPersistenceKey,
79+
metadata: Object.freeze({ ...(metadata || {}) }),
80+
});
81+
}
82+
83+
export function createOverlayExtensionContractMap(contracts = []) {
84+
const map = new Map();
85+
for (let i = 0; i < contracts.length; i += 1) {
86+
const contract = contracts[i];
87+
if (!contract || typeof contract !== 'object') {
88+
continue;
89+
}
90+
const id = String(contract.id || '').trim();
91+
if (!id) {
92+
continue;
93+
}
94+
map.set(id, contract);
95+
}
96+
return map;
97+
}
98+
99+
export function getOverlayControllerConfigFromContract(contract) {
100+
if (!contract || typeof contract !== 'object') {
101+
throw new Error('Overlay contract is required to resolve overlay controller config.');
102+
}
103+
return Object.freeze({
104+
overlays: contract.overlays,
105+
initialOverlayId: contract.initialOverlayId,
106+
cycleKey: contract.cycleKey,
107+
persistenceKey: contract.persistenceKey,
108+
});
109+
}

samples/phase-17/shared/overlayStackBySampleConfig.js

Lines changed: 48 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,33 +13,48 @@ import {
1313
OVERLAY_MOVEMENT_RUNTIME,
1414
createMovementOverlayCycleMap,
1515
} from '/samples/phase-17/shared/movementOverlayStack.js';
16+
import {
17+
createOverlayExtensionContractMap,
18+
defineOverlayExtensionContract,
19+
getOverlayControllerConfigFromContract,
20+
} from '/samples/phase-17/shared/overlayExpansionContracts.js';
1621

17-
const SAMPLE_OVERLAY_STACKS = Object.freeze({
18-
'1708': Object.freeze({
22+
const LEVEL17_OVERLAY_EXTENSION_CONTRACTS = Object.freeze([
23+
defineOverlayExtensionContract({
24+
id: '1708',
25+
channel: 'debug',
1926
overlays: Object.freeze(createMiniGameOverlayCycleMap()),
2027
initialOverlayId: OVERLAY_UI_LAYER,
2128
persistenceKey: 'phase17:1708:overlay-index',
2229
cycleKey: LEVEL17_OVERLAY_CYCLE_KEY,
2330
}),
24-
'1709': Object.freeze({
31+
defineOverlayExtensionContract({
32+
id: '1709',
33+
channel: 'debug',
2534
overlays: Object.freeze(createMovementOverlayCycleMap()),
2635
initialOverlayId: OVERLAY_MOVEMENT_RUNTIME,
2736
persistenceKey: 'phase17:1709:overlay-index',
2837
cycleKey: LEVEL17_OVERLAY_CYCLE_KEY,
2938
}),
30-
'1710': Object.freeze({
39+
defineOverlayExtensionContract({
40+
id: '1710',
41+
channel: 'debug',
3142
overlays: Object.freeze(createMiniGameOverlayCycleMap()),
3243
initialOverlayId: OVERLAY_UI_LAYER,
3344
persistenceKey: 'phase17:1710:overlay-index',
3445
cycleKey: LEVEL17_OVERLAY_CYCLE_KEY,
3546
}),
36-
'1711': Object.freeze({
47+
defineOverlayExtensionContract({
48+
id: '1711',
49+
channel: 'debug',
3750
overlays: Object.freeze(createMovementOverlayCycleMap()),
3851
initialOverlayId: OVERLAY_MOVEMENT_RUNTIME,
3952
persistenceKey: 'phase17:1711:overlay-index',
4053
cycleKey: LEVEL17_OVERLAY_CYCLE_KEY,
4154
}),
42-
'1712': Object.freeze({
55+
defineOverlayExtensionContract({
56+
id: '1712',
57+
channel: 'debug',
4358
overlays: Object.freeze([
4459
{ id: 'ui-layer', label: 'UI Layer' },
4560
{ id: 'mission-feed', label: 'Mission Feed' },
@@ -50,7 +65,9 @@ const SAMPLE_OVERLAY_STACKS = Object.freeze({
5065
persistenceKey: 'phase17:1712:overlay-index',
5166
cycleKey: LEVEL17_OVERLAY_CYCLE_KEY,
5267
}),
53-
'1713': Object.freeze({
68+
defineOverlayExtensionContract({
69+
id: '1713',
70+
channel: 'debug',
5471
overlays: Object.freeze([
5572
{ id: 'ui-layer', label: 'UI Layer' },
5673
{ id: 'mission-feed', label: 'Mission Feed' },
@@ -61,7 +78,30 @@ const SAMPLE_OVERLAY_STACKS = Object.freeze({
6178
persistenceKey: 'phase17:1713:overlay-index',
6279
cycleKey: LEVEL17_OVERLAY_CYCLE_KEY,
6380
}),
64-
});
81+
]);
82+
83+
const LEVEL17_OVERLAY_EXTENSION_CONTRACT_MAP = createOverlayExtensionContractMap(LEVEL17_OVERLAY_EXTENSION_CONTRACTS);
84+
85+
const SAMPLE_OVERLAY_STACKS = Object.freeze(
86+
Object.fromEntries(
87+
Array.from(LEVEL17_OVERLAY_EXTENSION_CONTRACT_MAP.entries(), ([sampleId, contract]) => [
88+
sampleId,
89+
getOverlayControllerConfigFromContract(contract),
90+
])
91+
)
92+
);
93+
94+
export function getLevel17OverlayExtensionContract(sampleId) {
95+
const id = String(sampleId || '').trim();
96+
if (!id) {
97+
return null;
98+
}
99+
return LEVEL17_OVERLAY_EXTENSION_CONTRACT_MAP.get(id) ?? null;
100+
}
101+
102+
export function listLevel17OverlayExtensionContracts() {
103+
return LEVEL17_OVERLAY_EXTENSION_CONTRACTS;
104+
}
65105

66106
export function getLevel17OverlayStackConfig(sampleId) {
67107
return SAMPLE_OVERLAY_STACKS[String(sampleId || '').trim()] ?? null;
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
Toolbox Aid
3+
David Quesenberry
4+
04/16/2026
5+
Phase17OverlayExpansionFramework.test.mjs
6+
*/
7+
import assert from 'node:assert/strict';
8+
import { LEVEL17_OVERLAY_CYCLE_KEY } from '../../samples/phase-17/shared/overlayCycleInput.js';
9+
import {
10+
defineOverlayExtensionContract,
11+
getOverlayControllerConfigFromContract,
12+
} from '../../samples/phase-17/shared/overlayExpansionContracts.js';
13+
import {
14+
getLevel17OverlayExtensionContract,
15+
getLevel17OverlayStackConfig,
16+
listLevel17OverlayExtensionContracts,
17+
} from '../../samples/phase-17/shared/overlayStackBySampleConfig.js';
18+
19+
function assertLevel17ContractsMatchControllerConfigs() {
20+
const contracts = listLevel17OverlayExtensionContracts();
21+
assert.equal(contracts.length, 6, 'Level 17 overlay extension contracts should be defined for samples 1708-1713.');
22+
23+
for (let sampleId = 1708; sampleId <= 1713; sampleId += 1) {
24+
const key = String(sampleId);
25+
const contract = getLevel17OverlayExtensionContract(key);
26+
const config = getLevel17OverlayStackConfig(key);
27+
assert.equal(Boolean(contract), true, `Overlay extension contract should exist for sample ${key}.`);
28+
assert.equal(Boolean(config), true, `Overlay controller config should exist for sample ${key}.`);
29+
30+
const resolved = getOverlayControllerConfigFromContract(contract);
31+
assert.deepEqual(resolved, config, `Overlay controller config for sample ${key} should be derived from extension contract.`);
32+
assert.equal(contract.cycleKey, LEVEL17_OVERLAY_CYCLE_KEY, `Overlay contract for sample ${key} should use shared cycle key.`);
33+
}
34+
}
35+
36+
function assertContractNormalizationForExpansionPath() {
37+
const contract = defineOverlayExtensionContract({
38+
id: 'future-sample',
39+
overlays: [
40+
{ id: 'runtime', label: 'Runtime' },
41+
{ id: 'runtime', label: 'Duplicate Runtime' },
42+
{ id: 'hud', label: 'HUD' },
43+
],
44+
initialOverlayId: 'missing-id',
45+
cycleKey: 'KeyG',
46+
persistenceKey: 'phaseX:future:overlay-index',
47+
channel: 'gameplay',
48+
metadata: { family: 'future-track' },
49+
});
50+
51+
assert.equal(contract.id, 'future-sample', 'Contract id should be preserved.');
52+
assert.equal(contract.channel, 'gameplay', 'Contract channel should be preserved.');
53+
assert.equal(contract.overlays.length, 2, 'Contract normalization should dedupe duplicate overlay ids.');
54+
assert.equal(contract.initialOverlayId, 'runtime', 'Missing initial overlay id should fall back to first normalized overlay.');
55+
assert.equal(contract.metadata.family, 'future-track', 'Contract metadata should be retained.');
56+
}
57+
58+
export function run() {
59+
assertLevel17ContractsMatchControllerConfigs();
60+
assertContractNormalizationForExpansionPath();
61+
}

0 commit comments

Comments
 (0)