Skip to content

Enforce en-GB-oxendict spelling in source (#249) - #259

Open
lodyai[bot] wants to merge 16 commits into
mainfrom
issue-249-decide-and-enforce-en-gb-oxendict-spelling-for-code-identifiers-not-just-prose
Open

Enforce en-GB-oxendict spelling in source (#249)#259
lodyai[bot] wants to merge 16 commits into
mainfrom
issue-249-decide-and-enforce-en-gb-oxendict-spelling-for-code-identifiers-not-just-prose

Conversation

@lodyai

@lodyai lodyai Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Summary

This branch applies the repository en-GB-oxendict policy to code identifiers
and source prose, closing the gap that allowed Python -ise identifiers to
drift without a failing gate. It normalizes the source corpus, widens the
pinned spelling check to tracked Markdown, Python and Rust files, and records
the policy and exception process in ADR-009.

Closes #249

ExecPlan: enforce-ize-identifiers.md

Review walkthrough

  • Start with the Makefile and scripts/tests/test_typos_rollout.py for
    configuration ordering, the widened pathspec, and the real-target
    integration test.
  • Review typos.local.toml for bounded external-contract exceptions and
    typos.toml for the regenerated Markdown code-span policy.
  • Inspect AGENTS.md, the developers guide, ADR-009, and the design document
    for the governing rule and its architectural reference.
  • Finish with the mechanical identifier, source-prose, and public-docstring
    corrections. GitHub artifacts wire keys, /artifacts URLs, and the
    established --artifact-name option remain unchanged.

Validation

  • make check-fmt: passed.
  • make test: passed (1,147 Python tests, 54 skipped; 104 Rust tests).
  • make typecheck: passed.
  • make lint: passed, including the Python, Rust, and spelling gates.
  • make markdownlint: passed.
  • make nixie: passed.
  • git diff --check: passed.

Adoption update

Rebased onto origin/main, preserving target-side refactors while resolving
all conflicts. The CLI help contract now uses semantic assertions rather than
CPython-version-sensitive wrapping snapshots. The post-rebase repair restores
the target HTTP split, corrects private spelling identifiers, and reconciles
the native-wheel file-list snapshot.

Summary by Sourcery

Extend automated en-GB-oxendict enforcement to source code and identifiers,
normalize existing spelling drift, and document the governing policy and
exception process.

Enhancements:

  • Enforce the repository’s en-GB-oxendict policy across tracked Markdown,
    Python, and Rust source, including identifiers, comments, docstrings,
    fixtures, and prose.
  • Normalize repository-owned spelling throughout source while preserving
    documented external GitHub API and CLI spellings.
  • Document the source spelling policy and narrowly scoped exception process in
    ADR-009 and contributor guidance.

Build:

  • Update the spelling Makefile targets to regenerate configuration before
    scanning all tracked Markdown, Python, and Rust files.

Documentation:

  • Add ADR-009 and link the spelling decision from the documentation index,
    design document, developers guide, and changelog.

Tests:

  • Add spelling-target integration coverage for configuration ordering, source
    path coverage, rejected spellings, and documented exceptions.
  • Add semantic CLI coverage for benchmark baseline fetching and preserve the
    related native-wheel snapshot updates.

References

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Summary

Enforce the repository-wide en-GB-oxendict spelling policy across identifiers, source prose, comments, docstrings, fixtures, and tracked Markdown, Python, and Rust files.

  • Rename internal -ise identifiers to -ize, including benchmark helpers and test utilities.
  • Extend the pinned typos gate with documented exceptions for external contracts and deliberate fixtures.
  • Add ADR-009, update AGENTS.md and contributor documentation, and record the completed ExecPlan in docs/execplans/enforce-ize-identifiers.md.
  • Add regression tests for source scanning, exceptions, near-misses, backticks, and Makefile integration.
  • Replace the version-sensitive CLI help snapshot with semantic assertions.
  • Update related documentation, changelog entries, and execution-plan references.

Formatting, tests, type checking, linting, documentation checks, Makefile validation, and diff checks pass.

Walkthrough

The change standardizes repository spelling to en-GB-oxendict, extends the pinned typos gate to Markdown, Python, and Rust, renames internal -ise identifiers, and documents narrow spelling exceptions.

Changes

Oxford spelling enforcement

Layer / File(s) Summary
Policy, gate, and exception configuration
AGENTS.md, Makefile, typos*.toml, scripts/*, docs/*, CHANGELOG.md
The spelling policy now covers identifiers, comments, docstrings, prose, and tracked Markdown, Python, and Rust files. Generated configuration, documented exceptions, Makefile targets, ADRs, and rollout tests support the gate.
Artefact API and helper renames
benchmarks/fetch_main_benchmark_baseline.py, tests/helpers/maturin.py, tests/helpers/maturin_wheel.py, cuprum/unittests/*maturin*, cuprum/unittests/test_fetch_main_benchmark_baseline*
Benchmark baseline APIs, wheel helpers, tests, and CLI support use artefact terminology. The --artifact-name option remains unchanged.
Identifier and test-helper alignment
cuprum/_backend.py, cuprum/unittests/test_*, tests/helpers/maturin_wheel.py
Private identifiers, local variables, test names, helper names, and CrossHair references use -ize spellings.
Source and test prose corrections
benchmarks/*, cuprum/*, rust/*, tests/*
Comments, docstrings, headings, assertion messages, and module documentation use the selected spelling policy without changing runtime behaviour.

Possibly related PRs

Suggested labels: Issue

Suggested reviewers: leynos

Poem

Oxford words march in line,
Typos guard each source-file sign.
Artefacts keep their proper name,
Tests record the spelling game.
-ize and -yse now align.

🚥 Pre-merge checks | ✅ 19 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Testing (Property / Proof) ⚠️ Warning The PR adds generic Dictionary-to-TOML and source-gate invariants, but new tests use only fixed examples and parametrization; no Hypothesis or CrossHair property test was added. Add a Hypothesis property test over valid Dictionary contents and orderings. Assert deterministic parseable output, Markdown-only exception scoping, and preservation of every generated Oxford mapping.
✅ Passed checks (19 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Accept the implementation: it satisfies issue #249 by renaming identifiers, widening the spelling gate, documenting exceptions, and adding regression tests.
Out of Scope Changes check ✅ Passed Keep the changes: the CLI tests, documentation, configuration, and benchmark terminology updates support the stated spelling-enforcement objectives.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Testing (Overall) ✅ Passed New tests exercise the Makefile path expansion and ordering, real typos failures and scoped exceptions, renderer output, and CLI help, validation, failure, and extraction behaviour.
User-Facing Documentation ✅ Passed Pass this check: the PR changes contributor spelling enforcement and internal/test/CI names, not library user behaviour; AGENTS.md, the developers' guide, ADR-009, and CHANGELOG.md document the cha...
Developer Documentation ✅ Passed Accept the check: the developers' guide documents the pinned gate, source scope, regeneration, exceptions, and coverage; ADR-009 and the design reference record the decision; the ExecPlan is COMPLETE.
Module-Level Documentation ✅ Passed Accept: every tracked Python module has a module docstring, and each changed Rust module has //! documentation stating its purpose, use, and component relationship.
Testing (Unit And Behavioural) ✅ Passed Accept the check: tests cover Makefile ordering and all source types, pinned spelling failures and exceptions, CLI help/defaults/errors/success, archive traversal, retries, and redirects.
Testing (Compile-Time / Ui) ✅ Passed Accept this change: Rust changes are documentation-only, no TypeScript changes exist, and CLI/wheel output checks use focused semantic assertions and normalized snapshots.
Unit Architecture ✅ Passed Pass this check: keep the explicit split; render_typos_config is pure, writes and refreshes are named separately, Make targets expose sequencing, and benchmark query changes are mechanical renames.
Domain Architecture ✅ Passed Keep the change: core cuprum diffs only rename locals or update prose, while GitHub, filesystem, and spelling infrastructure remains in benchmarks/, scripts/, and Makefile boundaries.
Observability ✅ Passed Pass this check: the cumulative diff only renames symbols, stabilizes CLI help, and updates spelling tooling; network paths are unchanged and no logging, metrics, tracing, or alerts were added.
Security And Privacy ✅ Passed Accept this check: the PR adds no workflow or permission changes or credential material; it retains bounded token use, strips cross-origin auth, encodes query inputs, and rejects archive path trave...
Performance And Resource Use ✅ Passed Keep the change: functional additions are bounded spelling scans and tests; the 276-file, 2.56 MB scan is linear, while downloader retries and per-run queries are unchanged.
Concurrency And State ✅ Passed Accept this check: concurrency-sensitive edits are spelling or identifier renames; Make prerequisites order config before consumers, and the integration test asserts generation precedes scanning.
Architectural Complexity And Maintainability ✅ Passed Keep the change: it adds only a scoped Markdown renderer branch, explicit Make targets, and focused test seams; no new dependencies, cycles, or speculative framework appear.
Rust Compiler Lint Integrity ✅ Passed The branch changes only six Rust comment/doc lines; no Rust code, imports, module boundaries, lint allowances, artificial anchors, or clone calls changed. Existing expectations are narrow and justi...
Title check ✅ Passed The title clearly describes the main change, en-GB-oxendict spelling enforcement in source, and references issue #249 as required.
Description check ✅ Passed The description accurately explains the spelling enforcement, identifier updates, configuration changes, documentation, tests, and related issue.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-249-decide-and-enforce-en-gb-oxendict-spelling-for-code-identifiers-not-just-prose

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

@sourcery-ai

sourcery-ai Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

This PR widens the repository spelling gate to cover Markdown, Python, and Rust, normalizes internal spellings and identifiers to en-GB-oxendict (Oxford -ize/-yse) conventions, and documents the policy and exception workflow, including narrowly scoped ignores for external contracts and test fixtures.

Flow diagram for widened spelling gate and exception workflow

flowchart TD
    Dev[Run make spelling]
    Dev --> MSpelling[Makefile spelling recipe]

    MSpelling --> GenTypos[generate_typos_config.py]
    GenTypos --> SharedDict[Shared estate dictionary]
    GenTypos --> LocalOverlay[typos.local.toml]
    GenTypos --> TyposToml[Generated typos.toml]

    MSpelling --> TyposCheck[typos spelling check]
    TyposCheck --> Files[Tracked *.md, *.py, *.rs]

    LocalOverlay --> Ignores[patterns.ignore for external contracts and fixtures]
    Ignores --> TyposCheck
Loading

File-Level Changes

Change Details Files
Rename internal benchmark artefact helpers from American to British spelling while preserving external GitHub Actions contracts.
  • Rename ArtifactQuery to ArtefactQuery and artifact_* parameters/locals to artefact_* in the benchmark baseline fetch helper.
  • Update redirect handler, selection, extraction, and CLI functions to use artefact terminology, keeping the --artifact-name CLI flag and GitHub 'artifacts' wire keys unchanged.
  • Adjust unit tests to import the renamed artefact helpers, update helper names and docstrings, and keep fixtures aligned with the new identifiers.
benchmarks/fetch_main_benchmark_baseline.py
cuprum/unittests/test_fetch_main_benchmark_baseline.py
Extend the spelling gate to enforce en-GB-oxendict policy on Markdown, Python, and Rust files and add regression coverage for the Makefile recipe.
  • Change the Makefile spelling recipe to run typos against tracked *.md, *.py, and *.rs files and update its description to cover prose and source.
  • Add a test that parses the Makefile spelling target and asserts the three pathspec globs are present.
  • Update typos.local.toml and the generated typos.toml to add narrowly anchored ignore patterns for GitHub Actions artefact literals and deliberate spelling/byte-splitting fixtures.
Makefile
scripts/tests/test_typos_rollout.py
typos.local.toml
typos.toml
Document the source spelling policy, scope, and exception workflow, and record the architectural decision.
  • Expand the developers guide spelling policy to explicitly govern identifiers, comments, docstrings, fixtures, and prose across Markdown, Python, and Rust.
  • Clarify AGENTS.md to require en-GB-oxendict spelling for code and documentation, describe the source-wide typos gate, and explain how to add narrow exceptions in typos.local.toml.
  • Add ADR-008 and index it from contents.md and the developers guide to record the decision to enforce Oxford spelling in source, including consequences.
  • Add a Changed entry in CHANGELOG.md describing the widened source spelling enforcement.
docs/developers-guide.md
AGENTS.md
docs/adr-008-enforce-oxford-spelling-in-source.md
docs/contents.md
CHANGELOG.md
Normalize spelling across Python tests, helpers, and benchmarks to match en-GB-oxendict and improve consistency in comments, docstrings, and helper names.
  • Standardize spellings such as recognize/normalize/serialize/parameterize/behavior across unit tests, property-based tests, and behaviour tests while keeping external interface names intact.
  • Rename summarise_* helpers to summarize_* in folded summary tests and benchmarks, keeping public summarize_folded_file API stable.
  • Update maturin helpers and tests to use artefact spelling and normalize metadata/wheel entry helper names.
  • Adjust various comments and docstrings in core modules and adapters to use consistent American/British choices per policy (e.g., centralize, serialise vs serialize) without changing public APIs.
cuprum/unittests/test_line_splitting.py
cuprum/unittests/test_folded_summary.py
benchmarks/summarize_folded.py
tests/helpers/maturin.py
cuprum/unittests/test_maturin_build.py
cuprum/unittests/test_args_validators_property.py
cuprum/unittests/test_backend_resolver_property.py
cuprum/unittests/test_pipeline.py
cuprum/unittests/test_sh_property_based.py
cuprum/unittests/test_sh.py
benchmarks/_benchmark_type_validators.py
benchmarks/_benchmark_types.py
cuprum/_pipeline_config.py
cuprum/adapters/tracing_adapter.py
cuprum/builders/args.py
cuprum/sh.py
cuprum/unittests/test_rust_streams.py
cuprum/unittests/test_safe_cmd_stdin.py
cuprum/unittests/test_env_context.py
cuprum/unittests/test_profile_driver.py
cuprum/unittests/test_rust_splice.py
cuprum/unittests/test_safe_cmd_context.py
cuprum/unittests/test_safe_cmd_run.py
cuprum/unittests/test_stream_drain.py
cuprum/unittests/test_tee_profile_worker_core.py
cuprum/unittests/test_tee_profile_worker_concurrency.py
cuprum/unittests/test_tee_profile_worker_selector_metrics.py
cuprum/unittests/test_tracing_span_concurrency.py
benchmarks/tee_profile_worker.py
benchmarks/profile_tee_hotpath.py
benchmarks/sinks.py
benchmarks/tee_profile_driver.py
benchmarks/tee_profile_scenarios.py
cuprum/_pipeline_internals.py
cuprum/adapters/metrics_adapter.py
cuprum/context/registration.py
cuprum/unittests/_adapter_test_support.py
cuprum/unittests/_tee_profile_concurrency_support.py
cuprum/unittests/test_builder_property_based.py
cuprum/unittests/test_tracing_span_stateful.py
rust/cuprum-rust/src/splice/mod.rs
tests/behaviour/test_rust_streams_behaviour.py
tests/behaviour/test_telemetry_adapters.py
tests/helpers/execution.py
Add an ExecPlan describing the implementation plan, constraints, risks, and validation strategy for enforcing Oxford spelling in source identifiers.
  • Introduce enforce-ize-identifiers ExecPlan capturing milestones for identifier renames, source corpus cleanup, gate widening, documentation updates, and final publication.
  • Record constraints around public API stability, external contracts, generator usage, and gating order, plus progress and decision log entries tied to this branch.
  • Describe the validation stack and how the spelling regression test should fail before and succeed after the Makefile change.
docs/execplans/enforce-ize-identifiers.md

Assessment against linked issues

Issue Objective Addressed Explanation
#249 Rename the twelve internal Python identifiers using -ise spelling to their en-GB-oxendict -ize equivalents and keep all references consistent.
#249 Extend the spelling gate to check source files (at least *.py and *.rs) in addition to Markdown, and configure typos to handle necessary external-API spelling exceptions.
#249 Update contributor documentation (AGENTS.md and related docs) to explicitly state that en-GB-oxendict spelling applies to code identifiers as well as comments and prose, and describe the widened scope and exception process.

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

lodyai Bot pushed a commit that referenced this pull request Jul 30, 2026
Link the Unreleased changelog entry to draft PR #259 and close the living
ExecPlan with final validation, review, branch, and publication evidence.
codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

@leynos
leynos marked this pull request as ready for review July 30, 2026 22:27

@sourcery-ai sourcery-ai 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.

Sorry @leynos, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: eed2e13566

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread docs/adr-008-enforce-oxford-spelling-in-source.md Outdated
@pandalump

Copy link
Copy Markdown
Collaborator

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot added the Issue label Jul 31, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cuprum/_pipeline_internals.py`:
- Line 7: Update the module docstring near the ``CommandResult`` assembly
description to use the repository-required “centralize” spelling instead of
“centralise”; leave the surrounding wording unchanged.

In `@docs/developers-guide.md`:
- Around line 1303-1304: Update the exception sentence in the documentation near
the existing docstrings, string fixtures, and prose guidance to match ADR-008:
exempt spellings required by external contracts and deliberate spelling-test
fixtures, rather than limiting exemptions to external APIs.

In `@scripts/tests/test_typos_rollout.py`:
- Line 32: Add diagnostic messages to the new assertions in the test covering
spelling targets and Oxford correction mappings. Update the assertion involving
spelling_recipe and the assertion involving mappings["organize"] to use the
Python assert message form, preserving the proposed failure-context messages and
existing conditions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: d764fa9d-7f15-40f1-bdf6-6d1d35d40253

📥 Commits

Reviewing files that changed from the base of the PR and between 682ffd6 and eed2e13.

📒 Files selected for processing (57)
  • AGENTS.md
  • CHANGELOG.md
  • Makefile
  • benchmarks/_benchmark_type_validators.py
  • benchmarks/_benchmark_types.py
  • benchmarks/fetch_main_benchmark_baseline.py
  • benchmarks/profile_tee_hotpath.py
  • benchmarks/sinks.py
  • benchmarks/summarize_folded.py
  • benchmarks/tee_profile_driver.py
  • benchmarks/tee_profile_scenarios.py
  • benchmarks/tee_profile_worker.py
  • cuprum/_backend.py
  • cuprum/_pipeline_config.py
  • cuprum/_pipeline_internals.py
  • cuprum/adapters/metrics_adapter.py
  • cuprum/adapters/tracing_adapter.py
  • cuprum/builders/args.py
  • cuprum/context/registration.py
  • cuprum/sh.py
  • cuprum/unittests/_adapter_test_support.py
  • cuprum/unittests/_tee_profile_concurrency_support.py
  • cuprum/unittests/test_args_validators_property.py
  • cuprum/unittests/test_backend_resolver_property.py
  • cuprum/unittests/test_builder_property_based.py
  • cuprum/unittests/test_env_context.py
  • cuprum/unittests/test_fetch_main_benchmark_baseline.py
  • cuprum/unittests/test_folded_summary.py
  • cuprum/unittests/test_line_splitting.py
  • cuprum/unittests/test_maturin_build.py
  • cuprum/unittests/test_pipeline.py
  • cuprum/unittests/test_profile_driver.py
  • cuprum/unittests/test_rust_splice.py
  • cuprum/unittests/test_rust_streams.py
  • cuprum/unittests/test_safe_cmd_context.py
  • cuprum/unittests/test_safe_cmd_run.py
  • cuprum/unittests/test_safe_cmd_stdin.py
  • cuprum/unittests/test_sh.py
  • cuprum/unittests/test_sh_property_based.py
  • cuprum/unittests/test_stream_drain.py
  • cuprum/unittests/test_tee_profile_worker_concurrency.py
  • cuprum/unittests/test_tee_profile_worker_core.py
  • cuprum/unittests/test_tee_profile_worker_selector_metrics.py
  • cuprum/unittests/test_tracing_span_concurrency.py
  • cuprum/unittests/test_tracing_span_stateful.py
  • docs/adr-008-enforce-oxford-spelling-in-source.md
  • docs/contents.md
  • docs/developers-guide.md
  • docs/execplans/enforce-ize-identifiers.md
  • rust/cuprum-rust/src/splice/mod.rs
  • scripts/tests/test_typos_rollout.py
  • tests/behaviour/test_rust_streams_behaviour.py
  • tests/behaviour/test_telemetry_adapters.py
  • tests/helpers/execution.py
  • tests/helpers/maturin.py
  • typos.local.toml
  • typos.toml
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • leynos/shared-actions (auto-detected)
  • leynos/pylint-pypy-shim (auto-detected)
  • leynos/whitaker (auto-detected)

Comment thread cuprum/_pipeline_internals.py
Comment thread docs/developers-guide.md Outdated
Comment thread scripts/tests/test_typos_rollout.py Outdated
codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

@buzzybee-df12

Copy link
Copy Markdown
Collaborator

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@benchmarks/fetch_main_benchmark_baseline.py`:
- Around line 42-49: Expand the public NumPy-style docstrings at
benchmarks/fetch_main_benchmark_baseline.py:42-49 for ArtefactQuery, documenting
every query field and api_base_url; at
benchmarks/fetch_main_benchmark_baseline.py:210-216, document payload inputs,
selection order, and the None result; at
benchmarks/fetch_main_benchmark_baseline.py:247-252, document extraction results
and archive failure conditions; at
benchmarks/fetch_main_benchmark_baseline.py:270-275, document query, token,
result, and GitHub-response validation failures; and at
benchmarks/fetch_main_benchmark_baseline.py:357-358, document CLI arguments,
exit codes, and token-failure behavior. Also expand the public helper docstring
at tests/helpers/maturin.py:221-232 to document root, out_dir, the wheel result,
and include a useful example call, adding appropriate Parameters, Returns, and
Raises sections where applicable.

In `@docs/contents.md`:
- Around line 42-43: Update the ADR-008 entry in docs/contents.md to use the
existing [adr-008] reference label instead of an inline destination, ensuring it
reuses the single link definition already declared later in the document and
matches the surrounding reference-style entries.

In `@docs/developers-guide.md`:
- Around line 1306-1310: Update the documentation’s “shared estate dictionary”
wording to use the generator’s established terminology: shared/base
en-GB-oxendict dictionary and local cache. Keep the description aligned with the
implementation’s refresh and merge behavior.

In `@Makefile`:
- Around line 129-132: Update the Makefile spelling targets so configuration
generation is a shared prerequisite that runs before both spelling-helper-test
and the final scan. Ensure _run_spelling_gate and the commands under spelling
use the freshly generated typos.toml, and add or update relevant tests to verify
generation precedes validation.

In `@scripts/tests/test_typos_rollout.py`:
- Around line 79-91: Extend test_spelling_gate_preserves_documented_exceptions
with parameterized fixtures covering every local spelling-gate exception: the
/artifacts path, teh and ises fixture pairs, and the byte-splitting case. Feed
each fixture through _run_spelling_gate and assert success, while retaining
existing coverage for --artifact-name, artifacts, and mappings["organise"].

In `@typos.local.toml`:
- Around line 14-18: Update the typos.local.toml exceptions for "artifacts",
/artifacts, and --artifact-name to use boundaries that match only the intended
external API or CLI literals, not longer repository-owned values. Add near-miss
cases covering longer values to scripts/tests/test_typos_rollout.py, then
regenerate typos.toml from the updated configuration.

In `@typos.toml`:
- Line 34: Move the fenced-code and inline-code ignore patterns from
[default].extend-ignore-re into [type.markdown] so they apply only to Markdown
scanning, updating the generator or typos.local.toml as the source of truth and
regenerating typos.toml. Add fixtures verifying backtick-wrapped “organise” is
rejected in both Python and Rust files.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: a2cb2f5e-44d5-4b0e-b501-1b657212ea41

📥 Commits

Reviewing files that changed from the base of the PR and between 682ffd6 and 7743a0b.

📒 Files selected for processing (58)
  • AGENTS.md
  • CHANGELOG.md
  • Makefile
  • benchmarks/_benchmark_type_validators.py
  • benchmarks/_benchmark_types.py
  • benchmarks/fetch_main_benchmark_baseline.py
  • benchmarks/profile_tee_hotpath.py
  • benchmarks/sinks.py
  • benchmarks/summarize_folded.py
  • benchmarks/tee_profile_driver.py
  • benchmarks/tee_profile_scenarios.py
  • benchmarks/tee_profile_worker.py
  • cuprum/_backend.py
  • cuprum/_pipeline_config.py
  • cuprum/_pipeline_internals.py
  • cuprum/adapters/metrics_adapter.py
  • cuprum/adapters/tracing_adapter.py
  • cuprum/builders/args.py
  • cuprum/context/registration.py
  • cuprum/sh.py
  • cuprum/unittests/__snapshots__/test_fetch_main_benchmark_baseline.ambr
  • cuprum/unittests/_adapter_test_support.py
  • cuprum/unittests/_tee_profile_concurrency_support.py
  • cuprum/unittests/test_args_validators_property.py
  • cuprum/unittests/test_backend_resolver_property.py
  • cuprum/unittests/test_builder_property_based.py
  • cuprum/unittests/test_env_context.py
  • cuprum/unittests/test_fetch_main_benchmark_baseline.py
  • cuprum/unittests/test_folded_summary.py
  • cuprum/unittests/test_line_splitting.py
  • cuprum/unittests/test_maturin_build.py
  • cuprum/unittests/test_pipeline.py
  • cuprum/unittests/test_profile_driver.py
  • cuprum/unittests/test_rust_splice.py
  • cuprum/unittests/test_rust_streams.py
  • cuprum/unittests/test_safe_cmd_context.py
  • cuprum/unittests/test_safe_cmd_run.py
  • cuprum/unittests/test_safe_cmd_stdin.py
  • cuprum/unittests/test_sh.py
  • cuprum/unittests/test_sh_property_based.py
  • cuprum/unittests/test_stream_drain.py
  • cuprum/unittests/test_tee_profile_worker_concurrency.py
  • cuprum/unittests/test_tee_profile_worker_core.py
  • cuprum/unittests/test_tee_profile_worker_selector_metrics.py
  • cuprum/unittests/test_tracing_span_concurrency.py
  • cuprum/unittests/test_tracing_span_stateful.py
  • docs/adr-008-enforce-oxford-spelling-in-source.md
  • docs/contents.md
  • docs/developers-guide.md
  • docs/execplans/enforce-ize-identifiers.md
  • rust/cuprum-rust/src/splice/mod.rs
  • scripts/tests/test_typos_rollout.py
  • tests/behaviour/test_rust_streams_behaviour.py
  • tests/behaviour/test_telemetry_adapters.py
  • tests/helpers/execution.py
  • tests/helpers/maturin.py
  • typos.local.toml
  • typos.toml
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • leynos/shared-actions (auto-detected)
  • leynos/pylint-pypy-shim (auto-detected)
  • leynos/whitaker (auto-detected)

Comment thread benchmarks/fetch_main_benchmark_baseline.py
Comment thread docs/contents.md Outdated
Comment thread docs/developers-guide.md Outdated
Comment thread Makefile Outdated
Comment thread scripts/tests/test_typos_rollout.py Outdated
Comment thread typos.local.toml Outdated
Comment thread typos.toml Outdated
lodyai Bot pushed a commit that referenced this pull request Aug 2, 2026
Link the Unreleased changelog entry to draft PR #259 and close the living
ExecPlan with final validation, review, branch, and publication evidence.
@lodyai
lodyai Bot force-pushed the issue-249-decide-and-enforce-en-gb-oxendict-spelling-for-code-identifiers-not-just-prose branch from 7743a0b to 9a4851f Compare August 2, 2026 12:00
codescene-access[bot]

This comment was marked as outdated.

lodyai Bot pushed a commit that referenced this pull request Aug 4, 2026
Link the Unreleased changelog entry to draft PR #259 and close the living
ExecPlan with final validation, review, branch, and publication evidence.
@lodyai
lodyai Bot force-pushed the issue-249-decide-and-enforce-en-gb-oxendict-spelling-for-code-identifiers-not-just-prose branch from 9a4851f to a71b7d5 Compare August 4, 2026 19:00
codescene-access[bot]

This comment was marked as outdated.

@leynos

leynos commented Aug 4, 2026

Copy link
Copy Markdown
Owner

@coderabbitai Please investigate the cause of the following issue using codegraph exploration and research, identify a fix and provide an AI coding agent prompt for the fix:

=================================== FAILURES ===================================
___________________ test_cli_help_and_missing_token_snapshot ___________________

capsys = <_pytest.capture.CaptureFixture object at 0x7f0190e514f0>
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7f0190e51d90>
snapshot = dict({
  'help': list([
    'usage: fetch_main_benchmark_baseline.py [-h] --repository REPOSITORY',
    '             ...containing the GitHub token.',
  ]),
  'missing_token': 'missing GitHub token in environment variable GITHUB_TOKEN',
})
tmp_path = PosixPath('/tmp/pytest-of-runner/pytest-0/test_cli_help_and_missing_toke0')

    def test_cli_help_and_missing_token_snapshot(
        capsys: pytest.CaptureFixture[str],
        monkeypatch: pytest.MonkeyPatch,
        snapshot: SnapshotAssertion,
        tmp_path: pth.Path,
    ) -> None:
        """CLI help and token errors retain their user-facing contract."""
        monkeypatch.setattr(sys, "argv", ["fetch_main_benchmark_baseline.py"])
        with pytest.raises(SystemExit) as help_exit:
            main(["--help"])
        help_text = capsys.readouterr().out
        monkeypatch.delenv("GITHUB_TOKEN", raising=False)
        with pytest.raises(SystemExit) as token_exit:
            main(_main_cli_args(tmp_path))
        token_error = str(token_exit.value)
    
        assert help_exit.value.code == 0
        assert "--artifact-name" in help_text
        assert "Artefact name to download" in help_text
        assert "missing GitHub token" in token_error
>       assert {"help": help_text.splitlines(), "missing_token": token_error} == snapshot
E       AssertionError: assert [+ received] == [- snapshot]
E           dict({
E             ...
E               'usage: fetch_main_benchmark_baseline.py [-h] --repository REPOSITORY',
E         -     '                                        --workflow WORKFLOW',
E         +     '                                        --workflow WORKFLOW --artifact-name',
E         -     '                                        --artifact-name ARTEFACT_NAME',
E         -     '                                        --output-dir OUTPUT_DIR',
E         +     '                                        ARTEFACT_NAME --output-dir OUTPUT_DIR',
E               '                                        [--branch BRANCH] [--event EVENT]',
E             ...
E           })

cuprum/unittests/test_fetch_main_benchmark_baseline.py:209: AssertionError
--------------------------- snapshot report summary ----------------------------
1 snapshot failed. 14 snapshots passed.
=========================== short test summary info ============================
FAILED cuprum/unittests/test_fetch_main_benchmark_baseline.py::test_cli_help_and_missing_token_snapshot - AssertionError: assert [+ received] == [- snapshot]
    dict({
      ...
        'usage: fetch_main_benchmark_baseline.py [-h] --repository REPOSITORY',
  -     '                                        --workflow WORKFLOW',
  +     '                                        --workflow WORKFLOW --artifact-name',
  -     '                                        --artifact-name ARTEFACT_NAME',
  -     '                                        --output-dir OUTPUT_DIR',
  +     '                                        ARTEFACT_NAME --output-dir OUTPUT_DIR',
        '                                        [--branch BRANCH] [--event EVENT]',
      ...
    })
============= 1 failed, 817 passed, 52 skipped in 86.36s (0:01:26) =============
make: *** [Makefile:199: test] Error 1

@coderabbitai

This comment was marked as resolved.

@pandalump

Copy link
Copy Markdown
Collaborator

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@leynos
leynos force-pushed the issue-249-decide-and-enforce-en-gb-oxendict-spelling-for-code-identifiers-not-just-prose branch from da02b0f to b852fee Compare August 25, 2026 02:56
codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

leynos pushed a commit that referenced this pull request Aug 25, 2026
Link the Unreleased changelog entry to draft PR #259 and close the living
ExecPlan with final validation, review, branch, and publication evidence.
@leynos
leynos force-pushed the issue-249-decide-and-enforce-en-gb-oxendict-spelling-for-code-identifiers-not-just-prose branch from b852fee to eee86ab Compare August 25, 2026 10:37
codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

leynos added a commit that referenced this pull request Aug 25, 2026
`test_adapter_fail_fast` and `test_tracing_span_concurrency` used the -ise
forms in module docstrings, method docstrings, and comments, which the
en-GB-oxendict house rule spells -ize; `test_pipeline_wait_state_machine`
had the same in "initialises".

`test_an_unrecognised_phase_still_raises` is renamed with them. The rule
that preserves existing -ise identifiers exists to protect names other code
depends on, and this one has no consumers at all: it is a test method, named
nowhere but its own definition. Leaving it would plant a fresh counterexample
just as PR #259 lands the identifier policy.
leynos added a commit that referenced this pull request Aug 25, 2026
… telemetry (#73, #285) (#243)

* Extract pure completion transition; state-machine test it (#73)

Pipeline completion ordering lived inline in _process_completed_task,
which mutated _PipelineWaitState, read the clock, and terminated stages
in one async method — untestable at the state-transition level.

Extract the pure decision into _PipelineWaitState.record_completion(
completed_idx, exit_code, *, ended_at): it stamps the exit code and end
time (clock injected), latches the first non-zero exit in completion
order as failure_index, and returns whether the remaining downstream
stages must be terminated. _process_completed_task now calls it and keeps
the clock read and the async termination side effect.

Add cuprum/unittests/test_pipeline_wait.py: a Hypothesis
RuleBasedStateMachine drives random completion orders (restarting fresh
pipelines when drained) and asserts first-failure semantics, timing-slot
population, and the termination decision; example tests pin the
boundaries (first-completed failure wins, final-stage failure requests no
termination, all-success records no failure, fail-fast fires once).

Regenerate the maturin wheel-manifest snapshot for the new test file.

Closes #73

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Segregate the completion command from the fail-fast query

Review findings on the completion transition:

- record_completion both mutated the wait state and returned the
  termination decision, so a query was inseparable from a command,
  against the command/query segregation rule in AGENTS.md. Split it:
  record_completion is now a command returning None, and the new
  should_terminate_others(completed_idx) query reports the fail-fast
  decision from state without changing it, so it is repeatable and
  order-independent. _process_completed_task calls the command, then the
  query.
- AGENTS.md requires function documentation to demonstrate usage and
  outcome; both methods gained worked examples.
- The docstring described terminating the "remaining downstream stages".
  _terminate_pipeline_remaining_stages stops every still-running stage
  except the failed one, upstream included, so the wording now says
  "every other still-running stage".
- The suite only covered the pure transition, so it could not catch
  inverted or omitted termination, or a mis-forwarded clock or index. Add
  TestProcessCompletedTask, which drives the real async boundary with a
  recording termination double and an injected perf_counter. Verified
  non-vacuous: omitting the termination call and forwarding the wrong
  clock each fail these tests.
- Every bare assertion in the suite gained a diagnostic message.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Document the pipeline completion-ordering seam

The pre-merge Developer Documentation check flagged that the new
record_completion / should_terminate_others seam was described only in
docstrings, with the design doc covering fail-fast behaviour generally.

Add a "Completion ordering seam" subsection to the fail-fast policy in the
design doc: what each half of the command/query split does, that the clock
is injected so the transition is deterministic, that completion order
rather than stage order decides which failure latches, and that
_process_completed_task is the sole caller joining the two.

Also correct the adjacent policy wording, which said fail-fast terminates
"the remaining stages": it terminates every other still-running stage,
upstream included. That is the same inaccuracy already corrected in the
docstrings on this branch.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Verify the completion transition with CrossHair; observe fail-fast

Completes issue #73's CrossHair criterion and the two unresolved warnings
on this PR, without weakening the command/query split.

CrossHair (test_pipeline_wait_crosshair.py): PEP 316 contracts confirm,
over a bounded symbolic model, that a completion writes only its own slot,
that the first non-zero completion latches failure_index and a later one
never replaces it, that should_terminate_others holds exactly for a
non-final first failure (covering final-stage and single-stage
pipelines), and that repeating the query changes nothing. The model caps
the pipeline at three stages, exit codes at -2..2, and timestamps at
0.0..4.0, and builds the state directly, so no asyncio task, subprocess,
or clock enters the symbolic space. Verified genuine rather than assumed:
dropping the final-stage exclusion and letting a later failure re-latch
each yield POST_FAIL instead of CONFIRMED.

The import-time probe follows test_line_splitting.py and degrades to a
skip only for a missing dependency (ImportError) or a tracer that cannot
handle the interpreter (TraceException); every other failure is re-raised,
so a supported interpreter runs the verification rather than warning past
it. That probe logic existed already, so per the AGENTS.md abstraction
policy it moves to the shared cuprum/unittests/_crosshair_support.py that
both modules import, instead of being duplicated. test_line_splitting.py's
eleven harness tests cover it unchanged.

Observability: _process_completed_task now emits two structured records
through logging.getLogger(__name__) — pipeline_stage_first_failure when a
completion newly latches, and pipeline_fail_fast_termination immediately
before termination is awaited — sharing cuprum_stage_index,
cuprum_exit_code, and cuprum_duration_s, and distinguished by a stable
cuprum_action. Neither fires for a success, a later failure, or a
final-stage or single-stage failure. Logging stays in the async caller;
moving it into the command or query would break the determinism the
symbolic verification depends on. Five log-capture tests pin those cases,
and are non-vacuous: logging unconditionally, or dropping the termination
record, each fail them.

Documents the seam, the record fields, and both verification commands in
the developers guide, with a matching note in the design doc.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Group completion pairs to clear the argument-count finding

CodeScene flagged Excess Number of Function Arguments on both
_latches_first_failure and _first_failure_latch_contract, each taking five
scalars. Replace the parallel index/exit-code arguments with one
(stage_index, exit_code) tuple per completion, so both take three:
stage_count plus the two completion pairs, in completion order.

Grouping per completion rather than per kind keeps the two values that
describe a single event together, which is what the arguments already
meant. The helper unpacks both tuples immediately, leaving the state
construction, the ordered record_completion calls with their 1.0 and 2.0
timestamps, and the latch assertions untouched.

The symbolic domain is preserved exactly: the contract's preconditions now
bound each tuple element individually — two distinct valid stage indexes
and two exit codes in -2..2 — rather than the former scalars. Plain typed
tuples keep CrossHair's inputs primitive and bounded; no dataclass,
NamedTuple, or runtime construction is introduced.

Verified the refactor did not weaken the exploration rather than assuming
it: letting a later failure re-latch, and latching a hard-coded index,
each still yield POST_FAIL instead of CONFIRMED. CodeScene health for the
file goes from 9.68 to 10.0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Document the fail-fast records in the users guide

The two records this PR adds are emitted at WARNING on a default logging
configuration, so users see them whether or not they went looking. They
were documented only in the developers guide, which is the wrong audience
for output that arrives unbidden.

Add a fail-fast diagnosis section under pipeline execution covering both
records, their shared fields, and the two behaviours that otherwise read as
bugs: only the first failure is reported, because fail-fast makes the
remaining stages fail too and reporting each would bury the cause; and a
final-stage failure emits no termination record because nothing was left
running.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Say which stages fail-fast terminates

The users guide said fail-fast terminates "the remaining stages", which
reads as the stages after the failure. It terminates every *other*
still-running stage: `_stages_to_terminate` selects every index that is
neither the failed stage nor already settled, so an upstream producer is
stopped just as a downstream consumer is.

`should_terminate_others` and the design document already say this
explicitly; the guide was the one place left implying downstream-only,
which matters because a user reasoning about a slow upstream producer
would draw the wrong conclusion.

Correct the pre-existing bullet as well as the new table row, so the two
statements a few lines apart cannot disagree.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Describe the completion seam in the module docstring

"Pipeline waiting logic with fail-fast semantics" named the subject but
not the structure, so a reader had to reconstruct the command/query split
from the code and could not tell which neighbouring module owns what.

State the seam: the command latches the first failure in completion
order, the query reports whether to stop the others, and
`_process_completed_task` is the only place the two are joined. Name what
deliberately lives elsewhere — process termination and cleanup in
`_process_lifecycle`, pipe-task collection in `_pipeline_streams` — so the
boundary is explicit rather than inferred from the imports.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Split the pipeline-wait tests by concern

`test_pipeline_wait.py` had reached 539 lines, past the 400-line cap, and
mixed four distinct concerns: a Hypothesis state machine, pinned boundary
cases, the async wiring, and the structured records.

Split it along those lines into four modules, each named for what it
covers and none above 191 lines. The state machine and the examples now
say in their docstrings how they relate — one generalises what the other
pins — which the single file left implicit.

Extract the shared scaffolding into `_pipeline_wait_support.py`. The two
`fake_terminate` doubles were near-identical but had drifted: one recorded
`(index, cancel_grace)` pairs, the other only indices. `record_terminations`
keeps the richer form, so a test that needs the grace period no longer has
to reintroduce its own copy to get it.

Update the developers guide, which pointed at the single file.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Make simultaneous completions deterministic, and pin the clamp

Four review findings on the pipeline-wait observability work.

`_wait_for_pipeline` iterated the set `asyncio.wait` returns, so stages
completing in the same batch had no completion order left to observe and
`failure_index` fell out of set iteration — the module docstring's "first
non-zero exit in completion order" was a claim the code could not keep.
Sort each batch by stage index. That is both deterministic and the more
useful answer: in a pipeline the upstream failure is what caused the
downstream ones it triggered, so it is the one worth naming.

Add the two tests that pin it, driving real settled stages through
`_wait_for_pipeline`. Recording the processing order rather than only the
outcome makes the check deterministic instead of relying on set order
happening to differ from stage order. Mutation-verified: reverting to the
bare set fails the ordering test on every run.

Add the duration-clamp case. Every existing observability test starts a
stage at zero and reads a later clock, so all of them hold whether or not
`max(0.0, ...)` is there; this one inverts the pair and would publish
-87.5 seconds without it. Mutation-verified.

Add a syrupy snapshot over the whole record shape — logger, level,
rendered message, and every `cuprum_` field. The field assertions each
cover the part their test is about; the snapshot catches a change to any
part no test happened to assert on.

Derive the log fields only when a record will carry them. Every stage
passes through this function and most emit nothing, so a success no
longer allocates fields it discards.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Pin completion order at the async boundary, not just in the transition

Every case in `TestProcessCompletedTask` drove a single completion, so
none of them could tell completion order from stage order: a boundary
that latched the lowest-indexed failure instead of the first one to
complete passed all three. The Hypothesis state machine does catch that
mutant, but only against the pure transition — it says nothing about the
index `_process_completed_task` actually passes to `record_completion`.

Add a two-completion case where stage 2 fails before stage 0, and a
`_run_completions` driver to sequence it. The clock advances per
completion rather than staying pinned, so each stage's `ended_at` is
distinguishable and a boundary that reused an earlier reading is
visible.

This is the counterpart to `TestSimultaneousCompletions`: stage order is
the tie-break *within* one `asyncio.wait` batch, where completion order
cannot be observed, and completion order decides across batches. Neither
test alone pins that boundary.

Mutation-verified three ways — latching the lowest index, terminating on
every non-final failure, and reusing an earlier end time each fail it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Say which stage wins when a batch settles together

The guide and the design record described completion order as the sole rule
for latching `failure_index`, but `_wait_for_pipeline` also sorts each
`asyncio.wait` batch by stage index. That sort was undocumented outside the
module, so a reader could not tell whether it encoded a priority or merely
made an unobservable order deterministic.

Document it as the tie-break it is: real completion order decides across
batches, and ascending stage index only orders stages *within* one batch,
where `asyncio.wait` returns an unordered set and the alternative is a
`failure_index` that varies run to run. Record why the tie is broken towards
the earliest stage — an upstream failure causes the downstream ones it
triggers — and name the test that would catch the sort's removal, since tests
that fail stages at distinct times cannot.

Align roadmap 2.1.2 with the same wording: it said "terminate downstream"
where the implementation terminates every other still-running stage, upstream
producers included.

* Let a fail-fast record say which run it came from

The fail-fast records carried a stage index, an exit code, and a duration.
That describes a failure but does not locate it: two pipelines running
concurrently in one process both report a stage 0, and nothing joined a record
to the span or lifecycle events the observe hooks publish for that same stage.

Carry the stage's existing `ExecId` as `cuprum_exec_id`, plus
`cuprum_stage_count` so a record is self-describing. The token reaches the
wait path as `_PipelineSpawnResult.stages`, a `_StageWaitContext` grouping the
per-stage data the wait already needed, so `started_at` and the tokens travel
together rather than as parallel arguments. `_PipelineWaitState` defaults it
to empty and reports `None`, because the pure transition never reads it and
the symbolic model must not carry it.

Report the teardown as well as its start. `pipeline_fail_fast_termination` is
emitted *before* termination is awaited, so on its own it cannot distinguish a
teardown that finished from one still blocked on a stage that will not die. A
new `pipeline_fail_fast_terminated` record closes that gap once termination
returns, carrying how many stages were stopped —
`_terminate_pipeline_remaining_stages` now returns that count rather than
having the caller recompute the selection — and how long it took.

Move the record payload into `cuprum/_pipeline_wait_records.py`: it is a
published contract the users' guide documents, and keeping it beside the
verified ordering decision invited editing one while meaning the other. The
logger name is hard-coded there rather than following `__name__`, because
operators are told to attach handlers to `cuprum._pipeline_wait`.

No metric and no trace event. The library emits neither on its own —
`MetricsHook` is an opt-in adapter over `ExecEvent`, and its phase match is
fail-closed, so a fail-fast phase would make it raise for every user who has
already registered it. Both would need a new observability surface rather than
a use of an existing one; `cuprum_action` is a stable low-cardinality event
name a log-based counter can key on meanwhile.

* Tell the hooks when a pipeline gives up on itself

A fail-fast teardown was, until now, reported only as log records on
`cuprum._pipeline_wait`. That is fine for a human reading logs and
useless for the metrics and tracing integrations, which would have had
to parse log text to notice that a pipeline had been torn down early.

Add a `pipeline_fail_fast` `ExecPhase`, emitted once per pipeline for
the completion that *newly latches* `failure_index` and still leaves
stages to stop — so not for a success, not for a failure that follows
the latch, and not for a final-stage or single-stage failure, none of
which trigger a teardown. It is published *before* termination is
requested, so a consumer sees the decision even when the teardown then
blocks on a stage that will not die.

The event reuses the failing stage's existing `exec_id`. That is what
lets a trace show the teardown on the span the stage's own `start`
opened, and it is why `_StageWaitContext` now carries the stage
observations alongside the tokens: publishing an `ExecEvent` needs the
stage's program, argv, and tags, not merely its token. Both are derived
from one observation tuple at the single construction site, so a log
record and the matching event cannot disagree about which stage failed.
`record_completion` and `should_terminate_others` stay a pure command
and query; emission stays at the async boundary.

`stage_index` and `stage_count` are typed fields rather than tags
because caller-supplied tags are merged last and may legitimately
shadow `pipeline_stage_index`; the decision has to report the index the
coordinator acted on.

The adapters render it three ways: `MetricsHook` increments
`cuprum_pipeline_fail_fast_total`, labelled by `program` and `project`
and nothing else — `exec_id` would make the series unbounded, and stage
index and exit code would multiply it for no aggregate a dashboard
needs; `TracingHook` adds a `cuprum.pipeline_fail_fast` span event to
the failing stage's open span, starting and ending none; the logging
adapter renders it at `LogLevels.fail_fast_level`, WARNING by default,
rather than falling through to a generic DEBUG message that reports
only the program.

Adding a phase is a contract change, recorded as such in the changelog.
`MetricsHook`'s match is fail-closed and hook failures are re-raised, so
a hook that rejects unknown phases will raise until updated. In this
repository that is one adapter, updated here; `TracingHook` and the
structured logging hook are both fail-open. Third-party fail-closed
hooks need an explicit arm, which is a versioning consideration rather
than a reason not to emit.

The `Span` and `Tracer` protocols move to `tracing_protocols.py`, re-
exported unchanged. This is a precondition rather than a drive-by: the
hook module was at the 400-line ceiling, and the contract reads better
without the correlation machinery around it.

* Give the silent-completion cases a name each

The parametrized test took its stage count, completion sequence, and
reason as three separate arguments, which CodeScene flags as excess
arguments and which reads as a bare tuple at the call site. Wrap them in
a `_SilentCase` record, matching the `_RecordCase` pattern the sibling
observability module already uses for the same shape.

Behaviour is unchanged; all three cases still run and still fail when
the emission condition is widened to cover final-stage or single-stage
failures.

* Teach the merged oracles about the eighth phase

The rebase onto main brought in the metrics reducer and the two
adapter property suites from #247, which enumerate `ExecPhase` and the
counter table independently of production so a wrong name cannot pass
by construction. Those enumerations stopped at `exit` and so never saw
`pipeline_fail_fast`, leaving the new arm and its counter unexercised
by the very oracles written to catch a bad mapping.

Widen both enumerations and give the logging level property its own
`fail_fast_level` expectation, sampled rather than defaulted so the
mapping is checked and not merely observed. Adding the phase to the
level suite failed until the expected-level map gained an arm, which is
the point of deriving it independently.

Also reconcile the documented lists the two branches each half-updated:
the metrics counter set, the tracing drop list, the configurable log
levels, and the `cuprum_phase` values.

* Say what fail-fast really terminates, and who projects it

Three published descriptions of the fail-fast telemetry disagreed with the
code they describe.

The termination scope was written as "the surviving stages", which reads as
the stages downstream of the failure. `should_terminate_others` stops *every
other still-running stage*, upstream producers included, so all five prose
sites now use that one phrase.

The design document still claimed fail-fast emits neither a metric nor a
trace event and that both would need a new observability surface. Section 8.4
of the same document has documented the opposite since the adapters landed:
the wait path publishes records and one event, and `MetricsHook` and
`TracingHook` project that event onto the counter and the failing stage's
open span.

The users' guide claimed the metrics adapter publishes `exec_id`. It does
not — `cuprum_pipeline_fail_fast_total` is labelled by `program` and
`project` alone — so a counter spike is joined to a stage through the
matching log record or event, never through a label. Its tracing drop list
also omitted `stdin_error`, which `TracingHook` drops like the rest.

* Pin the exit race, and drop a suppression instead of moving it

`Span` and `Tracer` each carried a `# pylint: disable=unnecessary-ellipsis`,
which the project's suppression policy does not allow. The rule is enabled
deliberately and it is the docstring-plus-ellipsis pair it objects to, so the
suppression could not simply be narrowed. `MetricsCollector` — the sibling
adapter protocol, next door — has always written its stubs as
`raise NotImplementedError`. The tracing protocols now do the same, and the
suppressions are gone rather than relocated.

`_handle_exit` pops a span under the lifecycle lock and ends it outside, so
there is a window where the span object exists, is not yet ended, and is no
longer reachable through the active map. Two tests now park a `Span.end` to
hold that window open and assert what must happen inside it: a
`pipeline_fail_fast` for the same execution finds nothing and is dropped, as
any uncorrelatable event is; and one for a *different* execution is recorded
without waiting on the parked end.

Both bite. Ending the span before detaching it records the fail-fast on a
span the backend has already closed; holding the lock across `end()` stalls
the unrelated fail-fast until the backend returns.

* Give the clock its own seam, and the helpers their prefix

`_pipeline_wait_records` exports three helpers into one private module, and
all three read as public API. Their neighbours across the wait path —
`_terminate_pipeline_remaining_stages`, `_collect_pipe_results`,
`_stages_to_terminate` — and the dataclass in this very file are all
prefixed. These now are too.

Pinning the clock reached through `_pipeline_wait.time`, which is the stdlib
`time` module object, so every test that pinned it replaced
`time.perf_counter` for the whole process while it ran. The wait module now
binds `perf_counter` itself, which gives the tests an attribute of their own
to patch. The advancing variant that `test_pipeline_wait_async` had inline
moves to the support module as `AdvancingClock`, so both clock helpers patch
the one seam and a future change to it lands in one place.

* Stop the tests grading their own homework

`test_the_helper_counts_the_stages_it_stopped` computed its expectation from
`_stages_to_terminate`, which is the very selection the helper it checks
derives its targets from. Any change that moved both together passed. The
counts are now stated per case, and a case where the failed stage's wait task
is still running has been added — without it, sparing the failed stage and
sparing an already-settled stage are indistinguishable.

`test_first_nonzero_exit_latches_failure_index` claimed to pin a lower-indexed
stage failing after a higher-indexed one, but had stage 2 *succeed* first, so
a rule that latched the lowest failing index agreed with it throughout. Stage
2 now fails first, and stage 0's later, lower-indexed failure must not
displace it.

Two modules hand-rolled the create-task/settle/assign-index/call sequence that
`apply_completions` already provides, and a third copy of the
select-one-`cuprum_`-field comprehension sat in the correlation module. Both
now come from the support module, so a change to the boundary signature is one
edit rather than three.

`drive_completions` documented a `completions` parameter it does not take, and
the CrossHair module pointed at a `test_pipeline_wait.py` that does not exist.

* Say what fail-fast really terminates, and what a raising hook does

Two user-facing claims did not match the code they describe.

The pipeline notes said a stage exiting non-zero terminates every other
still-running stage. The coordinator is narrower than that: only the
*first* failure latches, and `should_terminate_others` answers `True`
only when that failure is also not the final stage. A failing final
stage, and any single-stage pipeline, terminate nothing — there is
nothing left running to stop — and later failures are usually
consequences of the first rather than new causes. The design document's
decision list carried the same unqualified claim, phrased as "the
remaining stages"; it now uses the wording the other five prose sites
settled on.

The guide also said nothing about what happens when an observe hook
raises, which matters now that `pipeline_fail_fast` is a new phase a
fail-closed hook will reject. Cuprum does not swallow the failure: it
logs, then re-raises the hook's own exception type out of `run()` /
`run_sync()`. The two hook kinds differ only in when. A synchronous
hook raises inline, so emission of that event stops and later hooks do
not see it; an awaitable hook raises inside its scheduled task, so
every hook still receives the event and the failure surfaces at the
drain before the run returns.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Stop the tests restating what the helpers already say

Four review points, none of which change behaviour:

- `_UNIT_COUNTER_PHASES` was a plain list beside a `MappingProxyType`
  sibling. Make it a tuple so neither of the two views a test can take
  of the same pairs is mutable.
- The fail-fast wiring tests ran three real subprocesses each, three
  times over, for one pipeline's worth of events. `scoped` is entered
  and left inside the helper and the tests only read what it returned,
  so a module-scoped fixture serves all three. It returns a tuple, so
  no test can hand the next one a shortened sequence.
- Two assertions re-implemented the `vars(record)` comprehension that
  `field_values` and `record_actions` already provide. Use them.
- Drop the `hook._active_spans == {}` assertion. It reaches into
  private state to restate what the two public assertions above it
  already pin: no event recorded, and the span ended.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Do not announce a teardown that has nothing to tear down

`should_terminate_others` reasons from stage positions alone, so it
answers `True` for any non-final latched failure — including one whose
sibling stages have all already exited. That happens whenever a whole
pipeline settles into a single `asyncio.wait` batch: the batch is
handled in stage order, so an upstream failure can be reached after
every other stage is done. The fail-fast event, both termination
records, and therefore `cuprum_pipeline_fail_fast_total` all fired for
that case, reporting a termination decision with no subject and a
closing `cuprum_terminated_stage_count` of zero that contradicted the
record which opened it.

`_process_completed_task` now asks `_has_stages_to_terminate` — which
wraps the same `_stages_to_terminate` reducer the teardown picks its
targets with, so the announcement and the teardown cannot disagree —
and withholds the event, both records, and the termination call when
nothing is left running. `pipeline_stage_first_failure` still fires:
the failure latched, and `failure_index` reports it either way; what is
absent is the termination decision, not the failure.

`record_completion` and `should_terminate_others` stay pure, and the
event still precedes the termination request.

Two supporting changes the above needed:

- `_StageWaitContext` was frozen but held `started_at` as a list, which
  `_PipelineWaitState.from_processes` then aliased — the "immutable
  snapshot" and the live bookkeeping were one object. The field is now
  a tuple and the wait state copies it.
- `apply_completions` gave the state a single wait task, so every
  sibling read as settled. It now builds one task per stage and swaps
  in a settled one per completion, as `_wait_for_pipeline` does, and
  takes a `before_each` hook so the clock-advancing driver in
  `test_pipeline_wait_async` can use it instead of duplicating it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

The docs record the new case alongside three wording fixes from the
same review round: the design doc's error-propagation policy is no
longer hedged as provisional, `failure_index` is described as the stage
that failed first rather than the one that triggered fail-fast, and the
determinism claim is scoped to stages settling in one wait batch.

* Say which failures have nothing left to stop

The developers' guide listed the completions that emit no termination
record as "a final-stage or single-stage failure". That set grew when
`_process_completed_task` started checking whether any sibling stage was
still running: a batch is handled in stage order, so an upstream failure
can be reached after every other stage has exited, and that case is now
silent too. Name the real condition, point at the reducer that decides
it, and say that the first-failure record is exempt.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Leave the fail-fast path once there is no teardown

Adding the live-stage check pushed `_process_completed_task` to a
cyclomatic complexity of 9, on CodeScene's threshold, and dropped the
module's code health to 9.68.

The two trailing branches both asked whether termination was happening,
so return once instead. Everything after that point is the teardown, and
the latch is re-tested on its own rather than paired with
`terminate_others` in a compound condition. Same behaviour, two fewer
decision points, code health back to 10.00.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Say what the lifecycle module owns, once

`_process_lifecycle` carried a one-line docstring over 397 lines that
spawn processes, tear them down on two separate routes, and shield that
teardown from caller cancellation. None of that was stated where a
reader arrives.

Give the module a docstring covering spawn and cleanup-on-error, the
fail-fast selection trio, the timed-out teardown, the shielding
rationale, and the division of labour with `_pipeline_wait`, which
decides while this module executes.

The shielding rationale was previously repeated verbatim in three helper
docstrings. Now that the module owns it, condense those three to the
detail local to each, keeping the file within the 400-line ceiling.

Also clarify in the users' guide that the fixed `WARNING` of the
Table-1 records is not the configurable `fail_fast_level`, which belongs
to the opt-in adapter, and note in the security note that
`pipeline_fail_fast` inherits the same `cuprum_argv` projection as every
other phase.

* Make _BatchRun immutable in fact, not just in name

`_BatchRun` is `frozen=True`, which stops a field being rebound but not
the four lists behind those fields being appended to. A holder of the
record could still change what the run reported.

Hold tuples instead, snapshotting the collectors at construction, once
the run that fills them is over. `field_values` and `record_actions`
take a `Sequence` so they still accept both the tuples and the raw
`caplog.records` list.

* Say when the fail-fast event stays silent

The release note and the normative event contract both described
`pipeline_fail_fast` as firing whenever a non-final stage is the first to
fail. That is only half the rule: the emission is gated on there being
another stage still running. A non-final failure processed after every
sibling has already settled still latches `failure_index`, but emits no
event and no termination records, because there is nothing left to stop.

Both documents now state that condition, so a reader building a metrics or
tracing integration against them is not surprised by a run that fails
fast-ish and publishes nothing.

* Hold teardown against every cancellation

`_await_teardown_shielded` shielded the first await and then re-awaited the
teardown bare. A shield protects only the await it wraps, so the retry was
unprotected: a second cancellation aimed at the caller landed on the
teardown itself, cancelling the SIGTERM/grace/SIGKILL escalation mid-flight.
The `suppress` around it then swallowed the evidence, and the docstring's
claim that teardown "completes regardless" was false — a SIGTERM-immune
stage could outlive the run that spawned it.

The wait now loops on a fresh `asyncio.shield` until the teardown future is
actually done, recording each cancellation and re-raising the first, so the
caller still sees exactly one. `cuprum.sh._execute_with_hooks` keeps its
single shield: it drains bookkeeping tasks rather than owning process
lifetimes, so a second cancellation there orphans nothing.

The new case in `test_pipeline_teardown_cancellation` cancels the waiter
twice inside an event-coordinated grace window and asserts the stage was
still reaped; it also asserts the escalation had not yet run when the second
cancellation landed, so it cannot pass by racing the clock. Restoring the
unshielded retry fails it.

The cancellation cases move out of `test_pipeline_timeouts` into that new
module: adding them there would have pushed the file past the 400-line cap,
and "what the caller sees when a deadline expires" and "what survives when
the caller cancels mid-teardown" are two concerns anyway.

* Spell test prose the way the house spells it

`test_adapter_fail_fast` and `test_tracing_span_concurrency` used the -ise
forms in module docstrings, method docstrings, and comments, which the
en-GB-oxendict house rule spells -ize; `test_pipeline_wait_state_machine`
had the same in "initialises".

`test_an_unrecognised_phase_still_raises` is renamed with them. The rule
that preserves existing -ise identifiers exists to protect names other code
depends on, and this one has no consumers at all: it is a test method, named
nowhere but its own definition. Leaving it would plant a fresh counterexample
just as PR #259 lands the identifier policy.

* Keep downstream stages alive past the failing one

The wiring test's three stages could all settle before the waiter was
resumed. `asyncio.wait(..., FIRST_COMPLETED)` returns every task that
finished, not just the first, so one batch could hold all three: stage 0 is
then processed after its siblings, the settled-batch gate this PR added
finds nothing left to terminate, and the run emits no `pipeline_fail_fast`
event. The fixture's `assert len(fail_fast) == 1` would fail, on a run that
behaved exactly as specified.

The downstream stages now sleep briefly once their stdin closes, which puts
them in a later batch than the failing stage. The fixture docstring records
why, so the delay is not read as an arbitrary sleep and removed. It is
module-scoped, so it is paid once.

* Give the remaining frozen test cases tuple fields

`_Driven`, `_SilentCase`, and `_RecordCase` are `frozen=True`, which blocks
rebinding a field but not mutating a list held in one. `_BatchRun` was moved
to tuples earlier in this PR for that reason; these three were left behind.

Their list fields become tuples, `_drive` snapshots both collectors as it
builds its result, and the assertions compare against `()` and a tuple of
actions. The shared `CompletionPlan` still takes a list, so the two call
sites that feed it convert explicitly rather than widening a helper the rest
of the suite shares.

* Time the cancellations against the signal, not the clock

Two teardown cancellation cases waited a fixed 0.02s against a 0.2s grace
window before cancelling. A slow scheduler could deliver the cancellation
before the teardown task took its first turn, failing for a reason unrelated
to the shield. Both now wait on the process double's `signalled` event, as
the second-cancellation case already did, so the cancellation lands inside
the grace window by construction.

Bound the readiness-marker poll in the end-to-end case with a deadline and a
`pytest.fail`, mirroring `wait_for_process_death`, so a child that never
starts fails the test instead of hanging the session.

Narrow the suppression around the cancelled run from `BaseException` to the
two outcomes the await can actually produce, so `KeyboardInterrupt` and
unrelated failures still propagate.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Pull the stage tag apart from the stage index

Every fail-fast test kept `tags["pipeline_stage_index"]` and the typed
`ExecEvent.stage_index` field in lock-step, so an implementation that read
the tag instead of the field would have passed the whole suite. The two can
legitimately disagree: `_build_pipeline_observations` merges
`ExecutionContext.tags` last, so a caller may shadow the coordinator's own
index — which is precisely why the typed fields exist.

Add `test_pipeline_fail_fast_tag_shadowing.py`, which sets the shadowing tags
to an impossible 99 and pins both channels at both levels:

- End to end, through `run_sync` with a real three-stage pipeline, so the
  merge in `_build_pipeline_observations` is the one under test.
- At the emission seam, driving one chosen completion (stage 1 of 4) through
  `_process_completed_task`, which also pins the `cuprum_stage_index` log
  field against the same regression.

Each level also asserts the caller's tag arrives unaltered, so neither can
pass on a run where the shadowing tags never took effect.

Verified by mutation: making `_emit_fail_fast_event` read
`observation.tags["pipeline_stage_index"]` fails both new assertions with
(99, 99) while the rest of the suite stays green.

The real-pipeline runner moves to `_fail_fast_pipeline_support.py` so the
wiring module and this one share one definition of the failing pipeline, and
`make_stage_observations` gains a `tag_overrides` argument that is merged last
for the same reason the production builder merges the caller's tags last.

Refs #73, #285.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Route fail-fast telemetry through adapters (#73)

Publish one typed fail-fast event through observe hooks and let the logging
adapter render its warning. Omit raw arguments and caller tags from that
warning, while preserving metric and tracing projections.

Replace timing-based test coordination with FIFO event gates, ensure
cancellation tests reap reported PIDs on every path, and update the
completion transition, documentation, and snapshots to match the boundary.

* Sanitize pipeline fail-fast telemetry (#73)

Restore the canonical fail-fast WARNING records while emitting an observe
event stripped of argv, environment, working directory, and caller tags.

Read the failing-stage UUID directly from its observation, and cover the
sanitized event, negative-duration boundary, and documented correlation.

* Fix post-rebase pipeline wait integration (#73)

Preserve the lost-wakeup fallback while passing immutable stage context
through the updated wait API. Align the legacy and synthetic test doubles,
stabilise timeout teardown startup, and regenerate the wheel manifest.

* Align pipeline docs with rebase lint rules (#73)

Preserve the rebased fail-fast and cancellation behaviour while applying
the formatter and NumPy return documentation required by the new main
branch lint policy.

* Split adapter projection property assertions

Extract the independent logging, tracing, and metrics assertion groups so
the Hypothesis property remains a concise coordinator without weakening its
projection contracts.

* Harden pipeline fail-fast observability (#73)

Route pipeline-wait warnings through the structured logging adapter,
keep the sanitised event's trusted project label, and serialise same-stage
trace callbacks against exit.

Cover record ordering, process-exit polling isolation, and the fail-fast
tracing race while updating the user and design contracts.

* Split pipeline-wait observability tests (#73)

Move the record-ordering contract out of the async-boundary suite so
each pipeline-wait test module stays below the repository's size limit.

Document the complete split suite and regenerate the wheel-manifest
snapshot for the new test module.

* Report confirmed pipeline fail-fast terminations (#73)

Count one verified outcome for every selected teardown target, excluding
stages that settle after selection before they can be terminated.

Cover the race directly, split batch completion coverage into its own
module, and clarify the completion-record and event channels.

* Group pipeline completion report fields (#73)

Carry each record's action, message, and optional fields in one immutable
report value so the adapter boundary no longer accepts an excessive argument
surface.

Keep the published fields unchanged and assert that teardown outcome fields
remain exclusive to the termination-outcome record.

* Align replayed pipeline expectations

Match target-era pipeline tests to the current wait context and final-stage
fail-fast policy. Restore formatter-required spacing and remove the replayed
documentation blank line.

* Unify tracing protocol boundary (#73)

Keep legacy tracing imports as compatibility re-exports while making the
public module the sole protocol definition. Document the separate direct
pipeline-wait record contract and extract the verified-teardown race setup.

* Refine pipeline fail-fast review contracts (#73)

Preserve the direct pipeline-wait records while adding their execution
correlation token to the structured fail-fast log projection. Clarify
the associated telemetry and protocol boundaries, and improve failure
diagnostics without changing teardown semantics.

* Inject pipeline event clocks (#285)

Make lifecycle and fail-fast event timestamps deterministic at the
observation boundary, and avoid rebuilding logging level maps for every
event. Keep the reporter and structured-event channels distinct in the
user documentation.

* Repair rebase merge artefacts

* Repair rebase documentation and lint gates

Restore target-branch timeout observability documentation that was lost
during the rebase, while retaining the pipeline fail-fast additions.

Keep `TracingHook` below the module-size limit without weakening its
documented attribute contract, and resolve the remaining Markdown and
spelling gate findings.

* Close retried HTTP errors

Close handled `HTTPError` instances before retrying or propagating them,
preventing deferred resource-cleanup warnings in CI.

Make CrossHair fallback tests select their own warning so unrelated
warnings cannot change their assertions.

---------

Co-authored-by: leynos <leynos@rohga>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
leynos and others added 16 commits August 25, 2026 20:19
Rename private, test-local, and function-local `-ise` identifiers to their
Oxford `-ize` forms without changing the public API. Keep the CrossHair
contract and diagnostic references aligned with the renamed helpers.

Add the living execution plan that governs source-corpus cleanup, spelling
gate expansion, policy documentation, validation, and publication.
Extend the spelling gate to tracked Python and Rust source and lock the
Makefile pathspec with a regression test. Normalize the existing source
corpus so the widened gate starts green.

Keep GitHub Actions wire vocabulary, the established CLI option, and
deliberate spelling-test fixtures through narrowly anchored local patterns.
Regenerate the tracked spelling configuration from that overlay.
Make identifiers, comments, docstrings, fixtures, and prose explicit parts
of the en-GB-oxendict policy while retaining narrowly documented external
contract exceptions.

Record the decision in ADR-008, index it for maintainers, and note the new
contributor-facing source gate in the Unreleased changelog.
Link the Unreleased changelog entry to draft PR #259 and close the living
ExecPlan with final validation, review, branch, and publication evidence.
Align the documented exception scope with ADR-008 and add actionable
failure context to the spelling-policy regression assertions.
Exercise the configured spelling scanner across Markdown, Python, and Rust
fixtures, including the documented external-contract exceptions. Snapshot
the benchmark baseline CLI help and token error while retaining semantic
assertions for the stable option and Oxford wording.
Exercise the real Make spelling target, generate configuration before all
consumers, and keep Markdown code ignores scoped to Markdown. Tighten local
contract exceptions and cover their accepted and near-miss forms.

Document ADR-008 in the design, complete the affected public docstrings,
and preserve `main`'s maturin test refactor while applying the source
spelling policy.
Update newly added upstream prose to satisfy the widened source spelling
gate and keep the Maturin helper reference aligned with its renamed symbol.
ADR-008 is already claimed by the Rust pump observation channel decision
in an older, further-advanced pull request. Take the next free number so
the two can land in either order without a filename collision.

Updates the contents index, the developers' guide, the design document,
and the ExecPlan alongside the file rename.
The stored Syrupy transcript pinned argparse's line wrapping, not the
CLI's contract. CPython 3.12 wraps `--artifact-name ARTEFACT_NAME` across
two usage lines where 3.13 and 3.14 keep it whole, so the snapshot failed
the 3.12 job while every other version passed.

Replace it with assertions on what the interface actually promises: the
help exit code, the description, every option name and its help sentence,
the optional-argument defaults, rejection of each required option when
omitted, and the exact missing-token message. Whitespace is collapsed
before matching so rewrapping cannot break the assertions again.

Move the command-line surface tests into their own module, with the
shared argument builder in a support module, keeping both files inside
the 400-line limit.
`test_cli_help_documents_every_option` now asserts that the usage text
identifies `fetch_main_benchmark_baseline.py`. Patching `sys.argv` no
longer achieves that: Python 3.14 derives argparse's default prog from
the actual invocation, so under `python -m pytest` the usage line read
`python3 -m pytest`. The parser now pins `prog` explicitly — the tool's
name is part of its contract in a way the wrapping never was — and the
redundant argv patch and its `sys` import are gone. Verified 9/9 on
3.12, 3.13, and 3.14.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Address the review findings on the spelling-policy branch.

`benchmarks/fetch_main_benchmark_baseline.py` derived its argparse
description from `__doc__`, so any documentation the module needed would
have landed in `--help`. Give the parser its own short description
literal, then expand the module docstring with the script's purpose, the
`benchmark-ratchet` CI boundary that is its only caller, the meaning of
exit code 3, and a usage example. `--help` is unchanged.

`render_typos_config` is public but carried a one-line docstring. Give it
Parameters, Returns and Raises sections, and state the partition it
performs: patterns in `_MARKDOWN_IGNORE_PATTERNS` describe Markdown
syntax and are emitted under `[type.markdown]` so they exempt only `*.md`,
while every other pattern stays global.

The inline-code exemption restored in #294 had no end-to-end cover. Add
two cases to `test_spelling_gate_preserves_documented_exceptions` that
run the real pinned scanner over `.md` fixtures: an `-ise` term in an
inline code span, and one in a fenced block. Both must pass the gate,
while plain `.md` prose stays gated by the existing detection test.

Give the remaining bare assertions in both touched test modules the
diagnostic messages the test-path rule requires.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Retain target-side benchmark and pipeline refactors while removing duplicate
rebase fragments. Keep GitHub's public `artifacts` protocol values intact,
but apply the repository spelling policy to private identifiers and prose.

Update the CLI parser destination and wheel snapshot so the split benchmark
tests continue to exercise the current command-line contract.
Apply the source spelling policy to observability prose introduced by the
rebased target branch, so the expanded spelling gate remains green.
Keep the rebased unreleased entries distinct from the historical release so
Markdown lint recognizes the intended list and heading boundaries.
Restore the target branch's pipeline timing support, GitHub redirect
handler, and line-splitting contract after the rebase duplicated their
definitions. Keep the branch's private `-ize` spelling policy corrections
and apply it to two newly introduced test docstrings.
@leynos
leynos force-pushed the issue-249-decide-and-enforce-en-gb-oxendict-spelling-for-code-identifiers-not-just-prose branch from 64378ca to a658e4c Compare August 25, 2026 18:46
codescene-access[bot]

This comment was marked as outdated.

@codescene-access codescene-access Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

No quality gates enabled for this code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Decide and enforce en-GB-oxendict spelling for code identifiers, not just prose

3 participants