Sprint 29: fortsh Link Audit
Prerequisites
Sprints 18–28 — every functional sprint, parity gate, performance tuning.
Goals
End-to-end link of fortsh under afs-ld. fortsh is ~57 KLoC Fortran 2018, 55 modules, heavy iso_c_binding, allocatable strings, derived types. Linking it is the first real-world stress test of everything before this sprint. Fix what breaks. No excuses.
Deliverables
1. fortsh build pipeline under afs-ld
cd fortsh
AFS_LD=1 armfortas --std=f2018 -O2 <all sources> -o fortsh
Expected:
- Build succeeds.
./fortsh --versionprints the expected version string.- Interactive mode starts and reads a line.
./fortsh -c "echo hello"prints "hello".
2. Failure taxonomy
Anticipated categories (adjust during sprint based on what actually breaks):
- Symbol resolution: missing runtime symbols, weak-coalesce wrong winner, common-size mismatches.
- Relocation math: PAGE21/PAGEOFF12 miscomputation on specific offsets, SUBTRACTOR pair issues in eh_frame.
- TLV: thread-local I/O state failing at runtime.
- Unwind: backtrace on crash produces garbage.
- Dead-strip: functions stripped that were live (or live that should have been stripped).
- Chained fixups: a chain crossing a page boundary or containing a bad
nextoffset. - Code signature: kernel-kill on exec.
Each class has a known file/function starting point for triage from earlier sprints.
3. Audit process
Same rules as armfortas audits (armfortas/CLAUDE.md):
- Assume nothing works until proven otherwise. Every subsystem gets exercised by some fortsh code path.
- Stubs and placeholders are synonyms for broken. If fortsh passes a case only because of a hand-patched workaround, the sprint isn't done.
- Wrong output is worse than crashes. A fortsh that "runs" but produces wrong answers is a critical failure.
- Don't soften findings. "Major" = wrong answers. "Critical" = silent corruption.
- Fix now unless it genuinely requires a later sprint.
Every bug becomes a regression test in tests/parity_corpus/fortsh_*/.
4. Runtime behavior matrix
Curated list of fortsh scenarios run under the afs-ld-linked binary:
- Interactive
echo,cat,ls(builtins). - Pipe:
echo hello | cat. - Redirect:
echo hello > /tmp/f. - Variables:
x=1; echo $x. - Scripts:
./fortsh script.fsh. - Error paths:
nonexistent_commandreturns non-zero. iso_c_bindingcalls into libc from Fortran.- Allocatable string assignment:
s = s // "more". - Derived-type shell_state_t access.
Every item green, or the sprint isn't done.
5. Differential vs system-ld-linked fortsh
Same fortsh source, linked by both. Runtime behavior must match for every scenario in §4. Binary size within 5%. Load-command shape equivalent. Output byte-by-byte for the parts our Sprint 27 rules cover.
6. Perf check
Link time for fortsh under afs-ld is within Sprint 28's 2× budget.
7. Audit report
.docs/audits/sprint29_fortsh.md (or wherever the project convention puts audit reports, parallel to armfortas's audit structure): a brutally honest writeup of what worked, what broke, what was fixed, what remains. No soft-pedaling.
Testing Strategy
- Full fortsh test suite executed under both linker paths.
- Each scenario in §4 scripted as a parity test.
- Perf budget asserted.
- Memory usage at link time within reason (< 1 GiB on fortsh).
Definition of Done
- fortsh links under afs-ld.
- Every scenario in §4 passes.
- All fortsh integration tests pass.
- Differential with ld-linked fortsh matches on every runtime scenario.
- Audit report filed; no open critical items.
View source
| 1 | # Sprint 29: fortsh Link Audit |
| 2 | |
| 3 | ## Prerequisites |
| 4 | Sprints 18–28 — every functional sprint, parity gate, performance tuning. |
| 5 | |
| 6 | ## Goals |
| 7 | End-to-end link of fortsh under afs-ld. fortsh is ~57 KLoC Fortran 2018, 55 modules, heavy `iso_c_binding`, allocatable strings, derived types. Linking it is the first real-world stress test of everything before this sprint. Fix what breaks. No excuses. |
| 8 | |
| 9 | ## Deliverables |
| 10 | |
| 11 | ### 1. fortsh build pipeline under afs-ld |
| 12 | |
| 13 | ``` |
| 14 | cd fortsh |
| 15 | AFS_LD=1 armfortas --std=f2018 -O2 <all sources> -o fortsh |
| 16 | ``` |
| 17 | |
| 18 | Expected: |
| 19 | - Build succeeds. |
| 20 | - `./fortsh --version` prints the expected version string. |
| 21 | - Interactive mode starts and reads a line. |
| 22 | - `./fortsh -c "echo hello"` prints "hello". |
| 23 | |
| 24 | ### 2. Failure taxonomy |
| 25 | |
| 26 | Anticipated categories (adjust during sprint based on what actually breaks): |
| 27 | |
| 28 | - **Symbol resolution**: missing runtime symbols, weak-coalesce wrong winner, common-size mismatches. |
| 29 | - **Relocation math**: PAGE21/PAGEOFF12 miscomputation on specific offsets, SUBTRACTOR pair issues in eh_frame. |
| 30 | - **TLV**: thread-local I/O state failing at runtime. |
| 31 | - **Unwind**: backtrace on crash produces garbage. |
| 32 | - **Dead-strip**: functions stripped that were live (or live that should have been stripped). |
| 33 | - **Chained fixups**: a chain crossing a page boundary or containing a bad `next` offset. |
| 34 | - **Code signature**: kernel-kill on exec. |
| 35 | |
| 36 | Each class has a known file/function starting point for triage from earlier sprints. |
| 37 | |
| 38 | ### 3. Audit process |
| 39 | |
| 40 | Same rules as armfortas audits (`armfortas/CLAUDE.md`): |
| 41 | |
| 42 | - **Assume nothing works until proven otherwise.** Every subsystem gets exercised by some fortsh code path. |
| 43 | - **Stubs and placeholders are synonyms for broken.** If fortsh passes a case only because of a hand-patched workaround, the sprint isn't done. |
| 44 | - **Wrong output is worse than crashes.** A fortsh that "runs" but produces wrong answers is a critical failure. |
| 45 | - **Don't soften findings.** "Major" = wrong answers. "Critical" = silent corruption. |
| 46 | - **Fix now unless it genuinely requires a later sprint.** |
| 47 | |
| 48 | Every bug becomes a regression test in `tests/parity_corpus/fortsh_*/`. |
| 49 | |
| 50 | ### 4. Runtime behavior matrix |
| 51 | |
| 52 | Curated list of fortsh scenarios run under the afs-ld-linked binary: |
| 53 | |
| 54 | - Interactive `echo`, `cat`, `ls` (builtins). |
| 55 | - Pipe: `echo hello | cat`. |
| 56 | - Redirect: `echo hello > /tmp/f`. |
| 57 | - Variables: `x=1; echo $x`. |
| 58 | - Scripts: `./fortsh script.fsh`. |
| 59 | - Error paths: `nonexistent_command` returns non-zero. |
| 60 | - `iso_c_binding` calls into libc from Fortran. |
| 61 | - Allocatable string assignment: `s = s // "more"`. |
| 62 | - Derived-type shell_state_t access. |
| 63 | |
| 64 | Every item green, or the sprint isn't done. |
| 65 | |
| 66 | ### 5. Differential vs system-ld-linked fortsh |
| 67 | |
| 68 | Same fortsh source, linked by both. Runtime behavior **must** match for every scenario in §4. Binary size within 5%. Load-command shape equivalent. Output byte-by-byte for the parts our Sprint 27 rules cover. |
| 69 | |
| 70 | ### 6. Perf check |
| 71 | |
| 72 | Link time for fortsh under afs-ld is within Sprint 28's 2× budget. |
| 73 | |
| 74 | ### 7. Audit report |
| 75 | |
| 76 | `.docs/audits/sprint29_fortsh.md` (or wherever the project convention puts audit reports, parallel to armfortas's audit structure): a brutally honest writeup of what worked, what broke, what was fixed, what remains. No soft-pedaling. |
| 77 | |
| 78 | ## Testing Strategy |
| 79 | |
| 80 | - Full fortsh test suite executed under both linker paths. |
| 81 | - Each scenario in §4 scripted as a parity test. |
| 82 | - Perf budget asserted. |
| 83 | - Memory usage at link time within reason (< 1 GiB on fortsh). |
| 84 | |
| 85 | ## Definition of Done |
| 86 | |
| 87 | - fortsh links under afs-ld. |
| 88 | - Every scenario in §4 passes. |
| 89 | - All fortsh integration tests pass. |
| 90 | - Differential with ld-linked fortsh matches on every runtime scenario. |
| 91 | - Audit report filed; no open critical items. |