Compare commits

..
Author SHA1 Message Date
jarvis-at-skic 9ac6e2bc6f Merge pull request 'docs: concrete workflow and skill metrics on slide 14' (#46) from docs/slide14-concrete-metrics into main
Build and publish Eldorado documentation / build (push) Successful in 44s
2026-10-09 00:13:50 -07:00
jarvis-at-skic 946b6f1cbb docs: give slide 14 concrete workflow and skill metrics
Build and publish Eldorado documentation / build (pull_request) Successful in 46s
2026-10-09 07:08:51 +00:00
jarvis-at-skic accca0cb1f Merge pull request 'fix: clarify slide 13 weekly allocation' (#45) from fix/slide13-weekly-allocation into main
Build and publish Eldorado documentation / build (push) Successful in 48s
2026-10-08 23:54:18 -07:00
jarvis-at-skic b33b59659c fix: label slide 13 as an example weekly allocation
Build and publish Eldorado documentation / build (pull_request) Successful in 47s
2026-10-09 06:49:30 +00:00
jarvis-at-skic 86b1c769a3 Merge pull request 'fix: refine presentation slides 2, 5 and 7' (#44) from fix/slide-2-headings into main
Build and publish Eldorado documentation / build (push) Successful in 46s
2026-10-08 23:37:45 -07:00
jarvis-at-skic 499049eff3 fix: refine presentation slides 2, 5 and 7
Build and publish Eldorado documentation / build (pull_request) Successful in 56s
2026-10-09 06:32:21 +00:00
jarvis-at-skic 46bb8b2b48 Merge pull request 'fix: restore complete Strategy narrative in presentation' (#43) from fix/full-strategy-presentation into main
Build and publish Eldorado documentation / build (push) Successful in 52s
2026-10-08 12:38:16 -07:00
6 changed files with 113 additions and 17 deletions

No files matched your search

@@ -21,7 +21,7 @@ export const slides = [
"id": "starting-point",
"title": "We are not starting from “no AI”.",
"minutes": 2,
"body": "<div class=\"facts\"><div><strong>≈80 → ≈100</strong><span>engineers, by year end</span></div><div><strong>&gt;10</strong><span>teams</span></div><div><strong>Already using AI</strong><span>uneven depth and quality</span></div></div><div class=\"contrast\"><p><b>Assignment facts</b><br>Solo architect → CTO<br>No formal authority over teams</p><p><b>Unknown until discovery</b><br>Stack, tools, process, baselines<br>Capacity and actual bottlenecks</p></div>",
"body": "<div class=\"facts\"><div><strong>≈80 → ≈100</strong><span>engineers, by year end</span></div><div><strong>&gt;10</strong><span>teams</span></div><div><strong>Already using AI</strong><span>uneven depth and quality</span></div></div><div class=\"contrast\"><p>Solo architect → CTO<br>No formal authority over teams</p><p>Stack, tools, process, baselines<br>Capacity and actual bottlenecks</p></div>",
"notes": "These headcount and usage facts are from the assignment, not independent research. The comparison is existing AI-assisted practice, never an invented no-AI baseline. Delivery is a starting hypothesis, not a finding. The documented departmental model is not evidence of actual assignment staffing. Ask about stack and tool/data restrictions in discovery; do not assume familiar clients are in use. Every timeline and proposed allocation in this deck remains subject to agreement.",
"sources": [
"PDF: Context; question 1; final assumptions instruction",
@@ -63,7 +63,7 @@ export const slides = [
"id": "contributors",
"title": "People contribute throughout the cycle.",
"minutes": 1,
"body": `<p class="diagram-intro">Eight separate AI Agents, each human-controlled.</p>` + contributionsFigure + `<div class="rail">Dashed ⇢ contribution from outside the cycle.</div>`,
"body": `<p class="diagram-intro">Eight separate AI Agents, each human-controlled.</p>` + contributionsFigure + `<p class="small">AI Agent = Claude, Cline, ChatGPT or a similar tool, used under human control.</p><div class="rail">Dashed ⇢ contribution from outside the cycle.</div>`,
"notes": "Keep the previous cycle in mind: these eight pairs are its contribution layer, not a second lifecycle or eight central coordinators. Each separate human-controlled AI Agent contributes to its corresponding phase from outside the cycle. The central autonomous agent remains connected to every phase, executing and governing under human direction. Splitting the original topology into two presentation-sized views preserves all eight lifecycle arrows, eight governance spokes and eight contribution arrows without miniaturizing seventeen nodes. Laptop client choice and adapters are implementation options, not a promise of installed support. Human review, product and risk decisions remain necessary.",
"sources": [
"target-operating-model"
@@ -90,7 +90,7 @@ export const slides = [
"id": "learning-loop",
"title": "Connected skills. A repeating learning loop.",
"minutes": 2,
"body": "<div class=\"learning-loop\"><span>Understand work</span><i>→</i><span>Measure + baseline</span><i>→</i><span>Engineer topology</span><i>↓</i><span>Reassess</span><i>←</i><span>Collect results</span><i>←</i><span>Use in real work</span></div><div class=\"rail\">↻ Repeat within each phase. Not six global rollout stages.</div><p class=\"statement\">Triggers → skills → handoffs → acceptance.</p><p class=\"small\">Relationships and cooperation—not merely a catalogue.</p>",
"body": "<div class=\"learning-loop\"><span>Understand work</span><i>→</i><span>Measure + baseline</span><i>↓</i><span>Engineer topology</span><i>→</i><span>Use in real work</span><i>↓</i><span>Collect results</span><i>→</i><span>Reassess</span></div><div class=\"rail\">↻ Repeat within each phase. Not six global rollout stages.</div><p class=\"statement\">Triggers → skills → handoffs → acceptance.</p><p class=\"small\">Relationships and cooperation—not merely a catalogue.</p>",
"notes": "Within every adopted phase repeat the loop: understand the current situation, select relevant metrics and baseline, engineer connected skills, use them in real work, collect outcomes/friction/feedback, then reassess and repeat. Topology defines what exists, how skills relate, when each triggers and how they cooperate. In Delivery trace codebase principles through system logic, tests, builds/packaging and deployment before designing that topology. A code-change skill may hand an exact patch and regression evidence to independent review; its trigger is an eligible change, not an ambient right to act. Governance and Measurement support the whole loop. Qualify revisions before use, and reconsider simpler interventions whenever the evidence says they fit better.",
"sources": [
"roadmap",
@@ -172,7 +172,7 @@ export const slides = [
"id": "capacity",
"title": "One architect. One active live pilot.",
"minutes": 1,
"body": "<div class=\"capacity\" aria-label=\"Proposed weekly allocation: two days hands-on, one evaluation, one relationships, one support and decisions\"><div class=\"pair\">2 days<br><b>Pair + build</b></div><div>1 day<br><b>Evaluate</b></div><div>1 day<br><b>Relationships</b></div><div>1 day<br><b>Decisions + buffer</b></div></div><p class=\"statement\">Support is work.<br>Put it in the budget.</p><p class=\"small\">Proposed capacity envelope—not known company availability.</p>",
"body": "<p>Example weekly allocation for the AI architect</p><div class=\"capacity\" aria-label=\"Proposed weekly allocation: two days hands-on, one evaluation, one relationships, one support and decisions\"><div class=\"pair\">2 days<br><b>Pair + build</b></div><div>1 day<br><b>Evaluate</b></div><div>1 day<br><b>Relationships</b></div><div>1 day<br><b>Decisions + buffer</b></div></div><p class=\"statement\">Support is work.<br>Put it in the budget.</p><p class=\"small\">Proposed capacity envelope—not known company availability.</p>",
"notes": "This five-day allocation is a planning envelope, not a claim about company staffing. Two days are hands-on, one evaluation, one coaching/relationships, one sponsor decisions, support and contingency. Team lead agreement must include review time. A second team begins only when the first is supportable. Offer reusable examples and bounded office hours, not a permanent help desk to more than ten teams. Excess support demand becomes a CTO investment decision or a smaller scope.",
"sources": [
"six-months-roadmap"
@@ -183,13 +183,19 @@ export const slides = [
},
{
"id": "measures",
"title": "Three measures. One decision.",
"title": "Measure better work—and skill health",
"minutes": 2,
"body": "<div class=\"measure-list\"><div><b>Accepted work</b><span>Accepted / all matured eligible starts</span></div><div><b>Review + rework</b><span>Comparable effort, including shifted burden</span></div><div><b>Escaped defects</b><span>Severity and complete follow-up</span></div></div><div class=\"rail\">Alongside: setup, support, upkeep and tool cost. Critical effects stop work.</div><p class=\"small\">Task / PR / CI / defect links + a small effort record; no individual league tables.</p>",
"notes": "Use a small balanced set matched to the mechanism, not all sixteen optional metric contracts. Predeclare eligibility, maturity horizons, denominator and follow-up. Register every eligible task start, including failures, abandoned work and unresolved matured starts; pending tasks stay separate. Link tasks to exact PR/CI acceptance and defect records, with a minimal effort log. Record checked reasons for returns, such as missing regression evidence. Agree access, purpose and retention; avoid raw prompt collection and individual performance ranking. Show total setup and recurring cost beside outcomes. If linkage is incomplete, hold the claim.",
"body": "<div class=\"measurement-panels\"><div class=\"workflow-panel\"><h3>Is the work improving?</h3><p class=\"metric-context\">Baseline → pilot · like-for-like cohorts</p><div class=\"metric\"><b>Validated task success (%)</b><span>Independently accepted tasks ÷ all matured eligible starts.</span></div><div class=\"metric\"><b>Changes with escaped quality issues (%)</b><span>Changes with confirmed defects ÷ accepted changes with completed follow-up.</span></div><div class=\"metric\"><b>Cost per accepted outcome <em>(currency/outcome)</em></b><span>Allocated cohort cost, including failures and review/rework ÷ independently accepted tasks.</span></div></div><div class=\"skill-panel\"><h3>Are skills fit and maintained?</h3><p class=\"metric-context\">Separate qualification + catalog populations</p><div class=\"metric\"><b>Skill selection precision (%)</b><span>Correct activations ÷ all scored activations in labeled evaluation.</span></div><div class=\"metric\"><b>Skills within substantive review window (%)</b><span>Owned, reviewed, not-overdue revisions ÷ all active approved revisions.</span></div><p class=\"qualification-note\">Independent quality qualification still required.</p></div></div><p class=\"measurement-caution\">Choose relevant measures · Unknown is not zero<br>Skill health is not productivity proof.</p>",
"notes": "Choose a small relevant subset from the sixteen optional contracts, not a requirement to collect all five shown here or all sixteen. These are candidate definitions, not actual company values or installed telemetry: task success, escaped quality, cost and precision have prototype calculations without network collectors; substantive-review coverage is unimplemented. Workflow only: compare like-for-like periods/cohorts with the same eligibility, task mix, observation and follow-up horizons, acceptance rubric and source coverage; record staffing, model/client and concurrent process changes. Before/after differences are not causal ROI. Validated task success is 100 times distinct independently accepted tasks by as_of divided by all eligible starts whose preregistered observation horizon has matured. Count each task once across retries, clients and skills. Failures, abandonment and unresolved matured starts remain in the denominator; unknown outcomes are not labeled failures. Exclude pre-start cancellations, sandbox/evaluation and out-of-scope tasks; flag invalid/missing starts and show unknown counts and coverage. Escaped quality is 100 times accepted changes with at least one linked confirmed post-acceptance defect during approved follow-up divided by accepted changes whose predeclared follow-up is complete. Count affected changes, not raw defects; separate severity and absolute defect/severe-incident counts. Show immature changes and pending triage separately; missing linkage or incomplete follow-up is not a clean result. Cost divides allocated costs for every task/attempt in the matured start cohort through as_of, including failed and abandoned attempts and shared period costs, by distinct independently accepted tasks in that same cohort. Include licenses, compute, tooling, setup, qualification, onboarding, support, upkeep and human quality review/rework effort at approved rates without double counting; exclude unrelated projects and documented accounting-basis exclusions. Use one approved reporting currency or dated approved conversion. Missing cost buckets mean a partial estimate with coverage, not total cost; zero accepted outcomes means null with full cost shown. Restate late invoices. Skill selection precision is 100 times true-positive activations divided by true-positive plus false-positive activations in candidate/client evaluation trials with independently labeled relevance and known activation. Freeze the corpus, retain negative/failed trials; unknown activation/relevance is unscorable, reported with coverage and blocks qualification if planned coverage is incomplete. This does not measure artifact success or live workflow acceptance. Substantive-review coverage is 100 times active approved revisions with current owner, linked substantive review evidence and review_due_at at or after as_of divided by all active approved revisions in the catalog snapshot. Experimental/retired revisions are excluded; report deprecated/revoked-in-use and ownerless counts separately. Missing owner/date/evidence stays outside the numerator. Review evidence includes regression, security and usefulness, not a rubber-stamped date. Independent Quality qualification still tests artifact fitness, safe execution and revision regression. Keep qualification trials and live workflow starts separate; never pool their denominators. No applicable skill population is N/A, not a baseline zero; missing evidence for an existing population is unknown, not good health. Predeclared live skill/version slices may reuse the workflow contract unchanged, without summing overlapping slices. Agree access, purpose and retention; link tracker, PR/CI, defect, cost and minimal effort evidence without individual rankings. If evidence is incomplete, limit or hold the claim; critical policy effects stop work regardless of averages.",
"sources": [
"measurement",
"six-months-roadmap"
"six-months-roadmap",
"measurement/validated-task-success",
"measurement/changes-with-escaped-quality-issues",
"measurement/cost-per-accepted-outcome",
"measurement/skill-selection-precision",
"measurement/skills-within-substantive-review-window",
"skills-quality"
],
"kind": "",
"section": "6 Months Roadmap",
@@ -85,15 +85,24 @@ html.print-pdf .toolbar{display:none}html.print-pdf .reveal.deck{top:0;height:10
.deck .journey small,.deck .horizon small{display:block;font-size:17px;color:#805b00;font-weight:900;letter-spacing:.04em;margin-bottom:14px}
.deck .journey b{font-size:31px;display:block;margin-bottom:20px}
.deck .journey span{font-size:25px}.deck .journey i{font-style:normal}
.deck .learning-loop{display:grid;grid-template-columns:1fr 40px 1fr 40px 1fr;align-items:center;gap:12px;margin:32px 0 22px}
.deck .learning-loop span{border:2px solid #14131a;padding:22px 10px;font-size:27px;text-align:center}
.deck .learning-loop i{font-style:normal;text-align:center}.deck .learning-loop i:nth-child(6){grid-column:5}
.deck .learning-loop{display:grid;grid-template-columns:1fr 28px 1fr 28px 1fr 28px 1fr;align-items:center;gap:0 8px;margin:22px 0}
.deck #learning-loop .statement{margin:16px 0 0}.deck #learning-loop .small{margin-top:12px}
.deck .learning-loop span{border:2px solid #14131a;padding:8px;font-size:27px;text-align:center}
.deck .learning-loop i{font-style:normal;text-align:center;line-height:1}
.deck .learning-loop>:nth-child(1){grid-area:1/1}.deck .learning-loop>:nth-child(2){grid-area:1/2}.deck .learning-loop>:nth-child(3){grid-area:1/3}
.deck .learning-loop>:nth-child(4){grid-area:2/3}
.deck .learning-loop>:nth-child(5){grid-area:3/3}.deck .learning-loop>:nth-child(6){grid-area:3/4}.deck .learning-loop>:nth-child(7){grid-area:3/5}
.deck .learning-loop>:nth-child(8){grid-area:4/5}
.deck .learning-loop>:nth-child(9){grid-area:5/5}.deck .learning-loop>:nth-child(10){grid-area:5/6}.deck .learning-loop>:nth-child(11){grid-area:5/7}
.deck .horizon{margin:35px 0;border-left:5px solid #ffc950;padding-left:25px}
.deck .horizon b{font-size:32px;display:block}.deck .horizon .near{background:#e9e9ec;padding:22px;margin-top:28px}
.deck .horizon span{display:block;font-size:25px;margin-top:14px}
.portrait .deck .target-cycle.wide{display:none}.portrait .deck .target-cycle.phone{display:block;height:540px}
.portrait .deck #target h2{font-size:40px}
.portrait .deck #target .small{font-size:24px;margin-top:20px}
.portrait .deck #contributors .small{margin:12px 0}
.portrait .deck #contributors .contribution-grid{gap:6px;margin:14px 0}
.portrait .deck #contributors .contribution-grid>div{padding:4px 8px}
.portrait .deck .contribution-grid{grid-template-columns:1fr 1fr;gap:12px;margin:24px 0}
.portrait .deck .contribution-grid>div{padding:8px;flex-direction:column;justify-content:center;gap:0;font-size:27px}
.portrait .deck .contribution-grid i{transform:rotate(90deg)}.portrait .deck .contribution-grid b{text-align:center}
@@ -101,6 +110,32 @@ html.print-pdf .toolbar{display:none}html.print-pdf .reveal.deck{top:0;height:10
.portrait .deck .journey>div{width:100%;padding:12px 0;border-top-width:4px}
.portrait .deck .journey b{font-size:29px;margin-bottom:8px}.portrait .deck .journey small{margin-bottom:8px}
.portrait .deck .journey i{transform:rotate(90deg);line-height:1}
.portrait .deck .learning-loop{grid-template-columns:1fr 28px 1fr 28px 1fr;gap:6px;margin:30px 0}
.portrait .deck .learning-loop span{font-size:24px;padding:20px 4px}
.portrait .deck .learning-loop{grid-template-columns:1fr 16px 1fr 16px 1fr 16px 1fr;gap:0 3px;margin:30px 0}
.portrait .deck .learning-loop span{font-size:24px;padding:14px 2px}
.portrait .deck .horizon b{font-size:32px}
/* Slide 14: outcomes lead; skill indicators remain a separate population. */
.deck .slides #measures{padding-top:32px}
.deck #measures h2{font-size:48px;margin-bottom:20px}
#measures .measurement-panels{display:grid;grid-template-columns:1.3fr 1fr;gap:24px}
#measures .workflow-panel,#measures .skill-panel{padding:16px 22px;font-size:21px;line-height:1.3;background:#e9e9ec;border-top:6px solid var(--gold)}
#measures .skill-panel{background:#eeedf0;border-top-color:#8b8994}
#measures h3{font-size:28px;line-height:1.15;letter-spacing:-.025em;font-weight:900;margin:0;text-transform:none;color:var(--ink)}
.deck #measures .metric-context{font-size:19px;line-height:1.3;margin:8px 0 12px;color:#62616b}
#measures .metric{border-top:1px solid #bbb9c2;padding:10px 0 0;margin-top:10px}
#measures .metric b{display:block;font-size:25px;line-height:1.2}
#measures .metric em{font-style:normal;font-size:20px;white-space:nowrap}
#measures .metric span{display:block;font-size:21px;line-height:1.3;margin-top:7px}
.deck #measures .qualification-note{border-left:4px solid var(--gold);padding-left:12px;font-size:21px;font-weight:700;line-height:1.25;margin:22px 0 0}
.deck #measures .measurement-caution{font-size:20px;line-height:1.4;color:#62616b;margin:16px 0 0}
.portrait .deck .slides #measures{padding-top:20px}
.portrait .deck #measures h2{font-size:38px;margin:12px 0 14px}
.portrait #measures .measurement-panels{grid-template-columns:1fr;gap:12px}
.portrait #measures .workflow-panel,.portrait #measures .skill-panel{padding:12px 16px}
.portrait #measures h3{font-size:29px}
.portrait .deck #measures .metric-context{font-size:20px;margin:6px 0 8px}
.portrait #measures .metric{padding-top:4px;margin-top:4px}
.portrait #measures .metric b{font-size:26px}
.portrait #measures .metric span{font-size:22px;line-height:1.2;margin-top:5px}
.portrait .deck #measures .qualification-note{font-size:22px;margin-top:12px}
.portrait .deck #measures .measurement-caution{font-size:21px;margin-top:12px}
+4 -2
View File
@@ -1,4 +1,5 @@
const {chromium}=require('playwright');
const staircaseFits=require('./presentation-staircase.cjs');
const assert=require('node:assert/strict');const fs=require('node:fs');
(async()=>{
const base=process.argv[2]||'http://127.0.0.1:43853';const root=base+'/eldorado-v1/documentation/';
@@ -21,8 +22,9 @@ const assert=require('node:assert/strict');const fs=require('node:fs');
const visited=[];
for(let i=0;i<23;i++){
assert.equal(await f.evaluate(()=>Reveal.getIndices().h),i);visited.push(await f.evaluate(()=>Reveal.getCurrentSlide().id));
const bad=await f.evaluate(()=>{const r=Reveal.getCurrentSlide().getBoundingClientRect();return [...Reveal.getCurrentSlide().querySelectorAll('svg,text,.journey,.learning-loop,.contribution-grid,h1,h2,p,li,table,.flow,.roles,.timeline')].filter(e=>!e.closest('.notes')&&e.getBoundingClientRect().width>0).filter(e=>{const b=e.getBoundingClientRect();return b.bottom>r.bottom+1||b.right>r.right+1||b.left<r.left-1}).map(e=>e.textContent)});assert.deepEqual(bad,[]);
if(i===4)assert(await f.evaluate(()=>[...document.querySelectorAll('#contributors .contribution-grid>div')].every(e=>{const a=e.querySelector('span').getBoundingClientRect(), arrow=e.querySelector('i').getBoundingClientRect(), b=e.querySelector('b').getBoundingClientRect();return a.bottom<=arrow.top+2&&arrow.bottom<=b.top+2&&Math.abs((a.left+a.right-b.left-b.right)/2)<2&&getComputedStyle(e.querySelector('i')).transform==='matrix(0, 1, -1, 0, 0, 0)'})),'contribution arrows must point down to their own phase');
const bad=await f.evaluate(()=>{const r=Reveal.getCurrentSlide().getBoundingClientRect();return [...Reveal.getCurrentSlide().querySelectorAll('.measurement-panels,.metric,.metric b,.metric span,.qualification-note,h3,svg,text,.journey,.learning-loop,.contribution-grid,h1,h2,p,li,table,.flow,.roles,.timeline')].filter(e=>!e.closest('.notes')&&e.getBoundingClientRect().width>0).filter(e=>{const b=e.getBoundingClientRect();return b.bottom>r.bottom+1||b.right>r.right+1||b.left<r.left-1}).map(e=>e.textContent)});assert.deepEqual(bad,[]);
if(i===6)assert(await f.evaluate(staircaseFits),'staircase aligns each arrow with its intended steps');
if(i===4)assert(await f.evaluate(()=>[...document.querySelectorAll('#contributors .contribution-grid>div')].every(e=>{const a=e.querySelector('span').getBoundingClientRect(), arrow=e.querySelector('i').getBoundingClientRect(), b=e.querySelector('b').getBoundingClientRect();return a.bottom<=arrow.top+2&&arrow.bottom<=b.top+2&&Math.abs((a.left+a.right-b.left-b.right)/2)<2&&getComputedStyle(e.querySelector('i')).transform==='matrix(0, 1, -1, 0, 0, 0)'})),'contribution arrows must point down to their own phase');
await iframe.screenshot({path:`${out}/${viewport.width}-${i+1}.png`});
if(i<22)await p.keyboard.press('ArrowRight');
}
@@ -0,0 +1,14 @@
// Browser-side geometry contract, shared by standalone and embedded acceptance.
module.exports = function staircaseFits() {
const els=[...document.querySelectorAll('#learning-loop .learning-loop>*')];
const b=els.map(e=>e.getBoundingClientRect());
const center=r=>({x:(r.left+r.right)/2,y:(r.top+r.bottom)/2});
const close=(a,b)=>Math.abs(a-b)<2;
return [[0,1,2],[4,5,6],[8,9,10]].every(([a,arrow,c])=>
close(center(b[a]).y,center(b[c]).y)&&close(center(b[a]).y,center(b[arrow]).y)&&
b[a].right<=b[arrow].left+2&&b[arrow].right<=b[c].left+2
)&&[[2,3,4],[6,7,8]].every(([a,arrow,c])=>
close(center(b[a]).x,center(b[c]).x)&&close(center(b[a]).x,center(b[arrow]).x)&&
b[a].bottom<=b[arrow].top+2&&b[arrow].bottom<=b[c].top+2
)&&els.filter(e=>e.tagName==='SPAN').every(e=>e.scrollWidth<=e.clientWidth+1);
};
+4 -2
View File
@@ -1,4 +1,5 @@
const {chromium}=require('playwright');
const staircaseFits=require('./presentation-staircase.cjs');
const fs=require('node:fs'); const assert=require('node:assert/strict');
(async()=>{
const base=process.argv[2]||'http://127.0.0.1:4179'; const root='/eldorado-v1/documentation/';const url=base+root+'presentations/less-prompting-more-shipping/';
@@ -18,9 +19,10 @@ const fs=require('node:fs'); const assert=require('node:assert/strict');
await page.evaluate(i=>Reveal.slide(i),i);await page.waitForTimeout(60);
const bad=await page.evaluate(()=>{
const slide=Reveal.getCurrentSlide(),r=slide.getBoundingClientRect();
return [...slide.querySelectorAll('svg,text,.journey,.learning-loop,.contribution-grid,h1,h2,p,li,td,th,.rail,.gate,.facts,.flow,.roles,.timeline,.capacity,.measure-list,.contrast,.comparison,.choices,.ledger')].filter(e=>!e.closest('.notes')&&e.getBoundingClientRect().width>0).filter(e=>{const b=e.getBoundingClientRect();return b.bottom>r.bottom+1||b.right>r.right+1||b.left<r.left-1||b.top<r.top-1}).map(e=>e.textContent.slice(0,60));
return [...slide.querySelectorAll('.measurement-panels,.metric,.metric b,.metric span,.qualification-note,h3,svg,text,.journey,.learning-loop,.contribution-grid,h1,h2,p,li,td,th,.rail,.gate,.facts,.flow,.roles,.timeline,.capacity,.measure-list,.contrast,.comparison,.choices,.ledger')].filter(e=>!e.closest('.notes')&&e.getBoundingClientRect().width>0).filter(e=>{const b=e.getBoundingClientRect();return b.bottom>r.bottom+1||b.right>r.right+1||b.left<r.left-1||b.top<r.top-1}).map(e=>e.textContent.slice(0,60));
});
if(i===4)assert(await page.evaluate(()=>[...document.querySelectorAll('#contributors .contribution-grid>div')].every(e=>{const a=e.querySelector('span').getBoundingClientRect(), arrow=e.querySelector('i').getBoundingClientRect(), b=e.querySelector('b').getBoundingClientRect();return a.bottom<=arrow.top+2&&arrow.bottom<=b.top+2&&Math.abs((a.left+a.right-b.left-b.right)/2)<2&&getComputedStyle(e.querySelector('i')).transform==='matrix(0, 1, -1, 0, 0, 0)'})),'contribution arrows must point down to their own phase');
if(i===6)assert(await page.evaluate(staircaseFits),'staircase aligns each arrow with its intended steps');
if(i===4)assert(await page.evaluate(()=>[...document.querySelectorAll('#contributors .contribution-grid>div')].every(e=>{const a=e.querySelector('span').getBoundingClientRect(), arrow=e.querySelector('i').getBoundingClientRect(), b=e.querySelector('b').getBoundingClientRect();return a.bottom<=arrow.top+2&&arrow.bottom<=b.top+2&&Math.abs((a.left+a.right-b.left-b.right)/2)<2&&getComputedStyle(e.querySelector('i')).transform==='matrix(0, 1, -1, 0, 0, 0)'})),'contribution arrows must point down to their own phase');
bounds.push({slide:i+1,overflow:bad});
await page.screenshot({path:`${out}/${viewport.width}-slide-${String(i+1).padStart(2,'0')}.png`});
}
+37
View File
@@ -14,6 +14,43 @@ test('deck preserves assignment coverage, 35+15 timing and traceable notes',()=>
for(const id of ['experiment']) assert.match(slides.find(s=>s.id===id).body,/fictional/i);
assert.match(slides.find(s=>s.id==='qualification').notes,/separately authorizes/);
});
test('starting point keeps content without the two removed headings',()=>{
const body=slides.find(s=>s.id==='starting-point').body;
assert.doesNotMatch(body,/Assignment facts|Unknown until discovery/);
for(const text of ['Solo architect → CTO','No formal authority over teams','Stack, tools, process, baselines','Capacity and actual bottlenecks'])assert(body.includes(text));
assert.doesNotMatch(body,/<p><br>/);
});
test('contributors explains human-controlled AI tools below the diagram',()=>{
const body=slides.find(s=>s.id==='contributors').body;
const definition='AI Agent = Claude, Cline, ChatGPT or a similar tool, used under human control.';
assert(body.includes(definition));
assert(body.indexOf(definition)>body.indexOf('contribution-grid'));
assert(body.includes('Dashed ⇢ contribution from outside the cycle.'));
});
test('capacity visibly frames the days as an example weekly allocation',()=>{
const slide=slides.find(s=>s.id==='capacity');
assert.equal(slide.title,'One architect. One active live pilot.');
assert(slide.body.startsWith('<p>Example weekly allocation for the AI architect</p><div class="capacity"'));
for(const text of ['2 days','Pair + build','Evaluate','Relationships','Decisions + buffer','Proposed capacity envelope—not known company availability.']) assert(slide.body.includes(text));
});
test('measurement names five numerical candidates and separates populations',()=>{
const s=slides.find(s=>s.id==='measures');
assert.equal(s.minutes,2);
assert.equal(s.title,'Measure better work—and skill health');
for(const label of ['Validated task success (%)','Changes with escaped quality issues (%)','Cost per accepted outcome','currency/outcome','Skill selection precision (%)','Skills within substantive review window (%)','Baseline → pilot','Independent quality qualification still required','Choose relevant measures','Unknown is not zero','Skill health is not productivity proof']) assert(s.body.includes(label),label);
assert.equal((s.body.match(/class="metric"/g)||[]).length,5);
for(const text of ['matured eligible starts','completed follow-up','labeled evaluation','active approved revisions'])assert(s.body.includes(text),text);
for(const name of ['validated-task-success','changes-with-escaped-quality-issues','cost-per-accepted-outcome','skill-selection-precision','skills-within-substantive-review-window'])assert(s.sources.includes('measurement/'+name));
for(const term of ['N/A','unknown','unimplemented','partial','review/rework','like-for-like','zero accepted'])assert(s.notes.includes(term),term);
assert.doesNotMatch(s.body,/Accepted work<|\d+%/);
});
test('learning loop staircase preserves the original semantic sequence',()=>{
const body=slides.find(s=>s.id==='learning-loop').body;
const loop=body.split('</div>')[0];
assert.deepEqual([...loop.matchAll(/<span>(.*?)<\/span>/g)].map(m=>m[1]),['Understand work','Measure + baseline','Engineer topology','Use in real work','Collect results','Reassess']);
assert.deepEqual([...loop.matchAll(/<i>(.*?)<\/i>/g)].map(m=>m[1]),['→','↓','→','↓','→']);
assert(body.includes('↻ Repeat within each phase.'));
});
test('build emits local pinned Reveal assets, stable IDs and no production CDN',()=>{
execFileSync(process.execPath,['scripts/build-presentations.mjs']);
const dir='static/presentations/less-prompting-more-shipping'; const html=read(`${dir}/index.html`);