Skip to content

Accept null SPM provenance in Stage 12 aggregate artifacts - #787

Merged
anth-volk merged 3 commits into
mainfrom
fix/stage12-null-spm-provenance
Oct 14, 2026
Merged

anth-volk merged 3 commits into
mainfrom
fix/stage12-null-spm-provenance

Conversation

@anth-volk

@anth-volk anth-volk commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #786

Same-Repository Draft PR Check

  • This PR is a draft.
  • This PR is from a branch in PolicyEngine/policyengine-sim-api, not a personal fork.

Summary

Stage 12 rejected every aggregate report without an SPM calculation, which includes every UK report. Between 6 and 13 October 2026 this caused 42 of the 153 production Stage 12 failures. All 42 were UK nationwide reports whose baseline and reform simulations had already succeeded.

  • Cause: SingleYearMacroOutput.spm_provenance is optional and defaults to None, so a report with no SPM calculation serializes "spm_provenance": null. AggregateReportArtifactPayload.validate_spm_provenance checked only that the key was present. It then validated null as an SPMComparisonProvenance object, which failed inside Stage12ArtifactStore.write_aggregate. The coordinator recorded the failure as the generic Stage 12 report coordination failed.
  • Fix: the validator now validates spm_provenance only when a value is supplied. An absent or null field is accepted. Every non-null value is still validated, so malformed or mismatched provenance records are still rejected.
  • Why the tests missed it: the coordinator tests' artifact double, FakeArtifacts.write_aggregate, saved any payload without validating it. It now writes through the real Stage12ArtifactStore, so the coordinator tests enforce the same aggregate contract as production.
  • Deployment: the change is in the contract library used by the Stage 12 coordinator. It takes effect with a stage12-deploy.yml run and needs no change to the selected worker version. The other changes are test-only.

Testing

  • Rebased on main after Release Stage 12 database sessions and keep original failures visible #789 and Add a rules registry that makes every enforced and unenforced rule visible #752. The three coordinator tests Release Stage 12 database sessions and keep original failures visible #789 added now use _aggregate_payload too, because one of them aggregates successfully and the real store rejects {"result": "complete"}.
  • New contract tests in libs/policyengine-simulation-contract/tests/test_stage12_execution.py:
    • An absent spm_provenance and a null one are both accepted.
    • Present but invalid values ({}, a string, a list, 0) are rejected.
  • New executor test in projects/policyengine-simulation-executor/tests/test_stage12_runtime.py:
    • It builds a UK report aggregate with build_aggregate_report, using the real SingleYearMacroOutput serialization.
    • It writes that aggregate with FakeArtifacts.write_aggregate, which now calls the real Stage12ArtifactStore.write_aggregate over an in-memory object store.
    • It checks that the stored artifact keeps spm_provenance: null and matches the returned digest.
  • The coordinator test double now validates aggregates:
    • FakeArtifacts.write_aggregate delegates to a real Stage12ArtifactStore, so it runs the same AggregateReportArtifactPayload validation and encoding as production. The stored aggregate path now follows the production layout under the run's artifact prefix, and one coordinator test checks that path.
    • With validation on, 10 coordinator tests failed because their stand-in aggregators returned payloads that never met the contract, such as {"result": "complete"}. They now return a valid payload from a _aggregate_payload helper, with the same result as before.
    • The stand-in aggregators that must not run also return a valid payload. If one ran, its aggregate would be saved and the existing aggregate_writes == [] checks would fail, rather than a validation error hiding the run.
  • Shared test support, following docs/engineering/skills/testing.md:
    • fixtures/fake_object_store.py holds InMemoryObjectStore. It replaces the two identical copies in test_stage12_artifacts.py and test_stage12_runtime.py.
    • fixtures/macro_output.py, renamed from fixtures/test_simulation_api_contracts.py, holds the shared macro result and its key sets. The Stage 12 runtime, output builder and standalone app tests no longer import the API contract tests' fixture module.
  • The new tests reproduce the defect. With the previous validator, the null-provenance contract case and the UK aggregate test both fail with ValidationError. In a one-off check with the previous validator, a coordinator run whose aggregate carries spm_provenance: null also failed through the validating double, recording ValidationError. The absent-field and invalid-value cases pass both before and after, as guards should.
  • Suites, on Python 3.13 with uv run --frozen:
    • libs/policyengine-simulation-contract: 153 passed.
    • projects/policyengine-simulation-executor, all of tests/: 1058 passed, 22 skipped, 3 deselected (the integration marker).
    • Of those, tests/test_stage12_runtime.py and tests/test_stage12_artifacts.py: 54 passed.
  • Formatting and lint: with CI's pinned ruff 0.15.22, ruff format --check, ruff check and git diff --check pass on every changed file.
  • Replay of a failed report: the coordinator's aggregate for one failed production UK report was rebuilt from its saved simulation outputs. It failed validation before this change and validates after it.

🤖 Generated with Claude Code

@anth-volk
anth-volk force-pushed the fix/stage12-null-spm-provenance branch from f7525ee to 8278e12 Compare October 14, 2026 17:17
anth-volk and others added 3 commits October 14, 2026 23:49
Reports without an SPM calculation, including every UK report, serialize
the optional spm_provenance field as null. The aggregate validator checked
only that the key existed and then validated null as an
SPMComparisonProvenance object, so every such aggregate was rejected
before it was saved.

Validate the field only when a value is supplied. Malformed provenance
records are still rejected.

Fixes #786

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Stage 12 artifact and runtime tests each defined the same in-memory
object store, and the runtime tests imported the macro result from the
API contract tests' fixture module. Move the object store to
fixtures/fake_object_store.py and the macro result and its key sets to
fixtures/macro_output.py, and import both from there in every test that
uses them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
FakeArtifacts.write_aggregate encoded whatever it was given, so no
coordinator test enforced AggregateReportArtifactPayload, and the null
SPM provenance rejection reached production without a failing coordinator
test. Write aggregates through the real Stage12ArtifactStore over the
shared in-memory object store instead, so the coordinator tests run the
same validation and encoding as production.

The coordinator tests' stand-in aggregators returned payloads such as
{"result": "complete"} that never satisfied the contract. They now return
a contract-valid payload with the same result. Aggregators that must not
run return one too, so an unexpected run is still caught as a written
aggregate rather than hidden by a validation failure.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@anth-volk
anth-volk force-pushed the fix/stage12-null-spm-provenance branch from 25c1ee0 to 0951722 Compare October 14, 2026 19:50
@anth-volk
anth-volk force-pushed the fix/stage12-null-spm-provenance branch from 0951722 to d29ce9d Compare October 14, 2026 19:56
@anth-volk
anth-volk marked this pull request as ready for review October 14, 2026 20:13
@anth-volk
anth-volk merged commit fc1ddca into main Oct 14, 2026
14 checks passed
@anth-volk
anth-volk deleted the fix/stage12-null-spm-provenance branch October 14, 2026 20:13
anth-volk added a commit that referenced this pull request Oct 14, 2026
#787 made the coordinator tests' artifact double write aggregates through
the real Stage12ArtifactStore, which rejects anything that is not a valid
AggregateReportArtifactPayload. Use _aggregate_payload for this PR's
aggregator stubs, as #787 did for main's. The redacted comparison-failure
test needs a valid aggregate for the report to succeed. An aggregator that
must not run now shows up as a written aggregate if it does run, instead of
as a rejection hidden by the failure the test expects.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
anth-volk added a commit that referenced this pull request Oct 14, 2026
#787 made the coordinator tests' artifact double write aggregates through
the real Stage12ArtifactStore, which rejects anything that is not a valid
AggregateReportArtifactPayload. Use _aggregate_payload for this PR's
aggregator stubs that must not run, as #787 did for main's, so an
unexpected run shows up as a written aggregate instead of as a rejection
hidden by the failure the test expects.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
anth-volk added a commit that referenced this pull request Oct 14, 2026
#787 made the coordinator tests' artifact double write aggregates through
the real Stage12ArtifactStore, which rejects anything that is not a valid
AggregateReportArtifactPayload. Use _aggregate_payload for this PR's
aggregator stubs that must not run, as #787 did for main's, so an
unexpected run shows up as a written aggregate instead of as a rejection
hidden by the failure the test expects.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
anth-volk added a commit that referenced this pull request Oct 14, 2026
#787 landed on main after this stack branched. It renamed the macro result
fixture module to fixtures.macro_output and added fixtures.fake_object_store,
and those imports in the output builder, Stage 12 artifact and Stage 12
runtime tests are unsorted under the I rule this PR enables.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
anth-volk added a commit that referenced this pull request Oct 14, 2026
…n class (#791)

* Record a redacted Stage 12 failure cause instead of only the exception class

Stage 12 coordinator, worker and result-comparison failures stored only
type(error).__name__, so a database session limit, a UK aggregate
validation defect and an invalid reform path all appeared as RuntimeError
or ValidationError behind "Stage 12 report coordination failed".

Failure records now store a bounded summary of the form
"<type> in|after <stage> (<role>): <message>", capped at the 512-character
error_summary limit. Messages are redacted first: URL credentials, signed
URL query strings, secret-looking assignments, bearer and known token
formats, this process's secret environment values, Pydantic input values
and SQL statement parameters are removed. Stage tracking follows the
existing registered span names, so the summary names the stage that failed
or the last stage that finished before a failure between spans.

The coordinator no longer overwrites a cause that the failed worker already
persisted, and copies that cause onto the report record. The redacted
cause, with the original stack, is also recorded on the active operation
before the failure is persisted. The exceptions returned to Modal are
unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Redact more credential formats in Stage 12 failure causes

Redact quoted JSON and dictionary keys, non-Bearer Authorization schemes
and cookie headers, signed query strings without a URL scheme, and the
row values that PostgreSQL constraint violations echo in their detail,
from both the stored summary and the exported cause.

Patterns that could start inside a run of word characters now start
only where that run starts, so redaction is linear in the message
length. Redaction also reads at most 4,096 characters: the word that
limit cuts is dropped, and quoted values, SQL parameters and validation
inputs whose closing delimiter was cut are redacted to the end.

Take the summary length from the contract's ErrorSummary, detect
SQLAlchemy errors by type, and state in the module docstring that span
exception events are outside this redaction.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Share one stage helper for Stage 12 spans and failure progress

Replace the coordinator's stage_scope and stage_span closures and the
worker's _simulation_stage with tracked_stage, which opens a stage's
span or operation and records it as the process's progress. The
coordinator claim span uses it too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Test dispatch and comparison failure summaries in Stage 12

Assert the redacted summary stored on a child and its report when Modal
rejects a dispatch, and the comparison_error_summary stored when the
production result cannot be read. Reword fixture failure messages that
described themselves as sensitive, since the tests now assert those
messages are stored.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Return valid aggregates from the Stage 12 test aggregators

#787 made the coordinator tests' artifact double write aggregates through
the real Stage12ArtifactStore, which rejects anything that is not a valid
AggregateReportArtifactPayload. Use _aggregate_payload for this PR's
aggregator stubs, as #787 did for main's. The redacted comparison-failure
test needs a valid aggregate for the report to succeed. An aggregator that
must not run now shows up as a written aggregate if it does run, instead of
as a rejection hidden by the failure the test expects.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Anthony Volk <14987227+anth-volk@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
anth-volk added a commit that referenced this pull request Oct 15, 2026
* Enable ruff group 1 (safe auto-fixes) and fix everything it finds

Select FLY, FURB, I, PIE, PYI, RET, RSE, UP and W, and fix every finding:
ruff's safe fixes (import sorting, pyupgrade), then by hand:
- policyengine-fastapi re-exports JWTDecoder through __all__ and drops two
  unused imports (the 9 F401s noted in Phase 0).
- Two tests build their JSON receipts with json.dumps, not % formatting.
- smoke_app uses min() rather than sorted()[0] (ruff 0.16 FURB192).
scripts/** is linted as Python 3.11, the shared rules tool's minimum, so
import sorting matches policyengine-api byte for byte.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Run ruff check in CI and enforce the lint rule

The lint job now runs ruff check . on the whole repository, and
sim.quality.ruff-lint moves from advisory to enforced with the codes
selected in ruff.toml.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Add a changelog entry for ruff group 1

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Sort the imports in the budget output test from #767

#767 landed on main after this stack branched, and its new test imports
are unsorted under the I rule this PR enables.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Sort the imports #743 and #744 added

#743 and #744 landed on main after this stack branched. A dataset-digest
test from #743 and the tomllib import #744 added to the deployment assets
test are unsorted under the I rule this PR enables.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Sort the imports #787 added

#787 landed on main after this stack branched. It renamed the macro result
fixture module to fixtures.macro_output and added fixtures.fake_object_store,
and those imports in the output builder, Stage 12 artifact and Stage 12
runtime tests are unsorted under the I rule this PR enables.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Sort the imports #791 added

#791 landed on main after this stack branched. I, which this PR enables,
flags the import block of its new Stage 12 failure-cause tests; ruff's
safe fix sorts it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Anthony Volk <14987227+anth-volk@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
anth-volk added a commit that referenced this pull request Oct 16, 2026
* Annotate policyengine-fastapi for ruff ANN

Add the missing parameter and return annotations. AppExit forwards its
callback arguments as object rather than Any, the JWT decoder's private
validator returns the same dict type its caller declares, and the
metrics middleware types call_next as Starlette's RequestResponseEndpoint.
Ping endpoints already pass response_model explicitly, so their new
return annotations do not change the generated responses.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Annotate the simulation contract library for ruff ANN

Type the SPM resolution and validation entry points, the gateway request
pre-validators (object in, object out) and the Modal Dict store helpers.
The three wrap model serializers in spm.py stay unannotated with a
reasoned noqa: Pydantic turns a serializer's return annotation into the
model's serialization JSON schema, which would rewrite the OpenAPI spec.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Annotate the observability and Stage 12 persistence libraries for ruff ANN

Replace Any with object for values that are only inspected or passed
through, and type the persistence row helpers with SQLAlchemy's
RowMapping, Table and ColumnElement (imported for type checking only).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Annotate the simulation gateway for ruff ANN

Every annotated route passes response_model explicitly, so FastAPI keeps
using it and the OpenAPI golden file is unchanged. _bundle_payload keeps
Any for **dump_kwargs, forwarded unchanged to BaseModel.model_dump, with
a reasoned noqa. Test fixtures outside tests/ are annotated too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Annotate the simulation entry service for ruff ANN

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Annotate the simulation executor for ruff ANN

Type the runtime, output builders, Modal entrypoints, scripts and the test
fixtures that live outside tests/. PolicyEngine, Google Cloud Storage and
observability types are imported for type checking only, country modules
are ModuleType, and duck-typed analysis values that tests replace with
SimpleNamespace are object. _try_compute_output uses a ParamSpec so its
arguments are checked against the wrapped function.

The in-repo src/modal package shadows the Modal library here, so Modal
types stay quoted or behind TYPE_CHECKING. _image_setup.py may not import
anything at module level, so it uses a local TYPE_CHECKING flag. The
RemoteFunction protocol keeps Any (with a reasoned noqa) because it
mirrors modal.Function.remote and spawn, whose payloads vary per function.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Enable ruff group 3 (annotations)

Select ANN. Tests are exempt, alongside the docstring, magic-number and
assert rules a later group will enable. scripts/rules findings are left
for the next sync from policyengine-api.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Annotate the package-update script and the Stage 12 proof fixtures

_json_from_stdin returns object (every caller narrows it), and the proof
fixtures annotate their SQLAlchemy connection and table parameters. The
schema-authority proof reads the fixtures as text, so it is unaffected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Sync the annotated rules tool from policyengine-api

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Register ANN and the test-file ruff exemptions

sim.quality.ruff-lint now includes ANN, and the **/tests/** per-file
ignore list (S101, D1, ANN, PLR2004) is registered as
sim.quality.tests-ruff-exemptions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Annotate the routing and bundle helpers #743 and #744 added

#743 and #744 landed on main after this stack branched. ANN, which this
PR enables, wants annotations on #744's routing-store, publication-journal
and active-state helpers and on #743's bundle dataset-digest helper. The
Modal store annotations are quoted, as in the gateway, because the
executor tests replace the modal module with a stub.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Annotate the shared object store fake #787 added

#787 landed on main after this stack branched. ANN, which this PR enables,
wants annotations on the in-memory object store it moved into the executor's
fixtures. They match the byte interface of the real ArtifactStore it stands
in for.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Annotate the segmented national child calls #795 added

The respawn helpers take and return the Modal FunctionCall that
Function.spawn returns, rather than Any.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Annotate the permission checks #758 added

#758 landed on main after this stack branched. ANN, which this PR
enables, flags three of its additions:

- CallerAuthorizer.__init__ returns None;
- the gty before-validator takes the raw claim as object, as the
  project's other before-validators do, since it narrows the value with
  isinstance checks;
- the deployed permission-enforcement test returns None.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Annotate the job progress code #781 added

#781 landed on main after this stack branched. ANN, which this PR
enables, flags six of its additions:

- the contract's job progress store returns a modal.Dict, as the
  budget-window stores do;
- the Modal app's reporter factory returns a JobProgressReporter, and
  the stage-duration recorder takes the runtime and that reporter;
- StoredJobProgressReporter.__init__ returns None;
- the calculation-start hook takes the StageReportingSimulation whose
  on_first_ensure it calls.

The new annotation-only imports sit under TYPE_CHECKING.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Anthony Volk <14987227+anth-volk@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
anth-volk added a commit that referenced this pull request Oct 16, 2026
* Document the simulation contract library for ruff D

Validators, state helpers and URI functions get docstrings. Every Pydantic
model and enum whose class docstring would become a JSON-schema description
carries a D101 noqa instead, so the published contract schemas and the
OpenAPI documents built from them stay byte-identical.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Document the FastAPI support library for ruff D

The ping and health models and the /ping/alive route are mounted in the
executor app, so they keep their existing (OpenAPI-visible) docstrings under
noqa. The no-op observability methods keep their explicit return None under
RET501 noqa, and two stray string statements in the GCP module become the
docstrings they were meant to be.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Document the simulation observability library for ruff D

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Document the Stage 12 persistence library for ruff D

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Document the simulation entrypoint for ruff D

The entry routes are nested in create_app, so their docstrings (the OpenAPI
descriptions) are untouched and the generated spec is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Document the simulation gateway for ruff D

The /ping router endpoint keeps its OpenAPI description under a D205 noqa;
the golden OpenAPI test still passes. Two placeholder pass statements in the
test fixtures' mock exception classes go now that the classes have
docstrings (PIE790).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Document the simulation executor for ruff D

Covers the executor package, the Modal apps, fixtures, scripts and test
docstrings. Docstrings are added to several Modal functions; images copy
src/modal into a layer, so this changes those layers like any source edit.
Protocol stubs keep their ... bodies.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Document the integration tests, repo scripts and package markers for ruff D

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Document the Stage 12 schema-DDL proof fixtures for ruff D

The schema-authority proof reads the fixtures as text; the new docstrings
avoid DDL phrases, so the bad fixture is still rejected and the good one
accepted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Enable ruff group 4 (docstrings)

Select pydocstyle (D) and ignore D203 and D213, the incompatible halves of
the D203/D211 and D212/D213 pairs. Tests stay exempt from D1 only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Drop redundant return None from documented no-op methods

With a docstring as the body, the bare return None lines are redundant;
remove them instead of suppressing RET501.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Document published contract models and routes

Replace the 61 D101 suppressions on contract, ping and health models with
class docstrings written for API consumers, and fix the /ping/alive and
gateway /ping route docstrings instead of suppressing D205. Pydantic and
FastAPI copy these into schema and operation descriptions, so the entry,
gateway and executor OpenAPI documents gain descriptions; paths, operations,
properties, types and required lists are unchanged. The gateway golden spec
is regenerated with generate_openapi.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Use the Public API's docstrings in the shared rules tool

Both repos documented scripts/rules independently for ruff D, so the
copies drifted apart. Take the Public API's wording so the tool stays
byte-identical in both repos; only docstrings differ.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Register D in the ruff-lint rule

sim.quality.ruff-lint now lists D, and the rendered rules page and
AGENTS.md Rules block are regenerated for the new tool hash.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Put the docstrings #767 added in the imperative mood

#767 landed on main after this stack branched. D401, which this PR
enables, wants each docstring's summary line in the imperative mood.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Put the docstrings #743 and #744 added in the D style

#743 and #744 landed on main after this stack branched. D, which this PR
enables, flags their new code: blank lines after docstrings (D202), a
two-line summary (D205, D209), summaries that are not in the imperative
mood (D401), an escaped backslash in the sentinel's module docstring
(D301), and three public functions without docstrings (D103).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Put the docstrings #789 added in the D style

#789 landed on main after this stack branched. D, which this PR enables,
flags blank lines after its new docstrings (D202) and the undocumented
__call__ of its ConnectionOpener protocol (D102).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Document the shared object store fake #787 added

#787 landed on main after this stack branched. D, which this PR enables,
flags the undocumented __init__ (D107), upload_bytes and read_bytes (D102)
of the in-memory object store it moved into the executor's fixtures.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Put the docstrings #795 added in the D style

D, which this PR enables, flags the undocumented group_label property
(D102), the budget check's run-on summary (D205, D209) and two test
helpers' summaries that are not in the imperative mood (D401).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Put the docstrings #791 added in the D style

#791 landed on main after this stack branched. D, which this PR enables,
flags blank lines after five of its new docstrings (D202) and the
undocumented __init__ (D107) and stage method (D102) of its StageProgress
tracker.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Put the docstrings #758 added in the D style

#758 landed on main after this stack branched. D, which this PR enables,
flags its permission checks and deployed permission test:

- CallerAuthorizer's initializer and authorize now say what enforcement
  does with a caller that lacks a permission, and that the missing
  permission is always recorded;
- the permission_enforcement fixture says when it skips and when it
  raises, and the enforced-permission deployment test says what it
  checks;
- the blank lines after the docstrings of is_client_credentials,
  granted_permissions, the gty validator and the Cloud Run
  deployment-environment test helper go.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Put the docstrings #781 added in the D style

#781 landed on main after this stack branched. D, which this PR enables,
flags its job progress code:

- the JobProgressReporter protocol and each of its methods say which
  stage boundary the runtime reports; the null reporter's methods say
  what they discard, with the docstring as the body;
- StoredJobProgressReporter's initializer, record, start, set_execution,
  enter, start_segments, segment_completed and attempts say how the
  record changes, including when a call changes nothing;
- the contract's job progress store functions say what they read and
  write, and StageReportingSimulation and its ensure say when the
  callback runs;
- the floor and maximum helpers, the Modal reporter factory, the
  published-record helper and the gateway's stage-field helper are in
  the imperative mood, and blank lines after docstrings go.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Anthony Volk <14987227+anth-volk@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Stage 12 rejects aggregates whose optional SPM provenance is null

1 participant