mapspinner 0.1.1

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.
@@ -0,0 +1,209 @@
1
+ export const meta = {
2
+ name: 'speed-dna',
3
+ description: 'Optimize every part of TV8 speed (cold compile + FPS) through the 12-principle synthesized-engineering-DNA lens: measure-first, subtract before adding, physics-first constraints, empirical cross-axis arbitration, adversarial verify. Fans out subagents; composes the fps-perf + startup-perf workflows as sub-steps.',
4
+ whenToUse: 'When the user wants a holistic speed pass over BOTH cold-compile time AND runtime FPS, ranked and arbitrated as one budget rather than two independent passes. Honours the measured-dead-lever record (FS source-shrink is dead; atlas activation is the only real compile lever) so it does not re-chase refuted work.',
5
+ phases: [
6
+ { title: 'Constraints', detail: 'P5 physics-first: enumerate the hard speed constraints (ANGLE cold-HLSL translation cost is intrinsic + driver-cached; no getProgramBinary; VS 14-oct broadShapeM x3/vertex; behind-limb cull on, frustum off; measured-dead levers from recall) before any agent proposes a cut' },
7
+ { title: 'Measure', detail: 'P7 empirical: gather the live numbers that exist (args.measured gpuTimer VS/FS split per rung; args.compile cold/warm ms + translated-HLSL size). Absent numbers are flagged UNMEASURED, never guessed past.' },
8
+ { title: 'Map', detail: 'fan-out readers map every per-frame (FPS) and per-init (compile) cost across gl-render.js / planet-orchestrator.js / terrain.glsl, each tagged with the DNA principle that judges it' },
9
+ { title: 'Arbitrate', detail: 'P2 subtractive + P9 worst-case + P12 human-value: rank ALL levers across both axes into ONE budget; resolve cross-axis conflicts (a compile cut that costs FPS or vice versa); drop measured-dead levers; pick the single highest-payoff worst-case-respecting move first' },
10
+ { title: 'Cut', detail: 'P3 evolutionary + P6 adversarial: propose the concrete edit for each surviving lever with its no-regression witness; adversarially verify (could it seam / break LOD-invariance / change terrain / fail compile / regress the OTHER axis)' },
11
+ { title: 'Plan', detail: 'P10 honest interface: return the DNA-justified, witness-bearing, cross-axis-arbitrated cut plan; cold edits BATCHED into one reload (each shader edit = one ~110s cold compile)' },
12
+ ],
13
+ }
14
+
15
+ // ----------------------------------------------------------------------------
16
+ // speed-dna -- the unifying speed workflow. The repo already carries two single-axis
17
+ // workflows (fps-perf, startup-perf); this one does NOT duplicate them -- it composes them
18
+ // (P4 composition spine: each adds one capability, this one adds cross-axis arbitration on
19
+ // top) and overlays the 12 DNA principles as the explicit ranking lens.
20
+ //
21
+ // The load-bearing prior the DNA demands we respect (P7 measure-don't-assume, P3 revert-and-
22
+ // don't-rechase): COLD COMPILE FS SOURCE-SHRINK IS MEASURED-DEAD (recall
23
+ // tv8-compile-time-every-approach-evaluated-2026-06-05: guarding VS-only funcs out of the FS
24
+ // changed cold 109124->118939ms = driver noise; ANGLE already DCEs unused funcs; the cost is
25
+ // INTRINSIC ANGLE->HLSL translation of the FS main(), not char/branch/loop count). The ONLY
26
+ // real compile lever is ATLAS ACTIVATION (removes the per-vertex fractal from BOTH stages) --
27
+ // see the atlas-pivot workflow. So this workflow's compile axis routes to atlas, NOT to FS
28
+ // trimming, and any agent that proposes FS source-shrink for cold compile is contradicting a
29
+ // measured result. Warm reload is already 41ms (driver program cache persists across reloads;
30
+ // cold recurs only on SOURCE CHANGE = dev edits, or first-ever cold driver cache).
31
+ //
32
+ // The FPS prime lever (also measured): the VS runs 3x full 14-oct broadShapeM/vertex (displace
33
+ // + 2 central-diff taps) over the GRID mesh; over-tessellation was sub-pixel (GRID 24->16 cut
34
+ // verts/quad 676->324). broadShapeM MUST stay a pure LOD-invariant world-dir fn -- a per-tile /
35
+ // screen-space octave fade was TRIED + REFUTED (adjacent tiles diverge at the shared edge =
36
+ // seams/popping), so no FPS cut may reintroduce that seam (P6 adversarial: the refuted misuse
37
+ // will recur unless structurally blocked).
38
+ //
39
+ // args (all optional): { measured: <gpuTimer split per rung {fullMs,vsRasterMs,fsMs}>,
40
+ // compile: <{coldMs, warmMs, translatedFsChars, translatedVsChars}>,
41
+ // runFps: <bool, run the fps-perf child>, runStartup: <bool, run startup-perf child> }
42
+ // ----------------------------------------------------------------------------
43
+
44
+ const A = (typeof args === 'object' && args) ? args : {}
45
+ const measured = A.measured || null
46
+ const compile = A.compile || null
47
+
48
+ // The DNA principles, as the ranking lens every agent is handed. Kept as data (P1 data-first):
49
+ // the workflow does not branch on principle names, it passes them as the judgement frame.
50
+ const DNA = [
51
+ 'P1 data-first: is the cost a state/data-model problem (re-bake, cache shape) or genuinely compute?',
52
+ 'P2 subtractive: can the cost be REMOVED entirely rather than made faster? (kill an octave, a tap, a debug branch)',
53
+ 'P5 physics-first: what hardware/driver constraint bounds this? (ANGLE translation, ALU count, bandwidth, vert count)',
54
+ 'P7 empirical: is there a MEASURED number, or is this a guess? Refuse measured-dead levers (FS source-shrink for cold compile).',
55
+ 'P9 worst-case: does the cut help the WORST rung (close-approach / cold first-load), or only the average?',
56
+ 'P12 human-value: does a real user FEEL this? (a 110s once-per-cold-cache compile vs a per-frame stutter)',
57
+ ]
58
+
59
+ const SPEED_FILES = [
60
+ { path: 'src/gl-render.js', lens: 'COMPILE: terrain.glsl compile/link, KHR_parallel_shader_compile non-blocking path, probe+debug program split, GRID mesh size. FPS: per-frame instBuf upload, drawElementsInstanced, verts/quad.' },
61
+ { path: 'src/planet-orchestrator.js', lens: 'COMPILE: HPF bake (HPF_RES^2*6 sampleUV), anchor field, init timings. FPS: per-frame quadtree split (splitFactor/altSplitMul/maxLevel), behind-limb cull + forward-cone rescue, leaf emit count, deck cap.' },
62
+ { path: 'src/shaders/terrain.glsl', lens: 'COMPILE: FS main() intrinsic ANGLE-HLSL cost (NOT source-trimmable -- measured dead), debug displayMode blocks. FPS: VS vtxDisplace + lit-normal central-diff (2 extra full broadShapeM/vertex), broadShapeM octave count, FS per-pixel shade taps.' },
63
+ ]
64
+
65
+ const COST_SCHEMA = {
66
+ type: 'object',
67
+ properties: {
68
+ costs: {
69
+ type: 'array',
70
+ items: {
71
+ type: 'object',
72
+ properties: {
73
+ name: { type: 'string' },
74
+ file: { type: 'string' },
75
+ axis: { type: 'string', description: 'compile | fps | both' },
76
+ stage: { type: 'string', description: 'VS | FS | CPU-emit | LOD-quad-count | js-init | cold-compile' },
77
+ perWhat: { type: 'string', description: 'per-vertex | per-pixel | per-quad | per-frame | per-init | once-cold' },
78
+ cutIdea: { type: 'string' },
79
+ dnaPrinciple:{ type: 'string', description: 'which DNA principle most justifies attacking this (e.g. P2 subtractive)' },
80
+ measuredDead:{ type: 'boolean', description: 'true if this is a known measured-dead lever (e.g. FS source-shrink for cold compile)' },
81
+ crossAxisRisk:{ type: 'string', description: 'does cutting this regress the OTHER axis? (e.g. removing an octave helps FPS+compile but loses relief; an atlas helps compile but adds a sampler tap to FPS)' },
82
+ lodInvariant:{ type: 'boolean', description: 'true if an FPS cut keeps broadShapeM a pure LOD-invariant world-dir fn (no seam); n/a-> true for compile-only' },
83
+ regressionRisk: { type: 'string', description: 'low | medium | high + the witness that proves no-regression' },
84
+ },
85
+ required: ['name', 'file', 'axis', 'stage', 'cutIdea', 'dnaPrinciple', 'regressionRisk'],
86
+ },
87
+ },
88
+ },
89
+ required: ['costs'],
90
+ }
91
+
92
+ // --- Constraints (P5 physics-first) -- stated as data, before any agent reasons -------------
93
+ phase('Constraints')
94
+ const CONSTRAINTS = [
95
+ 'ANGLE cold HLSL translation of the terrain FS+VS main() is INTRINSIC (~110-120s on AMD); not reducible by source char/branch/loop count (MEASURED dead 2026-06-05). Driver-cached: warm reload 41ms; cold recurs only on source change (dev) or first-ever cache.',
96
+ 'No getProgramBinary on ANGLE-AMD -> the browser ANGLE blob cache is the only persistence; cannot ship a precompiled binary.',
97
+ 'The ONLY real compile lever is atlas activation (removes the per-vertex fractal from BOTH VS+FS translation units) -- routes to the atlas-pivot workflow, not FS trimming.',
98
+ 'FPS: VS runs 3x full 14-oct broadShapeM/vertex (displace + 2 central-diff taps) over the GRID mesh; behind-limb cull removes ~50-80% leaves; frustum cull intentionally OFF (mis-culled bulged on-screen quads).',
99
+ 'broadShapeM MUST stay a pure LOD-invariant world-dir fn: per-tile/screen-space octave fade TRIED + REFUTED (seams at shared tile edges).',
100
+ 'Each terrain.glsl edit = one fresh ~110s cold compile -> BATCH all shader edits into one reload.',
101
+ ]
102
+ log('Physics-first constraints fixed (' + CONSTRAINTS.length + '); measured-dead levers will be refused, not re-chased.')
103
+
104
+ // --- Measure (P7 empirical) -----------------------------------------------------------------
105
+ phase('Measure')
106
+ log(measured ? ('gpuTimer split provided: ' + JSON.stringify(measured))
107
+ : 'NO gpuTimer split passed (args.measured) -> FPS ranking flags every lever UNMEASURED; weight the VS triple-fractal prior, do not assert a number.')
108
+ log(compile ? ('compile numbers provided: ' + JSON.stringify(compile))
109
+ : 'NO compile numbers passed (args.compile) -> compile ranking uses the cold-HLSL-intrinsic prior; FS source-shrink stays measured-dead.')
110
+
111
+ // --- Map (fan-out) --------------------------------------------------------------------------
112
+ phase('Map')
113
+ const maps = await parallel(SPEED_FILES.map(f => () =>
114
+ agent(
115
+ 'Read ' + f.path + ' in the TV8 repo and map every SPEED cost it carries across BOTH axes (focus: ' + f.lens + '). ' +
116
+ 'Judge each cost through the synthesized-engineering-DNA lens:\n' + DNA.map(d => ' - ' + d).join('\n') + '\n' +
117
+ 'For each cost return name, file, axis (compile|fps|both), stage, perWhat, a concrete cutIdea, the dnaPrinciple that most justifies attacking it, ' +
118
+ 'measuredDead (true for FS source-shrink as a cold-compile lever and any other refuted lever), crossAxisRisk (does cutting it regress the other axis?), ' +
119
+ 'lodInvariant (does an FPS cut keep broadShapeM seam-free?), and regressionRisk + the witness. ' +
120
+ 'HARD CONSTRAINTS you must respect (do not propose anything that violates them):\n' + CONSTRAINTS.map(c => ' - ' + c).join('\n') + '\n' +
121
+ 'Do NOT edit anything; this is a read+map pass.',
122
+ { label: 'map:' + f.path.split('/').pop(), phase: 'Map', schema: COST_SCHEMA }
123
+ )
124
+ ))
125
+ const costs = maps.filter(Boolean).flatMap(m => m.costs)
126
+ const live = costs.filter(c => !c.measuredDead)
127
+ const dead = costs.filter(c => c.measuredDead)
128
+ log('mapped ' + costs.length + ' speed costs (' + live.length + ' live, ' + dead.length + ' measured-dead and dropped)')
129
+
130
+ // --- Arbitrate (P2 + P9 + P12) -- one budget across both axes, cross-axis conflicts resolved -
131
+ phase('Arbitrate')
132
+ const ranking = await agent(
133
+ 'You are arbitrating the TV8 speed budget across BOTH axes (cold-compile time AND runtime FPS) as ONE ranked plan, ' +
134
+ 'through the synthesized-engineering-DNA lens. Rank the LIVE levers below into a single ordered list, highest-payoff first, ' +
135
+ 'applying:\n' +
136
+ ' - P2 subtractive: a lever that REMOVES a cost outright outranks one that merely speeds it.\n' +
137
+ ' - P9 worst-case: a lever that helps the WORST rung (close-approach FPS / first-load cold compile) outranks an average-case win.\n' +
138
+ ' - P12 human-value: weight by what a real user FEELS (a once-per-cold-cache compile is felt once; a per-frame deck stutter is felt continuously).\n' +
139
+ ' - P7 empirical: levers WITHOUT a measured number are ranked but flagged unmeasured; never rank a measured-dead lever (already dropped).\n' +
140
+ 'CRITICALLY, resolve every CROSS-AXIS conflict explicitly: if a compile cut costs FPS (e.g. an atlas sampler tap) or an FPS cut costs compile/quality (e.g. dropping octaves loses relief), state the trade and which axis wins and WHY (cite the principle). ' +
141
+ (measured ? 'MEASURED gpuTimer split (authoritative for FPS): ' + JSON.stringify(measured) + '. ' : 'No FPS measurement -> weight the VS triple-fractal prior. ') +
142
+ (compile ? 'MEASURED compile numbers: ' + JSON.stringify(compile) + '. ' : 'No compile measurement -> cold-HLSL-intrinsic prior; atlas is the only real compile lever. ') +
143
+ 'Live levers:\n' + JSON.stringify(live, null, 2),
144
+ { label: 'arbitrate', phase: 'Arbitrate', schema: {
145
+ type: 'object',
146
+ properties: {
147
+ topLever: { type: 'string', description: 'the single highest-payoff move to do first' },
148
+ topAxis: { type: 'string', description: 'compile | fps | both' },
149
+ rationale:{ type: 'string', description: 'why this one first, in DNA-principle terms' },
150
+ crossAxisConflicts: { type: 'array', items: { type: 'object', properties: {
151
+ conflict: { type: 'string' }, winningAxis: { type: 'string' }, principle: { type: 'string' } },
152
+ required: ['conflict', 'winningAxis', 'principle'] } },
153
+ ranked: { type: 'array', items: { type: 'object', properties: {
154
+ name: { type: 'string' }, axis: { type: 'string' }, rank: { type: 'number' },
155
+ expectedSaving: { type: 'string' }, dnaPrinciple: { type: 'string' }, gate: { type: 'string' } },
156
+ required: ['name', 'axis', 'rank', 'expectedSaving', 'gate'] } },
157
+ }, required: ['topLever', 'topAxis', 'ranked'] } }
158
+ )
159
+
160
+ // --- Cut (P3 evolutionary propose + P6 adversarial verify) ----------------------------------
161
+ phase('Cut')
162
+ const cuts = await pipeline(
163
+ ranking.ranked.sort((a, b) => a.rank - b.rank),
164
+ sec => agent(
165
+ 'Propose the concrete code edit for TV8 speed lever "' + sec.name + '" (axis ' + sec.axis + ', expected saving ' + sec.expectedSaving + ', principle ' + (sec.dnaPrinciple || 'n/a') + '). ' +
166
+ 'Give the exact file, the old snippet, the new snippet, and the WITNESS that proves it with no regression on EITHER axis: ' +
167
+ 'for FPS -> re-measured __diag.gpuTimer fullMs/fsMs delta AND continuous normals across a tile boundary (no seam/popping) AND glError 0; ' +
168
+ 'for compile -> translated-HLSL size delta via WEBGL_debug_shaders OR a lab gate (maxElev/landFrac/hypsometry unchanged) OR node --check, AND a note that it does NOT regress FPS. ' +
169
+ 'Gate: ' + sec.gate + '. broadShapeM MUST stay a pure LOD-invariant world-dir fn (the refuted per-tile/screen-space gradient = seam). ' +
170
+ 'If this lever is FS source-shrink for cold compile, STOP and return safe=false (measured-dead). If risk is high, say so.',
171
+ { label: 'cut:' + sec.name, phase: 'Cut', schema: {
172
+ type: 'object',
173
+ properties: {
174
+ file: { type: 'string' }, oldSnippet: { type: 'string' }, newSnippet: { type: 'string' },
175
+ witness: { type: 'string' }, safe: { type: 'boolean' }, notes: { type: 'string' },
176
+ }, required: ['file', 'witness', 'safe'] } }
177
+ ),
178
+ (proposal, sec) => agent(
179
+ 'Adversarially verify this TV8 speed cut for "' + sec.name + '" (axis ' + sec.axis + '). Could it: reintroduce a tile-edge seam, break LOD invariance, ' +
180
+ 'change the rendered terrain (shape/biome/lighting), break a uniform, fail to compile, OR REGRESS THE OTHER SPEED AXIS (an FPS cut that bloats compile, or a compile cut that adds per-pixel/per-vertex work)? ' +
181
+ 'Is it a measured-dead lever (FS source-shrink for cold compile) dressed up? Default to safe=false if uncertain. Proposal:\n' + JSON.stringify(proposal, null, 2),
182
+ { label: 'verify:' + sec.name, phase: 'Cut', schema: {
183
+ type: 'object',
184
+ properties: { real: { type: 'boolean' }, safe: { type: 'boolean' }, regressesOtherAxis: { type: 'boolean' }, reason: { type: 'string' } },
185
+ required: ['real', 'safe', 'reason'] } }
186
+ ).then(v => ({ section: sec.name, axis: sec.axis, proposal, verdict: v }))
187
+ )
188
+
189
+ // --- Plan (P10 honest interface) ------------------------------------------------------------
190
+ phase('Plan')
191
+ const ok = cuts.filter(Boolean).filter(c => c.proposal.safe && c.verdict.safe && !c.verdict.regressesOtherAxis)
192
+ const gated = cuts.filter(Boolean).filter(c => !(c.proposal.safe && c.verdict.safe && !c.verdict.regressesOtherAxis))
193
+
194
+ return {
195
+ workflow: 'speed-dna',
196
+ constraints: CONSTRAINTS,
197
+ measuredDeadDropped: dead.map(c => ({ name: c.name, axis: c.axis, why: c.cutIdea })),
198
+ topLever: ranking.topLever,
199
+ topAxis: ranking.topAxis,
200
+ rationale: ranking.rationale,
201
+ crossAxisConflicts: ranking.crossAxisConflicts || [],
202
+ ranked: ranking.ranked,
203
+ safeCuts: ok.map(c => ({ section: c.section, axis: c.axis, file: c.proposal.file, witness: c.proposal.witness })),
204
+ gatedCuts: gated.map(c => ({ section: c.section, axis: c.axis, reason: c.verdict.reason })),
205
+ note: 'Apply safeCuts highest-rank-first. BATCH all terrain.glsl edits into ONE reload (each = a fresh ~110s cold compile). ' +
206
+ 'Re-measure __diag.gpuTimer (FPS) + translated-HLSL size (compile) after each batch on a HEADED browser. ' +
207
+ 'broadShapeM stays LOD-invariant (no per-tile/screen-space gradient that seams). FS source-shrink for cold compile is measured-dead -- do not apply. ' +
208
+ 'The real compile lever is atlas activation (atlas-pivot workflow). Witness continuous normals + glError 0 before COMPLETE.',
209
+ }
@@ -0,0 +1,117 @@
1
+ export const meta = {
2
+ name: 'startup-perf',
3
+ description: 'Find + attack TV8 startup bottlenecks: map init cost, attribute by cold-compile proxy, cut, verify',
4
+ whenToUse: 'When page startup/load time regresses, or before stepping up shader detail. Re-runs the measure->attribute->cut->verify loop.',
5
+ phases: [
6
+ { title: 'Map', detail: 'parallel readers over gl-render.js / planet-orchestrator.js / terrain.glsl init paths' },
7
+ { title: 'Attribute', detail: 'rank init sections by cold-compile cost proxy (translated-HLSL size, tap count, JS bake samples)' },
8
+ { title: 'Cut', detail: 'one reduction per ranked section (debug-split, probe-slim, octave-cut, bake-background)' },
9
+ { title: 'Verify', detail: 'per cut: lab gates + translated-size delta + node --check; render-run glError deferred to user reload' },
10
+ ],
11
+ }
12
+
13
+ // ----------------------------------------------------------------------------
14
+ // TV8 startup-perf workflow. The cold bottleneck is the ANGLE/HLSL translation of the giant terrain
15
+ // FS (~188s first-load on AMD; warm <0.1s, driver-cached). The driver cache is the only persistence
16
+ // (no getProgramBinary on ANGLE-AMD), so the only lever is the unrolled INSTRUCTION COUNT, proxied by
17
+ // WEBGL_debug_shaders getTranslatedShaderSource().length. This workflow maps init, ranks sections by
18
+ // that proxy, applies one cut per section, and verifies each. Cold seconds are unmeasurable while the
19
+ // driver cache is warm -> validation is the translated-size delta + the lab gates + node --check, with
20
+ // the live render glError + initTimings deferred to a user hard-reload (each shader edit = one fresh
21
+ // cold compile, so cuts are BATCHED into as few reloads as possible).
22
+ // ----------------------------------------------------------------------------
23
+
24
+ const INIT_FILES = [
25
+ { path: 'src/gl-render.js', lens: 'shader compile/link, program build, probe, parallel-compile, debug-program split' },
26
+ { path: 'src/planet-orchestrator.js', lens: 'HPF bake (HPF_RES^2*6 sampleUV), anchor field, init timings, background bake' },
27
+ { path: 'src/shaders/terrain.glsl', lens: 'broadShapeM octave count, debug displayMode blocks, biome/strata/water branches, _PROBE_ block' },
28
+ ]
29
+
30
+ const SECTION_SCHEMA = {
31
+ type: 'object',
32
+ properties: {
33
+ sections: {
34
+ type: 'array',
35
+ items: {
36
+ type: 'object',
37
+ properties: {
38
+ name: { type: 'string' },
39
+ file: { type: 'string' },
40
+ costKind: { type: 'string', description: 'cold-compile | js-init | both' },
41
+ proxyMetric: { type: 'string', description: 'translated-HLSL chars, snoise3 tap count, or sampleUV call count' },
42
+ cutIdea: { type: 'string' },
43
+ regressionRisk: { type: 'string', description: 'low | medium | high, and what gate proves no-regression' },
44
+ },
45
+ required: ['name', 'file', 'costKind', 'cutIdea', 'regressionRisk'],
46
+ },
47
+ },
48
+ },
49
+ required: ['sections'],
50
+ }
51
+
52
+ phase('Map')
53
+ const maps = await parallel(INIT_FILES.map(f => () =>
54
+ agent(
55
+ `Read ${f.path} in the TV8 repo and map every startup-init cost it carries (focus: ${f.lens}). ` +
56
+ `For each cost section return name, file, costKind (cold-compile|js-init|both), proxyMetric, a concrete cutIdea, ` +
57
+ `and regressionRisk with the gate that proves no-regression. Do NOT edit anything; this is a read+map pass.`,
58
+ { label: `map:${f.path.split('/').pop()}`, phase: 'Map', schema: SECTION_SCHEMA }
59
+ )
60
+ ))
61
+ const sections = maps.filter(Boolean).flatMap(m => m.sections)
62
+
63
+ phase('Attribute')
64
+ const ranking = await agent(
65
+ `Rank these TV8 startup-init sections by how much they cost the COLD shader compile + JS init, using the ` +
66
+ `cold-compile proxy = translated-HLSL instruction count (scales with unrolled snoise3 taps + branch count) ` +
67
+ `and, for JS, the sampleUV call count. Cheapest-to-cut-with-highest-payoff first; flag any whose cut has ` +
68
+ `medium/high regression risk so it is gated behind the lab. Sections:\n${JSON.stringify(sections, null, 2)}`,
69
+ { label: 'attribute', phase: 'Attribute', schema: {
70
+ type: 'object',
71
+ properties: {
72
+ ranked: { type: 'array', items: { type: 'object', properties: {
73
+ name: { type: 'string' }, rank: { type: 'number' }, expectedSaving: { type: 'string' },
74
+ gate: { type: 'string' } }, required: ['name', 'rank', 'expectedSaving', 'gate'] } },
75
+ summary: { type: 'string' },
76
+ }, required: ['ranked', 'summary'] } }
77
+ )
78
+
79
+ phase('Cut')
80
+ // Per ranked section, propose the concrete edit + the witness that proves it (translated-size delta /
81
+ // lab gate). Worktree isolation so parallel cut proposals do not collide. This workflow PROPOSES +
82
+ // verifies cuts; the human applies + batches the terrain.glsl edits into one reload (cold-compile cost).
83
+ const cuts = await pipeline(
84
+ ranking.ranked.sort((a, b) => a.rank - b.rank),
85
+ sec => agent(
86
+ `Propose the concrete code edit for TV8 startup section "${sec.name}" (expected saving ${sec.expectedSaving}). ` +
87
+ `Give the exact file, the old snippet, the new snippet, and the WITNESS that proves it (translated-HLSL ` +
88
+ `size delta via WEBGL_debug_shaders, OR a lab gate maxElev/landFrac/hypsometry unchanged, OR node --check). ` +
89
+ `Gate: ${sec.gate}. Do not break the lit/shape path; if risk is high, say so and require the lab gate.`,
90
+ { label: `cut:${sec.name}`, phase: 'Cut', schema: {
91
+ type: 'object',
92
+ properties: {
93
+ file: { type: 'string' }, oldSnippet: { type: 'string' }, newSnippet: { type: 'string' },
94
+ witness: { type: 'string' }, safe: { type: 'boolean' }, notes: { type: 'string' },
95
+ }, required: ['file', 'witness', 'safe'] } }
96
+ ),
97
+ (proposal, sec) => agent(
98
+ `Adversarially verify this TV8 startup cut proposal for "${sec.name}". Could it change the rendered terrain ` +
99
+ `(shape/biome/lighting), break a uniform, or fail to compile? Default to safe=false if uncertain. ` +
100
+ `Proposal:\n${JSON.stringify(proposal, null, 2)}`,
101
+ { label: `verify:${sec.name}`, phase: 'Verify', schema: {
102
+ type: 'object',
103
+ properties: { real: { type: 'boolean' }, safe: { type: 'boolean' }, reason: { type: 'string' } },
104
+ required: ['real', 'safe', 'reason'] } }
105
+ ).then(v => ({ section: sec.name, proposal, verdict: v }))
106
+ )
107
+
108
+ const safe = cuts.filter(Boolean).filter(c => c.proposal.safe && c.verdict.safe)
109
+ const risky = cuts.filter(Boolean).filter(c => !(c.proposal.safe && c.verdict.safe))
110
+ return {
111
+ attribution: ranking.summary,
112
+ ranked: ranking.ranked,
113
+ safeCuts: safe.map(c => ({ section: c.section, file: c.proposal.file, witness: c.proposal.witness })),
114
+ gatedCuts: risky.map(c => ({ section: c.section, reason: c.verdict.reason })),
115
+ note: 'Batch all terrain.glsl edits into ONE reload (each shader edit = a fresh cold compile). ' +
116
+ 'Validate via translated-size delta + lab gates; live glError/initTimings via a user hard-reload.',
117
+ }
@@ -0,0 +1,43 @@
1
+ name: publish
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+
7
+ jobs:
8
+ publish:
9
+ runs-on: ubuntu-latest
10
+ permissions:
11
+ contents: write
12
+ id-token: write
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+ - uses: actions/setup-node@v4
16
+ with:
17
+ node-version: 22
18
+ registry-url: https://registry.npmjs.org
19
+ - run: npm version patch --no-git-tag-version
20
+ - run: npm publish --provenance --access public
21
+ env:
22
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
23
+ - uses: stefanzweifel/git-auto-commit-action@v5
24
+ with:
25
+ commit_message: 'chore: bump version [skip ci]'
26
+ branch: main
27
+
28
+ pages:
29
+ runs-on: ubuntu-latest
30
+ permissions:
31
+ pages: write
32
+ id-token: write
33
+ environment:
34
+ name: github-pages
35
+ url: ${{ steps.deployment.outputs.page_url }}
36
+ steps:
37
+ - uses: actions/checkout@v4
38
+ - uses: actions/configure-pages@v5
39
+ - uses: actions/upload-pages-artifact@v3
40
+ with:
41
+ path: .
42
+ - id: deployment
43
+ uses: actions/deploy-pages@v4