For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
1Context
On 2026-10-19 Fadel M. Megahed printed the Step 6 report to PDF (replication_materials/findings/2026-10-18-report-print-before.pdf) and found it poor. His items: (a) each section starts a new page; (b) the request shows twice, the second time after "You:"; (c) the aliasing is one flat list, and the sentence "No main effect is aliased with a 2FI." is a bullet; (d) it is not clear how a run sheet with many factors or many runs prints; (e) the print needs a header and a footer with EXPERTA, its icon, the date and the version, if the HTML view stays as it is; (f) the citation needs a link to the app, in APA and in BibTeX form; (g) the report could say how the specification was made; (h) the report could say that his professorship pays for the LLM calls.
His answers on 2026-10-19: (g) yes, a short section; (h) an acknowledgment without an amount; (d) print up to a limit, with the limit set by the user, and a better HTML view of long run sheets that does not slow the page or harm the print; blank Response and Notes columns in the printed run sheet.
How the report works today: reportHtml() (lib/exports/report-html.ts) makes one HTML document with inline CSS and no script (decision C11). Step 6 shows it in a sandboxed frame (sandbox="allow-same-origin allow-modals", no scripts), "Report (.html)" downloads the same text, and "Print / PDF" prints the frame. The only print rules are h2 { break-before: page }, a repeated table header and rows that do not split.
Goal: The printed report reads as a short technical document: sections flow, the request shows once with its clarification rounds, the aliasing has structure, wide and long run sheets print in a controlled way with bench columns, every page has a running header and footer in Chromium, the citation comes in APA and BibTeX with the app link, and a new section records how the specification was made.
Architecture: The report stays one HTML file with inline CSS and no script. A new module lib/exports/report-css.ts holds the CSS and the measured print constants. Print-only parts (column blocks, bench columns, the row limit) are present in the HTML and shown only by @media print; the screen view gets a scroll box with a fixed header row. The card keeps the model ids and the prompt and schema versions of the translation, with defaults, so stored projects still read. A CSS-only toggle (radio inputs) switches the citation form, because no script runs in the frame.
Tech Stack: Next.js 16.3.8, React 19.3.0, TypeScript 5.9.3, zod 4.6.5, vitest 4.1.11, Playwright 1.63.0 (Chromium, page.pdf() for the print measurements). No new package.
Spec: replication_materials/plans/2026-10-14-ff-design-translator-design.md (version 6; Task 8 makes version 7), Sections 7.5, 8.1, 8.3 and 9.3; decision C11 (one HTML file, inline CSS, no script, no external resource).
2Decisions
| # | Decision | By |
|---|---|---|
| R1 | No forced page break. Sections flow; a heading stays with the text after it; a small table, a code block and an alias group do not split across pages. | FMM (a) |
| R2 | The Request section shows the request text once. The turns after the first (the clarification rounds) follow under "Clarification", as "Question from EXPERTA" and "Answer". | FMM (b) |
| R3 | Aliasing has two parts, "Main effects" and "Two-factor interactions". Each starts with its summary sentence as a paragraph, without "Example: ...", because the report lists every group. The groups are a numbered list in columns of at least 20rem. | FMM (c) |
| R4 | No JavaScript table package. The report has no script (C11), and the frame does not run scripts. A paged or virtual table shows only some rows, so the print would lose rows unless the package had its own print mode, and the package would have to be inlined into each report file. The screen view uses CSS only: a scroll box (at most 75vh), a sticky header row, a sticky Run column, alternate row shading, and the rows in groups of 256, each group with content-visibility: auto so the browser skips the layout of groups out of view. Task 1 measures that the print still shows every group and that the view of 32,768 rows is not slower. If either fails, the groups stay and content-visibility goes. |
Claude; FMM asked (d) |
| R5 | Print row limit, set in step 6: "Rows of the run sheet in print", with "All rows" and "First 64", "First 256", "First 1,024" (only the values below the number of rows), each with its estimated pages. Default: all rows when the sheet has at most 256 rows, else the first 256. A limited print shows a note under the table. The HTML view always has every row. | FMM (d); default by Claude |
| R6 | The run sheet prints on landscape pages (a named page). When a design has more factors than fit on one landscape page (K, measured in Task 1), the print uses column blocks: each block repeats Run and Std order. The header row repeats on each page. Blank Response and Notes columns are in print only, in the last block. | FMM (d), bench columns |
| R7 | Running header and footer from @page margin boxes, with app texts only (no user text inside CSS): top left "EXPERTA · Design report", top right the date, bottom left "expertadoe.app · version {APP_VERSION}", bottom right "Page {n} of {N}". Chromium and Edge print them; other browsers print without them. The screen view does not change. The icon is in the title block of page 1 (inline SVG, no url()). |
FMM (e) |
| R8 | The PDF file name: during print(), the app sets the page title to "{project name}: EXPERTA design report" and puts the old title back after. |
Claude |
| R9 | Citation in APA 7 and BibTeX, both with https://expertadoe.app. In the report, a CSS-only toggle; the print shows both forms and no controls. The home page gets the same two forms with a toggle. The report may contain one link, to https://expertadoe.app; no other href. |
FMM (f) |
| R10 | New section "How the specification was made", after "Specification", with its own check box in step 6. The card keeps the model id of each voter and the mode, the prompt, schema and Jev-question versions and the time of the interpretation, with defaults, so stored and imported projects still read (PROJECT_SCHEMA_VERSION stays 1). A project without these values says "Not recorded". |
FMM (g) |
| R11 | One acknowledgment line, no amount: "The API and hosting costs of EXPERTA are funded by the Raymond E. Glos Professorship in Business at Miami University, held by Fadel M. Megahed." It covers hosting too, so it is in every report (heading "Acknowledgment" in the Versions and citation part) and on the home page under "Maintained by". | FMM (h), text approved 2026-10-19 |
| R12 | Other improvements: the title block replaces the empty first page; the design notation prints in the accent colour (today it is white on a background that browsers do not print); the word length pattern is one row; "Std" becomes "Std order"; numbers use tabular digits; the Versions part is a table plus the run sheet SHA-256, and it no longer repeats the run order. | Claude |
| R13 | No change of report_version (the R report data do not change) and no app version change; the authors choose the version at release. |
Claude |
3Global Constraints
- No package changes. Implementers do not run
npm install(Dropbox). - Secrets and outside contact. Never read
.env. Subagents make no network call to Vercel, Upstash, GitHub or an LLM provider. - C11 stays. The report is one HTML document: inline
<style>, no<script>, no<link>, no@import, nourl(, and nohrefexcepthttps://expertadoe.app. Every inserted text goes throughescapeHtml(). No user text goes inside the CSS. - Same text everywhere. The frame, "Report (.html)" and the report in the zip stay byte-identical for the same contents and settings (
report.spec.tschecks it). - Texts under review stay. Do not change
content/glossary.json,data/guarantee-table.json, the R23 text or the references. - Fixed texts. Every new text is a fixed string, exported, with a unit test of its exact text; no advice words.
- Wording. About 80% ASD-STE100, plain academic tone; labels exactly as this plan gives them.
- Accessibility. The step 6 controls have visible labels; the axe sweep stays at zero violations; the citation toggle works with the keyboard.
- Measure, do not guess. Every print constant (rows per page, the number K) comes from Task 1, is stored in
config/print-layout.jsonwith its source CSV, and a test recomputes it. - Tests. All in
replication_materials/tests/. Test first.npm run check -- --fastafter each task;npm run checkafter Tasks 4, 7 and 8. - Commits. One per task, with the attribution lines of the session.
4Review Focus
- A factor name or a level with
</style>, quotes or a very long word: the report escapes it, nothing reaches the CSS, and a long word wraps in its cell, not across the page. Test in Task 2. - A project stored before this plan (no model ids in its card) and a project filled in by hand: the new section says "Not recorded" or "filled in by hand", and nothing breaks. Test in Task 5 and Task 6.
- A design with exactly K and K + 1 factors, and a sheet with exactly the limit and one row more: blocks and the note start at the right number. Test in Task 4.
- A pending clarification (the last turn is a question without an answer): it shows with "No answer yet." Test in Task 3.
- Print in a browser without margin boxes or named pages: the content still prints whole, in portrait, without the running header. Check in Task 7 (the CSS has no rule that hides content when those features are missing).
5Task 1: Print measurements
Files:
- Create:
lib/exports/report-css.ts(the print constants of R4 to R7 and the CSS text),replication_materials/tests/perf/print-layout.spec.ts,scripts/derive-print-layout.mjs,config/print-layout.json,replication_materials/tests/unit/print-layout.test.ts - Data:
replication_materials/tests/perf/print-layout.csv
Interfaces:
- Produces:
PRINT = { bodyPt: 10, tablePt: 9, cellPadding: "0.15rem 0.4rem", marginMm: { top: 16, right: 14, bottom: 18, left: 14 }, chunkRows: 256 }inreport-css.ts;config/print-layout.jsonwithrows_per_page_landscape(Letter and A4, the smaller one is used),max_factor_columns_landscape(K),content_visibility_print_ok: boolean,view_ms_withandview_ms_without(32,768 rows).printLayoutis imported from the JSON by Tasks 4 and 7.
6Task 2: Report frame, title block, running header and footer, PDF name
Files:
- Modify:
lib/exports/report-html.ts,lib/exports/report-css.ts,components/steps/ReportStep.tsx - Test:
replication_materials/tests/unit/report-html.test.ts,replication_materials/tests/e2e/report.spec.ts
Interfaces:
- Consumes:
PRINT(Task 1). - Produces:
REPORT_TITLE = "Design report";RUNNING_HEADER_LEFT = "EXPERTA · Design report";runningFooterLeft(version: string): string=expertadoe.app · version ${version};pdfTitle(projectName: string): string=${projectName}: EXPERTA design report;cssString(text: string): string(CSS string literal; accepts only[0-9A-Za-z .:·-], else throws).
7Task 3: Request, design properties and aliasing
Files:
- Modify:
lib/exports/report-html.ts,lib/report/explain.ts - Test:
replication_materials/tests/unit/report-html.test.ts,replication_materials/tests/unit/report/explain.test.ts(or the existing explain test file)
Interfaces:
- Produces:
aliasSentences(report, options?: { example: boolean })andmainAliasSentences(report, options?: { example: boolean }); the default{ example: true }keeps step 3 as it is. Texts:CLARIFICATION_HEADING = "Clarification",QUESTION_LABEL = "Question from EXPERTA",ANSWER_LABEL = "Answer",NO_ANSWER = "No answer yet.",MAIN_EFFECTS_HEADING = "Main effects",TWO_FACTOR_HEADING = "Two-factor interactions".
8Task 4: Run sheet on screen and in print
Files:
- Modify:
lib/exports/report-html.ts,lib/exports/report-css.ts,components/steps/ReportStep.tsx,lib/exports/archive.ts(it passes the same report options) - Test:
replication_materials/tests/unit/report-html.test.ts,replication_materials/tests/e2e/report.spec.ts
Interfaces:
- Consumes:
printLayout(Task 1). - Produces:
reportHtml(project, sections, now, options: ReportOptions = { printRows: null })withinterface ReportOptions { printRows: number | null }(null = all rows);printRowChoices(nrows: number): (number | null)[]= the values of[64, 256, 1024]belownrows, thennull;defaultPrintRows(nrows: number): number | null=nrows <= 256 ? null : 256;estimatedPages(rows: number): number=Math.ceil(rows / printLayout.rows_per_page_landscape). Texts:PRINT_ROWS_LABEL = "Rows of the run sheet in print",printRowsOption(rows: number | null, nrows: number): string=All rows (${nrows}, about ${p} pages)orFirst ${rows} (about ${p} pages)(numbers with thousands separators, "page" for 1),printNote(m: number, n: number): string=Rows 1 to ${m} of ${n} are printed. The full run sheet is in the .html, .csv and .xlsx files.,STD_HEADER = "Std order",RESPONSE_HEADER = "Response",NOTES_HEADER = "Notes",blockCaption(i: number, b: number, first: string, last: string): string=Run sheet, part ${i} of ${b}: ${first} to ${last}.
-
- Screen: the sheet is in
<div class="rs-scroll">; the body rows are in<tbody>groups ofPRINT.chunkRows(a 600-row sheet has 3); the CSS has the sticky header and the sticky first column for screen only. - Print, narrow (K factors): one table; each row has two empty
td.print-onlycells; the header hasResponseandNotesasth.print-only. - Print, wide (K + 1 factors): the screen table has
class="screen-only", and adiv.print-onlyholdsceil(factors / K)tables, each with Run and Std order, the captions fromblockCaption, Response and Notes only in the last. - Limit: with
printRows: 256and 257 rows, the rows after 256 haveclass="print-hidden"in the narrow table, the print blocks hold 256 rows, andprintNote(256, 257)is under the sheet in ap.print-only; withprintRows: nullthere is no note. printRowChoices(16)is[null];printRowChoices(4096)is[64, 256, 1024, null];defaultPrintRows(256)is null anddefaultPrintRows(257)is 256.- e2e: step 6 shows the select with
PRINT_ROWS_LABELonly when the sheet has more than 64 rows; its default followsdefaultPrintRows; the download still equals the frame; the zip report equals the download for the chosen value.
- Screen: the sheet is in
9Task 5: Keep the record of the interpretation in the card
Files:
- Modify:
lib/card/card.ts(VotersSchema,votersOf,cardFromTranslation),lib/projects/schema.ts(comment only), spec 8.1 - Test:
replication_materials/tests/unit/card/card.test.ts(or the existing card test file),replication_materials/tests/unit/projects/schema.test.ts(or the existing one)
Interfaces:
- Produces:
VotersSchemalines getmodel_id: z.string().nullable().default(null);VotersSchemagetsmeta: z.object({ mode: z.enum(["single", "ensemble"]), prompt_version: z.string(), schema_version: z.string(), jev_questions_version: z.string(), interpreted_at: z.string().regex(LOCAL_TIME_PATTERN) }).nullable().default(null).cardFromTranslation(answer, now, newId)fills both (the time fromisoWithOffset(now)).
10Task 6: Section "How the specification was made"
Files:
- Modify:
lib/exports/report-html.ts(REPORT_SECTIONS,SECTION_TITLES,CONTENTS_LABELS, a builder),components/spec-card/VoterSummary.tsx(its texts move to a pure function),lib/card/summary.ts(new) - Test:
replication_materials/tests/unit/report-html.test.ts,replication_materials/tests/unit/card/summary.test.ts(new),replication_materials/tests/e2e/report.spec.ts
Interfaces:
- Consumes: the card fields of Task 5.
- Produces:
voterSummary(voters: CardVoters): { heading: string; lines: string[]; jev: string | null }inlib/card/summary.ts, with the exact texts that step 2 shows today (VoterSummary renders from it);fieldLabel(path: FieldPath | "nfactors", card: Card): stringwith "Number of runs", "Minimum resolution", "Replications", "Repeat only", "Center points", "Randomized", "Seed", "Number of factors", and for a factor field${factor name}: ${"name" | "low" | "high" | "unit" | "type"}. Section keyprovenance, title and contents label "How the specification was made". Texts:PROVENANCE_HAND = "The specification was filled in by hand. No language model was used."PROVENANCE_IMPORTED = "The specification comes from an imported project file without a record of its interpretation."provenanceIntro(date: string, mode: "single" | "ensemble"): string=EXPERTA interpreted the request on ${date} in ${mode === "ensemble" ? "ensemble" : "single-model"} mode. The user checked the specification in step 2 and confirmed it.NOT_RECORDED = "Not recorded"disagreeText(labels: string[]): string=Fields where the voters gave different values: ${labels.join(", ")}. The user chose the value of each.checkText(labels: string[]): string=Fields with a quote that did not pass the evidence check: ${labels.join(", ")}.editedText(labels: string[]): string=Fields that the user changed in step 2: ${labels.join(", ")}.NO_CHANGES = "The voters agreed on every field, and the user changed no field."versionsText(meta): string=Prompt version ${p}, schema version ${s}, Jev questions version ${j}.; withoutmeta:VERSIONS_NOT_RECORDED = "The prompt and schema versions were not recorded for this project."
11Task 7: Citation in APA and BibTeX, the Versions part and the acknowledgment
Files:
- Modify:
lib/site.ts,lib/exports/report-html.ts,lib/exports/report-css.ts,app/page.tsx,components/site/CitationToggle.tsx(new client component) - Test:
replication_materials/tests/unit/site.test.ts(new or existing),replication_materials/tests/unit/report-html.test.ts,replication_materials/tests/e2e/home.spec.ts(or the existing home page spec)
Interfaces:
- Produces in
lib/site.ts:APP_URL = "https://expertadoe.app";CITATION_APA = \Vazquez, A. R., & Megahed, F. M. (2026). ${APP_FULL_NAME} (Version ${APP_VERSION}) [Computer software]. ${APP_URL}`(the title in italics in HTML);CITATION_BIBTEX` =@software{vazquez2026experta, author = {Vazquez, Alan R. and Megahed, Fadel M.}, title = {{EXPERTA}: Fractional Factorial Design Translator}, year = {2026}, version = {<APP_VERSION>}, url = {https://expertadoe.app} }CITATIONis removed; its users takeCITATION_APA. Toggle labels"APA"and"BibTeX".ACKNOWLEDGMENT_HEADING = "Acknowledgment"andFUNDING_LINE = "The API and hosting costs of EXPERTA are funded by the Raymond E. Glos Professorship in Business at Miami University, held by Fadel M. Megahed.": in every report after the citation, and on the home page under "Maintained by". Versions part: a table "Software" (Software, Version) with the rows EXPERTA, R, WebR, FrF2, DoE.base, Report format, thenRun sheet SHA-256: ${hash}when the project has it.
12Task 8: Documents, reference prints and the record
Files:
- Modify: spec Sections 7.5, 8.3 and 9.3 (version 7),
README.md,lib/review/known-issues.tsand its tests (the print item goes),replication_materials/process/process.json, the ledger of this plan, the spec page files - Create:
replication_materials/findings/2026-10-19-report-print-after-7f16.pdf,...-after-32f64.pdf,...-after-res3.pdf,...-after-4096.pdf(frompage.pdf()on reference projects: 7 factors in 16 runs; 32 factors in 64 runs; a resolution III design; 4,096 runs with the default limit)
13Execution notes
- The author approved both plans and the order on 2026-10-19, and the acknowledgment text in R11.
- Order: this plan runs after the plan of the deferred minors (
2026-10-19-alpha-minors-plan.md), on its own branch, with its own pull request and deploy. - After the deploy:
site.specwithFFD_EXPECT_CSP=1againsthttps://expertadoe.app, and one print of a real project by the author.