Adom Gantt
Public Made by Adomby adom
Interactive Gantt chart viewer for project timelines, with a table view and an analytics page over the same data. Data-driven, dark by default with a print-friendly light mode, and fully editable in the browser: drag bars to move or resize, multi-select and batch-shift dates, manage dependency arrows inline, reorder rows, filter by phase, and undo. One click auto-schedules the whole plan so every dependency lines up finish-to-start on working days, skipping weekends and US federal holidays, push
6600ce8
three-musketeers build: adom-gantt 0.57.0 and 0.57.1, 2026-09-29
What was built
adom-gantt 0.57.1, in gantt.html (server.js is unchanged), plus the package's first
browser test suite, test/ui_test.mjs.
- Collapse all / Expand all. One button at the start of the legend row. It persists across a refresh and the Table lens agrees with the chart.
- Text size survives a refresh. It did not, for two reasons: an exported snapshot discarded every saved preference, and in the live app a second open tab wrote its old size back over the new one. Both are fixed. Preferences are now written key by key, so no tab can overwrite what another tab changed.
- Quieter header. The bold repeat of the title and the dimmed description are gone.
The brand block shows
appName, or the project title when there is none. - Exact start date at every cell size.
- Project Duration and Time Remaining replace the axis cell count ("25 weeks"). On the
reference launch plan, on 2026-09-29, they read
16w 4dand15w 3d. They ignore the cell size, update on every kind of edit, and leave cancelled rows out. - The legend wraps, so all 20 sections are visible and clickable.
- The header fits by measuring, not by pixel breakpoints, so the readouts are never clipped and no control runs off screen from 320 to 2000px.
Independently verified state: a verifier drove all six behaviours in real Chrome and passed them with no page errors. The orchestrator re-ran the suite outside the builder: see Convergence for the final counts.
Decisions for you
- Re-export the launch plan wiki snapshot. the launch plan page carries
gantt/index.html, exported by 0.56.0. It keeps the text-size bug until it is exported again from 0.57.1. Recommendation: re-export and push. Cost of not doing it: anyone reading the plan on the wiki still loses their text size on refresh. - Cancelled rows and the start date. Duration and Remaining leave cancelled rows out. The start button still counts them, because it shifts the whole plan. On a plan whose earliest row is cancelled the two dates differ; each tooltip says why. Recommendation: keep. The alternative is a start button that moves rows it does not show.
- Time Remaining before the plan starts counts from today to the plan's end, so it can exceed Duration, with a visible "not started" note. Recommendation: keep, since the request was for a figure that accounts for today's date.
- Phone width. At 393px the header takes four rows and the legend chips become colour dots. Everything is reachable, but it is not a designed phone layout. Recommendation: leave until someone actually uses the plan on a phone.
- The suite now takes 12 to 14 minutes.
--onlyruns a subset. Recommendation: subset while working, full run before a publish.
Key decisions made during the build
- Text size had two causes, not one. The plan suspected the snapshot. The builder reproduced a second cause in the live app (the stale tab) before changing any code.
- Merge-on-write preferences. The first fix synced only the text size between tabs. The adversary showed the same overwrite still destroyed the collapse state, so the fix moved to the root: a save writes only the keys this tab changed.
- A measured header fit. The first build used pixel breakpoints, and the adversary showed the readouts clipped or hidden at 1280, 1366 and 1680. Breakpoints go stale the moment someone adds a button, so the header now measures itself.
- Collapse all lives in the legend, because the legend is the one row present in both the chart and the table.
- Cancelled rows are out of the clock, matching auto-schedule, the critical path and the hours totals.
- Print names the plan. Removing the title from the header left a printed chart anonymous, so a print-only title line carries it.
- Beyond the brief: the start month picker no longer turns the 31st into 3 March; an empty plan no longer throws a page error; one Ctrl+Z after the full editor's Apply now restores the section as well, and one Ctrl+Z takes back a whole batch Apply or a multi-bar drag where it used to restore a single row (both inherited from 0.56.0, both caught by the hardened suite).
Process appendix
Task: six header and view changes plus a test suite. Stage: published. Rounds: 2. Models: Builder Opus, Adversary Opus, Verifier Sonnet. Round 2 used three narrow reviewers in parallel (two Opus, one Sonnet) and a separate test hardener.
Adversary findings resolved
| Finding | Location | Severity | Resolution |
|---|---|---|---|
| Readouts clipped or hidden at common widths | gantt.html header CSS | High | Fixed: measured fit |
| Controls off screen above the wrap point | gantt.html header CSS | Med | Fixed: measured fit |
| Stale tab overwrites every pref except text size | savePrefs | Med | Fixed: merge-on-write |
| Printed chart does not name the plan | header, print CSS | Med | Fixed: print-only title |
| Cancelled rows inflate the clock | planSpan | Med | Fixed: left out |
| Remaining exceeds Duration before the start | readout | Low | Kept, visible note added |
| Viewport matrix too sparse to catch the clipping | ui_test.mjs | Med | Fixed: width sweep |
| Suite trusts whatever answers on its port | ui_test.mjs | Low | Fixed: free-port probe |
| Stored array swallows the snapshot's write | storedPrefs | Low | Fixed |
| Start button inexact for year-month data | syncStartBtn | Low | Fixed |
| Logs would ship in the package | .adomignore | Low | Fixed: excluded |
| Failed save marked as saved, never retried | savePrefs | Low-Med | Fixed in 0.57.1 |
| Invalid stored values linger | startup | Low | Fixed in 0.57.1 |
| Cleared store only partly restored | savePrefs | Low | Fixed in 0.57.1 |
| Label-width drag across startup lost | label resize | Low | Fixed in 0.57.1 |
| Wrapped header hides button words for nothing | fitHeader | Med | Fixed in 0.57.1 |
| Header changes state when web fonts arrive | fitHeader | Low-Med | Accepted; settles in one step |
| Error gate ignored failed own-server requests | ui_test.mjs | Med | Fixed in 0.57.1 |
| Visibility checks passed invisible elements | ui_test.mjs | Med | Fixed in 0.57.1 |
| Manifest: fictional flows, broad selectors, unwalked editors | ui_test.mjs | Med | Fixed in 0.57.1 |
| Undo after full-editor Apply keeps the new section | imApply | Med | Fixed in 0.57.1 |
| Batch Apply and multi-bar drag push one undo entry per row | snapshotMany | Med | Fixed in 0.57.1 |
Behavioral verification
| Flow exercised | What the verifier did | Result | Notes |
|---|---|---|---|
| Collapse all | Clicked, mixed states, 10 and 11 rapid clicks, reload | Pass | |
| Text size | Reload, two tabs, snapshot over http and file | Pass | |
| Header declutter | Compared with 0.56.0 at every width | Pass | |
| Exact start date | All five cell sizes | Pass | |
| Duration and Remaining | Hand-computed, then edits and undo | Pass | 116 and 108 days |
| Legend | 20 chips, click on and off, scroll | Pass | 3 rows at 1440 |
| Widths 393 to 1920, text 85 to 150% | Overflow and overlap checks | Pass | |
| Edge plans | Future, ended, cancelled, empty, one phase | Pass | |
| Print, light theme, lenses | Emulated print media | Pass | Real paper not tested |
Convergence
| Round | Adversary findings | Verifier | Suite (orchestrator's own run) |
|---|---|---|---|
| 1 | 11 (1 High, 5 Med, 5 Low) | not yet run | 201 passed, 0 failed |
| 1 fixes | Pass, all flows | 222 passed, 0 failed (real plan); 205, 0 (synthetic) | |
| 2 | 2 app (header), 4 app (prefs), 11 suite | 318 passed, 1 failed: the hardened suite caught the batch undo bug | |
| 2 fixes | 320 passed, 0 failed (real plan); 298, 0 (synthetic); 7 controls declared untested |
# three-musketeers build: adom-gantt 0.57.0 and 0.57.1, 2026-09-29
## What was built
adom-gantt 0.57.1, in `gantt.html` (`server.js` is unchanged), plus the package's first
browser test suite, `test/ui_test.mjs`.
* **Collapse all / Expand all.** One button at the start of the legend row. It persists
across a refresh and the Table lens agrees with the chart.
* **Text size survives a refresh.** It did not, for two reasons: an exported snapshot
discarded every saved preference, and in the live app a second open tab wrote its old
size back over the new one. Both are fixed. Preferences are now written key by key, so
no tab can overwrite what another tab changed.
* **Quieter header.** The bold repeat of the title and the dimmed description are gone.
The brand block shows `appName`, or the project title when there is none.
* **Exact start date** at every cell size.
* **Project Duration and Time Remaining** replace the axis cell count ("25 weeks"). On the
reference launch plan, on 2026-09-29, they read `16w 4d` and `15w 3d`. They ignore the cell
size, update on every kind of edit, and leave cancelled rows out.
* **The legend wraps**, so all 20 sections are visible and clickable.
* **The header fits by measuring**, not by pixel breakpoints, so the readouts are never
clipped and no control runs off screen from 320 to 2000px.
Independently verified state: a verifier drove all six behaviours in real Chrome and
passed them with no page errors. The orchestrator re-ran the suite outside the builder:
see Convergence for the final counts.
## Decisions for you
1. **Re-export the launch plan wiki snapshot.** the launch plan page carries
`gantt/index.html`, exported by 0.56.0. It keeps the text-size bug until it is
exported again from 0.57.1. Recommendation: re-export and push. Cost of not doing
it: anyone reading the plan on the wiki still loses their text size on refresh.
2. **Cancelled rows and the start date.** Duration and Remaining leave cancelled rows
out. The start button still counts them, because it shifts the whole plan. On a plan
whose earliest row is cancelled the two dates differ; each tooltip says why.
Recommendation: keep. The alternative is a start button that moves rows it does not
show.
3. **Time Remaining before the plan starts** counts from today to the plan's end, so it
can exceed Duration, with a visible "not started" note. Recommendation: keep, since
the request was for a figure that accounts for today's date.
4. **Phone width.** At 393px the header takes four rows and the legend chips become
colour dots. Everything is reachable, but it is not a designed phone layout.
Recommendation: leave until someone actually uses the plan on a phone.
5. **The suite now takes 12 to 14 minutes.** `--only` runs a subset. Recommendation:
subset while working, full run before a publish.
## Key decisions made during the build
* **Text size had two causes, not one.** The plan suspected the snapshot. The builder
reproduced a second cause in the live app (the stale tab) before changing any code.
* **Merge-on-write preferences.** The first fix synced only the text size between tabs.
The adversary showed the same overwrite still destroyed the collapse state, so the
fix moved to the root: a save writes only the keys this tab changed.
* **A measured header fit.** The first build used pixel breakpoints, and the adversary
showed the readouts clipped or hidden at 1280, 1366 and 1680. Breakpoints go stale
the moment someone adds a button, so the header now measures itself.
* **Collapse all lives in the legend,** because the legend is the one row present in
both the chart and the table.
* **Cancelled rows are out of the clock,** matching auto-schedule, the critical path
and the hours totals.
* **Print names the plan.** Removing the title from the header left a printed chart
anonymous, so a print-only title line carries it.
* **Beyond the brief:** the start month picker no longer turns the 31st into 3 March;
an empty plan no longer throws a page error; one Ctrl+Z after the full editor's Apply
now restores the section as well, and one Ctrl+Z takes back a whole batch Apply or a
multi-bar drag where it used to restore a single row (both inherited from 0.56.0, both
caught by the hardened suite).
---
## Process appendix
**Task:** six header and view changes plus a test suite. **Stage:** published.
**Rounds:** 2. **Models:** Builder Opus, Adversary Opus, Verifier Sonnet. Round 2 used
three narrow reviewers in parallel (two Opus, one Sonnet) and a separate test hardener.
### Adversary findings resolved
| Finding | Location | Severity | Resolution |
|---|---|---|---|
| Readouts clipped or hidden at common widths | gantt.html header CSS | High | Fixed: measured fit |
| Controls off screen above the wrap point | gantt.html header CSS | Med | Fixed: measured fit |
| Stale tab overwrites every pref except text size | savePrefs | Med | Fixed: merge-on-write |
| Printed chart does not name the plan | header, print CSS | Med | Fixed: print-only title |
| Cancelled rows inflate the clock | planSpan | Med | Fixed: left out |
| Remaining exceeds Duration before the start | readout | Low | Kept, visible note added |
| Viewport matrix too sparse to catch the clipping | ui_test.mjs | Med | Fixed: width sweep |
| Suite trusts whatever answers on its port | ui_test.mjs | Low | Fixed: free-port probe |
| Stored array swallows the snapshot's write | storedPrefs | Low | Fixed |
| Start button inexact for year-month data | syncStartBtn | Low | Fixed |
| Logs would ship in the package | .adomignore | Low | Fixed: excluded |
| Failed save marked as saved, never retried | savePrefs | Low-Med | Fixed in 0.57.1 |
| Invalid stored values linger | startup | Low | Fixed in 0.57.1 |
| Cleared store only partly restored | savePrefs | Low | Fixed in 0.57.1 |
| Label-width drag across startup lost | label resize | Low | Fixed in 0.57.1 |
| Wrapped header hides button words for nothing | fitHeader | Med | Fixed in 0.57.1 |
| Header changes state when web fonts arrive | fitHeader | Low-Med | Accepted; settles in one step |
| Error gate ignored failed own-server requests | ui_test.mjs | Med | Fixed in 0.57.1 |
| Visibility checks passed invisible elements | ui_test.mjs | Med | Fixed in 0.57.1 |
| Manifest: fictional flows, broad selectors, unwalked editors | ui_test.mjs | Med | Fixed in 0.57.1 |
| Undo after full-editor Apply keeps the new section | imApply | Med | Fixed in 0.57.1 |
| Batch Apply and multi-bar drag push one undo entry per row | snapshotMany | Med | Fixed in 0.57.1 |
### Behavioral verification
| Flow exercised | What the verifier did | Result | Notes |
|---|---|---|---|
| Collapse all | Clicked, mixed states, 10 and 11 rapid clicks, reload | Pass | |
| Text size | Reload, two tabs, snapshot over http and file | Pass | |
| Header declutter | Compared with 0.56.0 at every width | Pass | |
| Exact start date | All five cell sizes | Pass | |
| Duration and Remaining | Hand-computed, then edits and undo | Pass | 116 and 108 days |
| Legend | 20 chips, click on and off, scroll | Pass | 3 rows at 1440 |
| Widths 393 to 1920, text 85 to 150% | Overflow and overlap checks | Pass | |
| Edge plans | Future, ended, cancelled, empty, one phase | Pass | |
| Print, light theme, lenses | Emulated print media | Pass | Real paper not tested |
### Convergence
| Round | Adversary findings | Verifier | Suite (orchestrator's own run) |
|---|---|---|---|
| 1 | 11 (1 High, 5 Med, 5 Low) | not yet run | 201 passed, 0 failed |
| 1 fixes | | Pass, all flows | 222 passed, 0 failed (real plan); 205, 0 (synthetic) |
| 2 | 2 app (header), 4 app (prefs), 11 suite | | 318 passed, 1 failed: the hardened suite caught the batch undo bug |
| 2 fixes | | | 320 passed, 0 failed (real plan); 298, 0 (synthetic); 7 controls declared untested |