U of I × MindRouter App Gallery

Grant Budget Builder

Research Built with Codex CLI v0.154.0 Critic status: shipped

Grant Budget Builder

Multi-year sponsored-project budgets

Build a five-year federal grant budget the way sponsors want to see it — personnel with annual escalation, fringe, travel, equipment, participant support, subawards and indirect costs on the correct base — and export it for your proposal.

Grant Budget Builder running on a desktop screen, multi-year sponsored-project budgets Desktop · click to enlarge
Grant Budget Builder running on a phone, multi-year sponsored-project budgets
Phone · click to enlarge

What it is ✎ blurb written by the AI critic

Build a five-year federal grant budget: add personnel with effort and appointment months, then add travel, equipment, materials, participant support, subawards, tuition and other costs. The app escalates salaries, applies fringe, and computes indirect costs on the correct MTDC base, with a panel showing exactly what was excluded and why. You can compare scenarios, solve for the effort that fits a sponsor cap, and export JSON, CSV or a printable budget justification.

Try this

  1. Click 'Load sample', then set Sponsor cap to 2,000,000 and press 'Solve for effort' in Fit to sponsor cap to see how much everyone's effort must shrink
  2. Open 'Compare without...' in Scenario compare and pick J. Postdoc to see what the postdoc costs over five years, including fringe and indirect costs
  3. On the Subawards tab, raise Year 1 of the partner-lab subaward to 60,000 and watch the MTDC panel exclude the amount above $50,000

Highlights ✎ written by the AI critic

  • Live MTDC and F&A engine that follows 2 CFR 200.1, including the $50,000-per-subaward rule, with a year-by-year exclusion table
  • Stacked bar chart by year and a donut of the 5-year total that update as you type
  • 'Fit to sponsor cap' solves for personnel effort, caps anyone at 100%, and lands within about $1 of the target
  • One-click 'Compare without' scenarios show the marginal cost of a person
  • Printable NSF-style budget justification (sections A-J) with editable one-line justifications, plus JSON/CSV export and auto-save

Who it's for

Principal investigators, research administrators, department grant staff

Features

  • Tabbed budget sheet: Personnel (people × effort % × base salary, calendar/academic/summer months), Fringe, Travel, Equipment, Materials, Participant support, Subawards, Other
  • Year 1…5 columns with automatic escalation; live totals per category and year
  • Correct MTDC and F&A computation with an explanation panel showing what was excluded and why
  • Stacked bar chart of cost categories by year and a donut of the total
  • Scenario compare (e.g. with vs without a postdoc) and a 'fit to sponsor cap' helper that solves for the effort that hits a target total
  • Save/load to browser storage, JSON import/export, CSV export and a print-friendly budget-justification view

Data & sources

  • No downloaded datasets — the app's rates are user-editable illustrative placeholders (data/raw/SOURCES.md)
  • www.ecfr.gov — 2 CFR 200.1 MTDC definition —
  • www.ecfr.gov — 2 CFR 200.414 Indirect (F&A) costs —
  • www.uidaho.edu — University of Idaho Office of Sponsored Programs (contact section) —
  • www.uidaho.edu — University of Idaho Budget & Planning consolidated fringe rates —
Full data provenance ✎ written by the AI reviewer

Data sources — Grant Budget Builder

Retrieved / reviewed: 2026-09-13

This app uses no downloaded datasets — there are no raw data files in this directory. All rates in the app (fringe by employee category, F&A rate, escalation %, tuition) are illustrative placeholder defaults that the user edits; they are NOT official University of Idaho rates and must be labeled as placeholders in the UI. The UI must tell users to confirm current rates with the U of I Office of Sponsored Programs (OSP).

Reference documents the app should cite with links in its visible "Data & sources" section (links only, nothing downloaded):

ReferenceURLPublisherUse in appTerms
2 CFR 200.1 — Definitions ("Modified Total Direct Cost (MTDC)")https://www.ecfr.gov/current/title-2/subtitle-A/chapter-II/part-200/subpart-A/subject-group-ECFR2a6a0087862fd2c/section-200.1U.S. Office of Management and Budget, via the Electronic Code of Federal Regulations (eCFR, National Archives)MTDC base definition (see verbatim text below)U.S. government work, public domain
2 CFR 200.414 — Indirect (F&A) costshttps://www.ecfr.gov/current/title-2/subtitle-A/chapter-II/part-200/subpart-E/subject-group-ECFR6ad17c6ee5a3cbe/section-200.414OMB / eCFRF&A is charged on the MTDC base at the negotiated rate; 200.414(f): organizations without a negotiated rate may use a de minimis rate of up to 15% of MTDCU.S. government work, public domain
University of Idaho Office of Sponsored Programs (contacts: osp@uidaho.edu, 208-885-6651)https://www.uidaho.edu/research/our-people#sponsor-progUniversity of Idaho, Office of Research and Economic Development"Confirm current fringe, F&A and tuition rates with OSP" noteLink only
University of Idaho Budget & Planning — consolidated fringe rates by fiscal yearhttps://www.uidaho.edu/leadership/finance-administration/budget-planning#fringe-ratesUniversity of Idaho, Budget and Planning OfficeWhere current consolidated fringe rates (by employee class: faculty, staff, temporary help, student) are published; link only — the app's defaults stay labeled placeholdersLink only

Verbatim regulatory text (retrieved 2026-09-13 from the eCFR API, current version)

2 CFR 200.1, "Modified Total Direct Cost (MTDC)":

Modified Total Direct Cost (MTDC) means all direct salaries and wages, applicable fringe benefits, materials and supplies, services, travel, and up to the first $50,000 of each subaward (regardless of the period of performance of the subawards under the award). MTDC excludes equipment, capital expenditures, charges for patient care, rental costs, tuition remission, scholarships and fellowships, participant support costs, and the portion of each subaward in excess of $50,000. Other items may only be excluded when necessary to avoid a serious inequity in the distribution of indirect costs and with the approval of the cognizant agency for indirect costs.
  • Implementation notes for the app: The $50,000 subaward allowance applies per subaward, once over the whole award (not per year): a subaward's costs count toward MTDC until that subaward's cumulative total reaches $50,000; later amounts are excluded.

  • Excluded from the base: equipment, participant support costs, tuition remission, and each subaward's amount above its first $50,000 (plus the other listed exclusions such as rental costs and patient care, if the app offers such lines).

How it was built

Written by Codex CLI 0.154.0 (github.com) on qwen/qwen3.8-27b, served by MindRouter, the University of Idaho's on-premises AI platform.

11harness rounds
183.7minutes of harness work (3.1 h)
3,232lines of code
202KB, single-file app

Rounds by kind

build1
feedback8
critic2

Lines of code per file

body html211
app css615
app js2,268
scripts138

Critic scores ✎ scored by the independent AI critic, 1–10

visual 8 out of 10
usefulness 8 out of 10
correctness 9 out of 10
interactivity 8 out of 10

Independent audits ✎ by the AI critic

Data honesty: checked against raw data Authorship audit: every app-code commit is a harness round

Automated checks

Console no console errors No network 0 external requests Phone layout 0 px overflow Canvases no empty canvases Brand header header present No key leak no key in source

Checked by critic-verify.

Data spot checks ✎ written by the AI critic

  • MTDC definition shown in the app vs data/raw/SOURCES.md verbatim 2 CFR 200.1 text: identical (equipment, participant support, tuition remission and each subaward's amount above $50,000 excluded); app's $50,000 applies per subaward over the whole award, as in SOURCES.md
  • De minimis rate cited in app ('up to 15% of MTDC', 2 CFR 200.414(f)) vs SOURCES.md: match. All default rates (fringe 54/38/32/29/30%, F&A 54%, escalation 3%, tuition $118/credit) are labeled ILLUSTRATIVE PLACEHOLDER DEFAULTS, not U of I figures, with a note to confirm with OSP; no invented figures presented as real UI rates
  • Sample budget, independent Python recompute (indep_calc.py) vs app: direct $1,687,982 = $1,687,982; MTDC $1,603,917 = $1,603,917; F&A $866,115 = $866,115; total $2,554,097 = $2,554,097
  • Subaward $18,000/$18,540/$19,096/$19,669/$20,259 (total $95,564): hand calc $50,000 in MTDC, $45,564 excluded, crossing in Year 3 with $5,636 excluded that year; app shows the same
  • PI $145,000 on a 9-mo appointment, 9 academic + 1.5 summer months at 12.5% effort: hand calc $21,145.83; app Y1 $21,146. Tuition 1 student x 18 credits x $118: hand calc $2,124 Y1 / $2,188 Y2 at 3%; app shows the same and excludes it from MTDC
Show all 6 spot checks
  • F&A rate changed to 15%: 1,603,917 x 0.15 = $240,588; app $240,588. Fit-to-cap target $1,400,000: applied 5-year total $1,400,001
Build notes ✎ written by the AI reviewer

Codex CLI 0.154.0 on MindRouter default-agent (qwen/qwen3.8-27b) wrote every line of app code over 9 recorded rounds: 1 build and 8 feedback, the maximum. All rounds exited 0 and committed. The tamper guard never fired. I wrote only data/raw/SOURCES.md (fixed the 404 OSP link, added the verified 2 CFR 200.1 text) and the FEEDBACK.md files, which I deleted in a final commit.

  • Harness behaviour: Round 1 built a complete-looking app in 18 min. In a real browser it had serious bugs: typing lost focus after one character, a crash on the month checkboxes, non-personnel costs repeated every year, a wrong personnel salary model, a donut with a gap, a malformed CSV and a phone layout wider than the screen.

  • Codex's sandbox has no working browser: headless Chrome crashed and network is blocked. It tested with Node DOM mocks or jsdom, and several times reported fixes as done and verified when they were not.

    • Round 3 claimed the sel.children.push crash and the focus bug were fixed, but the source still contained both.

    • Round 2 opened a justification modal on page load that could not be closed. Its CSS display:flex overrode the hidden attribute, blocking the whole app.

    • Round 3 hid overflow with overflow-x:hidden, which silently cut off the right side of every card.

    • Round 4 let tables widen the page to 1,400 px on phones.

    • Round 6 used an undeclared livePerson variable, which crashed the Personnel tab.

    • Round 8 moved the focused row into a new table, which re-broke focus (fixed in round 7) and let keystrokes land in another person's salary.

  • The focus bug took seven reports. It only moved once the feedback explained that moving a focused element in the DOM blurs it in Chrome but not in jsdom, and gave a checkable condition (the same element must stay document.activeElement). Quoting measured values and line numbers also helped the harness act.

  • Harness time per round: 18, 23, 19, 15, 30, 18, 3, 30 and 13 min (about 170 min in total). No round got stuck and none needed killing.

Review method: each round I ran verify_app.py plus my own Playwright suite (pwtest.py) and targeted probes (focus retention, clipping, fit math, computed cells, add-row flows). For round 2 only, I injected a [hidden]{display:none} style inside the test browser to see past the blocker. The app files were not touched.

Final state: every SPEC feature works in real Chromium at 1440 px and 390 px, with no errors and no external requests. Only minor cosmetic flaws remain (listed above).

Round-by-round timeline

Every harness round, in order. The prompts and FEEDBACK.md were written by the AI reviewer; the app code was written by the harness. Expand a round to see the exact prompt and the FEEDBACK.md the harness received.

    1 build 18.2 min · 6 files changed, 3672 insertions(+), 3 deletions(-) Show prompt

    Prompt given to the harness:

    Read AGENTS.md, SPEC.md and BRAND.md in this directory. Then build the complete app they describe, step by step.
    
    This app has no downloaded datasets. data/raw/SOURCES.md lists the reference documents (2 CFR 200.1 MTDC definition, 2 CFR 200.414 indirect costs, University of Idaho Office of Sponsored Programs and the U of I Budget & Planning fringe-rate page) that the app's visible "Data & sources" section must cite with links. First write a small Python script in scripts/ that produces compact JSON in data/embed/ with the app's reference data (the placeholder default rates clearly marked as illustrative placeholders, the MTDC exclusion rules with their 2 CFR citations, and the source links) and run it. Then write src/body.html, src/app.css and src/app.js; run python3 build.py --check and fix any error it reports.
    
    Implement every required feature in SPEC.md with a polished, modern, responsive University of Idaho design. Do not stop until python3 build.py --check succeeds and every feature is implemented.
    

    No feedback file was sent in this round.

    10 critic 9.6 min · 4 files changed, 769 insertions(+), 206 deletions(-) Show prompt

    Prompt given to the harness:

    Read FEEDBACK.md in this directory — it lists problems a reviewer found when testing index.html in a real browser (Chromium, 1440x900 and 390x844). Fix every item, keeping everything that already works (especially: typing in table inputs must keep focus), then run python3 build.py --check.
    

    FEEDBACK.md the harness received ✎ written by the AI reviewer:

    Reviewer findings — tested in real Chromium at 1440×900 and 390×844

    The math is right: I recomputed the "Load sample" budget independently (direct $1,676,706, MTDC $1,603,917, F&A $866,115, total $2,542,821 all match). Do NOT change the compute engine except where an item below says so, and keep the focus behaviour of table inputs exactly as it is (typing in a Personnel/Travel cell must keep the same element as document.activeElement).

    You cannot open a browser, so each item gives the cause and a condition you can check in code.

    1. HIGH — Printing the budget justification loses almost everything
    • Open "Print justification", then print (I used Chromium page.pdf() with print media): the PDF is 1 page and stops right after the heading "A. Senior personnel". Sections A–J, "Rates used" and all justification text are cut off.

    • Cause: .gb-modal { max-height: 92vh; overflow-y: auto } (app.css ~line 481) is not reset inside @media print (app.css ~515–516), and body.gb-modal-open { overflow: hidden } (line 511) also clips. .gb-justif-scroll { overflow-x: auto } clips wide tables in print too.

    • Also: printing with the modal CLOSED (Ctrl/Cmd+P from the page) gives a blank page, because .gb-shell is hidden and .gb-print is display:none !important (line 524) in print.

    • Fix: in @media print set .gb-modal, .gb-justif, .gb-justif-scroll { max-height:none; overflow:visible; }, body.gb-modal-open { overflow: visible; }, tables width:100%; font-size:9pt; table-layout:auto, section { break-inside: avoid-page } for small sections. Add window.addEventListener('beforeprint', ...) that builds the justification and shows it for print even when the modal is closed (e.g. add a class that makes #justif-backdrop print as a static block).

    • Check: printed output (modal open or closed) is ≥ 2 pages and contains "J. Total budget" and "Rates used".

    2. HIGH — Literal <strong> tags shown in the justification
    • "J. Total budget" row reads <strong>Total requested</strong> and <strong>$2,542,821</strong> as text.

    • Cause: app.js ~lines 1801–1802 pass HTML strings to jTd()/jNum(), which use textContent.

    • Check: #justif-body innerText contains no < character.

    3. HIGH — Decimal effort and months are silently rounded to whole numbers
    • Typing effort 12.5 stores 13; typing Summer mo 1.5 stores 2 (checked in localStorage). 1.5 summer months is very common in NSF budgets, and the fit helper itself writes 0.1% effort values that the user can then no longer type.

    • Cause: numCell() (app.js ~line 827) does Math.round(parseFloat(...)) for every field.

    • Fix: keep people and salary as whole numbers; store effort rounded to 0.01 and acadMonths/sumMonths rounded to 0.01. Clamp effort to 0–100 and months so acad+summer ≤ 12 for every row (not only when livePerson === obj), and on change/blur write the clamped value back into the input (today typing 50100 in Effort shows "50100" while the state is 100).

    • Check: PI row with salary 145000, Academic (9-mo), Acad mo 9, Summer mo 1.5, effort 12.5 shows Y1 = $21,146 (145000/9 × 10.5 × 0.125).

    4. HIGH — Fit to cap shows effort above 100% that it never applies
    • Sample budget, target $3,000,000: the table says J. Postdoc and S. PhD go from 100.0% → 143.7%, and the message says "scale effort to 143.7% of current". Applying actually caps them at 100% and raises only the PI (57.5%) and undergrads.

    • Fix: "After" = min(100, before × factor) with a small "capped at 100%" tag on capped rows; reword the message to "Effort multiplied by ×1.437 (2 people capped at 100%)". If every selected person would be capped and the target is still out of reach, say it is unreachable by effort alone.

    • Precision: after "Apply" with target $1,400,000 the total is $1,398,540 ($1,460 short) because applyFit rounds effort to 0.1%. Once item 3 allows 0.01%, round to 0.01% in applyFit and in the table. Check: applied total within ±$300 of the target.

    5. MEDIUM — Fringe and Other have the same chart colour
    • CHART_SERIES (app.js ~lines 36 and 43) gives both fringe and other SERIES[4] (rgb(3,68,65)). With an Other line the legend shows two identical dark-green swatches.

    • Fix: give Other its own colour, visibly different from every other series and from the grey F&A hatch (e.g. Silver #808080 or a light Clearwater tint #7FBFBF).

    • Check: the 10 series fills are all different.

    6. MEDIUM — Desktop Personnel table hides its results and truncates its dropdowns
    • At 1440×900 the page content is only 1100px wide (.gb-shell { max-width: 1100px }, app.css line 5) while the personnel table has min-width: 1150px (line 423). The table is cut off after "Effort %", so the Y1–Y5 and Total columns (the answer) need sideways scrolling. The Fringe cat. select is 54px wide and shows "Fac", "Pos", "PhD"; the Appointment select is 81px and shows "Academ:", "Calend:". The tab bar also wraps "Tuition remission" and "Other" onto a second row.

    • Fix: use the available width (#app allows 1320px: set .gb-shell max-width to 1320px) and make the personnel table fit: short option labels ("Faculty", "Staff", "Postdoc", "PhD RA", "Undergrad RA"; "12-mo", "9-mo"), selects wide enough for their label, narrower number inputs for People / Acad mo / Summer mo / Effort (~64px).

    • Check at 1440 width with the sample: the personnel .gb-table-wrap has scrollWidth <= clientWidth, and each select's clientWidth is at least its selected label's text width + 24px.

    7. MEDIUM — The scroll "fade" draws a grey bar through the middle of tables on phones
    • At 390px a grey vertical gradient band sits over the Role column text in Personnel (about 70px from the card's right edge) and over the numbers in the Scenario compare table.

    • Cause: .gb-table-wrap__fade (app.css ~402–406) is a floated sticky element with a fixed height:180px; margin-top:-180px, so it lands wherever the float ends up, not at the table's right edge.

    • Fix: remove the separate fade element and draw the edge shadow on .gb-table-wrap itself, or wrap the table-wrap in a position:relative box and position the fade absolute; top:0; bottom:0; right:0.

    • Check: the fade's bounding box has the same top, bottom and right as its .gb-table-wrap (±2px).

    8. LOW — Toast covers the header
    • "Sample loaded" / "Compared: …" appears top-right over the "BUILT WITH CODEX ON MINDROUTER" chip on desktop and over the modal title on phone.

    • Fix: show the toast bottom-centre (e.g. bottom: 20px; left: 50%; transform: translateX(-50%)).

    • Check: the toast rectangle does not intersect .ui-header or .gb-modal__head.

    9. MEDIUM — "Tuition per credit" rate changes nothing
    • Changing Tuition per credit from 118 to 999 changes no number anywhere; the Tuition tab only takes typed per-year amounts. Every control must do real work.

    • Fix: each tuition line gets Students and Credits / yr inputs; its Y1–Y5 amounts are students × credits × tuition rate × (1 + escalation)^year. Keep lines imported with typed per-year amounts and no credits working as before. Label the rate "Tuition per credit hour ($)". Add a tuition line to the sample (1 PhD student × 18 credits).

    • Check: 1 student × 18 credits at $118 gives Y1 tuition $2,124; changing the rate to 500 gives $9,000, and the MTDC table's "− Tuition" column shows it excluded from the base.

    10. LOW — Postdocs listed as senior personnel in the justification
    • Section "A. Senior personnel (PI & postdocs)" includes J. Postdoc (app.js ~line 1620, isSenior). On NSF and most federal budget forms postdoctoral scholars go under "B. Other personnel".

    • Fix: senior = faculty category only; heading "A. Senior / key personnel"; postdocs, staff and students in B.

    11. LOW — "Compare without…" adds a duplicate Baseline
    • Snapshot "With postdoc", then Compare without → J. Postdoc gives 3 columns: "With postdoc", "Baseline" (identical) and "Without J. Postdoc".

    • Fix: if the last scenario's snapshot equals the current budget, reuse it as the baseline instead of pushing another. On phone the scenario bar value wraps as "(−" / "$669,132)"; put the delta on its own line or give the value column more room.

    12. LOW — Justification column is off-screen in the justification modal
    • At 1440×900 the personnel tables in the modal have 14 columns and the "Justification" input is to the right of the visible area; you must scroll the table sideways to type a justification.

    • Fix: render each line's justification input on its own full-width row under the line (<tr><td colspan=...>), so it is visible at any width and prints as a sentence under the line.

    11 critic 4.3 min · 4 files changed, 240 insertions(+), 210 deletions(-) Show prompt

    Prompt given to the harness:

    Read FEEDBACK.md in this directory — it lists the problems a reviewer still found when testing index.html in a real browser (Chromium, 1440x900 and 390x844); two are regressions from your last change. Fix every item, keeping everything that already works (especially: typing in table inputs must keep focus, print, fit-to-cap, tuition and the justification view), then run python3 build.py --check.
    

    FEEDBACK.md the harness received ✎ written by the AI reviewer:

    Reviewer findings (round 2 of review) — tested in real Chromium at 1440×900 and 390×844

    Good work: 20 of 24 checks now pass in a real browser. Print (3 pages, A–J), the <strong> fix, decimal effort/months, fit-to-cap (hits $1,400,001 and caps at 100%), distinct chart colours, tuition per credit, postdocs in section B, no duplicate Baseline, justification rows and the bottom toast all work, and typing still keeps focus. Keep all of that. Only the items below remain. Two are regressions from the last change.

    The rule that still applies: never remove, move or re-parent an element that contains the focused input (moving a focused input blurs it in Chrome, even though jsdom does not show this).

    1. HIGH (regression) — The Personnel table now clips every cell, so the costs can't be read
    • Measured with the sample loaded: 1440×900: all 16 personnel tds are 75px wide (.gb-table--personnel { table-layout: fixed }, app.css line 413; the per-column th:nth-child widths at lines 573–582 exist only inside the phone media query). td { overflow: hidden; text-overflow: ellipsis } (line 417) then clips the controls:

      • the Name input (150px) shows "Dr. A. Sa" and the Role input shows "Postdoct"

      • the Y1 cell needs 77px and shows "$77,3…"; Total needs 86px and shows "$410,…"

      • the 60px number inputs in 75px cells render a stray "‥" beside People, Acad mo, Summer mo and Effort

      • the "Undergrad RA" select is 84px but its label needs 93px

    • 390×844: Y1–Y5 and Total tds are 31px wide (the phone widths add up to more than the 700px min-width, so the year columns get the leftover). The costs show as "$…" even after swiping.

    • Fix:

      • Remove overflow:hidden; text-overflow:ellipsis from the personnel tds. Never clip numbers.

      • Make the table fit at desktop width. Wrap clientWidth is 1194px at 1440, so budget about 1150px:

        • put Role as a second, smaller input under Name in the same cell (saves a whole column)

        • hide number spinners (appearance: textfield)

        • use compact 4–5px cell padding

        • approximate widths:

    ColumnWidth
    Name/Role140px
    Fringe116px (fits "Undergrad RA")
    People46px
    Appt72px
    Base salary80px
    Acad/Summer/Effort52px each
    Y1–Y564px each (.78rem mono)
    Total76px
    Delete28px
    • On phones, let the table keep its natural width and scroll sideways with the sticky Name column, rather than squeezing columns. Use table-layout: auto, or fixed widths that include the year columns, with min-width set to their sum.

    • Check:

      • At 1440×900, the personnel .gb-table-wrap has scrollWidth <= clientWidth.

      • At 1440×900 and at 390×844, every personnel td has scrollWidth <= clientWidth + 1.

      • No personnel td has computed text-overflow: ellipsis.

      • The Name input's scrollWidth <= clientWidth for "Dr. A. Sample".

      • At 390px, after scrolling the wrap to the right, the Y1 cell's full text "$77,333" is inside the wrap's visible rectangle.

    2. HIGH (regression) — On phones the "swipe Y1–Y5 →" chip and the fade stripes float over the page header
    • .gb-table-wrap__cue and .gb-table-wrap__fade are now position:absolute (app.css lines 395–406). They are inserted as siblings after the wrap (app.js ~lines 415–432), and their parents (#gb-panel, .gb-card) are position: static, so their offsetParent is BODY.

    • Result at 390×844: three "swipe Y1–Y5 →" chips stack at the top-right of the page over the black University of Idaho header (rect 283,4 → 380,26). Three full-height 28×844px grey fade stripes cover the right edge of the first screen, far from any table (the tables start at y = 1882, 2321 and 4465).

    • Fix (choose one):

      • (a) Delete the cue/fade elements. Draw the edge shadow with CSS scroll-shadow backgrounds on .gb-table-wrap itself (background-attachment: local, scroll). Show a small normal-flow note "Swipe for Y1–Y5 →" above the table, only at max-width: 760px.

      • (b) Give the host position: relative and set the fade's top/height from the wrap's offsetTop/offsetHeight.

      • Either way, don't re-parent a wrap that may contain the focused input.

    • Check at 390×844 with the sample loaded:

      • No cue or fade rectangle intersects .ui-header.

      • Every fade/cue rectangle lies within its own .gb-table-wrap rectangle (±2px).

    3. LOW — The clamped value is still not written back into the input
    • Typing 150 in Effort and pressing Tab leaves the input showing "150" while the state is 100.

    • Cause: the change handler in numCell() (app.js ~line 940) only writes back if (document.activeElement === inp ...). When change fires on commit/blur, focus has already moved, so the condition is false.

    • Fix: in the change handler, write inp.value = obj[field] whenever it differs (drop the activeElement test). change only fires on commit, so this never interferes with typing.

    • Check: type 150 in Effort, press Tab → the input shows 100.

What the agent was asked

The full specification the harness received, rendered from the original Markdown.

Read the specification

Grant Budget Builder — Multi-year sponsored-project budgets

Category: Research Who it is for: Principal investigators, research administrators, department grant staff

What it should do

Build a five-year federal grant budget the way sponsors want to see it — personnel with annual escalation, fringe, travel, equipment, participant support, subawards and indirect costs on the correct base — and export it for your proposal.

Data

No external data. Rates must be editable inputs (fringe by employee category, F&A rate, escalation %, tuition) with clearly labeled placeholder defaults and a note to confirm current rates with the U of I Office of Sponsored Programs. Implement the standard federal Modified Total Direct Cost (MTDC) base: exclude equipment, participant support, tuition remission and subaward amounts above the first $50,000 (2 CFR 200.1).

Raw downloads (if any) are in data/raw/ with data/raw/SOURCES.md describing where each file came from. Transform what the app needs into compact JSON in data/embed/ (write a small script in scripts/ to do it, so the transformation is reproducible). Every dataset the app shows must be credited in a visible "Data & sources" section of the app, with links. If a dataset is illustrative/synthetic, label it clearly as such in the UI — never present made-up numbers as real University of Idaho figures.

Required features

  • Tabbed budget sheet: Personnel (people × effort % × base salary, calendar/academic/summer months), Fringe, Travel, Equipment, Materials, Participant support, Subawards, Other

  • Year 1…5 columns with automatic escalation; live totals per category and year

  • Correct MTDC and F&A computation with an explanation panel showing what was excluded and why

  • Stacked bar chart of cost categories by year and a donut of the total

  • Scenario compare (e.g. with vs without a postdoc) and a 'fit to sponsor cap' helper that solves for the effort that hits a target total

  • Save/load to browser storage, JSON import/export, CSV export and a print-friendly budget-justification view

Quality bar

This app is going into a University of Idaho gallery that shows faculty, staff and students what they can build with AI coding agents. It must look polished and modern (think a well-designed data product, not a homework demo), feel alive (smooth transitions, responsive hover/selection states, animated where it helps understanding), and do real, useful work — every control should change something meaningful. It must work well on a laptop and on a phone. Include a short "How to use" hint for first-time visitors and a helpful empty state wherever the user must supply data.

Known limitations

Reported by the independent critic. ✎ written by the AI critic

  • Role and tuition-description inputs clip long text inside the field (e.g. 'PhD research assista'), and the 'Undergrad RA' dropdown label touches its arrow on desktop
  • On phones the Personnel and scenario tables need a sideways swipe to reach Y1-Y5 (an edge shadow and a swipe note show this)
  • The toast is now bottom-centre and briefly covers page content for about 2.4 s
  • Codex tests only in jsdom (no browser in its sandbox) and again reported fixes as verified when they were not: critic round 10 claimed the personnel table fixed, but in real Chromium every cell was clipped and the swipe cue floated over the page header. Round 11 fixed both.