markdown · 3093 bytes Raw Blame History

Sprint 20: Driver Swap

Prerequisites

Sprints 18–19 — hello-world works, CLI complete.

Goals

Wire afs-ld into the armfortas driver. Initially gated behind AFS_LD=1. After the Sprint 27 parity gate, flip the default. Keep a fallback to system ld for at least one sprint after default-on.

Deliverables

1. Driver change site

armfortas/src/driver/mod.rs:

Two call sites ship today:

  • Single-file link path at lines 497–530.
  • Multi-file link path at lines 533–565.

Both build a Command::new("ld"). Refactor to:

fn linker_command() -> (Command, &'static str) {
    match env::var("AFS_LD").as_deref() {
        Ok("1") | Ok("true") => (Command::new(find_afs_ld()), "afs-ld"),
        _                    => (Command::new("ld"),        "system ld"),
    }
}

find_afs_ld():

  1. AFS_LD_PATH env var (full path to the binary).
  2. <workspace>/target/debug/afs-ld.
  3. <workspace>/target/release/afs-ld.
  4. PATH lookup.

Failure produces a clear diagnostic pointing to the env var and build commands.

2. Testing harness update

armfortas/src/testing.rs:871-908 (used by integration tests) — same refactor. Integration tests respect AFS_LD=1.

3. Flag pass-through parity

The driver today builds a fixed command. After this sprint it still builds the same command — afs-ld accepts the same flags. Differential in practice:

# System ld
ld hello.o libarmfortas_rt.a -lSystem -no_uuid -syslibroot <SDK> -e _main -o hello

# afs-ld (same args)
afs-ld hello.o libarmfortas_rt.a -lSystem -no_uuid -syslibroot <SDK> -e _main -o hello

4. Fallback semantics

If afs-ld errors, produce a driver-level diagnostic that cites afs-ld's exit status and stderr, plus a hint to retry with AFS_LD=0. Do not automatically retry with system ld — silently falling back masks real bugs.

5. Test coverage on both paths

cargo test --workspace runs all integration tests twice: once with AFS_LD=0 (baseline), once with AFS_LD=1. Divergence is a test failure. This is the CI gate for afs-ld adoption.

6. Preserving -no_uuid determinism

Driver passes -no_uuid today. Verify afs-ld honors it byte-identically: same inputs under same seed produce the same output (no process-id, no timestamp, no random padding).

7. Docs

Update armfortas/CLAUDE.md with a note about AFS_LD=1 and how to enable/disable. armfortas/README.md if/when it mentions linking.

Testing Strategy

  • tests/linker_swap.rs: runs hello-world both ways, asserts the binaries differ only in tolerated regions.
  • Integration suite under AFS_LD=1: every existing integration test must pass. This is the gate.
  • Failure-path test: a deliberately-broken link (missing symbol); both paths produce an error, not a segfault.

Definition of Done

  • AFS_LD=1 cargo test --workspace passes every test green.
  • Driver refactor lands on a branch that can be rolled back cleanly by flipping the env-var default.
  • Diagnostic quality on afs-ld failures matches or exceeds system ld's.
  • No silent fallback — afs-ld failures surface loudly.
View source
1 # Sprint 20: Driver Swap
2
3 ## Prerequisites
4 Sprints 18–19 — hello-world works, CLI complete.
5
6 ## Goals
7 Wire afs-ld into the armfortas driver. Initially gated behind `AFS_LD=1`. After the Sprint 27 parity gate, flip the default. Keep a fallback to system `ld` for at least one sprint after default-on.
8
9 ## Deliverables
10
11 ### 1. Driver change site
12 `armfortas/src/driver/mod.rs`:
13
14 Two call sites ship today:
15 - Single-file link path at lines 497–530.
16 - Multi-file link path at lines 533–565.
17
18 Both build a `Command::new("ld")`. Refactor to:
19
20 ```rust
21 fn linker_command() -> (Command, &'static str) {
22 match env::var("AFS_LD").as_deref() {
23 Ok("1") | Ok("true") => (Command::new(find_afs_ld()), "afs-ld"),
24 _ => (Command::new("ld"), "system ld"),
25 }
26 }
27 ```
28
29 `find_afs_ld()`:
30 1. `AFS_LD_PATH` env var (full path to the binary).
31 2. `<workspace>/target/debug/afs-ld`.
32 3. `<workspace>/target/release/afs-ld`.
33 4. `PATH` lookup.
34
35 Failure produces a clear diagnostic pointing to the env var and build commands.
36
37 ### 2. Testing harness update
38 `armfortas/src/testing.rs:871-908` (used by integration tests) — same refactor. Integration tests respect `AFS_LD=1`.
39
40 ### 3. Flag pass-through parity
41 The driver today builds a fixed command. After this sprint it still builds the same command — afs-ld accepts the same flags. Differential in practice:
42
43 ```
44 # System ld
45 ld hello.o libarmfortas_rt.a -lSystem -no_uuid -syslibroot <SDK> -e _main -o hello
46
47 # afs-ld (same args)
48 afs-ld hello.o libarmfortas_rt.a -lSystem -no_uuid -syslibroot <SDK> -e _main -o hello
49 ```
50
51 ### 4. Fallback semantics
52 If afs-ld errors, produce a driver-level diagnostic that cites afs-ld's exit status and stderr, plus a hint to retry with `AFS_LD=0`. Do **not** automatically retry with system `ld` — silently falling back masks real bugs.
53
54 ### 5. Test coverage on both paths
55 `cargo test --workspace` runs all integration tests twice: once with `AFS_LD=0` (baseline), once with `AFS_LD=1`. Divergence is a test failure. This is the CI gate for afs-ld adoption.
56
57 ### 6. Preserving `-no_uuid` determinism
58 Driver passes `-no_uuid` today. Verify afs-ld honors it byte-identically: same inputs under same seed produce the same output (no process-id, no timestamp, no random padding).
59
60 ### 7. Docs
61 Update `armfortas/CLAUDE.md` with a note about `AFS_LD=1` and how to enable/disable. `armfortas/README.md` if/when it mentions linking.
62
63 ## Testing Strategy
64 - `tests/linker_swap.rs`: runs hello-world both ways, asserts the binaries differ only in tolerated regions.
65 - Integration suite under `AFS_LD=1`: every existing integration test must pass. This is the gate.
66 - Failure-path test: a deliberately-broken link (missing symbol); both paths produce an error, not a segfault.
67
68 ## Definition of Done
69 - `AFS_LD=1 cargo test --workspace` passes every test green.
70 - Driver refactor lands on a branch that can be rolled back cleanly by flipping the env-var default.
71 - Diagnostic quality on afs-ld failures matches or exceeds system ld's.
72 - No silent fallback — afs-ld failures surface loudly.