Skip to content

Commit d47862c

Browse files
author
DavidQ
committed
PR_06_07_SAMPLE_LAUNCHER_FILTER_RESTORE & PR_07_04_NETWORK_RECONCILIATION
1 parent f6d4896 commit d47862c

10 files changed

Lines changed: 213 additions & 30 deletions

docs/dev/CODEX_COMMANDS.md

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

44
COMMAND:
5-
Implement PR_07_03_NETWORK_LATENCY_MODELING.
5+
Implement PR_07_04_NETWORK_RECONCILIATION.
66

77
Goal:
8-
Add latency modeling to simulation.
8+
Add reconciliation to simulation.
99

1010
Steps:
11-
1. Define latency model (delay/jitter).
12-
2. Inject into simulation inputs.
13-
3. Validate behavior.
11+
1. Define authoritative vs local state.
12+
2. Detect divergence.
13+
3. Apply correction.
1414

1515
Rules:
16-
- no networking
17-
- no reconciliation
18-
- no behavior changes outside latency simulation
16+
- no real networking
17+
- no prediction yet
18+
- no behavior changes outside simulation layer
1919

2020
Return ZIP:
21-
<project folder>/tmp/PR_07_03_NETWORK_LATENCY_MODELING.zip
21+
<project folder>/tmp/PR_07_04_NETWORK_RECONCILIATION.zip

docs/dev/COMMIT_COMMENT.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
PR_07_03_NETWORK_LATENCY_MODELING
1+
PR_07_04_NETWORK_RECONCILIATION
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Samples dependency cleanup package focused on removing non-public engine coupling and standardizing samples onto approved public/shared surfaces.
1+
Restore sample launcher phase dropdown and tag filter behavior with a surgical launcher-only fix.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
PR_06_07_SAMPLE_LAUNCHER_FILTER_RESTORE - fix map
2+
3+
File: samples/index.html
4+
5+
Targeted fixes:
6+
1) Added parseSampleLink(href)
7+
- New parser accepts canonical phase path variants via:
8+
/(?:^|\/)phase-?(\d{2})\/(\d{4})\/index\.html$/i
9+
- Standardizes extraction for phase and sampleId used by phase dropdown + filtering.
10+
11+
2) Added parseTagTokens(model)
12+
- Preserves data-tags behavior.
13+
- Adds data-primary as fallback/augmenting tag (deduplicated), restoring expected tag discoverability/filter interaction.
14+
15+
3) Updated link model construction
16+
- Replaced inline strict href regex logic with parseSampleLink.
17+
- Replaced inline data-tags parsing with parseTagTokens.
18+
19+
4) Removed duplicate resolveThumbnail declaration
20+
- Kept single canonical helper definition in launcher script to reduce regression risk.
21+
22+
Validation evidence:
23+
- Live sample links parsed: 197/197
24+
- Unique parsed phases: 15 (01..15)
25+
- Unparsed live links: 0
26+
- Unique filter tags generated (tags + primary fallback): 351
27+
- Runtime smoke: node tests/runtime/LaunchSmokeAllEntries.test.mjs passed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
PR_06_07_SAMPLE_LAUNCHER_FILTER_RESTORE - regression scan
2+
3+
Scope:
4+
- samples/index.html launcher filter script/UI only
5+
6+
Regression cause identified:
7+
1) Sample href parsing was too strict.
8+
- Previous parser accepted only: ./phase-XX/####/index.html
9+
- If launcher links are emitted in equivalent canonical variants (for example /samples/phase-XX/####/index.html or phaseXX), phase/sample IDs fail to parse.
10+
- Failed parse results in blank model.phase and model.sampleId, which prevents correct phase dropdown population and weakens filter matching.
11+
12+
2) Tag extraction ignored primary concept fallback.
13+
- Tags were sourced only from data-tags.
14+
- When data-tags are incomplete but data-primary is present, expected tag filter discoverability is reduced.
15+
16+
3) Duplicate helper declaration in filter script.
17+
- resolveThumbnail was declared twice.
18+
- Runtime behavior was not always broken, but duplicate declarations increase maintenance risk and regression susceptibility.
19+
20+
Observed impact pattern:
21+
- phase dropdown can appear incomplete/empty under path variant drift.
22+
- tag filter discoverability can regress for entries relying on primary concept.
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1-
[ ] latency model defined
2-
[ ] latency injection applied
1+
[ ] reconciliation model defined
2+
[ ] divergence detection working
3+
[ ] correction applied
34
[ ] validation complete
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# PR_06_07_SAMPLE_LAUNCHER_FILTER_RESTORE
2+
3+
## Purpose
4+
Restore the sample launcher phase dropdown and tag filter behavior without changing sample content or runtime behavior.
5+
6+
## Scope
7+
- samples/index.html and only the direct assets/scripts/styles it depends on for launcher filter UI behavior
8+
- no sample runtime changes
9+
- no network simulation changes
10+
- no launcher content restructuring beyond what is required to restore filters
11+
12+
## Required Work
13+
1. Identify why the phase dropdown no longer renders/behaves correctly.
14+
2. Identify why sample tags are no longer rendering/behaving correctly.
15+
3. Restore phase dropdown population/interaction.
16+
4. Restore tag rendering/filter interaction.
17+
5. Preserve existing sample entries, phase ordering, and link targets.
18+
6. Keep the fix surgical and execution-backed.
19+
20+
## Constraints
21+
- no broad launcher redesign
22+
- no sample data model redesign
23+
- no behavior changes outside launcher filter restoration
24+
- no changes to phase/sample content except what is required to restore filters
25+
26+
## Deliverables
27+
- docs/dev/reports/launcher_filter_regression_scan.txt
28+
- docs/dev/reports/launcher_filter_fix_map.txt
29+
- docs/dev/reports/validation_checklist.txt
30+
31+
## Validation
32+
- phase dropdown renders and filters correctly
33+
- tags render and filter correctly
34+
- sample links remain intact
35+
- no broken phase/sample navigation
36+
- impacted smoke/manual validation passes
37+
38+
## Output
39+
<project folder>/tmp/PR_06_07_SAMPLE_LAUNCHER_FILTER_RESTORE.zip
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# PR_07_04_NETWORK_RECONCILIATION
2+
3+
## Purpose
4+
Introduce reconciliation model for Phase 13 simulation.
5+
6+
## Scope
7+
- reconciliation logic only
8+
- no real networking
9+
- no prediction yet
10+
- no behavior changes outside simulation layer
11+
12+
## Tasks
13+
1. Define reconciliation model (authoritative vs local state).
14+
2. Apply correction when divergence detected.
15+
3. Maintain deterministic baseline compatibility.
16+
17+
## Deliverables
18+
- docs/dev/reports/reconciliation_model.txt
19+
- docs/dev/reports/validation_checklist.txt
20+
21+
## Validation
22+
- divergence corrected deterministically
23+
- latency model still respected
24+
- no regressions
25+
26+
## Output
27+
<project folder>/tmp/PR_07_04_NETWORK_RECONCILIATION.zip

samples/index.html

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -544,13 +544,10 @@ <h2>Phase 16 - 3D Games</h2>
544544
const linkModels = linkNodes.map((link) => {
545545
const labelText = String(link.textContent || '').trim();
546546
const href = String(link.getAttribute('href') || '');
547-
const match = href.match(/^\\.\\/phase-(\\d{2})\\/(\\d{4})\\/index\\.html$/);
548-
const phase = match ? match[1] : '';
549-
const sampleId = match ? match[2] : '';
550-
const tags = String(link.getAttribute('data-tags') || '')
551-
.split(',')
552-
.map((tag) => tag.trim().toLowerCase())
553-
.filter(Boolean);
547+
const parsed = parseSampleLink(href);
548+
const phase = parsed.phase;
549+
const sampleId = parsed.sampleId;
550+
const tags = parseTagTokens({ link });
554551
const titleText = labelText.toLowerCase();
555552
const descriptionText = String(link.getAttribute('title') || '').trim().toLowerCase();
556553
const searchText = [sampleId, titleText, descriptionText, tags.join(' ')].join(' ');
@@ -639,6 +636,32 @@ <h2>Phase 16 - 3D Games</h2>
639636
return typeof value === 'string' ? value.trim() : '';
640637
}
641638

639+
function parseSampleLink(hrefValue) {
640+
const href = String(hrefValue || '').trim();
641+
const match = href.match(/(?:^|\/)phase-?(\d{2})\/(\d{4})\/index\.html$/i);
642+
if (!match) {
643+
return { phase: '', sampleId: '' };
644+
}
645+
return {
646+
phase: String(match[1] || ''),
647+
sampleId: String(match[2] || '')
648+
};
649+
}
650+
651+
function parseTagTokens(model) {
652+
const tags = String(model.link.getAttribute('data-tags') || '')
653+
.split(',')
654+
.map((tag) => tag.trim().toLowerCase())
655+
.filter(Boolean);
656+
const primary = String(model.link.getAttribute('data-primary') || '')
657+
.trim()
658+
.toLowerCase();
659+
if (primary && !tags.includes(primary)) {
660+
tags.push(primary);
661+
}
662+
return tags;
663+
}
664+
642665
function ensureHoverCard() {
643666
if (hoverCard) {
644667
return hoverCard;
@@ -661,10 +684,6 @@ <h2>Phase 16 - 3D Games</h2>
661684
return hoverCard;
662685
}
663686

664-
function resolveThumbnail(model) {
665-
return model.thumbnail || model.preview || FALLBACK_THUMBNAIL;
666-
}
667-
668687
function resolvePreview(model) {
669688
return model.preview || model.thumbnail || FALLBACK_PREVIEW;
670689
}

samples/phase-13/1316/game/FakeLoopbackNetworkModel.js

Lines changed: 53 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ export default class FakeLoopbackNetworkModel {
4141
this.jitterMs = 3;
4242
this.replicationBacklog = 0;
4343
this.replicationTick = 0;
44+
this.authoritativeFrame = 0;
45+
this.localFrame = 0;
46+
this.lastReconcileAtMs = null;
47+
this.reconciliationCount = 0;
4448

4549
this.autoPacketTimerSeconds = 0;
4650
this.autoPacketIntervalSeconds = 0.65;
@@ -205,6 +209,39 @@ export default class FakeLoopbackNetworkModel {
205209
this.replicationBacklog = Math.max(0, this.inFlightPackets.length);
206210
}
207211

212+
updateDivergenceState() {
213+
this.authoritativeFrame = Math.max(0, Number(this.replicationTick) || 0);
214+
const ackedFloor = Math.max(0, Number(this.ackedSequence) || 0);
215+
if (this.localFrame < ackedFloor) {
216+
this.localFrame = ackedFloor;
217+
}
218+
this.localFrame = clamp(this.localFrame, 0, this.authoritativeFrame);
219+
}
220+
221+
applyReconciliation() {
222+
if (this.phase !== "connected") {
223+
return;
224+
}
225+
226+
const divergence = Math.max(0, this.authoritativeFrame - this.localFrame);
227+
const stableWindow = 2;
228+
if (divergence <= stableWindow) {
229+
return;
230+
}
231+
232+
const correction = Math.min(2, divergence - stableWindow);
233+
this.localFrame = clamp(this.localFrame + correction, 0, this.authoritativeFrame);
234+
this.reconciliationCount += 1;
235+
this.lastReconcileAtMs = Math.round(this.elapsedSeconds * 1000);
236+
237+
this.pushTrace("RECONCILE_APPLIED", {
238+
correctionFrames: correction,
239+
authoritativeFrame: this.authoritativeFrame,
240+
localFrame: this.localFrame,
241+
divergenceAfter: Math.max(0, this.authoritativeFrame - this.localFrame)
242+
});
243+
}
244+
208245
update(dtSeconds, options = {}) {
209246
const safeDt = asPositiveNumber(dtSeconds, 1 / 60);
210247
this.elapsedSeconds += safeDt;
@@ -217,11 +254,17 @@ export default class FakeLoopbackNetworkModel {
217254
this.updatePhaseProgress(safeDt);
218255
this.updateLatencySnapshot();
219256
this.updateAcks();
257+
this.updateDivergenceState();
258+
this.applyReconciliation();
220259
}
221260

222261
getSnapshot() {
223262
const traceTail = this.traceEvents.slice(-18);
224263

264+
const frameDelta = Math.max(0, this.authoritativeFrame - this.localFrame);
265+
const divergenceStatus = frameDelta > 8 ? "diverged" : (frameDelta > 2 ? "drifting" : "stable");
266+
const divergenceScore = frameDelta > 4 ? "warning" : "ok";
267+
225268
return {
226269
sessionId: this.sessionId,
227270
connection: {
@@ -242,13 +285,18 @@ export default class FakeLoopbackNetworkModel {
242285
nextSequence: this.nextSequence,
243286
ackedSequence: this.ackedSequence,
244287
pendingPackets: this.pendingPackets,
245-
backlog: this.replicationBacklog
288+
backlog: this.replicationBacklog,
289+
authoritativeFrame: this.authoritativeFrame,
290+
localFrame: this.localFrame,
291+
reconciliationCount: this.reconciliationCount,
292+
lastReconcileAtMs: this.lastReconcileAtMs
246293
},
247294
divergence: {
248-
status: "stable",
249-
score: this.replicationBacklog > 4 ? "warning" : "ok",
250-
serverFrame: this.replicationTick,
251-
clientFrame: Math.max(0, this.replicationTick - this.replicationBacklog)
295+
status: divergenceStatus,
296+
score: divergenceScore,
297+
frameDelta,
298+
serverFrame: this.authoritativeFrame,
299+
clientFrame: this.localFrame
252300
},
253301
trace: {
254302
totalEvents: this.traceEvents.length,

0 commit comments

Comments
 (0)