From 5d4c963823984ffc9ef7d61df88d9b8574682e1f Mon Sep 17 00:00:00 2001 From: Eric Willigers Date: Mon, 22 Jun 2026 11:46:23 +1000 Subject: [PATCH] Address Class B Accretive failures Rework five concept exercises whose early-task tests called a word that the instructions introduce in a later task, so a student who correctly completed the early tasks still saw failures. Also mark these as fixed in accretive-audit.md. --- accretive-audit.md | 47 ++++++++++--------- .../garden-gathering-tests.factor | 18 +++---- .../lighthouse-logbook-tests.factor | 8 ++-- .../poetry-club/poetry-club-tests.factor | 11 +++-- .../quayside-crew/quayside-crew-tests.factor | 12 ++--- .../tellers-triage-tests.factor | 12 +++-- 6 files changed, 59 insertions(+), 49 deletions(-) diff --git a/accretive-audit.md b/accretive-audit.md index f09efcfe..c4703d3b 100644 --- a/accretive-audit.md +++ b/accretive-audit.md @@ -45,14 +45,17 @@ Verified empirically with the factor runtime, via `bin/check-accretive`: Run it with `FACTOR=/path/to/factor bin/check-accretive [slug...]`. -## Results: 11 of 47 are not Accretive +## Results + +The initial audit found 11 of 47 not Accretive. The 5 Mode-B exercises have +since been fixed (test-only changes); 6 remain open (5 Mode A + 1 structural). | Verdict | Count | Exercises | |---|---|---| -| Accretive | 36 | all others | -| Mode A | 5 | bering-bearings, boatswains-bilge, dragons-descendants, factory-failsafe, pirates-path | -| Mode B | 5 | garden-gathering, lighthouse-logbook, poetry-club, quayside-crew, tellers-triage | -| Structural | 1 | telegraphers-tape | +| Accretive | 41 | all others | +| Mode A (open) | 5 | bering-bearings, boatswains-bilge, dragons-descendants, factory-failsafe, pirates-path | +| Mode B (fixed) | 5 | garden-gathering, lighthouse-logbook, poetry-club, quayside-crew, tellers-triage | +| Structural (open) | 1 | telegraphers-tape | ## Mode A — a definition is introduced after task 1 @@ -81,23 +84,25 @@ Run it with `FACTOR=/path/to/factor bin/check-accretive [slug...]`. `TUPLE: valve < disposable is-open ;`, a stubbed ``, and `M: valve dispose* drop ;` — so the file compiles; task 5 fills in the bodies. -## Mode B — an early task's test calls a later task's word - -Each of these is a test-design issue: the test for an early task observes its -result through a word the student has not been asked to write yet. Fix by -rewriting the early-task test to observe the early word directly, or by -reordering tasks. - -- **tellers-triage** — task 1 is `{ { } } [ new-queue serve-all ]`, which needs - `serve-all` (task 4); task 2's tests also use `serve-all`/`next-name`. -- **lighthouse-logbook** — a task-1 test `[ empty-log dup "x" sight ]` (checking - each log is fresh) needs `sight` (task 2). -- **garden-gathering** — a task-2 test uses `release` (task 3) to check that ids - keep increasing after a release. -- **poetry-club** — task 1's test uses `circle-of` (task 3); task 2's test uses - `same-circle?` (task 4). A disjoint-set is only observable through those. +## Mode B — an early task's test called a later task's word (fixed) + +Each was a test-design issue: the test for an early task observed its result +through a word the student had not been asked to write yet. All five were fixed +with test-only changes — moving the forward-referencing assertion into the task +that owns the word, or (for opaque structures) observing through a library word. + +- **tellers-triage** — task 1 `[ new-queue serve-all ]` needed `serve-all` + (task 4) and task 2 also used `serve-all`/`next-name`. Tasks 1–2 now observe + the min-heap through library `heap-empty?` / `heap-size` / `heap-peek`. +- **lighthouse-logbook** — a task-1 freshness test `[ empty-log dup "x" sight ]` + needed `sight` (task 2); moved to task 2. +- **garden-gathering** — a task-2 test used `release` (task 3) to check that ids + keep increasing; moved to task 3. +- **poetry-club** — task 1 used `circle-of` (task 3) and task 2 used + `same-circle?` (task 4); a disjoint-set is only observable through those, so + tasks 1–2 now observe circles through library `equiv?`. - **quayside-crew** — a task-3 test `[ 5 over hoist-crate tonnage>> ... ]` - uses `hoist-crate` (task 4) to show cranes are independent. + used `hoist-crate` (task 4) to show cranes are independent; moved to task 4. ## Structural diff --git a/exercises/concept/garden-gathering/garden-gathering/garden-gathering-tests.factor b/exercises/concept/garden-gathering/garden-gathering/garden-gathering-tests.factor index 39901fc5..7dc58370 100644 --- a/exercises/concept/garden-gathering/garden-gathering/garden-gathering-tests.factor +++ b/exercises/concept/garden-gathering/garden-gathering/garden-gathering-tests.factor @@ -23,15 +23,6 @@ TASK: 2 register list-registrations ] unit-test -! Plot ids continue increasing even after a release. -{ T{ plot { id 3 } { registered-to "Carol" } } } [ - open-garden - "Emma" register drop - "Bob" register drop - 1 release - "Carol" register -] unit-test - TASK: 3 release { V{ T{ plot { id 1 } { registered-to "Emma" } } } } [ open-garden @@ -49,6 +40,15 @@ TASK: 3 release list-registrations ] unit-test +! Plot ids continue increasing even after a release. +{ T{ plot { id 3 } { registered-to "Carol" } } } [ + open-garden + "Emma" register drop + "Bob" register drop + 1 release + "Carol" register +] unit-test + TASK: 4 get-registration { T{ plot { id 1 } { registered-to "Emma" } } } [ open-garden diff --git a/exercises/concept/lighthouse-logbook/lighthouse-logbook/lighthouse-logbook-tests.factor b/exercises/concept/lighthouse-logbook/lighthouse-logbook/lighthouse-logbook-tests.factor index 9d985814..63dc73d0 100644 --- a/exercises/concept/lighthouse-logbook/lighthouse-logbook/lighthouse-logbook-tests.factor +++ b/exercises/concept/lighthouse-logbook/lighthouse-logbook/lighthouse-logbook-tests.factor @@ -6,10 +6,6 @@ TASK: 1 empty-log STOP-HERE -! a fresh log each call (not the shared HS{ } literal) -{ HS{ "x" } } [ empty-log dup "x" sight ] unit-test -{ HS{ } } [ empty-log ] unit-test - TASK: 2 sight { HS{ "NS-1024" } } [ empty-log dup "NS-1024" sight ] unit-test @@ -21,6 +17,10 @@ TASK: 2 sight { HS{ "NS-1024" } } [ empty-log dup "NS-1024" sight dup "NS-1024" sight ] unit-test +! empty-log returns a fresh set each call, not a shared literal +{ HS{ } } +[ empty-log dup "x" sight drop empty-log ] unit-test + TASK: 3 seen? { t } [ HS{ "NS-1024" "WB-203" } "NS-1024" seen? ] unit-test { f } [ HS{ "NS-1024" "WB-203" } "X-99" seen? ] unit-test diff --git a/exercises/concept/poetry-club/poetry-club/poetry-club-tests.factor b/exercises/concept/poetry-club/poetry-club/poetry-club-tests.factor index adbc3da7..841bcdb8 100644 --- a/exercises/concept/poetry-club/poetry-club/poetry-club-tests.factor +++ b/exercises/concept/poetry-club/poetry-club/poetry-club-tests.factor @@ -1,18 +1,19 @@ -USING: exercism-tools kernel poetry-club tools.test ; +USING: disjoint-sets exercism-tools kernel poetry-club tools.test ; IN: poetry-club.tests TASK: 1 new-club -{ "Dickinson" } [ { "Keats" "Byron" "Dickinson" } new-club "Dickinson" swap circle-of ] unit-test +! every poet starts in a writing circle of their own +{ f } [ { "Keats" "Byron" "Dickinson" } new-club [ "Keats" "Byron" ] dip equiv? ] unit-test STOP-HERE -{ "Keats" } [ { "Keats" "Byron" "Dickinson" } new-club "Keats" swap circle-of ] unit-test +{ f } [ { "Keats" "Byron" "Dickinson" } new-club [ "Byron" "Dickinson" ] dip equiv? ] unit-test TASK: 2 collaborate { t } [ { "Keats" "Byron" "Dickinson" } new-club "Keats" "Byron" rot collaborate - [ "Keats" "Byron" ] dip same-circle? ] unit-test + [ "Keats" "Byron" ] dip equiv? ] unit-test { f } [ { "Keats" "Byron" "Dickinson" } new-club "Keats" "Byron" rot collaborate - [ "Keats" "Dickinson" ] dip same-circle? ] unit-test + [ "Keats" "Dickinson" ] dip equiv? ] unit-test TASK: 3 circle-of { "Shelley" } [ { "Shelley" "Blake" } new-club "Shelley" swap circle-of ] unit-test diff --git a/exercises/concept/quayside-crew/quayside-crew/quayside-crew-tests.factor b/exercises/concept/quayside-crew/quayside-crew/quayside-crew-tests.factor index d9ae5d17..9540e250 100644 --- a/exercises/concept/quayside-crew/quayside-crew/quayside-crew-tests.factor +++ b/exercises/concept/quayside-crew/quayside-crew/quayside-crew-tests.factor @@ -17,12 +17,6 @@ TASK: 2 weigh-all TASK: 3 { 0 } [ tonnage>> ] unit-test -! a fresh crane each call — mutating one doesn't affect another -{ 5 0 } [ - 5 over hoist-crate tonnage>> - tonnage>> -] unit-test - TASK: 4 hoist-crate { 35 } [ 35 over hoist-crate tonnage>> ] unit-test { 52 } @@ -33,6 +27,12 @@ TASK: 4 hoist-crate tonnage>> ] unit-test +! a fresh crane each call — mutating one doesn't affect another +{ 5 0 } [ + 5 over hoist-crate tonnage>> + tonnage>> +] unit-test + TASK: 5 crane-tonnage { 0 } [ crane-tonnage ] unit-test { 35 } [ 35 over hoist-crate crane-tonnage ] unit-test diff --git a/exercises/concept/tellers-triage/tellers-triage/tellers-triage-tests.factor b/exercises/concept/tellers-triage/tellers-triage/tellers-triage-tests.factor index dd9fea34..443afb58 100644 --- a/exercises/concept/tellers-triage/tellers-triage/tellers-triage-tests.factor +++ b/exercises/concept/tellers-triage/tellers-triage/tellers-triage-tests.factor @@ -1,14 +1,18 @@ -USING: exercism-tools kernel tellers-triage tools.test ; +USING: exercism-tools heaps kernel tellers-triage tools.test ; IN: tellers-triage.tests TASK: 1 new-queue -{ { } } [ new-queue serve-all ] unit-test +{ t } [ new-queue heap-empty? ] unit-test STOP-HERE +{ 0 } [ new-queue heap-size ] unit-test + TASK: 2 join-queue -{ { "alice" } } [ new-queue "alice" 5 rot join-queue serve-all ] unit-test -{ "bob" } [ new-queue "alice" 5 rot join-queue "bob" 2 rot join-queue next-name ] unit-test +{ 1 } [ new-queue "alice" 5 rot join-queue heap-size ] unit-test +! the customer and priority are stored together +{ "alice" 5 } [ new-queue "alice" 5 rot join-queue heap-peek ] unit-test +{ 2 } [ new-queue "alice" 5 rot join-queue "bob" 2 rot join-queue heap-size ] unit-test TASK: 3 next-name { "bob" } [ new-queue "alice" 5 rot join-queue "bob" 2 rot join-queue