Repository navigation
fix(release): close plan-doctor false positives and lint parity gap; prep v0.9.0 - #149
Merged
Merged
Conversation
…prep v0.9.0 Release review of v0.8.1 plus the unreleased work on main. Three defects found, two fixed here; plan state reconciled and v0.9.0 prepped. plan-doctor rules (CIB-005, closes #132 / ISS-010) The bundled skill judged plans against a stricter private convention than plans/aps-rules.md documents, drawing ~385 warnings on a consumer plan with no real defects. Status now normalises through the documented alias table (Proposed -> Draft, Done -> Complete) before W03/W04/W05/W07 evaluate, W04 counts Done as terminal, and the filename-prefix half of W02 becomes Info reported once per directory. A collapsing rule was added: a finding that hits 100% of files is house style, not signal. The skill now agrees with its sibling aps-planning skill, which already accepted the aliases. release-plan lint parity (CIB-006, closes ISS-011) R001-R004 existed in the Rust linter only, so bash and PowerShell never discovered plans/releases/v*.md at all - 42 files checked against Rust's 50, and a malformed release plan passed both fallbacks silently. Ports added with matching codes, severities and messages; discovery fixed in get_file_type and find_aps_files; output byte-identical across all three. The parity harness also dropped R codes from its findings regex and carried no release fixtures, so CI could not have caught this either. installer manifests Sourcing a new rule module from bin/aps without listing it in the installer manifests broke a vendored bash CLI on startup. Fixed across all fourteen sites; a new test derives the required list from bin/aps itself. plan reconciliation PROMPTS-001 and EXAMPLES-001 marked Complete against their merge evidence, prompts and examples modules closed, cli-redesign moved to In Progress, D-045 recorded as decided, roadmap overview rewritten to match what shipped. aps audit goes from two findings to none. The completed archive was filing v0.3.0-v0.7.0 tables under an Unreleased heading and never rolled v0.4.0-v0.8.1 at all; the false heading is fixed and the ~60-item backfill left to REL-005 rather than reconstructed by hand (ISS-012). new backlog items CIB-007 (ISS-013): neither fallback CLI can answer --version, though both stamp a version into project config - the exact question the pin-mismatch warning raises. CIB-008 (ISS-014, ISS-015): three residual cross-CLI lint divergences and release rules weaker than they read, both reported rather than fixed in one CLI only. v0.9.0 prep Version stamped to 0.9.0 across fifteen files; the checklist's stray-version grep is empty. Release narrative, changelog section and README highlights written. Tag, crates.io publish and Scoop hash remain outstanding. Verification: cargo test (201), clippy -D warnings, cargo fmt --check, test/run.sh, test/cli-parity.sh, markdownlint, aps lint (50 files, no issues), aps audit (no findings). PowerShell parity verified by the porter under pwsh 7.4.6; the local parity run skips that leg and CI covers it. APS: CIB-005 CIB-006 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N2expUMF61CKDC5zxZUeev
The release template writes module references as markdown links (`**APS modules:** [name](../modules/name.aps.md)`), and v0.5.0 follows that; v0.9.0 was using plain module names. Linking them makes the record machine-readable for release tooling, which resolves work-item scope by following those links out of `## What Ships`. With the links in place the record resolves 4 modules and 20 work items in scope, correctly identifying CLI-003 as the one item awaiting release closeout. APS: CIB-006 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N2expUMF61CKDC5zxZUeev
Implements the four subcommands REL-005 specifies, in the Rust CLI only.
new scaffold plans/releases/v<version>.md from the template
status modules, lifecycle dates, and work-item completion for a release
notes draft markdown from Complete items since the previous release
close advance Merged/Released items to Complete with release evidence
The closeout reads the prose release record plus the plan tree directly.
No JSON sidecar: APS is markdown-first, and a sibling project's equivalent
script was unusable precisely because it expected structured input the
records do not carry.
Two safety properties worth keeping. Dry-run is the default and was
verified to write nothing, by diffing a scratch copy of plans/ afterwards.
And --apply refuses a record whose Status is not Shipped/Released/Archived
unless --force, so a release cannot be closed out mid-prep; a record with
no ship evidence warns that it would stamp today rather than doing so
silently, with --date and --tag to override.
Two defects were found by running the commands against this repo's real
release records rather than fixtures, and fixed:
- status reported an unshipped release as shipped today, defaulting the
ship date to the planning date. v0.9.0 now renders Cut and Shipped as
absent, which is the honest answer.
- the (cut) label used by the v0.8.0 and v0.8.1 records was not
understood, so the cut date was reported as the ship date and the real
one dropped.
Module resolution follows markdown links to module plans out of
## What Ships, which the release template already specified; the v0.9.0
record was updated to use them in bc8d637.
REL-005 stays In Progress, not Complete. D-039 requires the bash and
PowerShell CLIs to carry the same surface with shared-fixture coverage
before a new command surface is done, and only the Rust phase has landed.
Phasing is allowed by the work item; shipping one-CLI-only is not. The
CHANGELOG entry sits under Unreleased rather than in v0.9.0 for the same
reason, so the release narrative's out-of-scope list stays accurate.
Verification: cargo fmt --check, clippy -D warnings, cargo test (230),
test/run.sh, test/cli-parity.sh, markdownlint, aps lint (50 files, no
issues).
APS: REL-005
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N2expUMF61CKDC5zxZUeev
CI's Rust CLI job failed on 40a0517 with clippy::unnecessary_sort_by at src/release.rs:95. Applied the lint's own suggestion: sort_by_key with an owned key, which version_key already returns. Root cause of the miss: CI installs dtolnay/rust-toolchain@stable and the repo pins no toolchain, so CI ran clippy 0.1.98 while this environment had 0.1.94 — a version where the lint does not exist. Local clippy reported zero errors on the same code. Installed 1.98.1 to match CI and re-verified rather than guessing: cargo clippy --locked --all-targets -- -D warnings 0 errors cargo fmt --check clean cargo test 230 passed test/run.sh all passed test/cli-parity.sh bash = Rust aps lint plans 50 files, no issues Behaviour is unchanged: version_key returns an owned (Vec<u64>, u8, String), so sort_by_key produces the same ordering. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N2expUMF61CKDC5zxZUeev
…ain gap
Follow-up bookkeeping from the aps release implementation and the CI
failure it caused.
REL-005 parity debt made specific. The ports implement a handover spec
covering flags, byte-exact output strings, exit codes, version ordering,
lifecycle-bucket classification, and write order. One prerequisite is
recorded: the pre-sweep v0.5.0 corpus that proves the closeout lives
inline in the Rust test module and must move to test/fixtures/release/ so
all three CLIs assert against the same bytes.
Three deliberate deviations from REL-005's spec are now on the record
rather than left implicit:
- the v0.5.0 sweep is reproduced semantically, not textually, because
the hand sweep wrote free-form status prose with evidence inside the
Status value; the tool writes a canonical status plus a separate
Released line that is lint-correct and idempotent
- notes windows by date, not by git tag range, since shelling out to
git would breach the no-runtime-dependencies constraint
- release.rs duplicates a small filename check rather than reusing the
private one in lint.rs
Also recorded: a latent defect found only by running the real binary.
propagate_version on the root Cli auto-adds --version to every subcommand
and collides with a positional named version, which clap detects at parse
time rather than build time, so it was a runtime panic. It is now pinned
by a clap_surface_builds test that debug_asserts the whole CLI surface.
CIB-009 (ISS pending): CI installs the latest stable Rust and the repo
pins no toolchain, so local clippy silently disagrees with CI's. That is
what let the unnecessary_sort_by lint through despite a clean local run.
The item proposes rust-toolchain.toml with channel = "stable" and names
the trade-off against pinning an exact version, leaving the decision open.
aps lint clean (50 files), aps audit no findings, markdownlint clean.
APS: REL-005
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N2expUMF61CKDC5zxZUeev
The previous commit referenced a pending issue entry for CIB-009 without creating it. Every other CIB item in this review carries an issues.md entry; this one now does too, and CIB-009 cross-references it. APS: CIB-009 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N2expUMF61CKDC5zxZUeev
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Release review of v0.8.1 plus the unreleased work on
main. CI was green and every distribution pin agreed, but three defects surfaced. Two are fixed here; the third is left to the tool being built for it.Closes #132.
Defects fixed
plan-doctor flooded healthy plans with false warnings (CIB-005, ISS-010)
The bundled skill judged plans against a stricter private convention than
plans/aps-rules.mddocuments. On a consumer plan with no real defects it drew roughly 385 warnings: 162Doneand 132Proposedstatuses false-flagged by W03, and all 91 module filenames flagged by W02. Because the skill bytes are embedded in the binary, a consumer could not patch it locally without desyncing.aps-managed.json.Status now normalises through the documented alias table before W03, W04, W05 and W07 evaluate. W04 counts
Doneas terminal. The filename-prefix half of W02 becomes a new Info code reported once per directory with a count, and a consistently applied undocumented status such asArchivedgets its own Info code rather than sharing W03.The fix also added a general collapsing rule to the report section: a finding that hits 100% of files is house style, not signal. That addresses the cause rather than the three symptoms. The skill now agrees with its sibling
aps-planningskill, which already accepted the aliases.Release-plan lint existed in one of three CLIs (CIB-006, ISS-011)
R001 to R004 were implemented in the Rust linter only, so the bash and PowerShell linters never discovered
plans/releases/v*.mdat all. On this repo's own plans bash checked 42 files where Rust checked 50, and a malformed release plan passed both fallbacks silently. D-039 makes bash and PowerShell maintained peers, which supersedes REL-003's "aps lint is now the Rust CLI" rationale.Discovery was the larger half of the defect:
get_file_typeneeded a release branch at the Rust classifier's precedence andfind_aps_filesneeded areleases/clause, pinned to byte-order sorting to match Rust. Full output including--jsonis now byte-identical across all three CLIs.CI could not have caught this either. The parity harness carried no release fixtures, and its findings regex matched only
(E|W)[0-9]{3}, so R codes were invisible even when present. Both are fixed.Installer manifests did not carry the new rule module
Sourcing a rule module from
bin/apswithout listing it in the installer manifests breaks a vendored or scaffolded bash CLI on startup. Fixed across all fourteen sites, and a new test derives the required list frombin/apsitself so the next rule module cannot repeat it.aps release— REL-005, Rust phaseScope grew after the release prep landed, so this PR now also carries the top-ranked Ready work item. Four subcommands:
newscaffolds a record from the template,statusreports modules and work-item completion,notesdrafts markdown from Complete items since the previous release, andcloseadvancesMerged/Releaseditems toCompletewith a release-evidence line.The closeout reads the prose record plus the plan tree directly, with no JSON sidecar, which is the work item's stated design constraint. Dry-run is the default and was verified to write nothing by diffing a scratch copy of
plans/afterwards. Two safety properties are worth calling out:--applyrefuses a record whose Status is not Shipped/Released/Archived unless--force, so a release cannot be closed out mid-prep, and a record missing ship evidence warns that it would stamp today rather than doing so silently.Two defects were caught by running it against this repo's real records rather than fixtures, and fixed:
statusreported an unshipped release as shipped today, defaulting the ship date to the planning date. A release tool that asserts an unshipped release shipped is worse than one that says nothing.(cut)label used by the v0.8.0 and v0.8.1 records was not understood, so the cut date was reported as the ship date and the real one dropped.REL-005 stays In Progress, not Complete. D-039 requires bash and PowerShell to carry the same surface with shared-fixture coverage before a new command surface is done, and only the Rust phase has landed. Phasing is explicitly allowed by the work item; shipping one-CLI-only is not. The changelog entry therefore sits under
Unreleasedrather than in the v0.9.0 section, so the release narrative's out-of-scope list stays accurate.Plan reconciliation
aps auditwent from two findings to none, and the only lint warning is gone.Unreleasedheading, and v0.4.0 through v0.8.1 were never rolled at all. The false heading is fixed; the roughly sixty-item backfill is left to REL-005 rather than reconstructed by hand, which would have meant inventing attribution. Tracked as ISS-012.New backlog items
Each carries an issue-log entry:
aps --version, though both know their version well enough to stamp it into project config. That is the exact question the pin-mismatch warning raises.v0garbage.md). Both reported rather than fixed in one CLI, which would have re-opened the gap just closed.v0.9.0 prep
Version stamped to 0.9.0 across fifteen files; the release checklist's stray-version grep comes back empty and both CLIs report 0.9.0. Release narrative, changelog section and README highlights written.
Not done, and deliberately left to a human: the tag push,
cargo publish, and the Scoop hash, which needs the publishedSHA256SUMS. The release narrative marks those channels pending.Verification
PowerShell parity was verified under pwsh 7.4.6, including that the ported rules actually fire rather than silently matching nothing. The local parity run skips that leg because pwsh is not on PATH here; CI covers it and passed green on the two earlier commits.
🤖 Generated with Claude Code
https://claude.ai/code/session_01N2expUMF61CKDC5zxZUeev