A late change, then the card you hand your team
By the end you will have pushed one late requirement through your framework's update path rather than around it, watched the new late-change tests go red then green, and filled an eight-question comparison card your team can read without taking this course.
This continues where Exercise 2
finished in day3-work. Starting here? Do
your track's install-and-init from Exercise 1, then
Exercise 2 through its self-check (its checkpoint saves time).
Setup
Quit Claude Code and paste this from the course root (cd .. first if you are in
the copy); later commands run inside day3-work, answers in
../work/.
mkdir -p work && { [ -d day3-work ] || cp -R sandbox/swift day3-work; } && cd day3-work && cp ../day-3-choose-your-adventure/starter/late-change.md . && cp ../day-3-choose-your-adventure/starter/acceptance/ChangeCLateChangeTests.swift Tests/LedgerKitTests/ && ls late-change.md Tests/LedgerKitTests/ChangeCLateChangeTests.swift
It lists both files.
mkdir -p work && { [ -d day3-work ] || cp -R sandbox/kotlin day3-work; } && cd day3-work && cp ../day-3-choose-your-adventure/starter/late-change.md . && cp ../day-3-choose-your-adventure/starter/acceptance/ChangeCLateChangeTest.kt src/test/kotlin/ && ls late-change.md src/test/kotlin/ChangeCLateChangeTest.kt
It lists both files.
Start claude there, with a second tab for shell commands. Your framework can read
this test file; step 3's red, before any code changes, proves it bites. Do not run the suite
before then.
Steps
Read
late-change.md. Before touching the framework, add one sentence to../work/map.mdpredicting which existing artifacts must change.Start the update with your tab's step 2 under Your track. Each prompt stops before code.
-
Run the suite at that stop. At least one new test fails, and nothing outside the late-change class does. A build applying the newest rule everywhere, as BMAD's checkpoint does, fails both. One that only handles a move to a later day fails just the earlier-day assertion (start day 5 to 2 from 2026-04-03) in
testStartDayMovesOnTheEffectiveDate.Swift laneswift test 2>&1 | grep -A1 "Test Suite 'ChangeCLateChangeTests' [pf]"A stand-in build that reads only the first cycle rule printed:
Test Suite 'ChangeCLateChangeTests' failed at …. Executed 2 tests, with 6 failures (0 unexpected) in … (…) secondsXCTest counts failed
XCTAssertEqualcalls (the two tests hold seven), so yours may differ. In plainswift test, everyerror:line namesChangeCLateChangeTests:…/Tests/LedgerKitTests/ChangeCLateChangeTests.swift:45: error: -[LedgerKitTests.ChangeCLateChangeTests testStartDayMovesOnTheEffectiveDate] : XCTAssertEqual failed: ("2026-04-05") is not equal to ("2026-04-20") - from 2026-04-01 the cycle opens on the 20thKotlin lane./gradlew testThe same stand-in printed:
> Task :test FAILED ChangeCLateChangeTest > testPeriodsBeforeTheChangeKeepTheOldStartDay() FAILED … ChangeCLateChangeTest > testStartDayMovesOnTheEffectiveDate() FAILED … … tests completed, 2 failed, 1 skipped … BUILD FAILED in …Each test's
FAILEDline namesChangeCLateChangeTest;> Task :test FAILEDis Gradle's task line. Let the framework implement, with your tab's step 4. Do not hand-edit source.
-
Run the same check, then Exercise 2's bounds check: the known-issue run and a diff against the shipped lane.
Swift laneswift test 2>&1 | grep -A1 "Test Suite 'ChangeCLateChangeTests' [pf]" LEDGER_RUN_KNOWN_ISSUES=1 swift test diff -ruN -x .build -x build -x .gradle -x .kotlin -x .claude -x specs -x run-log.md ../sandbox/swift . | grep '^diff ' | grep -v -e ' \./openspec/' -e ' \./docs/' -e ' \./_bmad'With the reference in place, the first:
Test Suite 'ChangeCLateChangeTests' passed at …. Executed 2 tests, with 0 failures (0 unexpected) in … (…) secondsThe second, one
error:line namingtestLEDGER7_JPYRendersWithTwoDecimalPlaces:Test Suite 'All tests' failed at …. Executed … tests, with 1 failure (0 unexpected) in … (…) secondsThe third:
diff -ruN … ../sandbox/swift/Sources/LedgerKit/StatementPeriods.swift ./Sources/LedgerKit/StatementPeriods.swift diff -ruN … ../sandbox/swift/Tests/LedgerKitTests/ChangeCAcceptanceTests.swift ./Tests/LedgerKitTests/ChangeCAcceptanceTests.swift diff -ruN … ../sandbox/swift/Tests/LedgerKitTests/ChangeCLateChangeTests.swift ./Tests/LedgerKitTests/ChangeCLateChangeTests.swift diff -ruN … ../sandbox/swift/Tests/LedgerKitTests/Fixtures/cycle-config.tsv ./Tests/LedgerKitTests/Fixtures/cycle-config.tsv diff -ruN … ../sandbox/swift/change-c-brief.md ./change-c-brief.md diff -ruN … ../sandbox/swift/late-change.md ./late-change.mdKotlin lane./gradlew test LEDGER_RUN_KNOWN_ISSUES=1 ./gradlew test diff -ruN -x .build -x build -x .gradle -x .kotlin -x .claude -x specs -x run-log.md ../sandbox/kotlin . | grep '^diff ' | grep -v -e ' \./openspec/' -e ' \./docs/' -e ' \./_bmad'With the reference in place:
BUILD SUCCESSFUL in …, then:KnownIssueTest > LEDGER-7 the JPY row renders with decimal places it should not have() FAILED … … tests completed, 1 failed … BUILD FAILED in …The third:
diff -ruN … ../sandbox/kotlin/change-c-brief.md ./change-c-brief.md diff -ruN … ../sandbox/kotlin/late-change.md ./late-change.md diff -ruN … ../sandbox/kotlin/src/main/kotlin/ledgerkit/StatementPeriods.kt ./src/main/kotlin/ledgerkit/StatementPeriods.kt diff -ruN … ../sandbox/kotlin/src/test/kotlin/ChangeCAcceptanceTest.kt ./src/test/kotlin/ChangeCAcceptanceTest.kt diff -ruN … ../sandbox/kotlin/src/test/kotlin/ChangeCLateChangeTest.kt ./src/test/kotlin/ChangeCLateChangeTest.kt diff -ruN … ../sandbox/kotlin/src/test/resources/cycle-config.tsv ./src/test/resources/cycle-config.tsvEvery diff line is under your lane's source or test folders, or is the brief or
late-change.md; theStatementPeriodsline names your framework's implementation file, your framework's own test file may add a line beside it, and on the LID tab./CLAUDE.mdappears. Record what became of the requirement you shipped, from the files your tab's step 6 names. Under your prediction in
../work/map.md, write one line: restated, edited in place, or left beside a second copy, and where. It is question six.-
Copy the blank card into
../work/; the guard skips one you have started.{ [ -f ../work/comparison-card.md ] || cp ../day-3-choose-your-adventure/starter/comparison-card.md ../work/; } && ls ../work/comparison-card.mdUnder each of its eight headings, write one bullet starting
-, at least a sentence long, from what you observed today, not from this course's pages. Questions two to five restate your map's first four answers; six is step 6's line. For eight, name the failure from Lesson 4, today's date, and the signal Lessons 1 and 2 asked you to pick, with the date you will count it again. Copy the signal from the## Signal (Lessons 1 and 2)line of../work/map.md; if that section holds no line of yours (or only the template's placeholder), replace it with one first. A signal names what you count, how often, and when you count next, for example: tickets where someone asked what done meant, counted weekly for two weeks, next count on the date two weeks from today. Read your card out loud once. An answer you cannot say without "it depends" is one you have not observed: look at the file again.
Compare your card with the reference card below, which is another track's card on purpose.
Your track
OpenSpec: start the update (step 2)
Exercise 2 did not archive its change, so archive it first, from the terminal rather than
the chat: the archive is a file move plus a spec sync, and the command line does it in one
run with no agent turn to wait for. The first line prints the change's name
(add-statement-periods if you took the checkpoint). --yes answers
the questions the archive can stop on: whether to sync the delta specs into
openspec/specs/, and, if a task is still unchecked (for example one that needs git,
which cannot run in day3-work), whether to continue anyway. With every task
complete the archive prints Task status: ✓ Complete and --yes just
skips the sync question.
openspec list
openspec archive <change-name> --yes
openspec list
ls openspec/specs
The archive moves the change into changes/archive/ under a date-prefixed
name; /opsx:archive in the chat does the same with the questions asked one at a
time. Then look at what the last two lines printed.
On the build machine, openspec list with nothing in flight printed No
active changes found. A ## MODIFIED Requirements delta needs its
requirement in openspec/specs/. Then propose:
/opsx:propose The statement cycle start day can change on a given date. The full requirement is in late-change.md in this project folder — read that file. This modifies a requirement that has already shipped and been archived, so express it as a MODIFIED delta against the existing requirement rather than as a new one. Do not write implementation code in this step.
OpenSpec: implement (step 4)
/opsx:apply
If it asks what to work on:
Work the task list for the late-change proposal. Implement only what its delta spec describes. Stay inside Sources/LedgerKit/ and Tests/LedgerKitTests/ on the Swift lane, or src/main/kotlin/ and src/test/kotlin/ on the Kotlin lane, and stop when the task list is complete.
OpenSpec: where to look (step 6)
grep -rn '^## ' openspec/changes/*/specs
The new delta's heading is the record; this exercise stops before archiving it. OpenSpec
1.13.1's sync, which /opsx:archive runs, updates a MODIFIED requirement in
place in openspec/specs/, keeping unmentioned scenarios; an ADDED one under a new
name lands beside the old.
LID: start the update (step 2)
Start the change as a plain message, as Exercise 2 did;
/linked-intent-dev:update-lid is for reconciling drift or changing mode. LID's skill
walks a change from the high-level design down, stopping after each phase:
The statement cycle start day can change on a given date. The full requirement is in late-change.md in this project folder — read that file. Walk the arrow from the top: check whether the high-level design covers this change, then update the low-level design, then the LK-PERIOD EARS requirements, then the tests, then the code. Do not edit the code first and back-fill the design. Tell me at each step which artifact you changed and which ID it carries. Stop for my review at the end of each phase.
Run step 3 at the stop after the requirements.
LID: implement (step 4)
After reading the changed requirement:
Continue down the arrow: update the tests for the LK-PERIOD requirements you changed first, then the code, and keep each // @spec annotation on the topmost function that owns the behaviour. Do not change any requirement to match code you have already written — if the code cannot satisfy a requirement, stop and tell me which one.
When it ends, list the // @spec annotations:
grep -rn '@spec LK-PERIOD' Sources Tests
grep -rn '@spec LK-PERIOD' src
LID: where to look (step 6)
grep -rn 'LK-PERIOD' docs/intent
One ID with changed text is an edit in place; a new ID repeating an old one is a second
copy. Status is an [x], [ ] or [D] marker, or the
Verified by: line the build run wrote.
BMAD: start the update (step 2)
Setting a shipped spec's frontmatter status back to
ready-for-dev is this course's path, not a documented one:
The statement cycle start day can change on a given date. The full requirement is in late-change.md in this project folder — read that file. Update the existing spec rather than creating a second one, set its status back to ready-for-dev when the spec is updated, and stop there so I can review before you build.
Before step 3, check for one spec, status ready-for-dev:
ls _bmad-output/implementation-artifacts
grep -rn '^status:' _bmad-output/implementation-artifacts --include=SPEC.md --include='spec-*.md'
BMAD: implement (step 4)
After reviewing the spec, hand it to /bmad-build: in 6.12.0 its routing sends
a ready-for-dev spec to implementation, then review, skipping the phase-4 chain's
bmad-sprint-planning and bmad-code-review. Approve the
uv command it asks to run; without uv it halts. Rename the folder in
the prompt if yours differs:
/bmad-build Build the updated spec in _bmad-output/implementation-artifacts/statement-periods/SPEC.md. Implement only what changed for the late requirement, keep every change inside Sources/LedgerKit/ and Tests/LedgerKitTests/ on the Swift lane, or src/main/kotlin/ and src/test/kotlin/ on the Kotlin lane, and tell me which stories changed status. Do not use git in this folder: wherever your workflow needs a diff, use the output of diff -ruN -x .build -x build -x .gradle -x .kotlin -x _bmad -x _bmad-output -x .claude ../sandbox/swift . (on the Kotlin lane, ../sandbox/kotlin) as the provided diff, and do not commit.
BMAD: where to look (step 6)
Run the same ls and grep. A second folder or a
spec-*.md file there is a second copy; one SPEC.md with changed
requirement text is an edit in place.
If you see this instead
The late-change file does not compile. Either this copy has no Change C (finish Exercise 2), or your framework named a type differently from the brief; rename through its artifact, never the test file.
…/Tests/LedgerKitTests/ChangeCLateChangeTests.swift:14:34: error: cannot find type 'StatementCycle' in scope
e: …/src/test/kotlin/ChangeCLateChangeTest.kt:19:32 Unresolved reference 'StatementCycle'.
Step 3 prints nothing. A build returning no periods for a two-rule cycle
crashes; plain swift test shows:
error: Exited with unexpected signal code 5
…
Swift/ContiguousArrayBuffer.swift:692: Fatal error: Index out of range
That counts as red.
Step 3 shows an exception. A build returning no periods for a two-rule
cycle fails both tests with java.lang.IndexOutOfBoundsException. That counts as
red.
Step 3 is already green. Your framework generalized the cycle in Exercise 2, moves to an earlier day included: say so under question six: you never saw these assertions bite.
At step 5 another test is red, or the diff leaves bounds. Send Exercise 2's repair prompt for your tab with the failing output; do not edit the test.
On the OpenSpec tab, openspec/specs/ is empty after the
archive. It skipped the sync, so a MODIFIED delta has nothing to modify; OpenSpec's
archive text calls that sync-blocked. Propose anyway and write that under question six.
Self-check
Run these, from inside day3-work
First, the two late-change tests.
swift test 2>&1 | grep -A1 "Test Suite 'ChangeCLateChangeTests' [pf]"
Pass is passed with Executed 2 tests, with 0 failures. No output
means a compile error or a crash; plain swift test shows which.
./gradlew test
grep -o 'tests="[0-9]*" skipped="[0-9]*" failures="[0-9]*" errors="[0-9]*"' build/test-results/test/TEST-ledgerkit.ChangeCLateChangeTest.xml
Pass is BUILD SUCCESSFUL in … from the first command and
tests="2" skipped="0" failures="0" errors="0" from the second. After
BUILD FAILED with e: lines, the report is left over from an earlier
run.
Then the bounds.
LEDGER_RUN_KNOWN_ISSUES=1 swift test 2>&1 | grep -e ': error: ' -e "Test Suite 'All tests' [pf]" -A1
Pass is one error: line, naming
testLEDGER7_JPYRendersWithTwoDecimalPlaces, and one failure in the summary:
…/Tests/LedgerKitTests/KnownIssueTests.swift:35: error: -[LedgerKitTests.KnownIssueTests testLEDGER7_JPYRendersWithTwoDecimalPlaces] : XCTAssertEqual failed: ("-¥1,280.00") is not equal to ("-¥128,000") - LEDGER-7: LK-014 (JPY) rendered the wrong amount.
expected: -¥128,000
--
Test Suite 'All tests' failed at ….
Executed … tests, with 1 failure (0 unexpected) in … (…) seconds
LEDGER_RUN_KNOWN_ISSUES=1 ./gradlew test
Pass is … tests completed, 1 failed, on KnownIssueTest
alone.
Then the card.
grep -c '^- .\{40,\}' ../work/comparison-card.md
Pass is 8; the blank card prints 0.
Done when: a late-change test was red at step 3,
both were green at step 5 with nothing else red, JPY alone fails the known-issue run, the diff
stays in bounds, the card check prints 8, and ../work/map.md holds step
1's prediction and step 6's line.
Reference answers
Open these after your card has eight answers. Unmarked lines are documented, or observed on the build machine on 2026-09-22; "this course's path" marks how this course runs the tool.
Your tab shows a different track's card, not your own: you already have your track's answers from your files, so the useful comparison is with a tool you did not run. Go heading by heading and look for where the two cards differ, then check that each of your answers names a file you opened rather than something the other card could have told you.
You ran OpenSpec: the reference card for BMAD
- Installs.
npx bmad-method install, needing Node.js 20.12 or later anduv; on the build machine 6.12.0 wrote 235 files (a_bmad/tree, 29 skills under.claude/skills/, an empty_bmad-output/) and leftCLAUDE.mdalone. - Specs live in a spec folder under
_bmad-output/implementation-artifacts/:SPEC.md,stories.yaml, andstories/<id>-<slug>.md. - Creates a spec:
/bmad-buildfollowed by what you want built; on Change C it wrote the spec folder with statusready-for-dev. - Implements:
/bmad-buildon aready-for-devspec goes to implementation, then a review step. The documented phase-4 chain isbmad-sprint-planning, thenbmad-build, thenbmad-code-review. - After ship the spec's frontmatter
statuscarries its state, one of the six values that drive BMAD's unattended loop. - Late requirement: update the spec, set its status back to
ready-for-dev, and hand it to/bmad-build(this course's path, not a documented one). Not run on the build machine. - Would not want: every skill starts by running a
uvcommand, and on the build machine a 6.12.0 session without permission to run shell commands halted before planning. In a locked-down environment it does not start. A judgement built on that observation. - Adopt it to fix Lesson 4's third question: work that starts before anyone has decided scope, sequence or review.
You ran LID: the reference card for OpenSpec
- Installs.
npm install -g @fission-ai/openspec@latest, needing Node 20.19.0 or later; pinned at 1.13.1. On the build machineopenspec initwrote fifteen files:openspec/config.yaml, two.gitkeepplaceholders underopenspec/, and six commands and six skills under.claude/. It wrote nothing intoCLAUDE.mdorAGENTS.md. - Specs live in
openspec/specs/; changes in flight inopenspec/changes/<name>/, which on Change C heldproposal.md,design.md,tasks.md,.openspec.yamland one delta spec per capability. - Creates a spec:
/opsx:propose <feature description>. - Implements:
/opsx:apply, working the task list. - After ship:
/opsx:archivemoves the change intochanges/archive/under a date-prefixed name. - Late requirement: the documented path is a second proposal whose
delta sits under
## MODIFIED Requirements, then apply and archive; the archive updates the shipped requirement in place. Not run on the build machine. - Would not want: telemetry is on unless each machine turns it off,
with
openspec config set telemetry.enabled falseorOPENSPEC_TELEMETRY=0, which a regulated team has to remember everywhere. A judgement. - Adopt it to fix Lesson 4's second question: a shipping codebase that changes a ticket at a time, where every spec attempt grows into a document about the whole system.
You ran BMAD: the reference card for LID
- Installs two Claude Code plugins,
/plugin marketplace add jszmajda/lidthen/plugin install linked-intent-dev@jszmajda-lidand/plugin install arrow-maintenance@jszmajda-lid, which at commit 831c1959 added about 1,090 tokens to every session on the build machine; the first/arrow-maintenance:map-codebaserun there appended a 29-line## LIDsection toCLAUDE.mdwith no marker comments; that run could not load the plugin's own skill body, so your run may write a different block. - Specs live in
docs/intent/<segment>/<segment>-specs.md, beside a design file per segment, below a high-level design atdocs/high-level-design.mdand an overlay index atdocs/arrows/index.yaml. - Creates a spec:
/arrow-maintenance:map-codebasebuilds the first tree from existing code; a later change starts from a prompt that describes it (this course's path). - Implements: tests, then code, are the last links of the same
cascade, and code carries
// @specwith the requirement IDs. - After ship nothing is archived: the requirement stays in the tree,
marked
[x]implemented,[ ]gap or[D]deferred in LID's documented format; the build's Change C run wrote aVerified by:line instead. - Late requirement: a prompt that walks the arrow from a check of the high-level design down through the low-level design, requirements, tests and code, stopping after each phase (this course's path). Not run on the build machine.
- Would not want: it writes into
CLAUDE.mdwithout markers, so the section is yours to keep short by hand. A judgement. - Adopt it to fix Lesson 4's first question: nobody can say which requirement a line of code serves.
Where the late change lands, per track
This build did not push the late change through any framework. Change C's planning ran through all three on 2026-09-22; the late change did not. So there is no build-machine observation to compare against, only the path each prompt on this page asks for. Your run is the observation: one machine, one date.
- OpenSpec: the record is a second change folder whose delta sits under
## MODIFIED Requirements. Archiving it would update the shipped requirement inopenspec/specs/in place, keeping the scenarios the delta does not mention; an ADDED requirement under a new name would sit beside the old one as a second copy. - LID: the record is the low-level design and the LK-PERIOD requirement, edited in place, with the tests and code below them. A second LK-PERIOD requirement that says the same thing as the first is a duplicate.
- BMAD: the record is the same
SPEC.md, its status moved back toready-for-devand on through/bmad-build. A second spec folder, or aspec-<slug>.mdfile beside it (where/bmad-buildwrites a spec it takes as new work), is a duplicate.
No implementation code is given for any track, and none for your own.
Reset
Quit Claude Code, then from inside day3-work run your lane's line;
../work/ is untouched.
cd .. && rm -rf day3-work && cp -R sandbox/swift day3-work && cd day3-work
cd .. && rm -rf day3-work && cp -R sandbox/kotlin day3-work && cd day3-work
Then run your track's install-and-init steps from Exercise 1, then Exercise 2 through its
self-check, then cd .. and paste this page's setup
block. OpenSpec on your PATH and the LID plugins at user scope survive; BMAD lives in the
project, so rerun its installer. Do not reset at the end: Lesson 5 and the wrap-up's third
challenge read this day3-work.
Sources
openspec-08,openspec-09,openspec-10,openspec-18—/opsx:propose <feature description>,/opsx:apply,/opsx:archiveandopenspec list, from OpenSpec's getting-started guide.openspec-14,openspec-20—## MODIFIED Requirementsand the date-prefixed archive folder, from OpenSpec's concepts page.openspec-obs-11—openspec archive <change-name> --yesskips the archive's confirmation prompts and continues past incomplete tasks, read from the command's option table and archive routine in OpenSpec 1.13.1's package, in this course's snapshot atday-3-choose-your-adventure/starter/vendored/openspec-1.13.1/package/(dist/cli/index.js,dist/core/archive.js).- What
/opsx:archiveasks (sync, incomplete tasks, sync-blocked) and what its sync does with MODIFIED and ADDED requirements — OpenSpec 1.13.1's own command text, in this course's snapshot atday-3-choose-your-adventure/starter/vendored/openspec-1.13.1/project-files/.claude/commands/opsx/(archive.md,sync.md). openspec-01,openspec-19— the npm install and the telemetry opt-out, from OpenSpec's installation guide and README.openspec-03— the Node requirement, from the npm registry entry;openspec-04— version 1.13.1, from the latest release;openspec-06— the foldersinitscaffolds, from OpenSpec setup.lid-01,lid-02,lid-03,lid-06,lid-08— the install commands,/linked-intent-dev:update-lidand/arrow-maintenance:map-codebase, from the LID repository.- The six phases from a high-level design check down to code, with a stop after each, and the
downward cascade when requirements change — LID's own text at commit 831c1959, in this
course's snapshot at
day-3-choose-your-adventure/starter/vendored/lid-831c1959/lid/(plugins/linked-intent-dev/skills/linked-intent-dev/SKILL.md,README.md). lid-11,lid-12— the// @specannotation and the status markers, from LID's AGENTS.md.lid-25— commit 831c1959, from the LID commit history.bmad-01,bmad-05— the install command and the Node requirement, from Install BMAD;bmad-07—/bmad-build, from Build your first change.bmad-06,bmad-11— theuvrequirement and the phase-4 chain, from the BMAD changelog.- How
/bmad-buildhalts withoutuv, routes aready-for-devspec to implementation, and writes a spec it takes as new work tospec-<slug>.mdin the implementation-artifacts folder — BMAD 6.12.0's own skill text, in this course's snapshot atday-3-choose-your-adventure/starter/vendored/bmad-6.12.0/project-files/.claude/skills/bmad-build/(SKILL.md,step-01-clarify-and-route.md,step-02-plan.md), with that folder set in_bmad/config.toml. bmad-12,bmad-13— the six status values and the spec folder layout, from BMAD's autonomous development loops page.- Observed on the build machine on 2026-09-22, one scratch copy of the Swift lane per track:
openspec-obs-02,openspec-obs-03(the fifteen files, the untouchedCLAUDE.md),openspec-obs-06(the empty-state line, on a freshly initialized project),openspec-obs-07(the change folder);lid-obs-02(the LID plugins installed at user scope),lid-obs-04(the token cost),lid-obs-05,lid-obs-06(the## LIDsection and the mapped tree),lid-obs-07(that mapping run could not load the plugin's own skill body);bmad-obs-01(version 6.12.0),bmad-obs-05(the 235 files),bmad-obs-10(no skill starts without itsuvcommand),bmad-obs-11(the Change C spec folder). - The quoted test output was recorded on this machine on 2026-09-23 in a fresh copy made by this page's setup block, one run per lane, with the reference Change C implementation copied in to stand for a framework's result; for step 3, a variant of it that reads only the first cycle rule, and for the empty-periods troubleshooting, one that returns no periods for a two-rule cycle.