Skip to content

feat: implement the Repository Hygiene and Layout Policy (design 2026-07-28) - #672

Merged
logbie merged 7 commits into
mainfrom
claude/policy-implementation-docs-fbmpeh
Jul 31, 2026
Merged

feat: implement the Repository Hygiene and Layout Policy (design 2026-07-28)#672
logbie merged 7 commits into
mainfrom
claude/policy-implementation-docs-fbmpeh

Conversation

@logbie

@logbie logbie commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator

Implements the approved Repository Hygiene and Layout design (Engineering/designs/2026-07-28-repository-hygiene-design.md) end to end: binding root policy, machine-readable enforcement profile, dependency-free checker, the full initial migration of legacy content, reference updates across governance/contributor/agent docs, and a blocking CI gate.

What this PR does

Policy & authority

  • Adds root REPOSITORY_HYGIENE.md (binding; incorporated as GOVERNANCE.md §3.8) and .repo-hygiene.toml (root allowlist, forbidden/retired paths, binary and size declarations, personal-path placeholders, [[generated]] exceptions, version sources, approved output roots).
  • References the policy from CONTRIBUTING.md, README.md, Docs/README.md, Docs/contributing/index.md, AGENTS.md, CLAUDE.md; .cursor/.jules files become thin adapters to root policy (perf lessons promoted to History/perf-lessons.md).

Enforcement

  • scripts/check_repo_hygiene.py — static mode (tracked-tree rules incl. archive sha256 verification, experiment Review-by expiry, and product-version drift across Cargo/npm/wix/lock mirrors) and working-tree mode (post-suite cleanliness outside approved output roots).
  • Blocking repo-hygiene CI matrix job (Linux + Windows); bump-version depends on it; integration-tests and run-wfl-programs end with working-tree checks; workflows that push directly (bump-version, versioning.yml, auto-fmt.yml) run the static check immediately before pushing.

Migration (118 static violations → 0)

  • Deleted: every tracked *.ast.txt/*.lex.txt dump (16 embedded a personal Windows home directory), an accidentally tracked 3.9 MB ELF binary, generated reports and the mutable docs-validation cache, .orig/.rej merge remnants, the raw clippy log, generated test_rust_files/, google_index.html, a tracked settings.local.json, dead crates/wfl_core, LabsTest/email.wfl (personal identifier — deliberately not archived), .rustfmt.toml (sole config is now rustfmt.toml, edition 2024).
  • Converted legacy probes into 15 asserted TestPrograms/{modules,constants,nexus}/ programs (describe/expect, all passing) and 5 tests/fixtures/ fixtures with a new tests/diagnostics_fixtures_test.rs asserting diagnostics + exit codes. Notable: load_container.wfl pins load module scope isolation as a negative test (the legacy probe asserted a sharing behavior that was never implemented — include from is the sharing mechanism), and even_inline_precedence.wfl pins exact division semantics the old probes misunderstood.
  • Moved: Dev Diary → History/dev-diary/2026/; Docs/development/Docs/contributing/; active designs/plans/evidence → Engineering/; Tools/ split into scripts/metrics/, scripts/docs/, examples/tools/, Engineering/components/, archive; build_msi.ps1scripts/build_windows_installer.ps1 (with the retired MSI launcher's -BumpVersion option); web examples → examples/web/ (personalized wording removed, analyzer-validated); prototypes → experiments/{nexus,syntax}/ with checker-enforced metadata (Experiment: Nexus prototype program (experiments/nexus/) #668, Experiment: pattern kitchen sink (experiments/syntax/) #669).
  • Archived 48 files indexed in Archive/manifest.json (path, original path, source commit, sha256, kind, status, reason, normative: false, security classification). WFLHASH review snapshots triaged: already public in-tree, classified public-historical.
  • Runners: run_integration_tests.sh/.ps1 now execute TestPrograms/ feature subdirectories (excluding docs_examples/, execute_pages/, test_data/; error_examples/ = expected-fail), so converted tests actually gate.

Producers fixed so gated suites leave the checkout clean (found by running the new working-tree gate over the full suite chain)

  • wfl --lex/--parse dumps now write under target/reports/dumps/ instead of beside the source file — the exact mechanism that produced the formerly tracked dumps.
  • Repo + TestPrograms .wflcfg disable in-repo debug reports and execution logs (closest-wins config discovery required both); the packaged installer config still enables debug reports for end users.
  • File I/O and middleware TestPrograms write under target/test-artifacts/; rust_loc_counter_test.wfl likewise; docs-validation report moved under target/reports/; the write-after-close Rust test cleans the stray file1 it currently creates.
  • error_examples/type_error.wfl used 42 plus "hello", which is legal string-coercing concatenation today and exited 0 — replaced with minus, which genuinely fails.
  • vscode-extension/package-lock.json had drifted to 26.7.46 vs 26.7.59 everywhere else — fixed, and bump_version.py now updates both lock version fields; .gitignore Icon? rule could swallow icons/ on case-insensitive filesystems; .vscode/ negations were dead; settings.local.json now ignored recursively.

Deferred (tracked, declared as transitional [[generated]] exceptions)

Test evidence (testing.md §15)

  • Risk class: R3 (changes CI gates / release controls; touches backward-compat-sensitive test infrastructure).
  • Acceptance criteria → tests: design's acceptance criteria → tests/tooling/test_check_repo_hygiene.py (27 fixture-tree cases: clean pass + one per violation class, both modes), tests/tooling/test_windows_installer_script.py (placement/retirement guards), tests/diagnostics_fixtures_test.rs (diagnostic + exit-status assertions), 15 asserted WFL end-to-end programs, checker static+working-tree runs over the real tree.
  • Red evidence: commit 6ae52ff (test: add failing repo-hygiene checker and installer placement tests) — 32 tests, 30 failures + 2 errors, each for the intended reason; ancestor of the Green commits.
  • Green evidence at head: tooling suite 32/32; cargo fmt --all -- --check clean; cargo clippy --all-targets --all-features -- -D warnings clean; cargo test --all exit 0 (150 suites, 0 failures); release-build integration gate scripts/run_integration_tests.sh exit 0 (130 passed, 0 failed, 24 skipped — including the recursive TestPrograms sweep); scripts/run_web_tests.sh 3/3; python3 scripts/validate_docs_examples.py --ci --force 19/19; static hygiene mode exit 0; working-tree mode exit 0 after all suites.
  • Platforms: Linux exercised end-to-end locally; Windows covered structurally (ps1 runner mirrored, hygiene job runs on windows-2025 in this PR's CI). MSI build/install evidence deferred with Consolidate product version onto Cargo.toml and stage the VSIX during packaging #670.
  • Not applicable, with reason: UI/a11y (no GUI); coverage gate (tracked conformance gap in testing.md).
  • Rollback/recovery: revert the PR; no history rewrite, no tags moved, archive preserves originals byte-for-byte (manifest records source commits and original paths).
  • Residual risk: scripts/test_bump_version.py mock harness was broken before this change (MagicMock/JSON serialization) — the new lock-update logic was verified functionally instead; Windows runner recursion not executed locally (CI exercises it); packaging consolidation deferred (Consolidate product version onto Cargo.toml and stage the VSIX during packaging #670).

🤖 Generated with Claude Code

https://claude.ai/code/session_01QVvQraMbMgEWU9dPSTaqqJ


Generated by Claude Code


Open in Devin Review

claude added 7 commits July 31, 2026 04:33
Red evidence for the Repository Hygiene and Layout Policy implementation
(Engineering/designs/2026-07-28-repository-hygiene-design.md). Defines the
contract for scripts/check_repo_hygiene.py (static and working-tree modes
over fixture git trees: root allowlist, forbidden suffixes, merge remnants,
local settings, retired paths, binary magic/size, personal absolute paths,
generated-file exception metadata, archive manifest checksums, experiment
metadata and review expiry, rustfmt uniqueness, version drift, and
post-suite tree cleanliness) plus structural guards for the
scripts/build_windows_installer.ps1 packaging entry point.

Run: python3 -m unittest discover -s tests/tooling
Result at this commit: 32 tests, 30 failures, 2 errors (checker and
entry point intentionally absent).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QVvQraMbMgEWU9dPSTaqqJ
Implements the enforcement layer of the approved design
(Engineering/designs/2026-07-28-repository-hygiene-design.md):

- REPOSITORY_HYGIENE.md: binding Repository Hygiene and Layout Policy
  (placement rules, tracked/ephemeral rules, output roots, archive
  manifest schema, single sources of truth, exceptions process).
- .repo-hygiene.toml: machine-readable profile (root allowlist, forbidden
  suffixes/names, retired paths, binary/size declarations, personal-path
  placeholders, generated-file exceptions, version sources, approved
  output roots). Transitional version mirrors (src/version.rs,
  .build_meta.json, wix.toml) and the installer-consumed pinned VSIX are
  declared exceptions pending the packaging consolidation.
- scripts/check_repo_hygiene.py: dependency-free checker with static and
  working-tree modes.

Green evidence: the 27 checker unit tests from the preceding Red commit
now pass (python3 -m unittest discover -s tests/tooling); the 5 installer
placement guards stay red until the migration commit. Static mode against
this commit reports 118 violations - the migration worklist the following
commits burn down, including 16 tracked AST dumps that embed a personal
Windows home directory and a real package-lock.json version drift
(26.7.46 vs 26.7.59).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QVvQraMbMgEWU9dPSTaqqJ
… homes

Applies the initial migration from the approved design
(Engineering/designs/2026-07-28-repository-hygiene-design.md). Static
hygiene mode is clean at this commit (118 violations -> 0) and all 32
tooling tests pass, including the installer placement guards.

Deleted reproducible/unsafe debris: compare_search_full_clean, every
tracked *.ast.txt / *.lex.txt dump (16 embedded a personal Windows home
directory), docs_code_blocks_report.json, validation_report.json, the
mutable docs validation cache and timestamp report, linter .orig/.rej
merge remnants, the raw clippy log, generated test_rust_files/,
google_index.html, Nexus/.claude/settings.local.json, dead
crates/wfl_core, LabsTest/email.wfl (personal identifier, not archived),
and .rustfmt.toml (rustfmt.toml is now the sole config, edition 2024).

Reclassified legacy WFL programs per the design's disposition table:
fixtures to tests/fixtures/{modules,diagnostics,tooling}/, 15 converted
asserted TestPrograms under modules/ constants/ nexus/ (describe/expect,
all passing; load_container pins load-module scope isolation as a
negative test), examples/web/{blog_server,html_server}.wfl (personalized
wording removed, analyzer-validated), experiments/{nexus,syntax}/ with
required metadata (issues #668/#669), and the remaining probes archived.
New tests/diagnostics_fixtures_test.rs asserts diagnostics and exit
status for the intentionally invalid fixtures (analyzer add-report gap
pinned as >=4, tracked in #671).

Split Tools/: rust_loc_counter.py -> scripts/metrics/, wfl_md_combiner.py
-> scripts/docs/, WFL ports -> examples/tools/ (validated; output moved
under target/reports/), spec -> Engineering/components/, MSI plan and its
harnesses -> Archive/implementation-plans/windows-installer/, config
checker and MSI launcher retired. build_msi.ps1 ->
scripts/build_windows_installer.ps1 (+ -BumpVersion from the retired
launcher).

Moved records: Dev diary -> History/dev-diary/2026/ (+ README),
Docs/development -> Docs/contributing, active designs/plans/evidence ->
Engineering/, Docs/Archive + superpowers plans + TODO + rust_loc_report +
root bug.md + WFLHASH reviews -> categorized Archive/ paths indexed by
Archive/manifest.json (sha256 per entry, 47 entries, all non-normative).
Promoted .jules/bolt.md lessons to History/perf-lessons.md; .jules and
.cursor files are now thin adapters to root policy.

Also: fixed .gitignore (Icon? case rule, .vscode negations, recursive
.claude/settings.local.json, dump/report ignores), repointed
rust_loc_counter_test.wfl at target/test-artifacts/, docs validation
report now writes to target/reports/docs-validation/, bump_version.py now
updates both package-lock.json version fields (fixing the live 26.7.46 vs
26.7.59 drift), and working-tree mode correctly parses -z rename records.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QVvQraMbMgEWU9dPSTaqqJ
…layout

- GOVERNANCE.md: new §3.8 makes REPOSITORY_HYGIENE.md a binding technical
  policy; related-documents table updated; Dev Diary and wflpkg paths fixed.
- CONTRIBUTING.md / README.md / Docs/README.md / Docs/contributing/index.md:
  reference the hygiene policy and checker; Docs/development links now point
  at Docs/contributing/; moved design docs now referenced in Engineering/.
- AGENTS.md and CLAUDE.md: project-structure sections describe the canonical
  layout (Engineering/, History/, Archive/, experiments/, tests/fixtures/),
  Dev Diary path updated to History/dev-diary/<year>/, and a Repository
  Hygiene critical rule added; .cursor/.jules noted as thin adapters.
- Archived the generated DOC_CODE_AUDIT.md snapshot (manifest entry added);
  Docs/README.md now tells readers to regenerate it under target/reports/.
- generate_rust_loc_report.py writes to target/reports/metrics/ instead of
  Docs/.
- Dev Diary entry for this change (History/dev-diary/2026/).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QVvQraMbMgEWU9dPSTaqqJ
… runner

- New early repo-hygiene matrix job (Linux + Windows) runs the checker's
  fixture-tree unit tests and static mode over the tracked tree;
  bump-version now depends on it.
- integration-tests and run-wfl-programs end with a working-tree hygiene
  check on both OSes, proving the suites leave the checkout clean outside
  the approved output roots.
- Workflows that write directly to the repository (ci.yml bump-version,
  versioning.yml, auto-fmt.yml) run the static check immediately before
  their push; update-security-doc.yml opens a PR and is covered by PR CI.
- run_integration_tests.sh/.ps1 now execute TestPrograms feature
  subdirectories (modules/, constants/, nexus/) recursively, excluding
  docs_examples/ (validated separately) and the execute_pages/ and
  test_data/ fixture trees, and treat error_examples/ programs as
  expected-fail — so a converted test that regresses actually gates.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QVvQraMbMgEWU9dPSTaqqJ
…nk fixes

- CHANGELOG.md: Unreleased Added/Changed entries for the hygiene policy,
  checker, CI gate, and layout migration.
- examples/web/README.md: document the two validated web examples.
- run_integration_tests.sh: replace mapfile -d with a while-read loop
  (macOS ships bash 3.2, which lacks mapfile -d).
- Fix relative links in design docs moved to Engineering/ so they resolve
  from their new location.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QVvQraMbMgEWU9dPSTaqqJ
…ample

Findings from running the new working-tree hygiene gate over the full
suite chain (integration, web, docs validation):

- wfl --lex/--parse dumps now write under target/reports/dumps/ with a
  flattened name instead of beside the source file — the exact mechanism
  that produced the formerly tracked AST/lex dumps (which embedded
  absolute personal paths). CLI docs in AGENTS.md/CLAUDE.md updated.
- Repo and TestPrograms .wflcfg set debug_report_enabled = false and
  execution_logging = false (config discovery is closest-wins, so both
  files need the switches). Intentional-error programs no longer drop
  *_debug.txt next to themselves on every run; the packaged installer
  config still enables debug reports for end users.
- File I/O and middleware TestPrograms write under
  target/test-artifacts/{file_io,web}/ instead of the repository root
  (flush_test_*, test_output.txt, test.log, test_append.log,
  middleware logs, google_index.html).
- tests/file_io_error_handling_test.rs: the write-after-close test also
  cleans the stray 'file1' the rejected write currently creates.
- error_examples/type_error.wfl used '42 plus "hello"', which is legal
  string-coercing concatenation today and exited 0 — the example was
  asserting an error that no longer exists. It now uses 'minus', which
  genuinely fails (docs must be honest; error examples too).
- Profile: python __pycache__ directories allowed as ignored output.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QVvQraMbMgEWU9dPSTaqqJ
@logbie
logbie merged commit 438780a into main Jul 31, 2026
6 of 9 checks passed
@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Too many files!

This PR contains 270 files, which is 170 over the limit of 100.

To get a review, narrow the scope:
• coderabbit review --committed # exclude uncommitted changes
• coderabbit review --dir # limit to a subdirectory
• coderabbit review --base # compare against a closer base

Upgrade to a paid plan to raise the limit.

This review couldn't start because sufficient usage credits or metered capacity aren't available. Add credits or update usage-based reviews in the billing tab, then retry.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: dee43b98-2fa0-4df1-8b90-5eedb43c6729

📥 Commits

Reviewing files that changed from the base of the PR and between 5b2fa98 and eb3ca3b.

⛔ Files ignored due to path filters (1)
  • vscode-extension/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (270)
  • .cursor/rules/wfl-rules.mdc
  • .github/workflows/auto-fmt.yml
  • .github/workflows/ci.yml
  • .github/workflows/versioning.yml
  • .gitignore
  • .jules/bolt.md
  • .repo-hygiene.toml
  • .rustfmt.toml
  • .wflcfg
  • AGENTS.md
  • Archive/README.md
  • Archive/bug-reports/refcell-zero-arg-action-panic.md
  • Archive/docs-archive/DOCUMENTATION_COMPLETE.md
  • Archive/docs-archive/DOCUMENTATION_FIXES_SUMMARY.md
  • Archive/docs-archive/FRAMEWORK_FINAL_REPORT.md
  • Archive/docs-archive/FRAMEWORK_PROPERTY_MUTATION_ISSUE.md
  • Archive/docs-archive/IOaudit.md
  • Archive/docs-archive/PARSER_REFACTOR_TODO.md
  • Archive/docs-archive/README.md
  • Archive/docs-archive/WFL_DOCUMENTATION_REBUILD_SUMMARY.md
  • Archive/docs-archive/hash3.md
  • Archive/docs-archive/keywords-technical-reference.md
  • Archive/docs-archive/math.md
  • Archive/docs-archive/parserbug.md
  • Archive/docs-archive/parserrefactor.md
  • Archive/implementation-plans/superpowers/2026-07-25-parser-compat-regressions.md
  • Archive/implementation-plans/superpowers/2026-07-25-stream-terminal-typeflow-fixes.md
  • Archive/implementation-plans/windows-installer/msi_enhancement_tdd_plan.md
  • Archive/implementation-plans/windows-installer/test_installation_combinations.py
  • Archive/implementation-plans/windows-installer/test_launch_msi_build.py
  • Archive/legacy-programs/nexus/nexus_partial.wfl
  • Archive/legacy-programs/nexus/simple_count_test.wfl
  • Archive/legacy-programs/nexus/test_even_check.wfl
  • Archive/legacy-programs/nexus/test_factorial.wfl
  • Archive/legacy-programs/nexus/test_fragment.wfl
  • Archive/legacy-programs/nexus/test_minimal.wfl
  • Archive/legacy-programs/nexus/test_section2.wfl
  • Archive/legacy-programs/nexus/test_sections_1_3.wfl
  • Archive/legacy-programs/nexus/test_with_check.wfl
  • Archive/legacy-programs/root/blog_server_minimal.wfl
  • Archive/legacy-programs/root/debug_split.wfl
  • Archive/legacy-programs/syntax/I made an interesting discovery her.txt
  • Archive/legacy-programs/syntax/test1.wfl
  • Archive/legacy-programs/tools/line_counter.wfl
  • Archive/legacy-programs/tools/rust_loc_counter_simple.wfl
  • Archive/legacy-programs/tools/test_recursive.wfl
  • Archive/manifest.json
  • Archive/planning/docs-TODO.md
  • Archive/reports/DOC_CODE_AUDIT.md
  • Archive/reports/rust_loc_report.md
  • Archive/retired-systems/wflpkg/README.md
  • Archive/retired-systems/wflpkg/wflhub_language_gaps_prd.md
  • Archive/retired-systems/wflpkg/wflpkg-adr-001-binary-and-crate-structure.md
  • Archive/retired-systems/wflpkg/wflpkg-brainstorm-results.md
  • Archive/retired-systems/wflpkg/wflpkg-manifest-grammar-1.0.md
  • Archive/retired-systems/wflpkg/wflpkg-open-decisions-resolved.md
  • Archive/retired-systems/wflpkg/wflpkg_prd.md
  • Archive/retired-systems/wflpkg/wflpkgdesign.md
  • Archive/security-reviews/wflhash/wflhashreview.md
  • Archive/security-reviews/wflhash/wflhashreview2.md
  • CHANGELOG.md
  • CLAUDE.md
  • CONTRIBUTING.md
  • Docs/02-getting-started/editor-setup.md
  • Docs/02-getting-started/resources.md
  • Docs/06-best-practices/collaboration-guide.md
  • Docs/README.md
  • Docs/contributing/architecture-overview.md
  • Docs/contributing/building-from-source.md
  • Docs/contributing/compiler-internals.md
  • Docs/contributing/contributing-guide.md
  • Docs/contributing/index.md
  • Docs/contributing/lsp-integration.md
  • Docs/contributing/mcp-integration.md
  • Docs/contributing/stdlib-higher-order-functions.md
  • Docs/guides/faq.md
  • Docs/reference/error-codes.md
  • Docs/reference/language-specification.md
  • Engineering/components/rust_loc_counter.md
  • Engineering/designs/response-streaming-design.md
  • Engineering/designs/route-construct-design.md
  • Engineering/designs/type-system-design.md
  • Engineering/evidence/testing-policy-exceptions/2026-07-24-pr-641-red-chronology.md
  • Engineering/plans/concurrency-phase-plan.md
  • GOVERNANCE.md
  • History/dev-diary/2026/2026-06-12-database-bindings-and-route-parameters.md
  • History/dev-diary/2026/2026-07-03-http-client-and-standard-crypto-issue-558.md
  • History/dev-diary/2026/2026-07-03-include-type-inference-issue-553.md
  • History/dev-diary/2026/2026-07-03-testprograms-triage-and-fixes.md
  • History/dev-diary/2026/2026-07-04-https-tls-web-server.md
  • History/dev-diary/2026/2026-07-04-natural-language-precedence-finally-between-error-binding.md
  • History/dev-diary/2026/2026-07-06-github-issues-batch-583-582-566-557-567.md
  • History/dev-diary/2026/2026-07-08-websockets-issue-555.md
  • History/dev-diary/2026/2026-07-09-auth-session-crypto-primitives.md
  • History/dev-diary/2026/2026-07-09-password-hashing-argon2-bcrypt-scrypt-pbkdf2.md
  • History/dev-diary/2026/2026-07-10-issue-560-residuals-try-returns-container-methods.md
  • History/dev-diary/2026/2026-07-10-test-mode-failure-accounting.md
  • History/dev-diary/2026/2026-07-10-web-server-bind-address-issue-466.md
  • History/dev-diary/2026/2026-07-10-web-server-gaps-issue-597.md
  • History/dev-diary/2026/2026-07-10-wflhash-experimental-dual-hash-docs.md
  • History/dev-diary/2026/2026-07-11-nothing-reassign-widen-issue-605.md
  • History/dev-diary/2026/2026-07-11-phase-0-concurrency-hardening.md
  • History/dev-diary/2026/2026-07-11-subprocess-policy-enforcement.md
  • History/dev-diary/2026/2026-07-11-wfl-version-constant-issue-602.md
  • History/dev-diary/2026/2026-07-12-shared-execution-budget.md
  • History/dev-diary/2026/2026-07-13-cargo-crate-updates.md
  • History/dev-diary/2026/2026-07-13-issue-610-phase-1-containment.md
  • History/dev-diary/2026/2026-07-13-issue-610-phase-1-inventory.md
  • History/dev-diary/2026/2026-07-13-issue-610-phase-1-scorecard-baseline.md
  • History/dev-diary/2026/2026-07-14-repl-errors-warnings-and-variable-storage.md
  • History/dev-diary/2026/2026-07-16-cycle-safe-values.md
  • History/dev-diary/2026/2026-07-18-display-multiple-values.md
  • History/dev-diary/2026/2026-07-18-issue-592-bare-zero-arg-include.md
  • History/dev-diary/2026/2026-07-20-action-overloading.md
  • History/dev-diary/2026/2026-07-22-concurrent-handler-run-state-isolation.md
  • History/dev-diary/2026/2026-07-22-concurrent-request-handlers.md
  • History/dev-diary/2026/2026-07-22-immediate-500-on-unanswered-request.md
  • History/dev-diary/2026/2026-07-22-outbound-response-streaming.md
  • History/dev-diary/2026/2026-07-22-server-response-streaming.md
  • History/dev-diary/2026/2026-07-22-stream-auto-close-on-handler-exit.md
  • History/dev-diary/2026/2026-07-22-write-line-file-write-backcompat.md
  • History/dev-diary/2026/2026-07-24-concurrent-loop-ordering-and-header-key-types.md
  • History/dev-diary/2026/2026-07-24-issue-642-completion.md
  • History/dev-diary/2026/2026-07-24-issue-642-p1-followups.md
  • History/dev-diary/2026/2026-07-24-issue-642-rereview-fixes.md
  • History/dev-diary/2026/2026-07-24-outbound-stream-lifecycle-p1.md
  • History/dev-diary/2026/2026-07-24-streaming-rereview-p1-blockers.md
  • History/dev-diary/2026/2026-07-25-issue-642-remaining-blockers.md
  • History/dev-diary/2026/2026-07-25-parser-streaming-compat-regressions.md
  • History/dev-diary/2026/2026-07-25-stream-terminal-typeflow-review-fixes.md
  • History/dev-diary/2026/2026-07-26-remove-wflpkg-system.md
  • History/dev-diary/2026/2026-07-26-sunset-javascript-transpiler.md
  • History/dev-diary/2026/2026-07-26-typechecker-gradual-contract-audit.md
  • History/dev-diary/2026/2026-07-27-ci-bump-version-toolchain.md
  • History/dev-diary/2026/2026-07-28-issue-654-list-alias-fixpoint.md
  • History/dev-diary/2026/2026-07-28-linux-musl-build-and-spaces-publishing.md
  • History/dev-diary/2026/2026-07-29-http-connection-reuse-retry.md
  • History/dev-diary/2026/2026-07-31-issue-662-immutable-release-checksums.md
  • History/dev-diary/2026/2026-07-31-repository-hygiene-implementation.md
  • History/dev-diary/README.md
  • History/perf-lessons.md
  • LabsTest/email.wfl
  • Nexus/.claude/settings.local.json
  • Nexus/test_action_syntax.wfl
  • Nexus/test_concat.wfl
  • Nexus/test_factorial_inline.wfl
  • Nexus/test_factorial_parens.wfl
  • Nexus/test_inline_even.wfl
  • Nexus/test_nested_loops.wfl
  • Nexus/test_simple_call.wfl
  • Nexus/test_skip_loop.wfl
  • Nexus/test_zero_concat.wfl
  • README.md
  • REPOSITORY_HYGIENE.md
  • TestPrograms/.wflcfg
  • TestPrograms/basic_syntax_comprehensive.wfl.ast.txt
  • TestPrograms/constants/current_syntax.wfl
  • TestPrograms/constants/deprecated_syntax_compat.wfl
  • TestPrograms/constants/mutable_and_constant_variables.wfl
  • TestPrograms/docs_examples/VALIDATION_REPORT.md
  • TestPrograms/docs_examples/_meta/validation_cache.json
  • TestPrograms/docs_examples/basic_syntax/control_flow_01.wfl.ast.txt
  • TestPrograms/docs_examples/basic_syntax/for_each_01.wfl.ast.txt
  • TestPrograms/docs_examples/basic_syntax/hello_world.wfl.ast.txt
  • TestPrograms/docs_examples/basic_syntax/loops_01.wfl.ast.txt
  • TestPrograms/docs_examples/basic_syntax/operators_01.wfl.ast.txt
  • TestPrograms/docs_examples/basic_syntax/variables_01.wfl.ast.txt
  • TestPrograms/docs_examples/keyword_reference/pattern_examples.wfl.ast.txt
  • TestPrograms/docs_examples/tour/01_hello.wfl.ast.txt
  • TestPrograms/docs_examples/tour/02_variables.wfl.ast.txt
  • TestPrograms/docs_examples/tour/03_conditionals.wfl.ast.txt
  • TestPrograms/docs_examples/tour/04_loops.wfl.ast.txt
  • TestPrograms/docs_examples/tour/05_lists.wfl.ast.txt
  • TestPrograms/docs_examples/tour/06_actions.wfl.ast.txt
  • TestPrograms/docs_examples/tour/07_errors.wfl.ast.txt
  • TestPrograms/docs_examples/tour/08_containers.wfl.ast.txt
  • TestPrograms/error_examples/type_error.wfl
  • TestPrograms/file_appending_test.wfl
  • TestPrograms/file_flush_test.wfl
  • TestPrograms/file_io_comprehensive.wfl
  • TestPrograms/middleware_minimal_test.wfl
  • TestPrograms/modules/include_container.wfl
  • TestPrograms/modules/include_top_level_return.wfl
  • TestPrograms/modules/load_container.wfl
  • TestPrograms/nexus/action_syntax.wfl
  • TestPrograms/nexus/action_value_call.wfl
  • TestPrograms/nexus/concat_zero_regression.wfl
  • TestPrograms/nexus/even_inline_precedence.wfl
  • TestPrograms/nexus/factorial_inline_precedence.wfl
  • TestPrograms/nexus/factorial_parenthesized_precedence.wfl
  • TestPrograms/nexus/loop_skip.wfl
  • TestPrograms/nexus/nested_loop_control.wfl
  • TestPrograms/nexus/numeric_concat.wfl
  • TestPrograms/rust_loc_counter_test.wfl
  • TestPrograms/simple_middleware_test.wfl
  • TestPrograms/simple_respond_test.wfl.lex.txt
  • TestPrograms/simple_web_server.wfl.ast.txt
  • TestPrograms/simple_web_test.wfl.ast.txt
  • TestPrograms/test_basic_server.wfl.ast.txt
  • TestPrograms/test_list_keyword.wfl.ast.txt
  • TestPrograms/test_request_properties.wfl.ast.txt
  • TestPrograms/test_web_request.wfl
  • TestPrograms/test_web_request.wfl.ast.txt
  • TestPrograms/test_web_server_response.wfl.ast.txt
  • TestPrograms/wait_request_test.wfl.ast.txt
  • TestPrograms/web_server_basic_test.wfl.ast.txt
  • TestPrograms/web_server_body_limit_test.wfl.ast.txt
  • TestPrograms/web_server_content_length_test.wfl.ast.txt
  • TestPrograms/web_server_example.wfl.lex.txt
  • TestPrograms/web_server_request_response_test.wfl.ast.txt
  • TestPrograms/web_server_tls.wfl.ast.txt
  • TestPrograms/write_content_test.wfl
  • Tools/.wflcfg
  • Tools/README.md
  • Tools/clippy errors/clippy1.txt
  • Tools/launch_msi_build.py
  • Tools/wfl_combiner.wfl.lex.txt
  • Tools/wfl_config_checker.py
  • blog_server.wfl.ast.txt
  • blog_server.wfl.lex.txt
  • compare_search_full_clean
  • crates/wfl_core/src/version.rs
  • docs_code_blocks_report.json
  • examples/tools/README.md
  • examples/tools/combine_markdown.wfl
  • examples/tools/rust_loc_counter.wfl
  • examples/web/README.md
  • examples/web/blog_server.wfl
  • examples/web/html_server.wfl
  • experiments/nexus/README.md
  • experiments/nexus/nexus.wfl
  • experiments/syntax/README.md
  • experiments/syntax/pattern_kitchen_sink.wfl
  • google_index.html
  • rustfmt.toml
  • scripts/README.md
  • scripts/build_windows_installer.ps1
  • scripts/bump_version.py
  • scripts/check_repo_hygiene.py
  • scripts/docs/combine_markdown.py
  • scripts/metrics/generate_rust_loc_report.py
  • scripts/run_integration_tests.ps1
  • scripts/run_integration_tests.sh
  • scripts/test_docs_code_blocks.py
  • scripts/validate_docs_examples.py
  • src/linter/mod.rs.orig
  • src/linter/mod.rs.rej
  • src/main.rs
  • syntax_test/pattern_debug.txt
  • syntax_test/test1.wfl.lex.txt
  • syntax_test/test_deprecated_constant.wfl
  • syntax_test/test_new_constant.wfl
  • syntax_test/test_variables_vs_constants.wfl
  • test_include.wfl
  • test_load_module.wfl
  • test_main_fix.wfl
  • test_rust_files/sample.rs
  • test_rust_files/subdir/lib.rs
  • tests/diagnostics_fixtures_test.rs
  • tests/file_io_error_handling_test.rs
  • tests/fixtures/diagnostics/constant_error_precedence.wfl
  • tests/fixtures/diagnostics/constant_immutability.wfl
  • tests/fixtures/diagnostics/export_non_constant.wfl
  • tests/fixtures/modules/export_and_top_level_return.wfl
  • tests/fixtures/modules/person_container.wfl
  • tests/fixtures/tooling/combiner/test_file_list.wfl
  • tests/tooling/test_check_repo_hygiene.py
  • tests/tooling/test_windows_installer_script.py
  • validation_report.json
  • vscode-extension/.gitignore

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@logbie
logbie deleted the claude/policy-implementation-docs-fbmpeh branch July 31, 2026 05:44

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 3 potential issues.

Open in Devin Review

Comment thread .gitignore
Comment on lines +81 to +83
# The macOS Finder icon file is literally "Icon\r". The old `Icon?` glob also
# swallowed the tracked `icons/` directory on case-insensitive filesystems.
Icon[\r]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 macOS Finder icon files are no longer ignored and will show up as uncommitted changes

The rule meant to hide the macOS Finder icon file was rewritten to Icon[\r] (.gitignore:84), which Git reads as the plain name "Iconr" instead of the carriage-return name the Finder actually creates, so the icon file stops being ignored.
Impact: On macOS the Finder-created icon file appears as an untracked change, which now fails the new post-test cleanliness check and can be committed by accident.

Why the bracket form does not match the carriage-return name

Git's wildmatch treats a backslash inside [...] as an escape of the next literal character, not as a C-style escape sequence; \r therefore means the letter r, so Icon[\r] is equivalent to Iconr.

Verified locally:

$ printf 'Icon[\\r]\n' > .gitignore
$ git status --porcelain --ignored | cat -A
?? "Icon\r"      <- macOS icon file NOT ignored
!! Iconr         <- unrelated name ignored instead

The working-tree gate added in this PR (.github/workflows/ci.yml:304-310) fails on any untracked file outside target/, so a developer on macOS running the suite locally would now hit this. The conventional fix is a literal carriage return after Icon (as in GitHub's macOS.gitignore).

Prompt for agents
The `.gitignore` entry `Icon[\r]` does not do what its comment claims. Git's wildmatch treats a backslash inside a bracket expression as an escape of the following literal character, so `[\r]` is the single character `r` — the pattern matches a file literally named `Iconr` and does NOT match the macOS Finder icon file, whose name is `Icon` followed by a carriage return (0x0D). The goal of the change (avoid the old `Icon?` glob swallowing the tracked `icons/` directory on case-insensitive filesystems) is still valid, but the replacement must contain an actual carriage-return byte after `Icon`, the way GitHub's canonical macOS.gitignore does it. Update the rule so the byte sequence `Icon\x0D` is written literally into the file (and keep a comment noting the trailing CR is significant so future edits/editors do not strip it).
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +342 to +346
review_by = datetime.date.fromisoformat(m.group(1))
if review_by < today:
report.add("experiment-expired", readme_rel,
f"Review-by {review_by} has passed: promote, archive, "
"or remove the experiment")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 Experiment Review-by dates become a hard CI failure on a fixed calendar date

Both experiment READMEs pin Review-by: 2026-10-30 (experiments/nexus/README.md:14, experiments/syntax/README.md:14) and check_experiments fails the blocking repo-hygiene job as soon as that date passes. That is the intended policy behaviour, but it means unrelated PRs will start failing CI on a date with no other trigger, and the only recovery is a policy-touching commit. Worth having issues #668/#669 scheduled well before the expiry so main does not go red unexpectedly.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +28 to +29
- name: Static hygiene check before push
run: python3 scripts/check_repo_hygiene.py --mode static

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 auto-fmt workflow calls the checker without setting up Python

The new step runs python3 scripts/check_repo_hygiene.py, but this job has no actions/setup-python step (unlike the repo-hygiene and bump-version jobs), so it depends on the runner image's default python3 being ≥ 3.11 — the checker exits 2 with HYGIENE-ERROR: Python 3.11+ with tomllib is required on anything older (confirmed locally on 3.10). The blacksmith-2vcpu-ubuntu-2404-arm image should ship 3.12, so this is likely fine, but it is an undeclared dependency that would turn into an opaque exit-2 failure blocking the auto-format push if the image ever changes.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

logbie pushed a commit that referenced this pull request Jul 31, 2026
`tests/diagnostics_fixtures_test.rs::constant_mutation_is_rejected_for_every_mutation_form`
landed with #672 pinning `>= 4` reports and a comment pointing at #671. With
the analyzer gap closed the fixture reports all five, so the assertion is now
an exact `== 5`.

Also updates the dev diary for the new repository layout: the file moved to
`History/dev-diary/2026/`, and the prose now points at
`tests/fixtures/diagnostics/constant_immutability.wfl` rather than the retired
`syntax_test/` tree.

Refs #671

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H9AnrAH4d8sTni73GWgkjR
logbie added a commit that referenced this pull request Jul 31, 2026
…675)

* fix(ci): repair post-merge gate failures on main

Three follow-ups from the first run of the hygiene gates on main:

- vscode-extension/package-lock.json: sync both version fields to 26.7.60.
  A 'Bump version to 26.7.60' commit landed on main between this branch's
  base and its merge, made with the pre-fix bump script that skipped the
  lock file; the merged tree therefore tripped the new version-drift rule
  (CI repo-hygiene job and auto-fmt pre-push check). Future bumps update
  the lock via the fixed scripts/bump_version.py already on main.
- ci.yml: set up Python 3.12 in the integration-tests and run-wfl-programs
  jobs. The Windows runner's default python lacks tomllib, so the new
  post-suite working-tree hygiene steps failed with HYGIENE-ERROR even
  though every test in both suites passed.
- rustfmt.toml: newline_style Unix -> Auto. The removed duplicate
  .rustfmt.toml (no newline_style) was rustfmt's effective config; with it
  gone, Unix style made the nightly Windows build's fmt check reject the
  autocrlf CRLF checkout. Auto restores the long-standing behavior.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QVvQraMbMgEWU9dPSTaqqJ

* fix: hash archive blobs from the git index, not working-tree bytes

The Windows repo-hygiene leg (previously cancelled by fail-fast, so never
observed) failed the archive-manifest fixture test: autocrlf materializes
text files with CRLF on Windows, so hashing working-tree bytes makes every
archived text file's sha256 drift from the manifest. The checker now hashes
the staged blob (git show :path) — canonical LF on every platform — and the
test fixture writes with newline='\n' so fixture bytes are deterministic.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QVvQraMbMgEWU9dPSTaqqJ

---------

Co-authored-by: Claude <noreply@anthropic.com>
logbie added a commit that referenced this pull request Jul 31, 2026
)

* test: red coverage for dropped `add ... to CONST` analyzer report (#671)

`add <value> to <name>` parses to `AddToListStatement`, which the analyzer
never checked for constness, so a program mutating a constant five ways got
only four `Cannot modify constant` reports. The same hole covers `remove
... from CONST` and `clear CONST`.

Red evidence: `add_to_constant_is_rejected_on_its_own` (0 reports, expected
1), `every_mutation_form_of_a_constant_is_reported` (4, expected 5) and
`list_mutation_statements_reject_constant_targets` (0, expected 3) all fail;
`mutable_targets_are_still_accepted` already passes and guards the fix
against over-reporting.

Refs #671

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H9AnrAH4d8sTni73GWgkjR

* fix(analyzer): report constant mutation for add/remove/clear (#671)

`add <value> to <name>` parses to `AddToListStatement` rather than
`Assignment` — the target's type is unknown at parse time, so the interpreter
decides between arithmetic and list-append at runtime. The analyzer's
`AddToListStatement` arm only checked that the target name was defined, never
that it was writable, so `add 10 to MAX_SIZE` on a constant produced no
report. A program mutating one constant five ways got four reports instead of
five; the `add` line was the silent one. `remove ... from` and `clear` had the
same hole.

The analyzer now tracks constants explicitly in a `constant_bindings` set of
`SymbolBindingKey`s, populated when a `store new constant` declaration is
defined, and `report_constant_mutation` consults it from all three bare-name
mutation statements. Keying by binding rather than name keeps an inner-scope
shadow from being mistaken for the constant.

`SymbolKind::Variable { mutable: false }` deliberately is NOT the test: action
parameters, container-method parameters, loop variables, try/when error
bindings, predefined globals, and REPL parent-scope variables are all
registered immutable without being constants, and appending to a list
parameter has always been legal.

This also closes a runtime hole for constant lists. A constant number was
already rejected at runtime via `Environment::assign`, but `add`/`remove`/
`clear` on a constant list mutated the `Rc<RefCell<Vec<Value>>>` in place
without ever reassigning the binding, so the constant check was bypassed.

Docs: `Docs/03-language-basics/variables-and-types.md` claimed constants were
"planned for future versions" and `Docs/06-best-practices/naming-conventions.md`
that "true immutability is limited today" — both false since `store new
constant` shipped. Both now document the real syntax and the rejected mutation
forms, with two validated examples registered in the docs-examples manifest.

Verified: `cargo test --all --no-fail-fast` (150 suites, 0 failures), clippy
`-D warnings`, `cargo fmt --check`, 111 `TestPrograms/` programs against the
release binary, and `scripts/validate_docs_examples.py` (21/21).

Fixes #671

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H9AnrAH4d8sTni73GWgkjR

* test: tighten the constant-mutation fixture pin to all 5 forms (#671)

`tests/diagnostics_fixtures_test.rs::constant_mutation_is_rejected_for_every_mutation_form`
landed with #672 pinning `>= 4` reports and a comment pointing at #671. With
the analyzer gap closed the fixture reports all five, so the assertion is now
an exact `== 5`.

Also updates the dev diary for the new repository layout: the file moved to
`History/dev-diary/2026/`, and the prose now points at
`tests/fixtures/diagnostics/constant_immutability.wfl` rather than the retired
`syntax_test/` tree.

Refs #671

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H9AnrAH4d8sTni73GWgkjR

* fix(analyzer): carry constants across branch merges; honest docs (#671)

Addresses three review findings on #674.

Constant markers survive a branch merge. `IfStatement`/`SingleLineIf`
re-define a binding created on both arms into the parent scope under a new
binding key, and `pop_scope_promoting_except` does the same for promoted
scopes — neither moved the `constant_bindings` entry. The symbol keeps
`mutable: false`, so `change` still reported while `add`/`remove`/`clear`
went silent: the #671 defect again, one scope up. Both paths now carry the
marker, mirroring the existing `mutable: then && else` merge (constant on
either arm means constant after). Before the fix, `add 1 to LIMIT` after such
a branch escaped analysis entirely and was caught only at runtime; for a
constant list it would not have been caught at all.

Docs no longer overclaim. "WFL refuses to modify it" / "every mutation form is
rejected" is false while `push with <list> and <value>` is unchecked. Both
pages now enumerate the forms that are actually checked and carry an explicit
known-gap callout pointing at #673, plus a note that a constant fixes the
binding rather than the contents reached through an alias.

The loop-variable negative test now tests its claim. It previously used
`add entry to gathered`, where the loop variable is the value and `gathered`
is the target. Rewritten so the loop variable is the mutation target, scoped to
the bare-name statements this change touches: `subtract`/`multiply` on a loop
variable do report, via the `Assignment` path's long-standing `mutable: false`
check, which is pre-existing and out of scope.

Also adds a `// CI-SKIP:` directive to `constants_immutable_01.wfl`. The CI
"Run WFL Programs" sweep covers `TestPrograms/docs_examples/` (unlike
run_integration_tests.sh) and expects exit 0, so the intentional error example
failed it. The docs validator still checks it statically and asserts it fails
semantic analysis.

Refs #671, #673

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H9AnrAH4d8sTni73GWgkjR

* fix(ci): unbreak the Windows hygiene gate and the version-drift check

Three failures that predate this branch and reproduce on main's own run for
438780a. They are unrelated to #671 but block this PR from going green.

1. Windows jobs had no Python. `integration-tests` and `run-wfl-programs` call
   scripts/check_repo_hygiene.py as their final step but never ran
   actions/setup-python, so on Windows they got the runner's bundled `python`
   (pre-3.11) and died with "HYGIENE-ERROR: Python 3.11+ with tomllib is
   required" regardless of what the job actually tested — the WFL sweep
   (141 passed, 0 failed), the docs-example validation (21/21) and the web
   tests (3/3) had all passed by then. Both jobs now set up Python 3.x, as
   `repo-hygiene` and `bump-version` already did. Switching Windows to
   `python3` was the other option and was rejected: on Windows that name can
   resolve to the Microsoft Store app-execution alias.

   `nightly.yml` also runs Python on Windows without setup-python, but only
   calls bump_version.py, which is stdlib-only and needs no tomllib — left
   alone.

2. Fixture writes were not byte-exact. FixtureTree.write used
   Path.write_text, which applies the platform newline translation, so on
   Windows every "\n" landed as "\r\n". test_archive_valid_manifest_passes
   computes its expected sha256 from the LF form, so it could never match the
   file the checker hashed — green on Linux, red on Windows. It now writes the
   caller's exact UTF-8 bytes (which also stops the locale encoding leaking
   in), with test_fixture_write_is_byte_exact pinning the behaviour.

3. The extension lockfile was stale. vscode-extension/package-lock.json still
   read 26.7.59 against Cargo.toml's 26.7.60, failing the static gate with
   "version-drift". bump_version.py already knows to update both mirrors; the
   26.7.60 bump landed without it. Set to the values that script would write.

Verified: `python3 -m unittest discover -s tests/tooling` 33 passed;
`check_repo_hygiene.py --mode static` now exits 0 (was 1 on a clean
origin/main worktree).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H9AnrAH4d8sTni73GWgkjR

* fix(ci): check archived content out verbatim so its sha256 pins hold

The Windows Repository Hygiene job failed with sha256 drift on all 48 archived
files at once. The manifest hashes are computed from the committed (LF) bytes,
but check_repo_hygiene.py hashes the file as it sits in the working tree, and
the Windows runner checks out with core.autocrlf=true — so every LF became
CRLF and every hash missed. Confirmed rather than assumed: re-hashing each
file's CRLF-converted bytes reproduces the exact "actual" digests from the CI
log.

This was latent, not new. The job previously died earlier, at the checker's
unit tests; fixing those in the parent commit let it reach the static check and
surface the next Windows-only failure behind it.

Archived files are immutable blobs pinned by hash, so a root .gitattributes
marks Archive/** as `-text`: no end-of-line translation on any platform, making
the working tree byte-identical to the commit. Fixing the checkout rather than
the checker keeps the hash a pin on repository content instead of on whatever
the local platform happened to write, and leaves the checker's semantics alone.

`.gitattributes` is added to the root allowlist in .repo-hygiene.toml. It is a
legitimate root-level Git config file alongside the already-allowed .gitignore,
and root is the only place it applies from — this is a gap in the allowlist,
not an exception carved out to silence a violation.

Verified by simulating the Windows checkout on Linux — clone with
core.autocrlf=true, then run the checker:
  before: 48 archive-manifest drift violations, exit 1
  after:  clean, exit 0

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H9AnrAH4d8sTni73GWgkjR

* test: cover the reachable promotion path; correct the risk class (#671)

Round 2 of review. Three of four findings were valid.

Test comment was wrong and coverage was thin. The branch test named
`pop_scope_promoting_except`, but a `check` merge goes through
`promote_constant_marker` — different function. Corrected, and the test now
also covers a constant *list* through `add`/`remove`/`clear`, the two forms
with no `Assignment` fallback behind them.

The other migration site is unreachable, and now says so. The reviewer asked
for a try/when regression exercising `pop_scope_promoting_except`. That test
cannot be written: a binding declared inside a try statement does not survive
it at all — even when declared on every path, a later reference reports
`Variable '<name>' is not defined` rather than resolving to a promoted key.
Rather than write a test that does not exercise what it claims, this pins the
premise (`try_scoped_declarations_do_not_escape_the_statement`) so the
migration gets real coverage the day try-scoping changes, and documents at the
call site why the code is kept: `constant_bindings` should not silently
diverge from the four alias maps migrated for the same keys beside it.
`outer_constants_survive_an_intervening_try` covers what a program can
actually observe.

Risk class R1 was wrong; it is R3. testing.md §5 puts backward compatibility
in R3 outright and forbids lowering the class, and this change does alter
behavior (`add`/`remove`/`clear` on a constant list: silently allowed →
rejected at analysis). The diary now records R3, notes that §11.3 does not
apply (no concurrency, lifecycle, streaming, untrusted input, or crypto), and
lists the failure-path and negative coverage that does.

Also bumps the two remaining `actions/setup-python@v4` uses to v5, matching
the jobs fixed in the previous commit and clearing the Node 20 deprecation
warning.

Not taken: running the static hygiene check inside the bump-version push-retry
loop. That job is pre-existing code this branch does not touch, and the
concern (a concurrent merge racing the retry) is real but belongs in its own
change rather than riding along with an analyzer fix.

Verified: 151 test binaries, clippy -D warnings, fmt --check, tooling tests 33
passed, static hygiene exit 0, CI program sweep 142/0/49, docs examples 21/21.

Refs #671

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H9AnrAH4d8sTni73GWgkjR

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants