From 7f1194d68b256d55f3b6cae6c1d181042091ea3c Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 18 Aug 2026 04:56:10 +0000 Subject: [PATCH 1/7] docs: expand ADRs 0001-0007 and rewrite buyer README Rewrite README as a customer/operator page for standalone Keyverse and published OIDC/OAuth/SCIM contracts. Expand accepted ADRs 0001-0007 with Context, Decision, Consequences, and independently opened official APA 7th records in docs/REFERENCES.md. Leave ADR 0008 and reserved 0009-0012 untouched. Co-authored-by: Seongho Bae --- CHANGELOG.md | 5 + README.md | 242 ++++++++++-------- docs/REFERENCES.md | 119 +++++++++ docs/adr/0001-keycloak-hub.md | 97 ++++++- docs/adr/0002-passwordless-local-accounts.md | 75 +++++- docs/adr/0003-identity-matching.md | 79 +++++- docs/adr/0004-desired-state-reconciliation.md | 94 ++++++- docs/adr/0005-secret-ownership.md | 84 +++++- docs/adr/0006-user-operation-lock.md | 61 ++++- docs/adr/0007-automation-authority.md | 59 ++++- docs/adr/README.md | 5 + 11 files changed, 787 insertions(+), 133 deletions(-) create mode 100644 docs/REFERENCES.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 5462dfb..00de261 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -55,6 +55,11 @@ Keep a Changelog, and releases use semantic versioning. ### Changed +- Buyer README and accepted ADRs 0001–0007 now describe Keyverse as a + standalone identity leaf/hub, point operators at published OIDC/OAuth + 2.0, SAML, LDAP, and SCIM contracts, and cite independently opened + official records in `docs/REFERENCES.md`. OAuth 2.1 is labeled an IETF + Internet-Draft, not a final RFC. - Relying-party deployment controllers now send validated, secret-free metadata to Keyverse desired-state PUT instead of applying client representations directly to Keycloak; confidential credential placement remains a separate diff --git a/README.md b/README.md index 5e24601..dabd1ff 100644 --- a/README.md +++ b/README.md @@ -1,59 +1,72 @@ -# cwl-idp — ecosystem central IdP - -The **ContextualWisdom ecosystem's central Identity Provider**, a standalone -component built on [**Keycloak**](https://www.keycloak.org) (Apache-2.0). It: - -- issues **OIDC / OAuth 2.1** to ecosystem relying parties (`naruon`, - `pg-erd-cloud`, `semantic-data-portal`, `clearfolio`, `contextual-orchestrator`, - and `newsdom-api` through the WAF edge); -- is **passwordless-first**: FIDO2 / passkeys are the default and the **password - authenticator is removed** from the login flow for ecosystem-local accounts; -- runs a **SCIM v2 server shim** for inbound provisioning into Keycloak; +# Keyverse (cwl-idp) + +Keyverse is the ContextualWisdomLab **identity leaf and hub**. It is the system of +record for **who a person is in this ecosystem**: local passwordless accounts, +inbound federation, inbound SCIM provisioning, and outbound OpenID Connect +tokens that relying parties consume. + +It is **not** the employment or org-tree system of record. Orgmetra owns +employment and organizational-tree truth. Keyverse does not copy Orgmetra +tables. Composition hubs such as **naruon** and **gyeot** may call this leaf; +they are not required to boot it. + +Keyverse must run **from this repository alone** (Compose or Helm in this repo) +and remain **callable** by relying parties over published OIDC/OAuth, SAML +broker, LDAP/AD user-storage, and SCIM contracts. + +## What this IdP does + +Built on [Keycloak](https://www.keycloak.org) (Apache-2.0) plus a Keyverse +account-unification admin service, the product: + +- issues **OpenID Connect** on **OAuth 2.0** to ecosystem relying parties + (`naruon`, `pg-erd-cloud`, `semantic-data-portal`, `clearfolio`, + `contextual-orchestrator`, and `newsdom-api` through the WAF edge); +- is **passwordless-first**: FIDO2 / passkeys are the default, and the + **password authenticator is removed** from the bound browser flow for + ecosystem-local accounts; +- runs a **SCIM 2.0 server shim** for inbound provisioning into Keycloak; - **federates external IdPs in** — employer ADFS via SAML, corporate LDAP/AD, - and optional personal OIDC — while keeping unverified email ineligible for - account linking; and -- adds an **account-unification** admin service to link one human's many external - identities and to **merge** two pre-existing accounts into one. + and optional personal OIDC — as **deployment data**, never as portable realm + code; and +- links one human's many external identities and **merges** two pre-existing + accounts into one survivor, never on an unverified email. + +> Employer ADFS and corporate directories are **external compatibility +> targets**, not peer hubs. Customer-specific federation stays in the +> deployment controller and KV store. -> Employer ADFS and corporate directories are **external, proprietary** -> compatibility targets—not the hub. cwl-idp is the hub, and customer-specific -> federation remains deployment data rather than portable realm code. +OAuth 2.0 ([RFC 6749](https://www.rfc-editor.org/rfc/rfc6749)) is the official +authorization-framework record. [OAuth 2.1](https://datatracker.ietf.org/doc/draft-ietf-oauth-v2-1/) +is an IETF Internet-Draft (`draft-ietf-oauth-v2-1-15`, work in progress) and +is not cited here as a final RFC. -RP client registrations and secrets live in the **IdP DB / KV**, never in an RP's -environment. +RP client registrations and confidential values live in the **IdP DB / KV**, +never in an RP's environment. Authorized identity data stays usable under +purpose-bound access control, encryption, and audit. ## Architecture ```text -external IdPs ──► cwl-idp (Keycloak) ──► OIDC to ecosystem RPs - ADFS (SAML) passwordless OIDC/OAuth +external IdPs ──► Keyverse (Keycloak + admin service) ──► OIDC to RPs + ADFS (SAML) passwordless OIDC / OAuth 2.0 LDAP/AD FIDO2 passkeys - OIDC (opt) SCIM v2 shim (inbound) + OIDC (opt) SCIM 2.0 shim (inbound) HR/IGA (SCIM) account-unification admin service -``` - -Architecture and trust boundaries: [`ARCHITECTURE.md`](ARCHITECTURE.md). Full -network diagram: [`docs/topology.md`](docs/topology.md). -## Repository layout +composition hubs (naruon, gyeot) MAY call this leaf +Orgmetra owns employment / org-tree truth (not copied here) +``` -| Path | What | -| --- | --- | -| `docker-compose.yml` | Standalone bring-up: Keycloak + Postgres + admin service (pinned by digest) | -| `deploy/keycloak/` | Portable Keycloak realm config-as-code, passwordless flows, shared scopes, concrete Naruon RP, and service-account bootstrap | -| `deploy/templates/` | Private deployment templates split between Keyverse preflight/desired state and explicit Keycloak Admin REST apply contracts | -| `deploy/bootstrap/` | Bootstrap pointer to the KV/DB config store | -| `deploy/scripts/healthz.sh` | Cross-component readiness probe | -| `scripts/validate_realm.py` | Realm config-as-code validator (CI gate) | -| `services/account_unification/` | FastAPI admin service (link + merge + SCIM + federation validation/desired state) with unit tests | -| `helm/cwl-idp/` | Helm chart (templated Keycloak + Postgres + admin service) | -| `docs/operations/` | Scheduled maintenance and product-development operating procedures | -| `docs/doctoring/` | Standards interpretation and APA 7th engineering traceability | -| `docs/` | Topology, passwordless policy, federation, merge flow, RP onboarding, and papers | +Trust boundaries: [`ARCHITECTURE.md`](ARCHITECTURE.md). Network diagram: +[`docs/topology.md`](docs/topology.md). Architecture decisions: +[`docs/adr/`](docs/adr/README.md). Standards bibliography: +[`docs/REFERENCES.md`](docs/REFERENCES.md). -## Quick start (standalone) +## Run this repository alone -Requires Docker or Podman with the compose plugin. +No sibling repository checkout is required. Docker or Podman with the compose +plugin is enough: ```bash cp .env.example .env # populate values from your KV (bootstrap transport) @@ -71,93 +84,98 @@ The stack imports the **passwordless-first** realm at first start WebAuthn passwordless authenticator and **no password authenticator**, plus `registrationAllowed:false` / `resetPasswordAllowed:false`. -### Register external federation +Production-shaped clusters use [`helm/cwl-idp/`](helm/cwl-idp/). -The portable realm contains no employer ADFS, LDAP/AD source, or other -customer-specific federation. Render deployment values from KV and preflight -every private payload before apply: +### Optional parent include -- SAML and external OIDC: - `POST /federation/identity-providers:validate`, followed by the Keyverse - desired-state `PUT` and reconciliation flow. -- LDAP and Active Directory: - `POST /federation/user-directories:validate`, followed by deployment-owned - private Keycloak component apply. The first profile is LDAPS-only, - read-only, Kerberos-disabled, and `trustEmail=false`. +A parent Compose or Helm chart **may** include this repo's +`docker-compose.yml` or depend on `helm/cwl-idp`. That is an optional +embed of **this** repository. Keyverse does not require naruon, gyeot, +Orgmetra, or any other sibling checkout in order to start. -LDAP preflight performs no DNS lookup, socket connection, bind, search, KV/DB -write, or Keycloak call. Its response redacts `bindDn` and `bindCredential` and -must never be used as the apply payload; apply the original private file only. +## How a relying party calls Keyverse -See [`docs/federation-onboarding.md`](docs/federation-onboarding.md), -[`deploy/keycloak/README.md`](deploy/keycloak/README.md), and -[`deploy/templates/README.md`](deploy/templates/README.md). +Each RP is a separate trust boundary. A README listing, repository +relationship, or client ID is not authorization. The RP validates issuer, +signature, audience, subject, and expiry, then applies its own +access-control policy ([ADR-0008](docs/adr/0008-keyverse-rp-authorization-boundary.md)). -### Onboard a relying party +Published operator contracts that already ship: -See [`docs/rp-onboarding.md`](docs/rp-onboarding.md). +| Contract | Purpose | +| --- | --- | +| Keycloak OIDC endpoints on the WAF edge | Authorization, token, JWKS, and logout for registered RPs | +| `POST /clients/relying-parties:validate` | Side-effect-free RP client preflight | +| `PUT /clients/relying-parties/{client_id}` | Secret-free RP desired state and reconcile | +| `POST /federation/identity-providers:validate` | Side-effect-free SAML/OIDC IdP preflight | +| `PUT /federation/identity-providers/{alias}` | Persist and converge an external IdP | +| `POST /federation/user-directories:validate` | Side-effect-free LDAP/AD preflight (no DNS, socket, bind, search, store, or Keycloak call) | +| `PUT /federation/user-directories/{name}` | Persist and converge a directory component | +| `/scim/v2/Users` | Inbound SCIM 2.0 user lifecycle | +| `POST /registration/accounts` | Password-free account create plus enrollment email | +| `GET /users/{user_id}`, `POST /merges` | Inspect and merge accounts | + +Confidential client secrets are placed by the deployment controller, not +returned in ordinary Keyverse responses. See +[`docs/rp-onboarding.md`](docs/rp-onboarding.md). -## Account unification & merge +### Register external federation -```bash -cd services/account_unification -python -m venv .venv && . .venv/bin/activate -pip install -e '.[dev]' -pytest -q -``` +The portable realm contains no employer ADFS, LDAP/AD source, or other +customer-specific federation. Render deployment values from KV and preflight +every private payload before apply. -Design: [`docs/merge-unification-flow.md`](docs/merge-unification-flow.md). -Matching precedence is **exact (idp, subject) → verified email → explicit link**, -and the engine **never merges on an unverified email**. +LDAP preflight redacts `bindDn` and `bindCredential` and must never be used +as the apply payload; apply the original private file only. The first +directory profile is LDAPS-only, read-only, Kerberos-disabled, and +`trustEmail=false`. -## Standalone AND submodule-embeddable +See [`docs/federation-onboarding.md`](docs/federation-onboarding.md) and +[`docs/ldap-directory-onboarding.md`](docs/ldap-directory-onboarding.md). -- **Standalone:** the compose file or the Helm chart. -- **Submodule:** add this repo as a git submodule and `include:` its - `docker-compose.yml`, or depend on `helm/cwl-idp`. Every component exposes a - `/healthz`-style readiness probe so the parent can gate on it. +## Account unification -## Configuration & secrets +Matching precedence is **exact `(identity_provider, subject)` → verified +email → explicit operator link**. The engine **never merges on an unverified +email**. Merged duplicates remain disabled tombstones with survivor lineage. +Design: [`docs/merge-unification-flow.md`](docs/merge-unification-flow.md). + +## Configuration and secrets Config and secrets are read from the **KV / DB store**, not from runtime -`os.getenv`. Environment variables are used **only as bootstrap transport** to -reach that store (`CWL_IDP_BOOTSTRAP` → `deploy/bootstrap/bootstrap.yaml`). -Database objects use two-word snake_case names (`idp_config_entries`, -`account_merge_audit`). +`os.getenv`. Environment variables are **bootstrap transport** only +(`CWL_IDP_BOOTSTRAP` → `deploy/bootstrap/bootstrap.yaml`). Database objects +use two-word-or-longer snake_case names (`idp_config_entries`, +`account_merge_audit`, `user_operation_lock_state`). -## Engine & licensing +## Engine and licensing - Engine: **Keycloak** (Apache-2.0). This repo: **Apache-2.0** (`LICENSE`). -- **Permissive OSS only** — no GPL/AGPL dependencies. cwl-idp deliberately does - **not** use ZITADEL (AGPL-3.0) nor the commercial scim-for-keycloak plugin; - the SCIM shim in this repo is our own Apache-2.0 code. - -## References - -Standards and papers live under `docs/papers/` and `docs/doctoring/`, including -NIST SP 800-63C federation, RFC 7644 SCIM, OIDC Core, SAML V2.0, and the LDAP -RFC 4511–4515 family. - ---- +- **Permissive OSS only** — no GPL/AGPL dependencies. The SCIM shim is + Apache-2.0 code in this repository. -🤖 Generated with [Claude Code](https://claude.com/claude-code) +## Where decisions and standards live -## Hourly OpenCode product development - -At minute 41 UTC, and only when no pull request exists and the exact `main` SHA -is healthy, Keyverse may run one bounded OpenCode development cycle through a -loopback NVIDIA NIM credential broker. The model works from a disposable -`git archive` without `.git`, GitHub credentials, Actions OIDC, publication -authority, or the upstream NIM credential. +| Path | What | +| --- | --- | +| [`docs/adr/`](docs/adr/README.md) | Accepted architecture decisions (0001–0008 on this branch) | +| [`docs/REFERENCES.md`](docs/REFERENCES.md) | APA 7th bibliography for ADR 0001–0007 | +| [`docs/doctoring/`](docs/doctoring/) | Feature-specific standards interpretation | +| [`docs/papers/`](docs/papers/README.md) | Offline copies of selected primary sources | +| [`docs/operations/`](docs/operations/) | Operator runbooks, including hourly product development | +| [`ARCHITECTURE.md`](ARCHITECTURE.md) | Runtime topology and trust boundaries | +| [`docs/rp-onboarding.md`](docs/rp-onboarding.md) | RP onboarding | +| [`docs/passwordless-policy.md`](docs/passwordless-policy.md) | Passwordless realm invariants | -A fresh job independently validates the sealed patch and re-runs the complete -100% production docstring, statement, and branch coverage gates plus package, -realm, Compose, and provider-template checks. Only then may a dedicated -`OPENCODE_PRODUCT_DEVELOPMENT_TOKEN` create one draft PR. Existing review-agent -workflows and credentials are unchanged; the development workflow cannot -approve, merge, tag, or release. +## Repository layout -Operations are documented in -[`docs/operations/hourly-product-development.md`](docs/operations/hourly-product-development.md). -Standards traceability and APA 7th references are recorded in -[`docs/doctoring/hourly-opencode-product-development.md`](docs/doctoring/hourly-opencode-product-development.md). +| Path | What | +| --- | --- | +| `docker-compose.yml` | Standalone bring-up: Keycloak + Postgres + admin service (pinned by digest) | +| `deploy/keycloak/` | Portable Keycloak realm config-as-code and service-account bootstrap | +| `deploy/templates/` | Private deployment templates for preflight and desired state | +| `deploy/bootstrap/` | Bootstrap pointer to the KV/DB config store | +| `deploy/scripts/healthz.sh` | Cross-component readiness probe | +| `scripts/validate_realm.py` | Realm config-as-code validator | +| `services/account_unification/` | FastAPI admin service (link, merge, SCIM, federation, RP desired state) | +| `helm/cwl-idp/` | Helm chart for the same three components | diff --git a/docs/REFERENCES.md b/docs/REFERENCES.md new file mode 100644 index 0000000..cf1b627 --- /dev/null +++ b/docs/REFERENCES.md @@ -0,0 +1,119 @@ +# References for ADR 0001–0007 + +This file is the APA 7th bibliography for the expansions of the accepted +architecture decisions [`0001`](adr/0001-keycloak-hub.md)–[`0007`](adr/0007-automation-authority.md). +Every entry was opened on an official catalog (RFC Editor, OpenID Foundation, +W3C TR, OASIS, NIST CSRC / nvlpubs) before citation. Feature-specific +doctoring records under [`docs/doctoring/`](doctoring/) keep their own +bibliographies; do not treat this file as a rewrite of those records. + +Internet-Drafts and W3C Candidate Recommendations are labeled as such and +are not treated as final RFCs or Recommendations. + +## Official records + +Cantor, S., Kemp, J., Philpott, R., & Maler, E. (Eds.). (2005, March 15). +*Assertions and protocols for the OASIS Security Assertion Markup Language +(SAML) V2.0* (OASIS Standard, document identifier saml-core-2.0-os). +Organization for the Advancement of Structured Information Standards. +https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf + +Denniss, W., & Bradley, J. (2017). *OAuth 2.0 for native apps* (BCP 212, +RFC 8252). Internet Engineering Task Force. +https://doi.org/10.17487/RFC8252 + +Hardt, D. (Ed.). (2012). *The OAuth 2.0 authorization framework* (RFC 6749). +Internet Engineering Task Force. https://doi.org/10.17487/RFC6749 + +Harrison, R. (Ed.). (2006). *Lightweight Directory Access Protocol (LDAP): +Authentication methods and security mechanisms* (RFC 4513). Internet +Engineering Task Force. https://doi.org/10.17487/RFC4513 + +Hodges, J., Jones, J. C., Jones, M. B., Kumar, A., & Lundberg, E. (Eds.). +(2021, April 8). *Web Authentication: An API for accessing Public Key +Credentials Level 2* (W3C Recommendation). World Wide Web Consortium. +https://www.w3.org/TR/2021/REC-webauthn-2-20210408/ + +Hunt, P., Grizzle, K., Ansari, M., Wahlstroem, E., & Mortimore, C. (2015). +*System for Cross-domain Identity Management: Protocol* (RFC 7644). +Internet Engineering Task Force. https://doi.org/10.17487/RFC7644 + +Hunt, P., Grizzle, K., Wahlstroem, E., & Mortimore, C. (2015). *System for +Cross-domain Identity Management: Core schema* (RFC 7643). Internet +Engineering Task Force. https://doi.org/10.17487/RFC7643 + +Lodderstedt, T., Bradley, J., Labunets, A., & Fett, D. (2025). *Best current +practice for OAuth 2.0 security* (BCP 240, RFC 9700). Internet Engineering +Task Force. https://doi.org/10.17487/RFC9700 + +OASIS Open. (2005, March 1). *Security Assertion Markup Language (SAML) +v2.0* [Standards catalog entry]. +https://www.oasis-open.org/standard/saml/ + +Sakimura, N., Bradley, J., & Agarwal, N. (2015). *Proof key for code +exchange by OAuth public clients* (RFC 7636). Internet Engineering Task +Force. https://doi.org/10.17487/RFC7636 + +Sakimura, N., Bradley, J., Jones, M., de Medeiros, B., & Mortimore, C. +(2023, December 15). *OpenID Connect Core 1.0 incorporating errata set 2*. +OpenID Foundation. https://openid.net/specs/openid-connect-core-1_0.html + +Sermersheim, J. (Ed.). (2006). *Lightweight Directory Access Protocol +(LDAP): The protocol* (RFC 4511). Internet Engineering Task Force. +https://doi.org/10.17487/RFC4511 + +Souppaya, M., Scarfone, K., & Dodson, D. (2022). *Secure Software +Development Framework (SSDF) version 1.1: Recommendations for mitigating +the risk of software vulnerabilities* (NIST Special Publication 800-218). +National Institute of Standards and Technology. +https://doi.org/10.6028/NIST.SP.800-218 + +Temoshok, D., Fenton, J. L., Choong, Y.-Y., Lefkovitz, N., Regenscheid, A., +Galluzzo, R., & Richer, J. P. (2025). *Digital identity guidelines: +Authentication and authenticator management* (NIST Special Publication +800-63B-4). National Institute of Standards and Technology. +https://doi.org/10.6028/NIST.SP.800-63B-4 + +Temoshok, D., Richer, J. P., Choong, Y.-Y., Fenton, J. L., Lefkovitz, N., +Regenscheid, A., & Galluzzo, R. (2025). *Digital identity guidelines: +Federation and assertions* (NIST Special Publication 800-63C-4). National +Institute of Standards and Technology. +https://doi.org/10.6028/NIST.SP.800-63C-4 + +Zeilenga, K. (Ed.). (2006). *Lightweight Directory Access Protocol (LDAP): +String representation of distinguished names* (RFC 4514). Internet +Engineering Task Force. https://doi.org/10.17487/RFC4514 + +## Vendor documentation (engine) + +Keycloak. (n.d.). *Server administration guide* (Version 26.7.1). Retrieved +August 18, 2026, from https://www.keycloak.org/docs/latest/server_admin/ + +## Work in progress (not final) + +Hardt, D., Parecki, A., & Lodderstedt, T. (2026, March 2). *The OAuth 2.1 +authorization framework* (Internet-Draft draft-ietf-oauth-v2-1-15). +Internet Engineering Task Force. +https://datatracker.ietf.org/doc/draft-ietf-oauth-v2-1/ + +World Wide Web Consortium. (2026, May 26). *Web Authentication: An API for +accessing Public Key Credentials Level 3* (W3C Candidate Recommendation +Snapshot). https://www.w3.org/TR/webauthn-3/ + +OAuth 2.1 remains an IETF Working Group Internet-Draft (IESG state: +I-D Exists; intended RFC status unset). WebAuthn Level 3 is a Candidate +Recommendation Snapshot, not a W3C Recommendation. Neither is cited as a +normative final record in ADR 0001–0007. + +## Catalog notes + +NIST SP 800-63B and SP 800-63C (June 2017; updated 2 March 2020) were +withdrawn on 1 August 2025 and superseded by SP 800-63B-4 and SP 800-63C-4 +(Final 31 July 2025). This bibliography cites the current finals opened at +https://csrc.nist.gov/pubs/sp/800/63/b/4/final, +https://csrc.nist.gov/pubs/sp/800/63/c/4/final, +https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-63B-4.pdf, +and +https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-63C-4.pdf. +Offline attachments under [`docs/papers/`](papers/README.md) may still +hold the withdrawn 2017 texts; ADR 0001–0007 use the 2025 finals. diff --git a/docs/adr/0001-keycloak-hub.md b/docs/adr/0001-keycloak-hub.md index 1dd0af9..e37e13e 100644 --- a/docs/adr/0001-keycloak-hub.md +++ b/docs/adr/0001-keycloak-hub.md @@ -1,6 +1,99 @@ # ADR-0001: Keep Keycloak and Keyverse as the ecosystem identity hub **Status:** Accepted -**Date:** 2026-08-09 +**Date:** 2026-08-09 +**Last expanded:** 2026-08-18 -Keyverse uses Keycloak as the standards-based identity engine and adds CWL-owned control services around it. Employer/customer ADFS, LDAP/AD, external OIDC, and HR/IGA are federation/provisioning sources rather than peer hubs. CWL relying parties trust the Keyverse/Keycloak boundary instead of administering those external systems directly. Customer-specific federation remains deployment data, not portable realm code. \ No newline at end of file +## Context + +ContextualWisdomLab products need one identity leaf that can run on its own +and still be called by relying parties. OpenID Connect Core defines an +OpenID Provider as an OAuth 2.0 authorization server that authenticates the +end-user and issues claims to a relying party (Sakimura et al., 2023). +OAuth 2.0 is the official authorization-framework record (Hardt, 2012). +SAML 2.0 defines assertions and protocols that a service provider uses to +accept authentication from an external asserting party (Cantor et al., +2005; OASIS Open, 2005). LDAP is a directory-access protocol, not an +identity hub (Sermersheim, 2006). SCIM 2.0 is an HTTP protocol for +cross-domain user lifecycle (Hunt, Grizzle, Ansari, et al., 2015). + +NIST SP 800-63C-4 describes federation as one credential service provider +supplying authentication attributes to separately administered relying +parties, and those relying parties using one or more providers (Temoshok, +Richer, et al., 2025). That model fits a single Keyverse issuer with +external employer ADFS, LDAP/AD, optional personal OIDC, and HR/IGA SCIM +as inbound sources. + +Keycloak's published administration guide documents OpenID Connect, OAuth +2.0, SAML, identity brokering, and LDAP/Active Directory user federation +in one Apache-2.0 engine (Keycloak, n.d.). Building the portable `cwl` +realm and Keyverse control plane on that engine avoids a second protocol +stack and keeps customer federation out of committed realm JSON. + +Composition hubs such as naruon and gyeot may call this leaf. Orgmetra +owns employment and org-tree truth; Keyverse does not copy those tables. + +## Decision + +Keyverse uses Keycloak as the standards-based identity engine and adds +CWL-owned control services around it. Employer/customer ADFS, LDAP/AD, +external OIDC, and HR/IGA are federation or provisioning sources rather +than peer hubs. CWL relying parties trust the Keyverse/Keycloak boundary +instead of administering those external systems directly. +Customer-specific federation remains deployment data, not portable realm +code. + +This repository must boot from its own Compose or Helm artifacts. Optional +parent include of this repo's Compose or Helm chart is allowed. A Keyverse +checkout must not require naruon, gyeot, Orgmetra, or any other sibling +repository. + +## Consequences + +- Ecosystem RPs implement OIDC client behavior against one issuer rather + than each administering ADFS, LDAP, or SCIM themselves. +- External IdP metadata, bind credentials, and RP client secrets stay in + the deployment KV/controller, so the portable realm stays reusable. +- Protocol coverage is bounded by what Keycloak already executes: OIDC and + OAuth 2.0 outbound, SAML and OIDC brokering inbound, LDAP/AD user + storage, plus the Keyverse SCIM shim and account-unification API. +- OAuth 2.1 is not treated as a final RFC; see + [`docs/REFERENCES.md`](../REFERENCES.md) for the current Internet-Draft + label. +- Authorization after a verified token remains an RP obligation + ([ADR-0008](0008-keyverse-rp-authorization-boundary.md)). + +## References + +See [`docs/REFERENCES.md`](../REFERENCES.md) for the full APA 7th entries +and official URLs/DOIs opened for this expansion. + +Cantor, S., Kemp, J., Philpott, R., & Maler, E. (Eds.). (2005). +*Assertions and protocols for the OASIS Security Assertion Markup Language +(SAML) V2.0*. OASIS. +https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf + +Hardt, D. (Ed.). (2012). *The OAuth 2.0 authorization framework* (RFC 6749). +https://doi.org/10.17487/RFC6749 + +Hunt, P., Grizzle, K., Ansari, M., Wahlstroem, E., & Mortimore, C. (2015). +*System for Cross-domain Identity Management: Protocol* (RFC 7644). +https://doi.org/10.17487/RFC7644 + +Keycloak. (n.d.). *Server administration guide* (Version 26.7.1). +https://www.keycloak.org/docs/latest/server_admin/ + +OASIS Open. (2005). *Security Assertion Markup Language (SAML) v2.0*. +https://www.oasis-open.org/standard/saml/ + +Sakimura, N., Bradley, J., Jones, M., de Medeiros, B., & Mortimore, C. +(2023). *OpenID Connect Core 1.0 incorporating errata set 2*. +https://openid.net/specs/openid-connect-core-1_0.html + +Sermersheim, J. (Ed.). (2006). *Lightweight Directory Access Protocol +(LDAP): The protocol* (RFC 4511). https://doi.org/10.17487/RFC4511 + +Temoshok, D., Richer, J. P., Choong, Y.-Y., Fenton, J. L., Lefkovitz, N., +Regenscheid, A., & Galluzzo, R. (2025). *Digital identity guidelines: +Federation and assertions* (NIST SP 800-63C-4). +https://doi.org/10.6028/NIST.SP.800-63C-4 diff --git a/docs/adr/0002-passwordless-local-accounts.md b/docs/adr/0002-passwordless-local-accounts.md index 0ff62ca..11564fd 100644 --- a/docs/adr/0002-passwordless-local-accounts.md +++ b/docs/adr/0002-passwordless-local-accounts.md @@ -1,6 +1,77 @@ # ADR-0002: Keep ecosystem-local accounts passwordless-first **Status:** Accepted -**Date:** 2026-08-09 +**Date:** 2026-08-09 +**Last expanded:** 2026-08-18 -The portable local browser flow uses WebAuthn/passkeys and does not include an ordinary password authenticator. Registration creates no password and uses a controlled enrollment action. External federation may rely on its upstream authentication policy, but Keyverse does not silently add a local password fallback for ecosystem-local accounts. Changing this boundary requires explicit security/product review and migration evidence. \ No newline at end of file +## Context + +Local Keyverse accounts are created and used inside the `cwl` realm, not at +an employer IdP. A reusable password in that flow would be phishable, +resettable, and leakable independently of any federated authenticator. + +W3C Web Authentication Level 2 is a Recommendation. It defines +origin-scoped public-key credentials for registration and authentication +ceremonies, with authenticators providing cryptographic proof of user +presence and consent (Hodges et al., 2021). WebAuthn Level 3 was opened as +a Candidate Recommendation Snapshot (26 May 2026) and is not treated as a +Recommendation here. + +NIST SP 800-63B-4 sets authenticator-assurance requirements for remote +authentication and covers phishing-resistant authenticators, including +syncable passkeys, as the current Digital Identity Guidelines +authentication volume (Temoshok, Fenton, et al., 2025). Those guidelines +are written for U.S. federal systems and are used here as authoritative +authenticator-management evidence, not as a claim that Keyverse is a +federal CSP. + +OpenID Connect Core treats authentication context as information an RP may +require before an entitlement decision; it does not require a password +authenticator at the OpenID Provider (Sakimura et al., 2023). Keycloak +already implements a WebAuthn passwordless authenticator that the portable +realm can bind as the only credential execution in the browser flow +(Keycloak, n.d.). + +## Decision + +The portable local browser flow uses WebAuthn/passkeys and does not include +an ordinary password authenticator. Registration creates no password and +uses a controlled enrollment action. External federation may rely on its +upstream authentication policy, but Keyverse does not silently add a local +password fallback for ecosystem-local accounts. Changing this boundary +requires explicit security/product review and migration evidence. + +## Consequences + +- The bound `browser-passwordless` flow and realm validator must keep + rejecting `auth-password-form` and related password authenticators. +- Headless registration sends `VERIFY_EMAIL` plus + `webauthn-register-passwordless` and rolls back the account if + enrollment initialization fails. +- Self-service password reset stays off (`resetPasswordAllowed:false`). +- Federated users authenticate at their upstream IdP; this decision does + not rewrite that upstream policy. +- A later move to WebAuthn Level 3 features needs a separate review after + that document becomes a Recommendation. + +## References + +See [`docs/REFERENCES.md`](../REFERENCES.md) for the full APA 7th entries +and official URLs/DOIs opened for this expansion. + +Hodges, J., Jones, J. C., Jones, M. B., Kumar, A., & Lundberg, E. (Eds.). +(2021, April 8). *Web Authentication: An API for accessing Public Key +Credentials Level 2* (W3C Recommendation). +https://www.w3.org/TR/2021/REC-webauthn-2-20210408/ + +Keycloak. (n.d.). *Server administration guide* (Version 26.7.1). +https://www.keycloak.org/docs/latest/server_admin/ + +Sakimura, N., Bradley, J., Jones, M., de Medeiros, B., & Mortimore, C. +(2023). *OpenID Connect Core 1.0 incorporating errata set 2*. +https://openid.net/specs/openid-connect-core-1_0.html + +Temoshok, D., Fenton, J. L., Choong, Y.-Y., Lefkovitz, N., Regenscheid, A., +Galluzzo, R., & Richer, J. P. (2025). *Digital identity guidelines: +Authentication and authenticator management* (NIST SP 800-63B-4). +https://doi.org/10.6028/NIST.SP.800-63B-4 diff --git a/docs/adr/0003-identity-matching.md b/docs/adr/0003-identity-matching.md index 4045434..f76b7f4 100644 --- a/docs/adr/0003-identity-matching.md +++ b/docs/adr/0003-identity-matching.md @@ -1,6 +1,81 @@ # ADR-0003: Use exact external subject, then verified email, then explicit link **Status:** Accepted -**Date:** 2026-08-09 +**Date:** 2026-08-09 +**Last expanded:** 2026-08-18 -Account matching precedence is exact `(identity_provider, subject)`, then verified email under policy, then explicit operator link. Unverified email never authorizes automatic linking or merge. Merged duplicate accounts remain disabled tombstones with survivor lineage. This decision is shared by account unification, federation, and SCIM so one path cannot weaken another's identity evidence. \ No newline at end of file +## Context + +The same human can arrive through a federated SAML or OIDC IdP, an LDAP +user-storage component, inbound SCIM, and a local passkey account. Those +paths must share one matching rule so a weaker path cannot link or merge +accounts that a stronger path would refuse. + +OpenID Connect Core defines a subject identifier as a locally unique and +never-reassigned identifier within the issuer for the end-user (Sakimura +et al., 2023). An exact `(identity_provider, subject)` pair is therefore +stronger evidence than an email string that may be unverified, recycled, +or typed by an operator. + +NIST SP 800-63C-4 requires federation participants to treat assertion +attributes according to the federation assurance and attribute-validation +rules of the deployment; an RP or hub must not treat an unvalidated +attribute as proof of the same subscriber (Temoshok, Richer, et al., +2025). SAML 2.0 likewise carries subject and attribute statements from an +asserting party; those statements are only as trustworthy as the +configured signature, issuer, and attribute contract (Cantor et al., +2005). SCIM 2.0 can create or replace a User resource, including emails, +without proving mailbox control (Hunt, Grizzle, Ansari, et al., 2015; +Hunt, Grizzle, Wahlstroem, & Mortimore, 2015). + +## Decision + +Account matching precedence is exact `(identity_provider, subject)`, then +verified email under policy, then explicit operator link. Unverified email +never authorizes automatic linking or merge. Merged duplicate accounts +remain disabled tombstones with survivor lineage. This decision is shared +by account unification, federation, and SCIM so one path cannot weaken +another's identity evidence. + +## Consequences + +- Auto-link and merge APIs reject an unverified-email coincidence even + when a caller sets `explicit_link=true` unless a stronger rule also + holds; `allow_unverified_email_link` remains audit evidence and must + stay false at startup. +- `trustEmail` on external IdP and LDAP sources defaults to false until + the upstream verification contract is independently reviewed. +- SCIM provisioning must honor tombstone and survivor pointers so a later + replace cannot resurrect a merged duplicate as a second live account. +- Email remains usable identity data under purpose-bound access control, + encryption, and audit. This decision does not prescribe display masking. +- Orgmetra employment or org-tree identifiers are out of scope for this + matcher; Keyverse does not copy those tables to invent a fourth + precedence key. + +## References + +See [`docs/REFERENCES.md`](../REFERENCES.md) for the full APA 7th entries +and official URLs/DOIs opened for this expansion. + +Cantor, S., Kemp, J., Philpott, R., & Maler, E. (Eds.). (2005). +*Assertions and protocols for the OASIS Security Assertion Markup Language +(SAML) V2.0*. OASIS. +https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf + +Hunt, P., Grizzle, K., Ansari, M., Wahlstroem, E., & Mortimore, C. (2015). +*System for Cross-domain Identity Management: Protocol* (RFC 7644). +https://doi.org/10.17487/RFC7644 + +Hunt, P., Grizzle, K., Wahlstroem, E., & Mortimore, C. (2015). *System for +Cross-domain Identity Management: Core schema* (RFC 7643). +https://doi.org/10.17487/RFC7643 + +Sakimura, N., Bradley, J., Jones, M., de Medeiros, B., & Mortimore, C. +(2023). *OpenID Connect Core 1.0 incorporating errata set 2*. +https://openid.net/specs/openid-connect-core-1_0.html + +Temoshok, D., Richer, J. P., Choong, Y.-Y., Fenton, J. L., Lefkovitz, N., +Regenscheid, A., & Galluzzo, R. (2025). *Digital identity guidelines: +Federation and assertions* (NIST SP 800-63C-4). +https://doi.org/10.6028/NIST.SP.800-63C-4 diff --git a/docs/adr/0004-desired-state-reconciliation.md b/docs/adr/0004-desired-state-reconciliation.md index 35ba95c..f06dc89 100644 --- a/docs/adr/0004-desired-state-reconciliation.md +++ b/docs/adr/0004-desired-state-reconciliation.md @@ -1,6 +1,96 @@ # ADR-0004: Use side-effect-free preflight and re-observed desired-state reconciliation **Status:** Accepted -**Date:** 2026-08-09 +**Date:** 2026-08-09 +**Last expanded:** 2026-08-18 -Federation, directory, and relying-party onboarding separate deterministic local preflight from external apply. Where Keyverse owns desired state, intent is persisted before remote mutation, duplicate remote matches fail closed, and a canonical apply receipt is written only after exact live re-observation. Delete uses remote-first ordering where local-first deletion could create false success. Preflight success never means external login/bind/provisioning success. \ No newline at end of file +## Context + +Operators must register SAML/OIDC identity providers, LDAP/AD user-storage +components, and OIDC relying-party clients without turning a validation +call into a live bind, metadata fetch, or silent Keycloak write. + +OpenID Connect Core assumes the RP already has issuer, authorization, +token, and related endpoint locations. Those values are normally obtained +via Discovery **or may be obtained via other mechanisms** (Sakimura et +al., 2023, §1). Keyverse chooses the latter for preflight: the operator +supplies pinned HTTPS endpoints. OAuth 2.0 Security BCP requires exact +redirect matching, PKCE for code flows, and rejects patterns that leak +codes or tokens (Lodderstedt et al., 2025; Sakimura, Bradley, & Agarwal, +2015). Native-app redirect guidance is recorded in RFC 8252; this +product's first RP profile does not accept loopback or private-use +schemes without a separate review (Denniss & Bradley, 2017). + +LDAP protocol operations include bind and search over a directory +connection (Sermersheim, 2006). LDAP authentication and StartTLS are +specified separately (Harrison, 2006). Distinguished-name strings used in +configuration must follow RFC 4514 syntax (Zeilenga, 2006). A local +preflight that only checks those syntactic and policy constraints is not +an LDAP session. + +SCIM 2.0 defines create, replace, patch, and delete as distinct protocol +operations whose success is determined by the resource server after the +request (Hunt, Grizzle, Ansari, et al., 2015). The same lesson applies to +Keycloak mutations: a local schema check is not an apply receipt. + +## Decision + +Federation, directory, and relying-party onboarding separate deterministic +local preflight from external apply. Where Keyverse owns desired state, +intent is persisted before remote mutation, duplicate remote matches fail +closed, and a canonical apply receipt is written only after exact live +re-observation. Delete uses remote-first ordering where local-first +deletion could create false success. Preflight success never means +external login, bind, or provisioning success. + +SAML and OIDC preflight perform no metadata or discovery fetch. LDAP +preflight performs no DNS lookup, socket, bind, search, storage write, or +Keycloak call. + +## Consequences + +- `POST ...:validate` routes stay pure functions of the submitted payload + and closed policy. +- `PUT` routes store intent, classify zero / one / many live objects, and + refuse to pick an arbitrary duplicate. +- Receipts are not written from the request body alone; operators compare + redacted live status to the original private file. +- Controlled login, bind, or SCIM evidence remains a later acceptance + step, not a preflight field. +- Mapper configuration on an RP client is issuer-side evidence only; it + does not prove the RP validated signature, issuer, expiry, or audience. + +## References + +See [`docs/REFERENCES.md`](../REFERENCES.md) for the full APA 7th entries +and official URLs/DOIs opened for this expansion. + +Denniss, W., & Bradley, J. (2017). *OAuth 2.0 for native apps* (RFC 8252). +https://doi.org/10.17487/RFC8252 + +Harrison, R. (Ed.). (2006). *Lightweight Directory Access Protocol (LDAP): +Authentication methods and security mechanisms* (RFC 4513). +https://doi.org/10.17487/RFC4513 + +Hunt, P., Grizzle, K., Ansari, M., Wahlstroem, E., & Mortimore, C. (2015). +*System for Cross-domain Identity Management: Protocol* (RFC 7644). +https://doi.org/10.17487/RFC7644 + +Lodderstedt, T., Bradley, J., Labunets, A., & Fett, D. (2025). *Best +current practice for OAuth 2.0 security* (RFC 9700). +https://doi.org/10.17487/RFC9700 + +Sakimura, N., Bradley, J., & Agarwal, N. (2015). *Proof key for code +exchange by OAuth public clients* (RFC 7636). +https://doi.org/10.17487/RFC7636 + +Sakimura, N., Bradley, J., Jones, M., de Medeiros, B., & Mortimore, C. +(2023). *OpenID Connect Core 1.0 incorporating errata set 2*. +https://openid.net/specs/openid-connect-core-1_0.html + +Sermersheim, J. (Ed.). (2006). *Lightweight Directory Access Protocol +(LDAP): The protocol* (RFC 4511). https://doi.org/10.17487/RFC4511 + +Zeilenga, K. (Ed.). (2006). *Lightweight Directory Access Protocol (LDAP): +String representation of distinguished names* (RFC 4514). +https://doi.org/10.17487/RFC4514 diff --git a/docs/adr/0005-secret-ownership.md b/docs/adr/0005-secret-ownership.md index 5e79284..5ff0a20 100644 --- a/docs/adr/0005-secret-ownership.md +++ b/docs/adr/0005-secret-ownership.md @@ -1,6 +1,86 @@ # ADR-0005: Separate portable configuration from deployment-private values **Status:** Accepted -**Date:** 2026-08-09 +**Date:** 2026-08-09 +**Last expanded:** 2026-08-18 -Portable realm configuration and ordinary desired-state records contain only the fields needed for reproducible identity policy. Deployment-specific confidential values remain owned by the deployment controller and its approved configuration store. Public repository artifacts, ordinary responses, and routine logs do not copy those private values. This keeps the portable realm reusable across tenants and supports controlled rotation and rollback. \ No newline at end of file +## Context + +The portable `cwl` realm and ordinary desired-state records must be +reusable across tenants. Bind credentials, operator bearers, confidential +client secrets, and signing material must not travel with those records +into git, operator JSON, or routine logs. + +OAuth 2.0 defines client authentication and client secrets as +confidential-client credentials held by the client and the authorization +server (Hardt, 2012, §2.3). The OAuth 2.0 Security BCP updates that +threat model: secrets in front-channel URLs, leaked redirectors, and +weak client authentication remain first-class failures (Lodderstedt et +al., 2025). PKCE protects the authorization code for public clients; it +does not replace confidential-client secret handling (Sakimura, Bradley, +& Agarwal, 2015). + +NIST SP 800-63C-4 treats federation and assertion protection as a +deployment concern between an IdP and separately administered RPs +(Temoshok, Richer, et al., 2025). LDAP bind credentials are directory +authentication secrets (Harrison, 2006). SCIM endpoints are +bearer-protected HTTP resources (Hunt, Grizzle, Ansari, et al., 2015). +None of those standards require publishing those secrets in a realm +export. + +## Decision + +Portable realm configuration and ordinary desired-state records contain +only the fields needed for reproducible identity policy. +Deployment-specific confidential values remain owned by the deployment +controller and its approved configuration store. Public repository +artifacts, ordinary responses, and routine logs do not copy those private +values. This keeps the portable realm reusable across tenants and +supports controlled rotation and rollback. + +Environment variables are bootstrap transport to reach that store +(`CWL_IDP_BOOTSTRAP`), not the runtime source of truth. + +## Consequences + +- RP desired-state PUT is secret-free; confidential credential placement + is a separate secret-management port. +- Preflight and status responses redact `bindDn`, `bindCredential`, + client secrets, and other known secret fields. +- Hardcoded RP routing claim values (`role`, `org`, `workspace`) are + visible product data and must not carry credentials or personal + secrets. +- Rotation and rollback are controller/KV operations; Keyverse receipts + record observed public client or provider state, not the secret itself. +- Authorized identity attributes remain usable under purpose-bound access + control, encryption, and audit. This decision does not prescribe + display masking. + +## References + +See [`docs/REFERENCES.md`](../REFERENCES.md) for the full APA 7th entries +and official URLs/DOIs opened for this expansion. + +Hardt, D. (Ed.). (2012). *The OAuth 2.0 authorization framework* (RFC 6749). +https://doi.org/10.17487/RFC6749 + +Harrison, R. (Ed.). (2006). *Lightweight Directory Access Protocol (LDAP): +Authentication methods and security mechanisms* (RFC 4513). +https://doi.org/10.17487/RFC4513 + +Hunt, P., Grizzle, K., Ansari, M., Wahlstroem, E., & Mortimore, C. (2015). +*System for Cross-domain Identity Management: Protocol* (RFC 7644). +https://doi.org/10.17487/RFC7644 + +Lodderstedt, T., Bradley, J., Labunets, A., & Fett, D. (2025). *Best +current practice for OAuth 2.0 security* (RFC 9700). +https://doi.org/10.17487/RFC9700 + +Sakimura, N., Bradley, J., & Agarwal, N. (2015). *Proof key for code +exchange by OAuth public clients* (RFC 7636). +https://doi.org/10.17487/RFC7636 + +Temoshok, D., Richer, J. P., Choong, Y.-Y., Fenton, J. L., Lefkovitz, N., +Regenscheid, A., & Galluzzo, R. (2025). *Digital identity guidelines: +Federation and assertions* (NIST SP 800-63C-4). +https://doi.org/10.6028/NIST.SP.800-63C-4 diff --git a/docs/adr/0006-user-operation-lock.md b/docs/adr/0006-user-operation-lock.md index 1a912ad..5bbaeab 100644 --- a/docs/adr/0006-user-operation-lock.md +++ b/docs/adr/0006-user-operation-lock.md @@ -1,23 +1,66 @@ # ADR-0006: Share one user-operation lock across merge and SCIM full replacement **Status:** Accepted -**Date:** 2026-08-09 +**Date:** 2026-08-09 +**Last expanded:** 2026-08-18 ## Context -Account merge/link operations and a SCIM full user replacement can target the same Keycloak user. The full replacement path reads tombstone state and then writes the user representation, so it must not race a merge that creates the tombstone between those operations. +Account merge/link operations and a SCIM full user replacement can target +the same Keycloak user. The full replacement path reads tombstone state +and then writes the user representation, so it must not race a merge that +creates the tombstone between those operations. -Protected `main` also supports the narrower `PATCH active=false` deprovisioning path. That PATCH path currently performs its read/deactivate/read sequence outside the shared cross-process lock. This ADR therefore must not imply that every SCIM mutation is serialized with merge. +SCIM 2.0 defines HTTP `PUT` as replace of the entire resource and `PATCH` +as a partial modification protocol with its own operation list (Hunt, +Grizzle, Ansari, et al., 2015, §§3.5.1–3.5.2). The core User schema +includes `active` and multi-valued emails but does not define merge or +tombstone semantics (Hunt, Grizzle, Wahlstroem, & Mortimore, 2015). +Keyverse therefore has to impose a product lock around the replace path +that can observe or overwrite survivor/tombstone attributes. + +Protected `main` also supports the narrower `PATCH active=false` +deprovisioning path. That PATCH path currently performs its +read/deactivate/read sequence outside the shared cross-process lock. This +ADR therefore must not imply that every SCIM mutation is serialized with +merge. ## Decision -Keyverse uses one cross-process user-operation lock boundary for account merge/link and SCIM `PUT /Users/{id}` full replacement. Those operations serialize consistently, preserve tombstone/survivor invariants, and can be retried or recovered from observed durable state. +Keyverse uses one cross-process user-operation lock boundary for account +merge/link and SCIM `PUT /Users/{id}` full replacement. Those operations +serialize consistently, preserve tombstone/survivor invariants, and can +be retried or recovered from observed durable state. -The current SCIM `PATCH active=false` path is explicitly outside this Accepted shared-lock guarantee. If PATCH or any future SCIM read-modify-write operation can affect tombstone, survivor, or reactivation invariants, it must join the same lock boundary and add a concurrency regression before documentation may claim equivalent serialization. +The current SCIM `PATCH active=false` path is explicitly outside this +Accepted shared-lock guarantee. If PATCH or any future SCIM +read-modify-write operation can affect tombstone, survivor, or +reactivation invariants, it must join the same lock boundary and add a +concurrency regression before documentation may claim equivalent +serialization. ## Consequences -- Merge and SCIM full replacement share one documented concurrency authority. -- The protected-main PATCH behavior remains usable but must not be described as transactionally serialized with merge. -- Expanding the lock guarantee requires a source/test change, not a documentation-only promotion. -- Clustered deployments must provide the same shared-lock semantics for every operation included in this boundary. \ No newline at end of file +- Merge and SCIM full replacement share one documented concurrency + authority. +- The protected-main PATCH behavior remains usable but must not be + described as transactionally serialized with merge. +- Expanding the lock guarantee requires a source/test change, not a + documentation-only promotion. +- Clustered deployments must provide the same shared-lock semantics for + every operation included in this boundary. +- Durable lock state uses `user_operation_lock_state`; merge audit uses + `account_merge_audit`. + +## References + +See [`docs/REFERENCES.md`](../REFERENCES.md) for the full APA 7th entries +and official URLs/DOIs opened for this expansion. + +Hunt, P., Grizzle, K., Ansari, M., Wahlstroem, E., & Mortimore, C. (2015). +*System for Cross-domain Identity Management: Protocol* (RFC 7644). +https://doi.org/10.17487/RFC7644 + +Hunt, P., Grizzle, K., Wahlstroem, E., & Mortimore, C. (2015). *System for +Cross-domain Identity Management: Core schema* (RFC 7643). +https://doi.org/10.17487/RFC7643 diff --git a/docs/adr/0007-automation-authority.md b/docs/adr/0007-automation-authority.md index 8e1b264..ceed058 100644 --- a/docs/adr/0007-automation-authority.md +++ b/docs/adr/0007-automation-authority.md @@ -1,6 +1,61 @@ # ADR-0007: Separate autonomous development from review, merge, and release authority **Status:** Accepted -**Date:** 2026-08-09 +**Date:** 2026-08-09 +**Last expanded:** 2026-08-18 -Autonomous development may inspect exact repository state, produce a bounded patch, and submit ordinary reviewable work after independent verification. It cannot create its own qualifying approval, bypass branch protection, merge protected main, tag, or publish a release. Model-provider credentials remain separate from reviewer, publication, and release credentials. PR #74 refines the hourly implementation while preserving this authority boundary. \ No newline at end of file +## Context + +Keyverse may use scheduled automation to inspect exact repository state +and propose bounded product work. Generated model output is untrusted. +If the same credential that writes a patch can also approve, merge, tag, +or publish a release, a single compromised or hallucinated run becomes a +release path. + +NIST SP 800-218 (SSDF 1.1) recommends defining roles and separating +duties across the software life cycle, reviewing changes before release, +and protecting the build and publication environment (Souppaya et al., +2022). Those practices are used here as secure-development evidence, not +as a claim that Keyverse is a federal information system. + +This decision is about **authority**. Operator procedures for the hourly +OpenCode loop live in +[`docs/operations/hourly-product-development.md`](../operations/hourly-product-development.md) +and are not restated in the buyer README. + +## Decision + +Autonomous development may inspect exact repository state, produce a +bounded patch, and submit ordinary reviewable work after independent +verification. It cannot create its own qualifying approval, bypass branch +protection, merge protected main, tag, or publish a release. +Model-provider credentials remain separate from reviewer, publication, +and release credentials. PR #74 refines the hourly implementation while +preserving this authority boundary. + +Existing review-agent workflows and their credentials stay on their +current system. They must not be repurposed, renamed, or broadened as a +side effect of product-development automation. + +## Consequences + +- A draft PR from automation is ordinary reviewable work, not a merge + grant. +- Independent verification (fresh checkout, complete quality gates) is + required before publication of a generated patch. +- Release tagging, image digest, SBOM, and rollback evidence remain a + human-owned release process after exact-main regression. +- Buyer-facing README does not describe the bot loop; operators follow + the operations guide. +- This ADR does not authorize stacking documentation or product work onto + unrelated open feature PRs. + +## References + +See [`docs/REFERENCES.md`](../REFERENCES.md) for the full APA 7th entries +and official URLs/DOIs opened for this expansion. + +Souppaya, M., Scarfone, K., & Dodson, D. (2022). *Secure Software +Development Framework (SSDF) version 1.1: Recommendations for mitigating +the risk of software vulnerabilities* (NIST SP 800-218). +https://doi.org/10.6028/NIST.SP.800-218 diff --git a/docs/adr/README.md b/docs/adr/README.md index e53fc6d..0e8d781 100644 --- a/docs/adr/README.md +++ b/docs/adr/README.md @@ -2,6 +2,11 @@ `Accepted` means the decision governs architecture; it does not imply an active PR has merged or a customer deployment has completed acceptance. +Accepted ADRs 0001–0007 are expanded in place with Context, Decision, +Consequences, and APA 7th references. The shared bibliography is +[`docs/REFERENCES.md`](../REFERENCES.md). ADR 0008 remains the RP +authorization boundary and is not rewritten by that expansion. + | ADR | Decision | Status | |---|---|---| | [0001](0001-keycloak-hub.md) | Keep Keycloak/Keyverse as the ecosystem identity hub | Accepted | From 01b6dc73f55680d6980e249bcf9dfd71945542f7 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 19 Aug 2026 20:47:36 +0900 Subject: [PATCH 2/7] build(account-unification): resync uv lockfile --- services/account_unification/uv.lock | 200 +++++++++++++++------------ 1 file changed, 115 insertions(+), 85 deletions(-) diff --git a/services/account_unification/uv.lock b/services/account_unification/uv.lock index 1ed80ca..bf57988 100644 --- a/services/account_unification/uv.lock +++ b/services/account_unification/uv.lock @@ -172,86 +172,116 @@ wheels = [ [[package]] name = "coverage" -version = "7.15.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/76/d0/55fe630f4cf94e3fcba868240fad8c8cdd1f764e2a932f8926347e6ec4cd/coverage-7.15.2.tar.gz", hash = "sha256:3df60dc267f0a2ca23cb7a9ab1109c62b9335ffbf519fcfe167157c28c09b81d", size = 927741, upload-time = "2026-07-15T18:56:19.558Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7d/3a/54536704f507d4573bf9161c4d0dd3dd59b6d85e48c664e901b6844d8e33/coverage-7.15.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2f1ec6f304b156669cfde653b4e9a953f5de87e247ea02ac599bce0ab2744036", size = 221414, upload-time = "2026-07-15T18:53:51.941Z" }, - { url = "https://files.pythonhosted.org/packages/b6/d9/8ba925d29743e3577b21e4d8c11a702b76bc93c41e7fdfd1177af63d4b8d/coverage-7.15.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4d3361879d736f469f45723c11ea1a5bbdaf1f6928f0e632c940378b5aa9b660", size = 221913, upload-time = "2026-07-15T18:53:53.682Z" }, - { url = "https://files.pythonhosted.org/packages/09/54/a855f3aa0187f2b431ade4e4791b77b56282cfb5d201c83ec26a31b5b36a/coverage-7.15.2-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:c6a98d698f9e2c8008d0370ec7fc452ebfcc530002ae2d0061170d768b992589", size = 252332, upload-time = "2026-07-15T18:53:55.467Z" }, - { url = "https://files.pythonhosted.org/packages/8e/d3/13ac97b4370640ba3452fc8559b06cc2f479ce3ba4a0b632a73e44c38a7d/coverage-7.15.2-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d50dd325e18ec25bfcc10cd7f99b04df1ab9ec76b0918c260e60817ad0643dee", size = 254243, upload-time = "2026-07-15T18:53:57.055Z" }, - { url = "https://files.pythonhosted.org/packages/88/83/5eca144942d8d0659d3f55176517f4a59cdc65eefd17146a0770935a3ebd/coverage-7.15.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:67d7602480a47bdf5b675635403625553ebaa70d5a62a657c035149fd401cea0", size = 256352, upload-time = "2026-07-15T18:53:58.83Z" }, - { url = "https://files.pythonhosted.org/packages/4e/ba/d3db2e01a50fc88cdb4c0f19542bcf6f61489e34dc9aa3538413e2459a38/coverage-7.15.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cee0f89f4767a6057c8fbf168f8135f18be651300496086bd873e3189fed0487", size = 258313, upload-time = "2026-07-15T18:54:00.497Z" }, - { url = "https://files.pythonhosted.org/packages/78/b3/aba83416e9177df28e5186d856c19158c59fc0e7e814aaa61a4a2354ad1b/coverage-7.15.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a29ec5305a7335aacee2d799e3422e91e1c8a12474986e2b3b07e315c91be82f", size = 252449, upload-time = "2026-07-15T18:54:02.456Z" }, - { url = "https://files.pythonhosted.org/packages/6a/a5/4b00ecac0194431ab451b0f6710f8e2517d04cef60f821b14dec4637d575/coverage-7.15.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:48ccc6395958eda89093ecdc35644c86f23a8b23a7f4d44958812b721aad67c1", size = 254043, upload-time = "2026-07-15T18:54:04.072Z" }, - { url = "https://files.pythonhosted.org/packages/75/b6/cfa209b4313ee7f1b34da47efcd789ea51c024ad35af390e00f5a3c10a2e/coverage-7.15.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:81f382c5a94b434ec1f6da607edb904c76d7212e618cd4d1bc9f97bed4120ef5", size = 252107, upload-time = "2026-07-15T18:54:06.745Z" }, - { url = "https://files.pythonhosted.org/packages/36/67/e8cac5a6954038c98d7fe7eb9802afe7ab3ecb637bb7cc00e69b4148b56d/coverage-7.15.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:bbc808daf4f5cd567af8075ecc72d21c6dfef9a254709a621a84c217c935ebc0", size = 255873, upload-time = "2026-07-15T18:54:08.48Z" }, - { url = "https://files.pythonhosted.org/packages/2c/92/395cca9f330a86c3fe3471d73e2c102116c4c58fdc619dbbc125c6e93a54/coverage-7.15.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:a4c46b247b5d4b78f613bd89fea926d32b25c6cc61a50bd1e99ba310348f3dad", size = 251826, upload-time = "2026-07-15T18:54:10.083Z" }, - { url = "https://files.pythonhosted.org/packages/51/60/3e91b20295439652424f426b7086ec5bf4fbe3f604c73eda22b986c4fd6b/coverage-7.15.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:094dd37f3ef7b2da8b068b583d1f4c40f91c65197e16c52a71962d5d537fc5db", size = 252735, upload-time = "2026-07-15T18:54:11.878Z" }, - { url = "https://files.pythonhosted.org/packages/a5/eb/8c07839005e5e3c6b3877d3a6e2a80ce766589f31dd2b6882b78d59a7b8c/coverage-7.15.2-cp311-cp311-win32.whl", hash = "sha256:a63b9e190711134d581c4d703df5df09851b1acf99792c7aacbbe9f41f0283c9", size = 223500, upload-time = "2026-07-15T18:54:13.525Z" }, - { url = "https://files.pythonhosted.org/packages/2e/98/59d83c257cd59f0fbaf9d9ddb26b744a576760dfd1ae16e516408894a02b/coverage-7.15.2-cp311-cp311-win_amd64.whl", hash = "sha256:8bb9f4b4279187560796a4cdaca3b0a93dd97e48ee667df005f4ed9a97403688", size = 223973, upload-time = "2026-07-15T18:54:15.163Z" }, - { url = "https://files.pythonhosted.org/packages/ea/09/2d285c8bef5c4f695d120c1c96dc11715638aa8e134069f210bb6a62a9fe/coverage-7.15.2-cp311-cp311-win_arm64.whl", hash = "sha256:8c726b232659cbd2ae57ade46509eb068c9bd7a06df9fcbff6fe484870006934", size = 223519, upload-time = "2026-07-15T18:54:16.803Z" }, - { url = "https://files.pythonhosted.org/packages/6a/50/eb5bf42e531611a9f8d272556b1ed4de503f84a91413584094487cf69f8f/coverage-7.15.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:1adac78e5abc7c5438f7a209c9ca69d06542f0bf481d728b6989ea80b813fdf9", size = 221587, upload-time = "2026-07-15T18:54:18.439Z" }, - { url = "https://files.pythonhosted.org/packages/06/d1/da99af464c335d4e023a6efcd7ec30f63b88a43c93745154ab74ffb31cea/coverage-7.15.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b868acc62aa5de3be7a9d05c2333bf8359ca987e43f9cb30ff8fbda6a024ab73", size = 221943, upload-time = "2026-07-15T18:54:20.062Z" }, - { url = "https://files.pythonhosted.org/packages/5b/8a/13c42723d61ca447eafa18732e8141dd6a63f2732e1c7e1502c182dd88d7/coverage-7.15.2-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:6f6966fc30e6f06ca8f98fb0ce51eda6b111b3ee8d066a8b1ec9e77fa06ab55d", size = 253450, upload-time = "2026-07-15T18:54:21.765Z" }, - { url = "https://files.pythonhosted.org/packages/d7/29/99021303f98fbdcb63504b4d07bea4cc025b9b2dd907c4f07c85d50a0dab/coverage-7.15.2-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:68af907f595ab01a78f794932ff3bdf929c316d3000810d38dbc247129e26f8b", size = 256187, upload-time = "2026-07-15T18:54:23.4Z" }, - { url = "https://files.pythonhosted.org/packages/f9/a8/fd503715ed6ca9c5d742923aa5209257340b367a867b2ced0c7d4ba8a0b9/coverage-7.15.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:afa29e2eff3d5729267e2cb2fd4ce9d61c952932fb2694e34ccb5d9540c6a296", size = 257301, upload-time = "2026-07-15T18:54:25.183Z" }, - { url = "https://files.pythonhosted.org/packages/da/40/3f4b8fb409810036ebc2857d36adc0498c6e957b5df0290c5036b2e143f1/coverage-7.15.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bbf44513ceb1589e31948e20eafbde9deaface90e1a1afa5f5f77b4423d17ce6", size = 259562, upload-time = "2026-07-15T18:54:27.204Z" }, - { url = "https://files.pythonhosted.org/packages/0b/8a/9bdffbef47db77cce3d6b02a28f7e919b19f0106c4b080c2c2246040f885/coverage-7.15.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9deddf09eecb717b7f980414b43d90a5b22ff3967d2949ab29cb0aa83d9e9098", size = 253841, upload-time = "2026-07-15T18:54:29.134Z" }, - { url = "https://files.pythonhosted.org/packages/1b/1e/9031efde019d31a06646261fce6dfc5c3c74e951e27a71e5c9a424563178/coverage-7.15.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ae901f7e55ba405c84ee1cab3d3e962e4e871e4a2bcb9c90911adbd69b42ac5a", size = 255221, upload-time = "2026-07-15T18:54:31.142Z" }, - { url = "https://files.pythonhosted.org/packages/56/db/787acde872389fc84a9ef9d8cd1ccc658e391ab4cb5b28092a714426a394/coverage-7.15.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a0f47002c6eeb7c280228467a4cb0cc15ca2103a8421b986b2d3ec04a0f9bd8b", size = 253366, upload-time = "2026-07-15T18:54:32.886Z" }, - { url = "https://files.pythonhosted.org/packages/2f/9b/6f57bc4b93c842eef1695f8cdaf2318e35e7ba54f5ba80d84be213ab7858/coverage-7.15.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1cd7a5beb7af3e864a13b1f0fb26efd3695da43ef0daf71e586adfffaf34d5b2", size = 257434, upload-time = "2026-07-15T18:54:34.7Z" }, - { url = "https://files.pythonhosted.org/packages/88/26/b3186a21b2acc83e451118978905c81c7072c3333707804db09a78c096a2/coverage-7.15.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:97a5c5457a9fb1d6c4e06cfb5dc835871fbfb6a6a51addc9e925bdeff5ef7440", size = 252935, upload-time = "2026-07-15T18:54:36.548Z" }, - { url = "https://files.pythonhosted.org/packages/20/c2/c9f3376b2e717ea69ed7a6e9a5fcab968fb0b290db6cf4bd9a1fc7541b75/coverage-7.15.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0901cfe6c13bcd2302da4f83e884555d2a22bda6e4c476f09ef204ba20ca536e", size = 254807, upload-time = "2026-07-15T18:54:38.296Z" }, - { url = "https://files.pythonhosted.org/packages/f0/e1/dfc15401f4a8aaeb486e1ba3e9e3c40522a6e38bd0ecf0b3f29cb8082957/coverage-7.15.2-cp312-cp312-win32.whl", hash = "sha256:b171bdd71cb7ff792bf32e376173b0ace7e7963e7e57c58dfc42063a6a7174cd", size = 223641, upload-time = "2026-07-15T18:54:40.103Z" }, - { url = "https://files.pythonhosted.org/packages/91/40/81b6d809d320cd366ec5bdf8176575e897dcb8efe7fb4b489ef9e93e4d13/coverage-7.15.2-cp312-cp312-win_amd64.whl", hash = "sha256:582edc45c2040543fef83341be23c43024a3ab3ae0c2d8bc498a06282905ad40", size = 224172, upload-time = "2026-07-15T18:54:41.882Z" }, - { url = "https://files.pythonhosted.org/packages/ef/28/9f14ec438149f7de557f45518f09b4a7917b795cc37083aa7db482693f8c/coverage-7.15.2-cp312-cp312-win_arm64.whl", hash = "sha256:a638db90c61cd219aeee65e83a24fdaa57269a741ae0cf773309208ac862cee3", size = 223556, upload-time = "2026-07-15T18:54:43.674Z" }, - { url = "https://files.pythonhosted.org/packages/fc/d5/f8c838e6b7282976f7c918884b792df7a0c42c5bba5d99c60ad2d221d56d/coverage-7.15.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1121caa19159a38b5463eaae4b1e1fde81e525b15ecc5e000cd5b1a108f743a8", size = 221606, upload-time = "2026-07-15T18:54:45.448Z" }, - { url = "https://files.pythonhosted.org/packages/bf/37/97c926376364f66298cc44893b89cdf17b8bc406376497c4061ae4b8a8ff/coverage-7.15.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a300c6934e0989c327b9e8a1e110329da4641149f872bbe9f70168be66da76c1", size = 221982, upload-time = "2026-07-15T18:54:47.341Z" }, - { url = "https://files.pythonhosted.org/packages/b7/30/a36050a6e83c2135ee0776f452ca3948224befc6d7f26acecc082d0c106a/coverage-7.15.2-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:2617f8799d268fabdeef42a7e89ac3a23e1deee9025427db2df970f99a89a578", size = 252972, upload-time = "2026-07-15T18:54:49.2Z" }, - { url = "https://files.pythonhosted.org/packages/31/d3/06b5f1daf95f0f15ab05bd75f26ba5f3c8b33d0bb72f3aaa3cf41d1bad3a/coverage-7.15.2-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7dc2950a2992cd676d35c20ae63522836deeb034f08874699d14068710af3dc1", size = 255569, upload-time = "2026-07-15T18:54:51.098Z" }, - { url = "https://files.pythonhosted.org/packages/81/1c/9afb3f8de2b8d36960391c48559a2e3ff96594b58099f115921549ea8d0d/coverage-7.15.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9e36686f7a442185db2400b3df171aac520869faf9deb59df687d28659eda2a6", size = 256806, upload-time = "2026-07-15T18:54:53.145Z" }, - { url = "https://files.pythonhosted.org/packages/64/d8/b989f96061a5e32d82fddd1b1b9ff48a7c8f8ae7606f0e80fd9de54b1e33/coverage-7.15.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7d29ca7bd67af6e12e74632d65f026eabc1364da5c254494cd914446a28a3ef7", size = 258936, upload-time = "2026-07-15T18:54:55.015Z" }, - { url = "https://files.pythonhosted.org/packages/b8/fa/f99771f5110457c7b511c1935ca49ddf288218eaa84322e028b9334146ae/coverage-7.15.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:db9c8438057e5b0f6a22a0af99c0c1d26b57fbbdbd1be5861ddb8f897fcc3a2d", size = 253178, upload-time = "2026-07-15T18:54:57.527Z" }, - { url = "https://files.pythonhosted.org/packages/f6/96/c098a6044d119c751ceede7be91035fa8310170ec24a6523aff72f0a5793/coverage-7.15.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:63022c4c8dec1d0342f05c3ede99842fe3d007689acc45e86f123a1746e4a026", size = 254934, upload-time = "2026-07-15T18:54:59.41Z" }, - { url = "https://files.pythonhosted.org/packages/b2/a2/1457b3a7a50c8d77500103b97a046db863e2f59a1cf6d2f814595f349885/coverage-7.15.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:6c0be82b4d4aa5b2704e08518e2252f3e3d110164bcca826816801052e48a7aa", size = 252898, upload-time = "2026-07-15T18:55:01.338Z" }, - { url = "https://files.pythonhosted.org/packages/6c/0e/76958874c471ecfcdde0d2b2747bb2c61bdbf34a40636f4ce9db9923e643/coverage-7.15.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4510fb9cdf6bb02dfa6af0be4a534b8102d086e22e4a33f8836df663da3d660d", size = 257056, upload-time = "2026-07-15T18:55:03.243Z" }, - { url = "https://files.pythonhosted.org/packages/7c/7c/3d7c4e3bf58baa40327dc7edc2272b17cf02299366d52763db1b0ca1556a/coverage-7.15.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:42ec3d989421b174a2ab607c1539f24127ad362757b7f1c0c0d7a2993f7eb37b", size = 252718, upload-time = "2026-07-15T18:55:05.029Z" }, - { url = "https://files.pythonhosted.org/packages/c8/b8/1cecffed9ce14fb25be9ba42d37b6bb61485c9a3ddd43cd3dde36b6087d8/coverage-7.15.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e8f91bce78e32343af184c3b7fa28fcf5a9e2641f4b6623d392038f804939188", size = 254490, upload-time = "2026-07-15T18:55:06.889Z" }, - { url = "https://files.pythonhosted.org/packages/6c/2c/42984561bc7f4c045dca67516a0c50ee5ef8d84352dbeb5559dc86c4823e/coverage-7.15.2-cp313-cp313-win32.whl", hash = "sha256:434e68d531858205895eb0d74b73d20b84260de426387d53c422a5acda2cf050", size = 223647, upload-time = "2026-07-15T18:55:08.941Z" }, - { url = "https://files.pythonhosted.org/packages/41/9f/39c7c9245efc583beddf89a87683574e663ed93637f3afb6cd7b88405676/coverage-7.15.2-cp313-cp313-win_amd64.whl", hash = "sha256:26c3b04a6377fd7c09800921fa934e3a17c0020439cd59df73e73ae1d4b6a78c", size = 224190, upload-time = "2026-07-15T18:55:10.789Z" }, - { url = "https://files.pythonhosted.org/packages/c7/de/3a2883cf8a213659280ef4b403059e17a9acaeb7fc7fd4105e1226ff2e6d/coverage-7.15.2-cp313-cp313-win_arm64.whl", hash = "sha256:3ed010aa1b69cda8e827aabfca9866216c980e2dca82ab9a78c5f83689964c8b", size = 223583, upload-time = "2026-07-15T18:55:12.678Z" }, - { url = "https://files.pythonhosted.org/packages/81/5f/aed265fd7a3551a394f36dfe41868aee709b7f95db4052205b4ad1563ac3/coverage-7.15.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:40f633c5c5fc783732f6312280122e859538fa24461235597c13d803ea9a108a", size = 221650, upload-time = "2026-07-15T18:55:14.527Z" }, - { url = "https://files.pythonhosted.org/packages/6b/2c/222ba12a545189017120f8eddfc1a0bd4616b47d5d4a8d99421edb2fe4c6/coverage-7.15.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:075560438765b7a2ef43bf7aa7758661b53d889df47f062a31bda6c1ade553a2", size = 221988, upload-time = "2026-07-15T18:55:16.674Z" }, - { url = "https://files.pythonhosted.org/packages/aa/38/304b5877ab46e6c290b4292cfcf3fe28245f0e5597cad7f6acc91fc7e0a4/coverage-7.15.2-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:25fd15dd40a0a2c51a500d664ca29053c09c3259d998407bf982b6e114696138", size = 253029, upload-time = "2026-07-15T18:55:18.856Z" }, - { url = "https://files.pythonhosted.org/packages/6c/58/821b533b8db9e44cf1d8a97bd525149ced40dde1d0093da02cb78e715244/coverage-7.15.2-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b9a6367e4aff723e8ee8190836836124284e8fcd4265e307c844010cfa074f3f", size = 255536, upload-time = "2026-07-15T18:55:21.027Z" }, - { url = "https://files.pythonhosted.org/packages/f1/f2/7aa06604c389d32ea7f0a6a988359a7eafc3cd3f8e7bc2e88cd2fdf0b877/coverage-7.15.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9854ca62c152874b2060772503535be2e8f53f70b8aaa7686b094888d872f984", size = 256881, upload-time = "2026-07-15T18:55:23.125Z" }, - { url = "https://files.pythonhosted.org/packages/a2/4f/1ef342339c7916d0096bc5888cc0f653882cc7bc8f897d5cb89143287c9b/coverage-7.15.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:913b6c56e110da40e035bbd168353bf7aaa2544a5eaccea5d98a4629aac156c7", size = 259196, upload-time = "2026-07-15T18:55:25.099Z" }, - { url = "https://files.pythonhosted.org/packages/fe/f4/7ed055d7a9c5ec13b161773a115a5ccc6b0081d568c31fad830806306cc7/coverage-7.15.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:aaccad4129d735a8a4d526f26929894c9a4e8ef7034566f210b176749d6906e3", size = 253036, upload-time = "2026-07-15T18:55:27.018Z" }, - { url = "https://files.pythonhosted.org/packages/14/79/ea82cca18c242a3a38b6c017da39726aa62dcb64aa635abf79b92009975c/coverage-7.15.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a164b50081fc7357331c4024ef4d17b78ba325f8380d05f5a69599a7e05257ee", size = 254887, upload-time = "2026-07-15T18:55:29.084Z" }, - { url = "https://files.pythonhosted.org/packages/a4/ba/a136db3c0d9562b00e10b72540dbf3a33cd3bc5b95060c9308e247494623/coverage-7.15.2-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:bfd341ccf78128e72c094bc70cc25b3ef309c33c7c2c66ba3ed4309549e02de1", size = 252852, upload-time = "2026-07-15T18:55:31.184Z" }, - { url = "https://files.pythonhosted.org/packages/17/17/ea334246b16b7d059953fad6fdefa11e33c68efbd3fe37b1098120a1fac2/coverage-7.15.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:1473b3ba8e7ee0f076117b1a72c23f579a2b9e2bb742f48a8d86ea27ca93f91a", size = 257128, upload-time = "2026-07-15T18:55:33.163Z" }, - { url = "https://files.pythonhosted.org/packages/ed/c3/074fb66d46d607855f710876b117cbda562c5ab08363528e78820449f937/coverage-7.15.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:17c432b5f73ad52ef46fb06019f6fa7c66ce381961cf0f7dfd1d3a4bd3a98145", size = 252668, upload-time = "2026-07-15T18:55:35.063Z" }, - { url = "https://files.pythonhosted.org/packages/e1/c1/f620850ada9b36435921c9a3a8057013422b1d964eb4bf37fe138724d192/coverage-7.15.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:77f0ef5011df53a4bd1b35211ab122287f8d9b8d7aa1c4553e5c2deb24b1d446", size = 254325, upload-time = "2026-07-15T18:55:37.125Z" }, - { url = "https://files.pythonhosted.org/packages/cc/31/a729ca3689404493af82ef8e6ff70bd88bdda8da89aeef6ca9b387aeb2b4/coverage-7.15.2-cp314-cp314-win32.whl", hash = "sha256:f653e5d7248c1191ec988a85c72edeab46c3ff44f90639a4ed4874ec0be90243", size = 223844, upload-time = "2026-07-15T18:55:39.078Z" }, - { url = "https://files.pythonhosted.org/packages/c6/83/5d809dc808fb1698c671f3e372259bb9158e64b7ea526fc6ab7de64de9fe/coverage-7.15.2-cp314-cp314-win_amd64.whl", hash = "sha256:9911f31aad8906abe337c271343485cf20df5e70df5d2f57f9f136e7b55f26bc", size = 224331, upload-time = "2026-07-15T18:55:41.346Z" }, - { url = "https://files.pythonhosted.org/packages/16/4e/35e488548e952795829e129995c4174df33bf432b591d1aa42c8d9e4e7ad/coverage-7.15.2-cp314-cp314-win_arm64.whl", hash = "sha256:e38def96ad59853824c97953fdcd2c320a84ba3ce99b417db78af8bb6c3db635", size = 223760, upload-time = "2026-07-15T18:55:43.518Z" }, - { url = "https://files.pythonhosted.org/packages/ed/49/dd2c86cd6374038f6e415fb5bfb86db5218553209c081384a020369dee79/coverage-7.15.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:835ec4e20b45f0a7f63ed78f94065aca00de033403df8377bfe8b9c6abc0a7be", size = 222384, upload-time = "2026-07-15T18:55:45.569Z" }, - { url = "https://files.pythonhosted.org/packages/d3/74/173ff17a1c0808e5a438f549f6f145d5ac7528f2791310b63523e3200ac7/coverage-7.15.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7466cc7ab6dc0db871d264bf99e8779f0917ee63d40730af0552f71535a6e072", size = 222647, upload-time = "2026-07-15T18:55:47.544Z" }, - { url = "https://files.pythonhosted.org/packages/84/f8/b8cba872162356fb44ac79c10309d987206a4461e32072fc29228dad7331/coverage-7.15.2-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:e370c12133095ff18432de8c044962be85a5a96d90c6fcbce8e17e76236d2328", size = 264013, upload-time = "2026-07-15T18:55:49.768Z" }, - { url = "https://files.pythonhosted.org/packages/ee/67/a807a7586d0b8cae485308ddd55756f0806c92f8e0b411bacbf23c48edf3/coverage-7.15.2-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fe41909c9515c3bfdb5f02c4d1f857dba322d9a9a1178069b91eea77889df63a", size = 266135, upload-time = "2026-07-15T18:55:51.941Z" }, - { url = "https://files.pythonhosted.org/packages/ce/67/cd78771dc985f7e4ebdcc82b1a96d9a932af9e806f01f2f91a89f4c72e80/coverage-7.15.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6aa28cfb6488e5453b5b762d65f73aa586380f6693a04d58078ce228a29b06c0", size = 268555, upload-time = "2026-07-15T18:55:54.065Z" }, - { url = "https://files.pythonhosted.org/packages/18/3e/10134cf81275188c58568f324fc74aedff32c63ca4d5bbc513a91944a6f0/coverage-7.15.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bcc0aae933921d03096f53b0b03eeb702129fd406dee59f08d2efacc68681fa5", size = 269674, upload-time = "2026-07-15T18:55:56.066Z" }, - { url = "https://files.pythonhosted.org/packages/75/4a/771b77de446cba985dc414bbc5844bd21604da05dbc044286df8318a48a7/coverage-7.15.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7c63387e21ab21f512c69c9756a8c7dadd322c7275edb064064433c9a09c3743", size = 263101, upload-time = "2026-07-15T18:55:58.107Z" }, - { url = "https://files.pythonhosted.org/packages/5f/b5/70a7011da15f4071943361183aefa27847f3e3aec4fd335f1cb3d3a622b1/coverage-7.15.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0e55510bc98ae943cece9e667a6c0fe94c6a92913720dea34243657a17993d0c", size = 266007, upload-time = "2026-07-15T18:56:00.468Z" }, - { url = "https://files.pythonhosted.org/packages/b4/0d/f9547e804ce7ad49646ffeffac26699510efbe6c0f751b66fdc960c4e825/coverage-7.15.2-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:2ff08701be2d1556fc78b326c80a3e8042da09352ecb3819105f8e386c8a3071", size = 263611, upload-time = "2026-07-15T18:56:02.615Z" }, - { url = "https://files.pythonhosted.org/packages/ac/59/f576a396659c0efd351f5c1544f67c3560e89c7761cabf7f65e412beeda5/coverage-7.15.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:38c9518b7103826c403a461544e3c2e77151e8676d06eaed85911a97e962584a", size = 267344, upload-time = "2026-07-15T18:56:04.622Z" }, - { url = "https://files.pythonhosted.org/packages/7c/5d/c2e4fce3579c0cb635024293f1a32bbe26df101b3e3a69f22243d1352b6c/coverage-7.15.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:dee88b1ed88587abd8c0269a1fc1f4cc77f7750d1dfde2869e2a123af420e67d", size = 262456, upload-time = "2026-07-15T18:56:06.641Z" }, - { url = "https://files.pythonhosted.org/packages/bb/dd/956287d69436b66094bc4b57ac2da71e43bfd2a5524e958900b9f582fcf8/coverage-7.15.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:2fbeeeecea279727f8ac16c8e1133ddfeee793e985c86ae343d6a5ce744eef8c", size = 264771, upload-time = "2026-07-15T18:56:08.795Z" }, - { url = "https://files.pythonhosted.org/packages/2c/5a/6f979530c2734c575de77cf58f5f28d51f7123a94b5030fd9156fe5f363c/coverage-7.15.2-cp314-cp314t-win32.whl", hash = "sha256:cb0fddaa6884be6aae36ced9544b5e90f7d5f03845a2853bf47a14953a4e8688", size = 224151, upload-time = "2026-07-15T18:56:10.856Z" }, - { url = "https://files.pythonhosted.org/packages/54/7e/27f6b2a74d484742f4017553e710b01e396b23d809df3e95ca0bb9a2824b/coverage-7.15.2-cp314-cp314t-win_amd64.whl", hash = "sha256:77f091ea3a9cc611cd29f433565476bc1936c084ac8eee00ea0e7e70c27e4199", size = 224981, upload-time = "2026-07-15T18:56:12.928Z" }, - { url = "https://files.pythonhosted.org/packages/b1/48/284863423aa474240f6842bd00d680da22f4e6ea2e466618ef7c9c9e69a9/coverage-7.15.2-cp314-cp314t-win_arm64.whl", hash = "sha256:6fc448c377d6eeb00a47c673494bd9bae29280ca53987e1869e67ebedfe20658", size = 224294, upload-time = "2026-07-15T18:56:15.156Z" }, - { url = "https://files.pythonhosted.org/packages/ec/82/32e3bd191d498e64f6f911ad55d14006a0861e54869d2d32452326399e65/coverage-7.15.2-py3-none-any.whl", hash = "sha256:eb6bcae8d1a9d305351ecb108232441d11c5cfe9de840a04388ba5d2db8d735c", size = 213375, upload-time = "2026-07-15T18:56:17.305Z" }, +version = "7.15.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/be/c3/4f2195f512fb172aa425a8803a874b2baa9ba7f80ff7b6080998761fc701/coverage-7.15.4.tar.gz", hash = "sha256:0548198fff07ccf4faf469520bce1c2eceb1ce3e62891921138dec10907f9d00", size = 936952, upload-time = "2026-08-06T13:50:24.442Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/66/edcec7d7a0b524aa8923e22925fde6fe50ce005a113dca13ae1581455c4c/coverage-7.15.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bbac5abad70df71019988f83f26ac7092ff2642975def4429e98dc7585ef3490", size = 222367, upload-time = "2026-08-06T13:47:15.578Z" }, + { url = "https://files.pythonhosted.org/packages/e6/c6/ab8de429e2e8548faf58ec7e1674a4ce00414b4113942d3fe87109cf0f68/coverage-7.15.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:357a173465c7ce028d07a95cc2b63b5bf59f50ecdd5ad75c5cbb78ada984048e", size = 222874, upload-time = "2026-08-06T13:47:16.961Z" }, + { url = "https://files.pythonhosted.org/packages/be/c4/3b7b49587e8a6b9af79b3eb468d443d6042b6d65b47aa26586846a0d6566/coverage-7.15.4-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:21b803935e2efc3acebe9697197a294fccf5dc4e5382bd6369542ff7a7d2a1d7", size = 253287, upload-time = "2026-08-06T13:47:18.291Z" }, + { url = "https://files.pythonhosted.org/packages/fb/65/ec03b743a2a229c72cc1eff3e57be9d3564e9c6b4d5aba2d70744a3fc0d8/coverage-7.15.4-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7a2b580774a4786c1053157c0165e04476e03ff293993d7c148eee784a94bae6", size = 255199, upload-time = "2026-08-06T13:47:19.765Z" }, + { url = "https://files.pythonhosted.org/packages/41/4b/5163729e4b6582d61975cfd3ccab45b4ec53e21cf156d9941cb025188468/coverage-7.15.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a9464451c4efffe8d47ace5a540b10b0dc10e879066290f8600872b7f54a419d", size = 257308, upload-time = "2026-08-06T13:47:21.206Z" }, + { url = "https://files.pythonhosted.org/packages/86/08/2167a0f08fb87d702fa423a48578a32865464b7c9e1db3911ad7812ab414/coverage-7.15.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:de602f34123c2f4af1c1869c6dbbbd60da6d5983bf01937367295d135cccbfce", size = 259268, upload-time = "2026-08-06T13:47:22.503Z" }, + { url = "https://files.pythonhosted.org/packages/1e/e5/68eebae3053dbd48508edea559c21b23fbdf3460784f91370c83a86a6acd/coverage-7.15.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6879ded16a27f3eeca19b900c147e81616e7054db451471a611b2755ee5249f7", size = 253392, upload-time = "2026-08-06T13:47:23.88Z" }, + { url = "https://files.pythonhosted.org/packages/1a/46/fd4ced40a2b691c774e515c9b69500bfa64c7960b67fcee4b2f6fad97fc3/coverage-7.15.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:986be58c3ab54aae8d3496a6225eea74f760fdbe739b38bd442c7e8d133aa53b", size = 255001, upload-time = "2026-08-06T13:47:25.469Z" }, + { url = "https://files.pythonhosted.org/packages/53/25/ae2e5fa710bb6957a9aadeb9e3598d3b3e4af6587ce857ad42e8639a3f30/coverage-7.15.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:c6103639613fe6c1e989082948419bc77a2d26b6c825c99d7fad25f7d3d87afc", size = 253061, upload-time = "2026-08-06T13:47:26.845Z" }, + { url = "https://files.pythonhosted.org/packages/d7/31/67ddc0365db2c6e93ac8580bc4bbc50f65273262f973f63ebcdbc15c0495/coverage-7.15.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:d3af93dddb5659276c63bc16ac6466ac2033a70ca816097bbc06345b8ccdf571", size = 256831, upload-time = "2026-08-06T13:47:28.217Z" }, + { url = "https://files.pythonhosted.org/packages/f6/78/82b8fd18f57fb13f12d98fe874995bb2c4f9f17be8aff762c426323fdb96/coverage-7.15.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:b10075e5421d04265766a6d1dac809bbeb8a946fbb23c8f82c227409b2190719", size = 252781, upload-time = "2026-08-06T13:47:29.712Z" }, + { url = "https://files.pythonhosted.org/packages/0a/eb/6c74ef4dd12b252e573c49bdef9e2ac265bf3dbb79b8d7feb3266e084e9e/coverage-7.15.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a67a9f78b2942d87ba8ce3059c642164d2aedd65337377fb52fe9803656bc5c7", size = 253692, upload-time = "2026-08-06T13:47:31.192Z" }, + { url = "https://files.pythonhosted.org/packages/5a/66/eb9aed1c3fd2d36ee00eb173f434b14fa607fc056739c9a89ff4244010ea/coverage-7.15.4-cp311-cp311-win32.whl", hash = "sha256:69484d1aca26e322e1c3ce03f09341e84524ababad2d7202161738d83cc9f82e", size = 224461, upload-time = "2026-08-06T13:47:32.572Z" }, + { url = "https://files.pythonhosted.org/packages/e2/6d/81fa4161dfb3ed9d74e40d58647eff83a56b7612e78352581280fce2f477/coverage-7.15.4-cp311-cp311-win_amd64.whl", hash = "sha256:63fd6fcd1dd6e158f7eb78606e72933b3f6d01e7b747f99c6c12d764307a0fdc", size = 224937, upload-time = "2026-08-06T13:47:34.205Z" }, + { url = "https://files.pythonhosted.org/packages/5b/c1/d8dacf683c6cad3cf85ce68fd3774a6774ec402128822fdfaed920f11e6a/coverage-7.15.4-cp311-cp311-win_arm64.whl", hash = "sha256:ea82116c9893fa89e929b7f197ee5a1950a76e91cc5c85ba503fc02379d04890", size = 224479, upload-time = "2026-08-06T13:47:36.118Z" }, + { url = "https://files.pythonhosted.org/packages/1d/48/bc8d4ba7b37551a767bd863f15b3f80182b271c2f55975356f5f7dbe94c2/coverage-7.15.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d4fedd1f7f428f9fe83b1ead5e7cc87a43427be31aadafbac3ac0636dc7abb22", size = 222543, upload-time = "2026-08-06T13:47:37.562Z" }, + { url = "https://files.pythonhosted.org/packages/20/dd/88d6f83f1fffc974a3691a34a97951c5b12df7512a6782c5963883cbc058/coverage-7.15.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:37e2f0cdf58e2e1fed4e4d5a8f8786ae2f7eb80b478016876667dc4a01d60a97", size = 222905, upload-time = "2026-08-06T13:47:38.927Z" }, + { url = "https://files.pythonhosted.org/packages/bd/5c/54ee0d4748585bb0acab9891cd8d92f2d3593165b4e59fc9de113bfb3140/coverage-7.15.4-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:fb55d0e70bb15f2e81477613627286581414693d74ac7963c93a790dd453ca9d", size = 254407, upload-time = "2026-08-06T13:47:40.488Z" }, + { url = "https://files.pythonhosted.org/packages/8c/3f/f0642a372f494bd0d7dad3b497083b910194a5f1c88be2c94fef707c3b59/coverage-7.15.4-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:899b9da30f3c6c336566e3707495bb23e8302d39d862f01fa78c48b99b9437e2", size = 257145, upload-time = "2026-08-06T13:47:41.931Z" }, + { url = "https://files.pythonhosted.org/packages/71/17/8b46d0ed68251016002ec972c8fc0119961a765d0984cafb8bf317c43758/coverage-7.15.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d15715e8c46552827e5e4f30a35575a2dbcad14454cf3284c54483946bd16931", size = 258257, upload-time = "2026-08-06T13:47:43.527Z" }, + { url = "https://files.pythonhosted.org/packages/30/b8/8498a0e72d0adbe15477dd07463d2b3bb2c9f6a4815e8589e50939e2c3ae/coverage-7.15.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:002a438859f7b430bc99afeaf01a6d187dad1d0dc907b64cdeffc632a5db8fd8", size = 260517, upload-time = "2026-08-06T13:47:45.121Z" }, + { url = "https://files.pythonhosted.org/packages/41/e1/7dce19c3bdb1e3dd63e769508216500edad81bd5f69a26d724e32aceaf78/coverage-7.15.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e4193a04b518f7968f3099755f5509ee7cccc6dc2b92a6b14841934d22e222c9", size = 254785, upload-time = "2026-08-06T13:47:46.541Z" }, + { url = "https://files.pythonhosted.org/packages/dd/b1/e1494703c675a2561723cd9b89f45c9168782c31280c611b1f767851e57c/coverage-7.15.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e98dcc55d572b38e69d117da7e8e8efb8500f1f5eaf81ecd460a63220790b839", size = 256176, upload-time = "2026-08-06T13:47:48.155Z" }, + { url = "https://files.pythonhosted.org/packages/73/76/a5629d270fb638a43a4b10466f51e2f49d532c1aa4da2913cbbb150bbe0a/coverage-7.15.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:af6c538498ce66c10d3fd541c2a8d5b03da5850355add34e6cba564210cb9e72", size = 254321, upload-time = "2026-08-06T13:47:49.757Z" }, + { url = "https://files.pythonhosted.org/packages/ff/4f/9c44447218435d5766b911534f9d798144a5560f85e9a54ebe5f3f5d19f9/coverage-7.15.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1d10025d96ea89fc2f73714dbc4cbd433fe012c1ac9e23f895d7728b238b6e52", size = 258390, upload-time = "2026-08-06T13:47:51.248Z" }, + { url = "https://files.pythonhosted.org/packages/de/36/c1e127616fb3fa18a9ff71e76c417f2fd7424332a4870015ac224ef4c039/coverage-7.15.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d802e1947603162ded419bff83ac7489820355d2b856dfb09206574e3a37ac0c", size = 253894, upload-time = "2026-08-06T13:47:52.816Z" }, + { url = "https://files.pythonhosted.org/packages/e9/b9/fdb92c8ae7a8bb9b850cc253b7b3b9c8526f68130002048b5671cd510d09/coverage-7.15.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c2de40895718f91951b86712b4c5b694acaf9a0a49be13874896f599a1eed3f4", size = 255763, upload-time = "2026-08-06T13:47:54.296Z" }, + { url = "https://files.pythonhosted.org/packages/6f/c0/a7d51b2587c7bdb76e71b0896d2565bf7d60436b5122fc83e511adb1f7cd/coverage-7.15.4-cp312-cp312-win32.whl", hash = "sha256:5c3431b2161279b7db5c2a1aa58ae02e5cb8c3c42d93a5094be3f5537bd5b11b", size = 224597, upload-time = "2026-08-06T13:47:56.074Z" }, + { url = "https://files.pythonhosted.org/packages/49/b9/5c5f80cc55f5acaaca6dee677626bfcec8c87204a7809b438b08e84f4571/coverage-7.15.4-cp312-cp312-win_amd64.whl", hash = "sha256:6befeab5fb2b51c958ca4ac6c5d141a1e8240f4f76e46350f1911963deda49cd", size = 225135, upload-time = "2026-08-06T13:47:57.52Z" }, + { url = "https://files.pythonhosted.org/packages/47/e4/2a4561f89ff6bf7c925c287d0f2cce8bdf139c3a33735c87e3203401cf94/coverage-7.15.4-cp312-cp312-win_arm64.whl", hash = "sha256:67bc345491ab55b837277d76f5775d057e8c7f1ac44d890d8c2c82adde258c6f", size = 224515, upload-time = "2026-08-06T13:47:58.977Z" }, + { url = "https://files.pythonhosted.org/packages/f1/84/651a9310859673aaa3b3203f1aa1641ca60fcf2494683e1c9474c7172780/coverage-7.15.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c705b28feb2775dc82a25f1d473a370bc37ff93f5177f4e29ce2425f560f6921", size = 222565, upload-time = "2026-08-06T13:48:00.796Z" }, + { url = "https://files.pythonhosted.org/packages/82/f9/4dcf700137e8af550670f4d74d1b63828ce93e1e2b05e5f10710eb2ea987/coverage-7.15.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3ff205ab5e3ecc670f6a4dd19d9cbf12ede53dd41cfc1e15716ec961ea6d314e", size = 222936, upload-time = "2026-08-06T13:48:02.391Z" }, + { url = "https://files.pythonhosted.org/packages/07/4a/612ff1e780b3fbfd637486f542f84adc5503873d8b5d279dec1ffeef9414/coverage-7.15.4-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5172326e861a38b48b48befca15e0f477a26b283337a33a739c8fed229934e36", size = 253926, upload-time = "2026-08-06T13:48:04.382Z" }, + { url = "https://files.pythonhosted.org/packages/b0/04/d1cff1c2ead4708a6a79c01d3736b6a25bd38a36678398f72a8dd33dfad9/coverage-7.15.4-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:12b59c90084e3234fb11184886bf4a40f4f16a8c8f867be2e087b81f8e8868d4", size = 256523, upload-time = "2026-08-06T13:48:05.996Z" }, + { url = "https://files.pythonhosted.org/packages/b9/80/d34e13fb4b293cbdb9665838cf5522077b8ad14ef947550631a4bced36a5/coverage-7.15.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:349062d66f00b40fa2c1c222438bad25fabf755631b5d82937fe985c8008615c", size = 257759, upload-time = "2026-08-06T13:48:08.036Z" }, + { url = "https://files.pythonhosted.org/packages/0f/e7/2c5fe7636fdb0732fe0f09f308a5b066864078b7fc61f6678e8478554f2e/coverage-7.15.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4256ced708e598e05209bc1a8ab4074e04a51dba4c62fb45926a229af675ace7", size = 259890, upload-time = "2026-08-06T13:48:09.834Z" }, + { url = "https://files.pythonhosted.org/packages/92/28/9689f0858dfff59c2ea688938ab9fa2925631235df67126a42b6c5c70ae1/coverage-7.15.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d80f974b20782d9612c8b4c9beeca867074c7cf4079d1419843fa25a26428b25", size = 254121, upload-time = "2026-08-06T13:48:11.459Z" }, + { url = "https://files.pythonhosted.org/packages/f9/e2/785077c230c157243eb5aa9a26c3be260ecd02001bead54a3cada3df8e03/coverage-7.15.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2e179f19bfe1d31f8eeeaa12990194d761c4f62f0759661000bca6cd8729f40b", size = 255891, upload-time = "2026-08-06T13:48:13.209Z" }, + { url = "https://files.pythonhosted.org/packages/d4/90/e20371b17b40f912f21305c2db2f30efa3de306f7320fc916804872c85a4/coverage-7.15.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:8bc16bb47b7679670eceff71d78bfb7d6e5b143f6c2cd117487ec7c75e0d4b78", size = 253859, upload-time = "2026-08-06T13:48:14.736Z" }, + { url = "https://files.pythonhosted.org/packages/05/49/25371987ee459a5f67c0427fb75c74f9358e65f2c71fe75bf41c1b6c5fcb/coverage-7.15.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:1cd685005cd2c4200adfc14cf39a603b9320efab3f18a8f7f156d20c9cc3345f", size = 258011, upload-time = "2026-08-06T13:48:16.464Z" }, + { url = "https://files.pythonhosted.org/packages/30/6e/32e67467f6154bf4f1c4f63b05acc5097cba4237d45bbeeea446b52e8ac1/coverage-7.15.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:337399ad2c93b3acd2a937627dae8b3e86b66707cd3d3e856347999aadf1ef8d", size = 253676, upload-time = "2026-08-06T13:48:18.493Z" }, + { url = "https://files.pythonhosted.org/packages/03/c1/8b24192e89286399765155251f99ee9f070a9d637109018ac23d99b99f6f/coverage-7.15.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:96e257121228ec5cd2bb919276e94ac11074471bc37d68dbae0e8308cce15fff", size = 255453, upload-time = "2026-08-06T13:48:20.057Z" }, + { url = "https://files.pythonhosted.org/packages/16/6f/8b41ebdf67c87854e17c035336a90f1cfbad0c14c2a584301be6ff148718/coverage-7.15.4-cp313-cp313-win32.whl", hash = "sha256:c65a9e0dfc6143491879da4e13b5e30f8be192055de508d737fb14601edbd22c", size = 224605, upload-time = "2026-08-06T13:48:21.655Z" }, + { url = "https://files.pythonhosted.org/packages/e0/e2/2946c7f0b42b152ecb21ff1bdad72e3d301e790c0c487e4a86e8c9f69347/coverage-7.15.4-cp313-cp313-win_amd64.whl", hash = "sha256:2ff8f5e9b8f7a94f0c11c45631eee103dbcb7d63274edd12c56efe1be690b3b4", size = 225148, upload-time = "2026-08-06T13:48:23.376Z" }, + { url = "https://files.pythonhosted.org/packages/9e/83/3f4a69957f48ae7a0aba76c34743f88963d607b19e03f3f8e66f91cae0f9/coverage-7.15.4-cp313-cp313-win_arm64.whl", hash = "sha256:6e0a8a5083b096487d6cfced94cdd514d8f5db6f113610fb36c0620edb1028cf", size = 224536, upload-time = "2026-08-06T13:48:25.117Z" }, + { url = "https://files.pythonhosted.org/packages/ea/ac/748cf29eeb2d6be34a3176ce26a4f49e38085ee08e8935f05f6f26ed7e0f/coverage-7.15.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:770e9325ab5ea6d56f77e59b29ecfe0ac20b57a82a601876f90494a4dda0386f", size = 222608, upload-time = "2026-08-06T13:48:26.806Z" }, + { url = "https://files.pythonhosted.org/packages/0b/02/1abbf5c984677b0aa439cdacaccbf38d248939d8ef8fe1cc7a50d73edb77/coverage-7.15.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:d12b33a3a50a1676b7784dc8d00a0c6d66a9f2add4b85a041c19b6a7e53ef23c", size = 222940, upload-time = "2026-08-06T13:48:28.432Z" }, + { url = "https://files.pythonhosted.org/packages/eb/e1/ff8f9f53d9fcf586125b55d0b1f04ec1c14955fee41e83d5814bee141bb5/coverage-7.15.4-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5669c8378ebde86f5def7a25d29586631b58acc27ffde04399f678f3dfc6e082", size = 253985, upload-time = "2026-08-06T13:48:29.995Z" }, + { url = "https://files.pythonhosted.org/packages/a1/26/595759762e514e81be1d7d01ed03444303bcd152226a6529998d253f9201/coverage-7.15.4-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ff97a14362eef486483ed44042ca2027ea257df6ff768e62358ee0c9776925ac", size = 256492, upload-time = "2026-08-06T13:48:31.634Z" }, + { url = "https://files.pythonhosted.org/packages/24/68/b79aabac54d482be23b5fcdd4f4662bff24a78edc4ee29201726929936d5/coverage-7.15.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5a325e815318638aed1655d9c06e6d7c2d3d46c09231ce988070428a8762d734", size = 257837, upload-time = "2026-08-06T13:48:33.186Z" }, + { url = "https://files.pythonhosted.org/packages/09/0f/bf7f297885a5bf6fd71e5782404e0ff059ca09e8711ceb3a08544abde45a/coverage-7.15.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:474223409d88eb20d2d6a0d37ea60e8647a65a90cc008dc1f0410af5f64f1e0d", size = 260152, upload-time = "2026-08-06T13:48:34.75Z" }, + { url = "https://files.pythonhosted.org/packages/fd/f1/296744e854ff8368542343457414380465e9ceefb9192342feb9d3bc461d/coverage-7.15.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7f2f62ae3cd189dd2e13aece758c57b3eecbd27be070dbd4cbd10936049e5dbf", size = 253978, upload-time = "2026-08-06T13:48:36.434Z" }, + { url = "https://files.pythonhosted.org/packages/55/b0/bbdb2e9057493e66220a2e149ca2d301ba0e3a58a83bd6b90de9826d16f3/coverage-7.15.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:39ece820e29e0a2ba34b3ecb3be83c27e997eed8926f2ba6fe7ce7a0bda5843b", size = 255846, upload-time = "2026-08-06T13:48:38.317Z" }, + { url = "https://files.pythonhosted.org/packages/96/e4/38015b2b6d21258713bd17e76b59d033b191efb5703589cffd037dfbca20/coverage-7.15.4-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:f21b56dcace11dfe013014201f577dcd592b2a9b72182d930361b47cf6f73f25", size = 253808, upload-time = "2026-08-06T13:48:39.993Z" }, + { url = "https://files.pythonhosted.org/packages/0b/64/0d515c1e60ee6fbfd1a0e79c07cd87d388a233b7adc37758735677203808/coverage-7.15.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:93a3a0b662abcc10c73a47cbc72cd60f63618d6989fb2d1286e50eacd974f303", size = 258081, upload-time = "2026-08-06T13:48:41.971Z" }, + { url = "https://files.pythonhosted.org/packages/91/71/04d9e7a3642146c6351338aef4ef85ab11dbbb54744c13245caba1aad1c0/coverage-7.15.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:141fae2cabf5569b782c10afc4c850ce10f618c13f8db54765cba99cc839da1f", size = 253624, upload-time = "2026-08-06T13:48:43.731Z" }, + { url = "https://files.pythonhosted.org/packages/b4/a7/6c28b74c81ebff66987b0e2522ba5cffa3e90b0c33cb6a2eb264d4ee8cf1/coverage-7.15.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:81294c7e6ab30c5f74c0353b11b2fd6320e72d9bee6ac73b357caa8b916323a5", size = 255280, upload-time = "2026-08-06T13:48:45.58Z" }, + { url = "https://files.pythonhosted.org/packages/52/af/bc19996a7014b98d7bbb0f0939453c67074af65784a3aa16a789a07381fa/coverage-7.15.4-cp314-cp314-win32.whl", hash = "sha256:7bbd7d6418e0dab31a206af5203bd43ae36edb8e7fba1940b055d3e9249290d7", size = 224768, upload-time = "2026-08-06T13:48:47.525Z" }, + { url = "https://files.pythonhosted.org/packages/ee/90/219484e476d6e101ba0a444852579e05f5b75c37c611a42ed1190f73ef62/coverage-7.15.4-cp314-cp314-win_amd64.whl", hash = "sha256:f0204ed122758782970526057093f448051a39db9d810d4e344bb87a3546f425", size = 225259, upload-time = "2026-08-06T13:48:49.513Z" }, + { url = "https://files.pythonhosted.org/packages/b7/66/fa77daf4e383e5f776dac62c2409b6af81910ae6fe326bd5170dba74cc63/coverage-7.15.4-cp314-cp314-win_arm64.whl", hash = "sha256:9e71e7bc71c686a123347ae47a0de33a175e797a85bb57b791492adf4eec8ed8", size = 224684, upload-time = "2026-08-06T13:48:51.235Z" }, + { url = "https://files.pythonhosted.org/packages/58/5b/f03bf0ce362bbf3f785fa5219620d00778d4ac6fc9e407734828e9c672f6/coverage-7.15.4-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:7c922735321eef3f87c280a3d39afff6b646723a2880b862cda4ac7a093b8aa8", size = 223338, upload-time = "2026-08-06T13:48:52.896Z" }, + { url = "https://files.pythonhosted.org/packages/0f/76/e77d0ae22501831cc9f92193e8a957a5caa1dd177f90a6d1d9b106242d92/coverage-7.15.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f41c17c4668a655ce96d090d8d5ffdc24ef64b5a02f9753884d08483e8a4a41a", size = 223609, upload-time = "2026-08-06T13:48:54.688Z" }, + { url = "https://files.pythonhosted.org/packages/82/1a/b1f089da8d38ac612fa2dd6dc7f4a1a7657d12f3e261d2996edd3a838d0b/coverage-7.15.4-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:46822e9b6ff1c6a72b518c162c44a8f45a61a1d609c51084bf5b16c023c5037b", size = 264970, upload-time = "2026-08-06T13:48:56.403Z" }, + { url = "https://files.pythonhosted.org/packages/bf/31/e66d98d6e9c7fcc88470f1e234eaf6b1950dc0dfbf797f7282c1c861da24/coverage-7.15.4-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:3d6f4955b73b5445271379a59e3792b0d978f42d4a01e0cf7a67d9c33a3bb0a5", size = 267088, upload-time = "2026-08-06T13:48:58.41Z" }, + { url = "https://files.pythonhosted.org/packages/59/a1/ae94eb2c541add426378408379f233591e069040b1e2cdb33df9498a0682/coverage-7.15.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3fc9e047706fb4a9abb54f719d3aa643e80e5bb3818182c40aee01ac0f0247ba", size = 269508, upload-time = "2026-08-06T13:49:00.42Z" }, + { url = "https://files.pythonhosted.org/packages/9c/c7/88a10694a1c6a213569766aba9f25847b28155d4ac731b13226db216356d/coverage-7.15.4-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:05e491d4f3165d62d4f5c8fd48dfeabf2ae8f42cbbd484319af33ea851b78982", size = 270629, upload-time = "2026-08-06T13:49:02.234Z" }, + { url = "https://files.pythonhosted.org/packages/b3/34/d8b8232e5e55169933b59aabcef2fedfa4b9d8897361bb80fcbda146505f/coverage-7.15.4-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:226c66e80ec0598d3b9b4874123df167ccca342aca8714f77cac6829688ee09c", size = 264043, upload-time = "2026-08-06T13:49:04.102Z" }, + { url = "https://files.pythonhosted.org/packages/7e/35/58b009dbf8c471c7224716478b9fed4a7e1af15320e1ed41660978504663/coverage-7.15.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ac41cc14bebda0dbfb0628036b7f75706935c95bcc07fefe9a0f93614aa60a57", size = 266963, upload-time = "2026-08-06T13:49:05.821Z" }, + { url = "https://files.pythonhosted.org/packages/62/aa/57fbda1b42c892968273c56b6ee9dc0f1310850859230a507bc7873b1f65/coverage-7.15.4-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:8af623e5cd92080acddd02b38f2f406a2c3a0893c38950b211890361448fbf26", size = 264569, upload-time = "2026-08-06T13:49:07.706Z" }, + { url = "https://files.pythonhosted.org/packages/98/8a/360e6e7f24d477b7e889703af0afa878d15b6d4d8d2a822b2835c169a879/coverage-7.15.4-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:07545711d4f0f32852a18f18ad11f76f0109909d09e78b9008b4cfc67e829429", size = 268299, upload-time = "2026-08-06T13:49:09.587Z" }, + { url = "https://files.pythonhosted.org/packages/4e/89/6f701261aee21b6b5fa8f7872229406dc917e125069448292223bf213606/coverage-7.15.4-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:a0865421cfdc53654b342d515e5a233187590882d20b95752150e53f65460017", size = 263413, upload-time = "2026-08-06T13:49:11.604Z" }, + { url = "https://files.pythonhosted.org/packages/3f/0f/6f04036edc260ed425af83e834f627fad48941ce97b50bfe6edd8b6fa623/coverage-7.15.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:460115e32ee40566476db5048f9bec1e842c127ad8e6f8be745aad3ac9cbc839", size = 265725, upload-time = "2026-08-06T13:49:13.38Z" }, + { url = "https://files.pythonhosted.org/packages/c4/ce/d19b5d4d5c49a7bfb925fd74310fee7d28bc99520ac3367ccbc54e662518/coverage-7.15.4-cp314-cp314t-win32.whl", hash = "sha256:cbde877ef9dd7baf272b9bfef2b8a25edd45d9170fc326951dd20eb480335e85", size = 225079, upload-time = "2026-08-06T13:49:15.265Z" }, + { url = "https://files.pythonhosted.org/packages/26/bb/7aa1b3b173faee0679037ca950bbbe1247273656697994d8d13f80f8d4b4/coverage-7.15.4-cp314-cp314t-win_amd64.whl", hash = "sha256:3da9e92d1c551fd7563833e9ade686efb0c4b7363ab7681a94283958c950bf5e", size = 225911, upload-time = "2026-08-06T13:49:17.279Z" }, + { url = "https://files.pythonhosted.org/packages/81/1c/4ea9e47426d80038d9222db3c4534cb6021a74b237d3ff97ffd33b6600dd/coverage-7.15.4-cp314-cp314t-win_arm64.whl", hash = "sha256:3a54f5a0d85050c73a38f6793090ee83974531e67fe5e57a1da9bee11398aa5e", size = 225219, upload-time = "2026-08-06T13:49:19.293Z" }, + { url = "https://files.pythonhosted.org/packages/2b/c4/dc5d2ac8f9142e7ec7de66e7bf0591db29d78955a040bd915870d9c0e657/coverage-7.15.4-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:2c9872e4d9dc5d3cf616bf4b382f5a00359305a5be666a3dd0b5cdb4e49597f9", size = 222604, upload-time = "2026-08-06T13:49:21.279Z" }, + { url = "https://files.pythonhosted.org/packages/70/39/33e63df81fe2ee100897451841c821467635923e58e37c6bd4b46dd8106c/coverage-7.15.4-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:e101dbb4b9b72f0cddd8cdc8c9c5b47f456766f5e0ac82dbfb75e5c55409b78a", size = 222944, upload-time = "2026-08-06T13:49:23.187Z" }, + { url = "https://files.pythonhosted.org/packages/99/1f/ef3ffb5557febc75a0d97aa459d0266d7d741110265121cc6d8539343d44/coverage-7.15.4-cp315-cp315-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7d1abebdb047729e852b9c77a00497dfbeb11eb3a117e037d7dbc3ac8e5f5c54", size = 254050, upload-time = "2026-08-06T13:49:25.008Z" }, + { url = "https://files.pythonhosted.org/packages/6f/f5/1f0f6f77698c3601ca0ae7431e34b24c62ca2f06fecb23b73ed1f651d2be/coverage-7.15.4-cp315-cp315-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d28a4a899354d0ea6214cc59b4fa19eefbce1b9ff1688ab579acf49e894bd3fb", size = 256967, upload-time = "2026-08-06T13:49:26.896Z" }, + { url = "https://files.pythonhosted.org/packages/03/7a/2ed9bed79925f4367c83c77f66a89e5ca7229c288d2d19ad5f36d1ca0070/coverage-7.15.4-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ffb3c2aacea411cc7e1d27712490c11108e2de1d39019ae32915493a59a8b9ed", size = 258587, upload-time = "2026-08-06T13:49:28.692Z" }, + { url = "https://files.pythonhosted.org/packages/45/8c/fa34044f71b7cc4ecb6da9c2408770959b0591fa9b5fb6fb6bca38f94298/coverage-7.15.4-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a9447978a92f405d301123cfd39ff49895490efb769a758fe2734c7f631bf8ce", size = 260785, upload-time = "2026-08-06T13:49:30.472Z" }, + { url = "https://files.pythonhosted.org/packages/4f/54/d5727ce36b4524a7394ab9f5f1df378e1f23affcdab01037dc8655185cc7/coverage-7.15.4-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:050467a7983b8e2fe7dd41a78bb30c3e7f8c0b8cafda14b1c46f8b5e3cf2dd3c", size = 254545, upload-time = "2026-08-06T13:49:32.271Z" }, + { url = "https://files.pythonhosted.org/packages/dc/e6/6e3783e576719590194bdffb6dd6d85490801785b7c331e35a245d8cb8b5/coverage-7.15.4-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:d003b7a5708ddad5c206c79607a6b92abb6fc13c57d99d8a4468cc03a2941ced", size = 256682, upload-time = "2026-08-06T13:49:34.089Z" }, + { url = "https://files.pythonhosted.org/packages/dc/f2/bacdbde18b69ed2de424fcf64d9fb0a4913753d4f0eca8bae9daad69f4bd/coverage-7.15.4-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:c38efe30fd74e5c19e9433f11fb1f5dc9c6522770971b7c6145bbaa413dc8800", size = 254560, upload-time = "2026-08-06T13:49:36.052Z" }, + { url = "https://files.pythonhosted.org/packages/6c/a3/1fb927196e3477c1b48831169ab58ba08f451ba87ae311ff1de68b26a616/coverage-7.15.4-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:1f4f826d70f772ab8b0c052329580d7fe8b8abd191e4ce0c8f81aec6614665d3", size = 258792, upload-time = "2026-08-06T13:49:38.01Z" }, + { url = "https://files.pythonhosted.org/packages/41/58/30d4c149c69053de0edfe325614c1d28d508f62b1783e0e4a234d2e49136/coverage-7.15.4-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:4a4bf917c9953f57c957be31c1cd504e3bd2f34d4a352b9d391a3025336f6768", size = 253968, upload-time = "2026-08-06T13:49:39.934Z" }, + { url = "https://files.pythonhosted.org/packages/89/e4/77f639371b918aad30dda4051f95404b43578f7f2e2f87ba73e02ed1ff37/coverage-7.15.4-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:1c9bf40ebef178a45192c75c4964760bb261b0e6ad725da5fc4c93f674f19753", size = 255893, upload-time = "2026-08-06T13:49:41.825Z" }, + { url = "https://files.pythonhosted.org/packages/5c/62/13be29b3ddab35f14c87967a4820a05106d2a3eccb4fa4ff550bf30b75e0/coverage-7.15.4-cp315-cp315-win32.whl", hash = "sha256:43619d04c3671792d2c4706ae8bf45e265dc87bbd4078189ef8b847ea1e74be2", size = 224768, upload-time = "2026-08-06T13:49:44.08Z" }, + { url = "https://files.pythonhosted.org/packages/a1/70/af0c6be0f964af6954f6b74bc109b0dbca02824696d2520fb17fe1ab06e3/coverage-7.15.4-cp315-cp315-win_amd64.whl", hash = "sha256:be619439dbcd31a2eab10b32de9fff62c26ed4bab69dc32b8363fdaaa0882809", size = 225242, upload-time = "2026-08-06T13:49:45.899Z" }, + { url = "https://files.pythonhosted.org/packages/4f/2d/f3bd3aab899fc9efc18b53133ee68f5f98574ef480649b23e12962226387/coverage-7.15.4-cp315-cp315-win_arm64.whl", hash = "sha256:def597967dafc2e8d97c9097ea453c464e0bb8ed38f193a43070f10dc623bb6d", size = 224674, upload-time = "2026-08-06T13:49:48.322Z" }, + { url = "https://files.pythonhosted.org/packages/f5/ca/f69251cd63eabc6438321aea22148754cce758a26bde07dd490e3fe7cfc5/coverage-7.15.4-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:c7dbc748ac8a1e3e59a2b28bea47675e6e778081dbbf081bde0d75def2fcbe1d", size = 223333, upload-time = "2026-08-06T13:49:50.293Z" }, + { url = "https://files.pythonhosted.org/packages/a7/a7/037b53b2885b0d8447064432491a4d5a1014cd9f97a594d53acd0c04541a/coverage-7.15.4-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:2413074a5ecbb61a01a7888fc72db0ca324d13588c5b38bc0dd8564cdcdfea26", size = 223630, upload-time = "2026-08-06T13:49:52.637Z" }, + { url = "https://files.pythonhosted.org/packages/80/4f/152b8a4779ae90da11bb24f7467df8a59f0be48a5c52acb856325ca48289/coverage-7.15.4-cp315-cp315t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:4e6f6f632b7b2f714bf7a1346e8f97b650ee71f3c298aaad42a2ab60f0f07645", size = 264489, upload-time = "2026-08-06T13:49:54.52Z" }, + { url = "https://files.pythonhosted.org/packages/10/2d/84b4b9e0e1dd6528a51920ff7031f35b789382e467a28ec6a5a578cb8812/coverage-7.15.4-cp315-cp315t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8df457da2249d3c75ca2e5e835d59c725abfe92d27fdff6cd99eed85b51d5e9a", size = 267567, upload-time = "2026-08-06T13:49:56.721Z" }, + { url = "https://files.pythonhosted.org/packages/53/fc/ba01cc25299f9f8a2c8b02d3b28c53f3543d9fbfbe4e74fa2760b48f163e/coverage-7.15.4-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:050f66a08805acb5b8a23c6d4a517b1ecf82c08e81ed0e4bd727df065e5c6624", size = 270123, upload-time = "2026-08-06T13:49:58.736Z" }, + { url = "https://files.pythonhosted.org/packages/cf/d0/db2647cbf40b14f8c308f94ff7bf89c06d564e59f396906edf50086ec788/coverage-7.15.4-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1587fb771d1ccceef708fdde1e5af8c7ed24b486b61d13a321acb7d8145390aa", size = 271107, upload-time = "2026-08-06T13:50:00.811Z" }, + { url = "https://files.pythonhosted.org/packages/70/ff/4d2d17924552c458bb4f77dd631f0e3bc92fbbdf2d2d916cd4b33bbfd5b1/coverage-7.15.4-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8b4f1c3a69ca580f3fbd6b2046915f536d7f586874f25c1bb23add2a3c88d50f", size = 264955, upload-time = "2026-08-06T13:50:03.023Z" }, + { url = "https://files.pythonhosted.org/packages/ee/de/dc010c7a3691f396d93bbc26bfcafa1c2a3a351cd520470f15faf5795bd5/coverage-7.15.4-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:ffb58d7eff5b7f6ecc6fa21d6288ab7f968a212cb67d682c269c09b9eba3b66f", size = 267949, upload-time = "2026-08-06T13:50:05.557Z" }, + { url = "https://files.pythonhosted.org/packages/78/ea/dc96a11375e83c045c2f7c61fb6918277cfe9401db7c0f7b1d111a84b2e5/coverage-7.15.4-cp315-cp315t-musllinux_1_2_i686.whl", hash = "sha256:d9df165544774574ee004b953023d1bebada1894a80b1052a43d798b0f676e67", size = 264421, upload-time = "2026-08-06T13:50:07.612Z" }, + { url = "https://files.pythonhosted.org/packages/c8/86/b77131a0f9503ce461cd577076147d7a9040f0c5dda772686f729e2cc9cb/coverage-7.15.4-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:f9de0a24a4079b53e523b5c5e2c5945ec251ab486652659955187cf255a259bc", size = 269121, upload-time = "2026-08-06T13:50:09.58Z" }, + { url = "https://files.pythonhosted.org/packages/24/24/944bc35007862955e7ebf05754e645419dcf5d7526c52735cfa2715e8ebf/coverage-7.15.4-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:150089274bdc9f940628552cb92844e0223c987f1902ab8efe9f45a2ec758d88", size = 264565, upload-time = "2026-08-06T13:50:11.722Z" }, + { url = "https://files.pythonhosted.org/packages/c7/cc/a3bb9f93e7e740659163e2ea584f8196ddcd2c456a5dbe15f6c50105fec1/coverage-7.15.4-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:a58a94fed5da6997d258e8f7668c1e195fbd04a691d781b7558f1e468f9e68bc", size = 266522, upload-time = "2026-08-06T13:50:13.786Z" }, + { url = "https://files.pythonhosted.org/packages/49/dd/e0e40f3560d878d888c580698ff5ad1179f5e1c3ac949684ef66b41a3817/coverage-7.15.4-cp315-cp315t-win32.whl", hash = "sha256:ebd5a6d8466ff30836572f3ba2cae8a5e8f85029b1c6d5e2ed338dc472a5166a", size = 225068, upload-time = "2026-08-06T13:50:15.825Z" }, + { url = "https://files.pythonhosted.org/packages/c6/7e/37732ea80eebc30e976e4cdab15c190bc42d96959a42e38ddf6f8c60468f/coverage-7.15.4-cp315-cp315t-win_amd64.whl", hash = "sha256:288bde2a2d7ab6b6c2d7252fcde8b524387f2d970bdba9658fc6f8bbcaef0f9b", size = 225895, upload-time = "2026-08-06T13:50:17.928Z" }, + { url = "https://files.pythonhosted.org/packages/c6/08/1e00f7923eaaba45fb3d51dd794125fc766304b1df264f3a9c6557bfb30e/coverage-7.15.4-cp315-cp315t-win_arm64.whl", hash = "sha256:68be5e1de60ff13c9095bbec0e5a7fa45b33b101752215b91345ea1f61c4a278", size = 225213, upload-time = "2026-08-06T13:50:19.981Z" }, + { url = "https://files.pythonhosted.org/packages/b4/d9/e70c286c979378f061d8266e279b686ab0b0b688e1fe0af864684f23a77d/coverage-7.15.4-py3-none-any.whl", hash = "sha256:964730a1e9de9c0cf11be6a1a3c79ce419c34882842abd256086ba4698705e84", size = 214332, upload-time = "2026-08-06T13:50:22.192Z" }, ] [[package]] @@ -335,7 +365,7 @@ dev = [ [package.metadata] requires-dist = [ - { name = "coverage", marker = "extra == 'dev'", specifier = "==7.15.2" }, + { name = "coverage", marker = "extra == 'dev'", specifier = "==7.15.4" }, { name = "cryptography", specifier = "==50.0.0" }, { name = "fastapi", specifier = "==0.141.1" }, { name = "httpx", specifier = "==0.28.1" }, @@ -345,7 +375,7 @@ requires-dist = [ { name = "pytest", marker = "extra == 'dev'", specifier = "==9.1.1" }, { name = "pyyaml", specifier = "==6.0.3" }, { name = "ruff", marker = "extra == 'dev'", specifier = "==0.16.1" }, - { name = "setuptools", marker = "extra == 'dev'", specifier = "==83.0.0" }, + { name = "setuptools", marker = "extra == 'dev'", specifier = "==84.0.0" }, { name = "uvicorn", specifier = "==0.52.1" }, ] provides-extras = ["dev"] @@ -726,11 +756,11 @@ wheels = [ [[package]] name = "setuptools" -version = "83.0.0" +version = "84.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/34/26/f5d29e25ffdb535afef2d35cdb55b325298f96debd670da4c325e08d70f4/setuptools-83.0.0.tar.gz", hash = "sha256:025bccbbf0fa05b6192bc64ae1e7b16e001fd6d6d4d5de03c97b1c1ade523bef", size = 1154254, upload-time = "2026-07-04T15:31:22.699Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6d/44/f5da03a8ef95d369145c5bb53050e7877c9f3d312e128605fd9504829143/setuptools-84.0.0.tar.gz", hash = "sha256:f4695c21257f0d9b537ec2692c941d02ee143b7cc1276941349a546573b2ef73", size = 1168449, upload-time = "2026-08-08T18:27:58.365Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5d/40/e1e72872c6354b306daef1703549e8e83b4d43cfea356311bf722a043752/setuptools-83.0.0-py3-none-any.whl", hash = "sha256:29b23c360f22f414dc7336bb39178cc7bcbf6021ed2733cde173f09dba19abb3", size = 1008090, upload-time = "2026-07-04T15:31:20.885Z" }, + { url = "https://files.pythonhosted.org/packages/95/9c/c510029fc6ef33a6275cd2c5d3cecd6613dfd6aa401d57c54f1c18852ccf/setuptools-84.0.0-py3-none-any.whl", hash = "sha256:51a52592b3b99e102b609654876bd65f19f999935166d1352678931132b0c670", size = 818216, upload-time = "2026-08-08T18:27:56.719Z" }, ] [[package]] From 2f89ea33a17d2569e8f7e8a3c1b808aad845bb54 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 21 Aug 2026 07:18:25 +0900 Subject: [PATCH 3/7] docs: keep ADR expansion free of dependency changes --- services/account_unification/uv.lock | 200 ++++++++++++--------------- 1 file changed, 85 insertions(+), 115 deletions(-) diff --git a/services/account_unification/uv.lock b/services/account_unification/uv.lock index bf57988..1ed80ca 100644 --- a/services/account_unification/uv.lock +++ b/services/account_unification/uv.lock @@ -172,116 +172,86 @@ wheels = [ [[package]] name = "coverage" -version = "7.15.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/be/c3/4f2195f512fb172aa425a8803a874b2baa9ba7f80ff7b6080998761fc701/coverage-7.15.4.tar.gz", hash = "sha256:0548198fff07ccf4faf469520bce1c2eceb1ce3e62891921138dec10907f9d00", size = 936952, upload-time = "2026-08-06T13:50:24.442Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/66/edcec7d7a0b524aa8923e22925fde6fe50ce005a113dca13ae1581455c4c/coverage-7.15.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bbac5abad70df71019988f83f26ac7092ff2642975def4429e98dc7585ef3490", size = 222367, upload-time = "2026-08-06T13:47:15.578Z" }, - { url = "https://files.pythonhosted.org/packages/e6/c6/ab8de429e2e8548faf58ec7e1674a4ce00414b4113942d3fe87109cf0f68/coverage-7.15.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:357a173465c7ce028d07a95cc2b63b5bf59f50ecdd5ad75c5cbb78ada984048e", size = 222874, upload-time = "2026-08-06T13:47:16.961Z" }, - { url = "https://files.pythonhosted.org/packages/be/c4/3b7b49587e8a6b9af79b3eb468d443d6042b6d65b47aa26586846a0d6566/coverage-7.15.4-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:21b803935e2efc3acebe9697197a294fccf5dc4e5382bd6369542ff7a7d2a1d7", size = 253287, upload-time = "2026-08-06T13:47:18.291Z" }, - { url = "https://files.pythonhosted.org/packages/fb/65/ec03b743a2a229c72cc1eff3e57be9d3564e9c6b4d5aba2d70744a3fc0d8/coverage-7.15.4-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7a2b580774a4786c1053157c0165e04476e03ff293993d7c148eee784a94bae6", size = 255199, upload-time = "2026-08-06T13:47:19.765Z" }, - { url = "https://files.pythonhosted.org/packages/41/4b/5163729e4b6582d61975cfd3ccab45b4ec53e21cf156d9941cb025188468/coverage-7.15.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a9464451c4efffe8d47ace5a540b10b0dc10e879066290f8600872b7f54a419d", size = 257308, upload-time = "2026-08-06T13:47:21.206Z" }, - { url = "https://files.pythonhosted.org/packages/86/08/2167a0f08fb87d702fa423a48578a32865464b7c9e1db3911ad7812ab414/coverage-7.15.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:de602f34123c2f4af1c1869c6dbbbd60da6d5983bf01937367295d135cccbfce", size = 259268, upload-time = "2026-08-06T13:47:22.503Z" }, - { url = "https://files.pythonhosted.org/packages/1e/e5/68eebae3053dbd48508edea559c21b23fbdf3460784f91370c83a86a6acd/coverage-7.15.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6879ded16a27f3eeca19b900c147e81616e7054db451471a611b2755ee5249f7", size = 253392, upload-time = "2026-08-06T13:47:23.88Z" }, - { url = "https://files.pythonhosted.org/packages/1a/46/fd4ced40a2b691c774e515c9b69500bfa64c7960b67fcee4b2f6fad97fc3/coverage-7.15.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:986be58c3ab54aae8d3496a6225eea74f760fdbe739b38bd442c7e8d133aa53b", size = 255001, upload-time = "2026-08-06T13:47:25.469Z" }, - { url = "https://files.pythonhosted.org/packages/53/25/ae2e5fa710bb6957a9aadeb9e3598d3b3e4af6587ce857ad42e8639a3f30/coverage-7.15.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:c6103639613fe6c1e989082948419bc77a2d26b6c825c99d7fad25f7d3d87afc", size = 253061, upload-time = "2026-08-06T13:47:26.845Z" }, - { url = "https://files.pythonhosted.org/packages/d7/31/67ddc0365db2c6e93ac8580bc4bbc50f65273262f973f63ebcdbc15c0495/coverage-7.15.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:d3af93dddb5659276c63bc16ac6466ac2033a70ca816097bbc06345b8ccdf571", size = 256831, upload-time = "2026-08-06T13:47:28.217Z" }, - { url = "https://files.pythonhosted.org/packages/f6/78/82b8fd18f57fb13f12d98fe874995bb2c4f9f17be8aff762c426323fdb96/coverage-7.15.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:b10075e5421d04265766a6d1dac809bbeb8a946fbb23c8f82c227409b2190719", size = 252781, upload-time = "2026-08-06T13:47:29.712Z" }, - { url = "https://files.pythonhosted.org/packages/0a/eb/6c74ef4dd12b252e573c49bdef9e2ac265bf3dbb79b8d7feb3266e084e9e/coverage-7.15.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a67a9f78b2942d87ba8ce3059c642164d2aedd65337377fb52fe9803656bc5c7", size = 253692, upload-time = "2026-08-06T13:47:31.192Z" }, - { url = "https://files.pythonhosted.org/packages/5a/66/eb9aed1c3fd2d36ee00eb173f434b14fa607fc056739c9a89ff4244010ea/coverage-7.15.4-cp311-cp311-win32.whl", hash = "sha256:69484d1aca26e322e1c3ce03f09341e84524ababad2d7202161738d83cc9f82e", size = 224461, upload-time = "2026-08-06T13:47:32.572Z" }, - { url = "https://files.pythonhosted.org/packages/e2/6d/81fa4161dfb3ed9d74e40d58647eff83a56b7612e78352581280fce2f477/coverage-7.15.4-cp311-cp311-win_amd64.whl", hash = "sha256:63fd6fcd1dd6e158f7eb78606e72933b3f6d01e7b747f99c6c12d764307a0fdc", size = 224937, upload-time = "2026-08-06T13:47:34.205Z" }, - { url = "https://files.pythonhosted.org/packages/5b/c1/d8dacf683c6cad3cf85ce68fd3774a6774ec402128822fdfaed920f11e6a/coverage-7.15.4-cp311-cp311-win_arm64.whl", hash = "sha256:ea82116c9893fa89e929b7f197ee5a1950a76e91cc5c85ba503fc02379d04890", size = 224479, upload-time = "2026-08-06T13:47:36.118Z" }, - { url = "https://files.pythonhosted.org/packages/1d/48/bc8d4ba7b37551a767bd863f15b3f80182b271c2f55975356f5f7dbe94c2/coverage-7.15.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d4fedd1f7f428f9fe83b1ead5e7cc87a43427be31aadafbac3ac0636dc7abb22", size = 222543, upload-time = "2026-08-06T13:47:37.562Z" }, - { url = "https://files.pythonhosted.org/packages/20/dd/88d6f83f1fffc974a3691a34a97951c5b12df7512a6782c5963883cbc058/coverage-7.15.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:37e2f0cdf58e2e1fed4e4d5a8f8786ae2f7eb80b478016876667dc4a01d60a97", size = 222905, upload-time = "2026-08-06T13:47:38.927Z" }, - { url = "https://files.pythonhosted.org/packages/bd/5c/54ee0d4748585bb0acab9891cd8d92f2d3593165b4e59fc9de113bfb3140/coverage-7.15.4-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:fb55d0e70bb15f2e81477613627286581414693d74ac7963c93a790dd453ca9d", size = 254407, upload-time = "2026-08-06T13:47:40.488Z" }, - { url = "https://files.pythonhosted.org/packages/8c/3f/f0642a372f494bd0d7dad3b497083b910194a5f1c88be2c94fef707c3b59/coverage-7.15.4-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:899b9da30f3c6c336566e3707495bb23e8302d39d862f01fa78c48b99b9437e2", size = 257145, upload-time = "2026-08-06T13:47:41.931Z" }, - { url = "https://files.pythonhosted.org/packages/71/17/8b46d0ed68251016002ec972c8fc0119961a765d0984cafb8bf317c43758/coverage-7.15.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d15715e8c46552827e5e4f30a35575a2dbcad14454cf3284c54483946bd16931", size = 258257, upload-time = "2026-08-06T13:47:43.527Z" }, - { url = "https://files.pythonhosted.org/packages/30/b8/8498a0e72d0adbe15477dd07463d2b3bb2c9f6a4815e8589e50939e2c3ae/coverage-7.15.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:002a438859f7b430bc99afeaf01a6d187dad1d0dc907b64cdeffc632a5db8fd8", size = 260517, upload-time = "2026-08-06T13:47:45.121Z" }, - { url = "https://files.pythonhosted.org/packages/41/e1/7dce19c3bdb1e3dd63e769508216500edad81bd5f69a26d724e32aceaf78/coverage-7.15.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e4193a04b518f7968f3099755f5509ee7cccc6dc2b92a6b14841934d22e222c9", size = 254785, upload-time = "2026-08-06T13:47:46.541Z" }, - { url = "https://files.pythonhosted.org/packages/dd/b1/e1494703c675a2561723cd9b89f45c9168782c31280c611b1f767851e57c/coverage-7.15.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e98dcc55d572b38e69d117da7e8e8efb8500f1f5eaf81ecd460a63220790b839", size = 256176, upload-time = "2026-08-06T13:47:48.155Z" }, - { url = "https://files.pythonhosted.org/packages/73/76/a5629d270fb638a43a4b10466f51e2f49d532c1aa4da2913cbbb150bbe0a/coverage-7.15.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:af6c538498ce66c10d3fd541c2a8d5b03da5850355add34e6cba564210cb9e72", size = 254321, upload-time = "2026-08-06T13:47:49.757Z" }, - { url = "https://files.pythonhosted.org/packages/ff/4f/9c44447218435d5766b911534f9d798144a5560f85e9a54ebe5f3f5d19f9/coverage-7.15.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1d10025d96ea89fc2f73714dbc4cbd433fe012c1ac9e23f895d7728b238b6e52", size = 258390, upload-time = "2026-08-06T13:47:51.248Z" }, - { url = "https://files.pythonhosted.org/packages/de/36/c1e127616fb3fa18a9ff71e76c417f2fd7424332a4870015ac224ef4c039/coverage-7.15.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d802e1947603162ded419bff83ac7489820355d2b856dfb09206574e3a37ac0c", size = 253894, upload-time = "2026-08-06T13:47:52.816Z" }, - { url = "https://files.pythonhosted.org/packages/e9/b9/fdb92c8ae7a8bb9b850cc253b7b3b9c8526f68130002048b5671cd510d09/coverage-7.15.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c2de40895718f91951b86712b4c5b694acaf9a0a49be13874896f599a1eed3f4", size = 255763, upload-time = "2026-08-06T13:47:54.296Z" }, - { url = "https://files.pythonhosted.org/packages/6f/c0/a7d51b2587c7bdb76e71b0896d2565bf7d60436b5122fc83e511adb1f7cd/coverage-7.15.4-cp312-cp312-win32.whl", hash = "sha256:5c3431b2161279b7db5c2a1aa58ae02e5cb8c3c42d93a5094be3f5537bd5b11b", size = 224597, upload-time = "2026-08-06T13:47:56.074Z" }, - { url = "https://files.pythonhosted.org/packages/49/b9/5c5f80cc55f5acaaca6dee677626bfcec8c87204a7809b438b08e84f4571/coverage-7.15.4-cp312-cp312-win_amd64.whl", hash = "sha256:6befeab5fb2b51c958ca4ac6c5d141a1e8240f4f76e46350f1911963deda49cd", size = 225135, upload-time = "2026-08-06T13:47:57.52Z" }, - { url = "https://files.pythonhosted.org/packages/47/e4/2a4561f89ff6bf7c925c287d0f2cce8bdf139c3a33735c87e3203401cf94/coverage-7.15.4-cp312-cp312-win_arm64.whl", hash = "sha256:67bc345491ab55b837277d76f5775d057e8c7f1ac44d890d8c2c82adde258c6f", size = 224515, upload-time = "2026-08-06T13:47:58.977Z" }, - { url = "https://files.pythonhosted.org/packages/f1/84/651a9310859673aaa3b3203f1aa1641ca60fcf2494683e1c9474c7172780/coverage-7.15.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c705b28feb2775dc82a25f1d473a370bc37ff93f5177f4e29ce2425f560f6921", size = 222565, upload-time = "2026-08-06T13:48:00.796Z" }, - { url = "https://files.pythonhosted.org/packages/82/f9/4dcf700137e8af550670f4d74d1b63828ce93e1e2b05e5f10710eb2ea987/coverage-7.15.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3ff205ab5e3ecc670f6a4dd19d9cbf12ede53dd41cfc1e15716ec961ea6d314e", size = 222936, upload-time = "2026-08-06T13:48:02.391Z" }, - { url = "https://files.pythonhosted.org/packages/07/4a/612ff1e780b3fbfd637486f542f84adc5503873d8b5d279dec1ffeef9414/coverage-7.15.4-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5172326e861a38b48b48befca15e0f477a26b283337a33a739c8fed229934e36", size = 253926, upload-time = "2026-08-06T13:48:04.382Z" }, - { url = "https://files.pythonhosted.org/packages/b0/04/d1cff1c2ead4708a6a79c01d3736b6a25bd38a36678398f72a8dd33dfad9/coverage-7.15.4-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:12b59c90084e3234fb11184886bf4a40f4f16a8c8f867be2e087b81f8e8868d4", size = 256523, upload-time = "2026-08-06T13:48:05.996Z" }, - { url = "https://files.pythonhosted.org/packages/b9/80/d34e13fb4b293cbdb9665838cf5522077b8ad14ef947550631a4bced36a5/coverage-7.15.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:349062d66f00b40fa2c1c222438bad25fabf755631b5d82937fe985c8008615c", size = 257759, upload-time = "2026-08-06T13:48:08.036Z" }, - { url = "https://files.pythonhosted.org/packages/0f/e7/2c5fe7636fdb0732fe0f09f308a5b066864078b7fc61f6678e8478554f2e/coverage-7.15.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4256ced708e598e05209bc1a8ab4074e04a51dba4c62fb45926a229af675ace7", size = 259890, upload-time = "2026-08-06T13:48:09.834Z" }, - { url = "https://files.pythonhosted.org/packages/92/28/9689f0858dfff59c2ea688938ab9fa2925631235df67126a42b6c5c70ae1/coverage-7.15.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d80f974b20782d9612c8b4c9beeca867074c7cf4079d1419843fa25a26428b25", size = 254121, upload-time = "2026-08-06T13:48:11.459Z" }, - { url = "https://files.pythonhosted.org/packages/f9/e2/785077c230c157243eb5aa9a26c3be260ecd02001bead54a3cada3df8e03/coverage-7.15.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2e179f19bfe1d31f8eeeaa12990194d761c4f62f0759661000bca6cd8729f40b", size = 255891, upload-time = "2026-08-06T13:48:13.209Z" }, - { url = "https://files.pythonhosted.org/packages/d4/90/e20371b17b40f912f21305c2db2f30efa3de306f7320fc916804872c85a4/coverage-7.15.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:8bc16bb47b7679670eceff71d78bfb7d6e5b143f6c2cd117487ec7c75e0d4b78", size = 253859, upload-time = "2026-08-06T13:48:14.736Z" }, - { url = "https://files.pythonhosted.org/packages/05/49/25371987ee459a5f67c0427fb75c74f9358e65f2c71fe75bf41c1b6c5fcb/coverage-7.15.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:1cd685005cd2c4200adfc14cf39a603b9320efab3f18a8f7f156d20c9cc3345f", size = 258011, upload-time = "2026-08-06T13:48:16.464Z" }, - { url = "https://files.pythonhosted.org/packages/30/6e/32e67467f6154bf4f1c4f63b05acc5097cba4237d45bbeeea446b52e8ac1/coverage-7.15.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:337399ad2c93b3acd2a937627dae8b3e86b66707cd3d3e856347999aadf1ef8d", size = 253676, upload-time = "2026-08-06T13:48:18.493Z" }, - { url = "https://files.pythonhosted.org/packages/03/c1/8b24192e89286399765155251f99ee9f070a9d637109018ac23d99b99f6f/coverage-7.15.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:96e257121228ec5cd2bb919276e94ac11074471bc37d68dbae0e8308cce15fff", size = 255453, upload-time = "2026-08-06T13:48:20.057Z" }, - { url = "https://files.pythonhosted.org/packages/16/6f/8b41ebdf67c87854e17c035336a90f1cfbad0c14c2a584301be6ff148718/coverage-7.15.4-cp313-cp313-win32.whl", hash = "sha256:c65a9e0dfc6143491879da4e13b5e30f8be192055de508d737fb14601edbd22c", size = 224605, upload-time = "2026-08-06T13:48:21.655Z" }, - { url = "https://files.pythonhosted.org/packages/e0/e2/2946c7f0b42b152ecb21ff1bdad72e3d301e790c0c487e4a86e8c9f69347/coverage-7.15.4-cp313-cp313-win_amd64.whl", hash = "sha256:2ff8f5e9b8f7a94f0c11c45631eee103dbcb7d63274edd12c56efe1be690b3b4", size = 225148, upload-time = "2026-08-06T13:48:23.376Z" }, - { url = "https://files.pythonhosted.org/packages/9e/83/3f4a69957f48ae7a0aba76c34743f88963d607b19e03f3f8e66f91cae0f9/coverage-7.15.4-cp313-cp313-win_arm64.whl", hash = "sha256:6e0a8a5083b096487d6cfced94cdd514d8f5db6f113610fb36c0620edb1028cf", size = 224536, upload-time = "2026-08-06T13:48:25.117Z" }, - { url = "https://files.pythonhosted.org/packages/ea/ac/748cf29eeb2d6be34a3176ce26a4f49e38085ee08e8935f05f6f26ed7e0f/coverage-7.15.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:770e9325ab5ea6d56f77e59b29ecfe0ac20b57a82a601876f90494a4dda0386f", size = 222608, upload-time = "2026-08-06T13:48:26.806Z" }, - { url = "https://files.pythonhosted.org/packages/0b/02/1abbf5c984677b0aa439cdacaccbf38d248939d8ef8fe1cc7a50d73edb77/coverage-7.15.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:d12b33a3a50a1676b7784dc8d00a0c6d66a9f2add4b85a041c19b6a7e53ef23c", size = 222940, upload-time = "2026-08-06T13:48:28.432Z" }, - { url = "https://files.pythonhosted.org/packages/eb/e1/ff8f9f53d9fcf586125b55d0b1f04ec1c14955fee41e83d5814bee141bb5/coverage-7.15.4-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5669c8378ebde86f5def7a25d29586631b58acc27ffde04399f678f3dfc6e082", size = 253985, upload-time = "2026-08-06T13:48:29.995Z" }, - { url = "https://files.pythonhosted.org/packages/a1/26/595759762e514e81be1d7d01ed03444303bcd152226a6529998d253f9201/coverage-7.15.4-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ff97a14362eef486483ed44042ca2027ea257df6ff768e62358ee0c9776925ac", size = 256492, upload-time = "2026-08-06T13:48:31.634Z" }, - { url = "https://files.pythonhosted.org/packages/24/68/b79aabac54d482be23b5fcdd4f4662bff24a78edc4ee29201726929936d5/coverage-7.15.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5a325e815318638aed1655d9c06e6d7c2d3d46c09231ce988070428a8762d734", size = 257837, upload-time = "2026-08-06T13:48:33.186Z" }, - { url = "https://files.pythonhosted.org/packages/09/0f/bf7f297885a5bf6fd71e5782404e0ff059ca09e8711ceb3a08544abde45a/coverage-7.15.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:474223409d88eb20d2d6a0d37ea60e8647a65a90cc008dc1f0410af5f64f1e0d", size = 260152, upload-time = "2026-08-06T13:48:34.75Z" }, - { url = "https://files.pythonhosted.org/packages/fd/f1/296744e854ff8368542343457414380465e9ceefb9192342feb9d3bc461d/coverage-7.15.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7f2f62ae3cd189dd2e13aece758c57b3eecbd27be070dbd4cbd10936049e5dbf", size = 253978, upload-time = "2026-08-06T13:48:36.434Z" }, - { url = "https://files.pythonhosted.org/packages/55/b0/bbdb2e9057493e66220a2e149ca2d301ba0e3a58a83bd6b90de9826d16f3/coverage-7.15.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:39ece820e29e0a2ba34b3ecb3be83c27e997eed8926f2ba6fe7ce7a0bda5843b", size = 255846, upload-time = "2026-08-06T13:48:38.317Z" }, - { url = "https://files.pythonhosted.org/packages/96/e4/38015b2b6d21258713bd17e76b59d033b191efb5703589cffd037dfbca20/coverage-7.15.4-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:f21b56dcace11dfe013014201f577dcd592b2a9b72182d930361b47cf6f73f25", size = 253808, upload-time = "2026-08-06T13:48:39.993Z" }, - { url = "https://files.pythonhosted.org/packages/0b/64/0d515c1e60ee6fbfd1a0e79c07cd87d388a233b7adc37758735677203808/coverage-7.15.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:93a3a0b662abcc10c73a47cbc72cd60f63618d6989fb2d1286e50eacd974f303", size = 258081, upload-time = "2026-08-06T13:48:41.971Z" }, - { url = "https://files.pythonhosted.org/packages/91/71/04d9e7a3642146c6351338aef4ef85ab11dbbb54744c13245caba1aad1c0/coverage-7.15.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:141fae2cabf5569b782c10afc4c850ce10f618c13f8db54765cba99cc839da1f", size = 253624, upload-time = "2026-08-06T13:48:43.731Z" }, - { url = "https://files.pythonhosted.org/packages/b4/a7/6c28b74c81ebff66987b0e2522ba5cffa3e90b0c33cb6a2eb264d4ee8cf1/coverage-7.15.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:81294c7e6ab30c5f74c0353b11b2fd6320e72d9bee6ac73b357caa8b916323a5", size = 255280, upload-time = "2026-08-06T13:48:45.58Z" }, - { url = "https://files.pythonhosted.org/packages/52/af/bc19996a7014b98d7bbb0f0939453c67074af65784a3aa16a789a07381fa/coverage-7.15.4-cp314-cp314-win32.whl", hash = "sha256:7bbd7d6418e0dab31a206af5203bd43ae36edb8e7fba1940b055d3e9249290d7", size = 224768, upload-time = "2026-08-06T13:48:47.525Z" }, - { url = "https://files.pythonhosted.org/packages/ee/90/219484e476d6e101ba0a444852579e05f5b75c37c611a42ed1190f73ef62/coverage-7.15.4-cp314-cp314-win_amd64.whl", hash = "sha256:f0204ed122758782970526057093f448051a39db9d810d4e344bb87a3546f425", size = 225259, upload-time = "2026-08-06T13:48:49.513Z" }, - { url = "https://files.pythonhosted.org/packages/b7/66/fa77daf4e383e5f776dac62c2409b6af81910ae6fe326bd5170dba74cc63/coverage-7.15.4-cp314-cp314-win_arm64.whl", hash = "sha256:9e71e7bc71c686a123347ae47a0de33a175e797a85bb57b791492adf4eec8ed8", size = 224684, upload-time = "2026-08-06T13:48:51.235Z" }, - { url = "https://files.pythonhosted.org/packages/58/5b/f03bf0ce362bbf3f785fa5219620d00778d4ac6fc9e407734828e9c672f6/coverage-7.15.4-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:7c922735321eef3f87c280a3d39afff6b646723a2880b862cda4ac7a093b8aa8", size = 223338, upload-time = "2026-08-06T13:48:52.896Z" }, - { url = "https://files.pythonhosted.org/packages/0f/76/e77d0ae22501831cc9f92193e8a957a5caa1dd177f90a6d1d9b106242d92/coverage-7.15.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f41c17c4668a655ce96d090d8d5ffdc24ef64b5a02f9753884d08483e8a4a41a", size = 223609, upload-time = "2026-08-06T13:48:54.688Z" }, - { url = "https://files.pythonhosted.org/packages/82/1a/b1f089da8d38ac612fa2dd6dc7f4a1a7657d12f3e261d2996edd3a838d0b/coverage-7.15.4-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:46822e9b6ff1c6a72b518c162c44a8f45a61a1d609c51084bf5b16c023c5037b", size = 264970, upload-time = "2026-08-06T13:48:56.403Z" }, - { url = "https://files.pythonhosted.org/packages/bf/31/e66d98d6e9c7fcc88470f1e234eaf6b1950dc0dfbf797f7282c1c861da24/coverage-7.15.4-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:3d6f4955b73b5445271379a59e3792b0d978f42d4a01e0cf7a67d9c33a3bb0a5", size = 267088, upload-time = "2026-08-06T13:48:58.41Z" }, - { url = "https://files.pythonhosted.org/packages/59/a1/ae94eb2c541add426378408379f233591e069040b1e2cdb33df9498a0682/coverage-7.15.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3fc9e047706fb4a9abb54f719d3aa643e80e5bb3818182c40aee01ac0f0247ba", size = 269508, upload-time = "2026-08-06T13:49:00.42Z" }, - { url = "https://files.pythonhosted.org/packages/9c/c7/88a10694a1c6a213569766aba9f25847b28155d4ac731b13226db216356d/coverage-7.15.4-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:05e491d4f3165d62d4f5c8fd48dfeabf2ae8f42cbbd484319af33ea851b78982", size = 270629, upload-time = "2026-08-06T13:49:02.234Z" }, - { url = "https://files.pythonhosted.org/packages/b3/34/d8b8232e5e55169933b59aabcef2fedfa4b9d8897361bb80fcbda146505f/coverage-7.15.4-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:226c66e80ec0598d3b9b4874123df167ccca342aca8714f77cac6829688ee09c", size = 264043, upload-time = "2026-08-06T13:49:04.102Z" }, - { url = "https://files.pythonhosted.org/packages/7e/35/58b009dbf8c471c7224716478b9fed4a7e1af15320e1ed41660978504663/coverage-7.15.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ac41cc14bebda0dbfb0628036b7f75706935c95bcc07fefe9a0f93614aa60a57", size = 266963, upload-time = "2026-08-06T13:49:05.821Z" }, - { url = "https://files.pythonhosted.org/packages/62/aa/57fbda1b42c892968273c56b6ee9dc0f1310850859230a507bc7873b1f65/coverage-7.15.4-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:8af623e5cd92080acddd02b38f2f406a2c3a0893c38950b211890361448fbf26", size = 264569, upload-time = "2026-08-06T13:49:07.706Z" }, - { url = "https://files.pythonhosted.org/packages/98/8a/360e6e7f24d477b7e889703af0afa878d15b6d4d8d2a822b2835c169a879/coverage-7.15.4-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:07545711d4f0f32852a18f18ad11f76f0109909d09e78b9008b4cfc67e829429", size = 268299, upload-time = "2026-08-06T13:49:09.587Z" }, - { url = "https://files.pythonhosted.org/packages/4e/89/6f701261aee21b6b5fa8f7872229406dc917e125069448292223bf213606/coverage-7.15.4-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:a0865421cfdc53654b342d515e5a233187590882d20b95752150e53f65460017", size = 263413, upload-time = "2026-08-06T13:49:11.604Z" }, - { url = "https://files.pythonhosted.org/packages/3f/0f/6f04036edc260ed425af83e834f627fad48941ce97b50bfe6edd8b6fa623/coverage-7.15.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:460115e32ee40566476db5048f9bec1e842c127ad8e6f8be745aad3ac9cbc839", size = 265725, upload-time = "2026-08-06T13:49:13.38Z" }, - { url = "https://files.pythonhosted.org/packages/c4/ce/d19b5d4d5c49a7bfb925fd74310fee7d28bc99520ac3367ccbc54e662518/coverage-7.15.4-cp314-cp314t-win32.whl", hash = "sha256:cbde877ef9dd7baf272b9bfef2b8a25edd45d9170fc326951dd20eb480335e85", size = 225079, upload-time = "2026-08-06T13:49:15.265Z" }, - { url = "https://files.pythonhosted.org/packages/26/bb/7aa1b3b173faee0679037ca950bbbe1247273656697994d8d13f80f8d4b4/coverage-7.15.4-cp314-cp314t-win_amd64.whl", hash = "sha256:3da9e92d1c551fd7563833e9ade686efb0c4b7363ab7681a94283958c950bf5e", size = 225911, upload-time = "2026-08-06T13:49:17.279Z" }, - { url = "https://files.pythonhosted.org/packages/81/1c/4ea9e47426d80038d9222db3c4534cb6021a74b237d3ff97ffd33b6600dd/coverage-7.15.4-cp314-cp314t-win_arm64.whl", hash = "sha256:3a54f5a0d85050c73a38f6793090ee83974531e67fe5e57a1da9bee11398aa5e", size = 225219, upload-time = "2026-08-06T13:49:19.293Z" }, - { url = "https://files.pythonhosted.org/packages/2b/c4/dc5d2ac8f9142e7ec7de66e7bf0591db29d78955a040bd915870d9c0e657/coverage-7.15.4-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:2c9872e4d9dc5d3cf616bf4b382f5a00359305a5be666a3dd0b5cdb4e49597f9", size = 222604, upload-time = "2026-08-06T13:49:21.279Z" }, - { url = "https://files.pythonhosted.org/packages/70/39/33e63df81fe2ee100897451841c821467635923e58e37c6bd4b46dd8106c/coverage-7.15.4-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:e101dbb4b9b72f0cddd8cdc8c9c5b47f456766f5e0ac82dbfb75e5c55409b78a", size = 222944, upload-time = "2026-08-06T13:49:23.187Z" }, - { url = "https://files.pythonhosted.org/packages/99/1f/ef3ffb5557febc75a0d97aa459d0266d7d741110265121cc6d8539343d44/coverage-7.15.4-cp315-cp315-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7d1abebdb047729e852b9c77a00497dfbeb11eb3a117e037d7dbc3ac8e5f5c54", size = 254050, upload-time = "2026-08-06T13:49:25.008Z" }, - { url = "https://files.pythonhosted.org/packages/6f/f5/1f0f6f77698c3601ca0ae7431e34b24c62ca2f06fecb23b73ed1f651d2be/coverage-7.15.4-cp315-cp315-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d28a4a899354d0ea6214cc59b4fa19eefbce1b9ff1688ab579acf49e894bd3fb", size = 256967, upload-time = "2026-08-06T13:49:26.896Z" }, - { url = "https://files.pythonhosted.org/packages/03/7a/2ed9bed79925f4367c83c77f66a89e5ca7229c288d2d19ad5f36d1ca0070/coverage-7.15.4-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ffb3c2aacea411cc7e1d27712490c11108e2de1d39019ae32915493a59a8b9ed", size = 258587, upload-time = "2026-08-06T13:49:28.692Z" }, - { url = "https://files.pythonhosted.org/packages/45/8c/fa34044f71b7cc4ecb6da9c2408770959b0591fa9b5fb6fb6bca38f94298/coverage-7.15.4-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a9447978a92f405d301123cfd39ff49895490efb769a758fe2734c7f631bf8ce", size = 260785, upload-time = "2026-08-06T13:49:30.472Z" }, - { url = "https://files.pythonhosted.org/packages/4f/54/d5727ce36b4524a7394ab9f5f1df378e1f23affcdab01037dc8655185cc7/coverage-7.15.4-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:050467a7983b8e2fe7dd41a78bb30c3e7f8c0b8cafda14b1c46f8b5e3cf2dd3c", size = 254545, upload-time = "2026-08-06T13:49:32.271Z" }, - { url = "https://files.pythonhosted.org/packages/dc/e6/6e3783e576719590194bdffb6dd6d85490801785b7c331e35a245d8cb8b5/coverage-7.15.4-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:d003b7a5708ddad5c206c79607a6b92abb6fc13c57d99d8a4468cc03a2941ced", size = 256682, upload-time = "2026-08-06T13:49:34.089Z" }, - { url = "https://files.pythonhosted.org/packages/dc/f2/bacdbde18b69ed2de424fcf64d9fb0a4913753d4f0eca8bae9daad69f4bd/coverage-7.15.4-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:c38efe30fd74e5c19e9433f11fb1f5dc9c6522770971b7c6145bbaa413dc8800", size = 254560, upload-time = "2026-08-06T13:49:36.052Z" }, - { url = "https://files.pythonhosted.org/packages/6c/a3/1fb927196e3477c1b48831169ab58ba08f451ba87ae311ff1de68b26a616/coverage-7.15.4-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:1f4f826d70f772ab8b0c052329580d7fe8b8abd191e4ce0c8f81aec6614665d3", size = 258792, upload-time = "2026-08-06T13:49:38.01Z" }, - { url = "https://files.pythonhosted.org/packages/41/58/30d4c149c69053de0edfe325614c1d28d508f62b1783e0e4a234d2e49136/coverage-7.15.4-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:4a4bf917c9953f57c957be31c1cd504e3bd2f34d4a352b9d391a3025336f6768", size = 253968, upload-time = "2026-08-06T13:49:39.934Z" }, - { url = "https://files.pythonhosted.org/packages/89/e4/77f639371b918aad30dda4051f95404b43578f7f2e2f87ba73e02ed1ff37/coverage-7.15.4-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:1c9bf40ebef178a45192c75c4964760bb261b0e6ad725da5fc4c93f674f19753", size = 255893, upload-time = "2026-08-06T13:49:41.825Z" }, - { url = "https://files.pythonhosted.org/packages/5c/62/13be29b3ddab35f14c87967a4820a05106d2a3eccb4fa4ff550bf30b75e0/coverage-7.15.4-cp315-cp315-win32.whl", hash = "sha256:43619d04c3671792d2c4706ae8bf45e265dc87bbd4078189ef8b847ea1e74be2", size = 224768, upload-time = "2026-08-06T13:49:44.08Z" }, - { url = "https://files.pythonhosted.org/packages/a1/70/af0c6be0f964af6954f6b74bc109b0dbca02824696d2520fb17fe1ab06e3/coverage-7.15.4-cp315-cp315-win_amd64.whl", hash = "sha256:be619439dbcd31a2eab10b32de9fff62c26ed4bab69dc32b8363fdaaa0882809", size = 225242, upload-time = "2026-08-06T13:49:45.899Z" }, - { url = "https://files.pythonhosted.org/packages/4f/2d/f3bd3aab899fc9efc18b53133ee68f5f98574ef480649b23e12962226387/coverage-7.15.4-cp315-cp315-win_arm64.whl", hash = "sha256:def597967dafc2e8d97c9097ea453c464e0bb8ed38f193a43070f10dc623bb6d", size = 224674, upload-time = "2026-08-06T13:49:48.322Z" }, - { url = "https://files.pythonhosted.org/packages/f5/ca/f69251cd63eabc6438321aea22148754cce758a26bde07dd490e3fe7cfc5/coverage-7.15.4-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:c7dbc748ac8a1e3e59a2b28bea47675e6e778081dbbf081bde0d75def2fcbe1d", size = 223333, upload-time = "2026-08-06T13:49:50.293Z" }, - { url = "https://files.pythonhosted.org/packages/a7/a7/037b53b2885b0d8447064432491a4d5a1014cd9f97a594d53acd0c04541a/coverage-7.15.4-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:2413074a5ecbb61a01a7888fc72db0ca324d13588c5b38bc0dd8564cdcdfea26", size = 223630, upload-time = "2026-08-06T13:49:52.637Z" }, - { url = "https://files.pythonhosted.org/packages/80/4f/152b8a4779ae90da11bb24f7467df8a59f0be48a5c52acb856325ca48289/coverage-7.15.4-cp315-cp315t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:4e6f6f632b7b2f714bf7a1346e8f97b650ee71f3c298aaad42a2ab60f0f07645", size = 264489, upload-time = "2026-08-06T13:49:54.52Z" }, - { url = "https://files.pythonhosted.org/packages/10/2d/84b4b9e0e1dd6528a51920ff7031f35b789382e467a28ec6a5a578cb8812/coverage-7.15.4-cp315-cp315t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8df457da2249d3c75ca2e5e835d59c725abfe92d27fdff6cd99eed85b51d5e9a", size = 267567, upload-time = "2026-08-06T13:49:56.721Z" }, - { url = "https://files.pythonhosted.org/packages/53/fc/ba01cc25299f9f8a2c8b02d3b28c53f3543d9fbfbe4e74fa2760b48f163e/coverage-7.15.4-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:050f66a08805acb5b8a23c6d4a517b1ecf82c08e81ed0e4bd727df065e5c6624", size = 270123, upload-time = "2026-08-06T13:49:58.736Z" }, - { url = "https://files.pythonhosted.org/packages/cf/d0/db2647cbf40b14f8c308f94ff7bf89c06d564e59f396906edf50086ec788/coverage-7.15.4-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1587fb771d1ccceef708fdde1e5af8c7ed24b486b61d13a321acb7d8145390aa", size = 271107, upload-time = "2026-08-06T13:50:00.811Z" }, - { url = "https://files.pythonhosted.org/packages/70/ff/4d2d17924552c458bb4f77dd631f0e3bc92fbbdf2d2d916cd4b33bbfd5b1/coverage-7.15.4-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8b4f1c3a69ca580f3fbd6b2046915f536d7f586874f25c1bb23add2a3c88d50f", size = 264955, upload-time = "2026-08-06T13:50:03.023Z" }, - { url = "https://files.pythonhosted.org/packages/ee/de/dc010c7a3691f396d93bbc26bfcafa1c2a3a351cd520470f15faf5795bd5/coverage-7.15.4-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:ffb58d7eff5b7f6ecc6fa21d6288ab7f968a212cb67d682c269c09b9eba3b66f", size = 267949, upload-time = "2026-08-06T13:50:05.557Z" }, - { url = "https://files.pythonhosted.org/packages/78/ea/dc96a11375e83c045c2f7c61fb6918277cfe9401db7c0f7b1d111a84b2e5/coverage-7.15.4-cp315-cp315t-musllinux_1_2_i686.whl", hash = "sha256:d9df165544774574ee004b953023d1bebada1894a80b1052a43d798b0f676e67", size = 264421, upload-time = "2026-08-06T13:50:07.612Z" }, - { url = "https://files.pythonhosted.org/packages/c8/86/b77131a0f9503ce461cd577076147d7a9040f0c5dda772686f729e2cc9cb/coverage-7.15.4-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:f9de0a24a4079b53e523b5c5e2c5945ec251ab486652659955187cf255a259bc", size = 269121, upload-time = "2026-08-06T13:50:09.58Z" }, - { url = "https://files.pythonhosted.org/packages/24/24/944bc35007862955e7ebf05754e645419dcf5d7526c52735cfa2715e8ebf/coverage-7.15.4-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:150089274bdc9f940628552cb92844e0223c987f1902ab8efe9f45a2ec758d88", size = 264565, upload-time = "2026-08-06T13:50:11.722Z" }, - { url = "https://files.pythonhosted.org/packages/c7/cc/a3bb9f93e7e740659163e2ea584f8196ddcd2c456a5dbe15f6c50105fec1/coverage-7.15.4-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:a58a94fed5da6997d258e8f7668c1e195fbd04a691d781b7558f1e468f9e68bc", size = 266522, upload-time = "2026-08-06T13:50:13.786Z" }, - { url = "https://files.pythonhosted.org/packages/49/dd/e0e40f3560d878d888c580698ff5ad1179f5e1c3ac949684ef66b41a3817/coverage-7.15.4-cp315-cp315t-win32.whl", hash = "sha256:ebd5a6d8466ff30836572f3ba2cae8a5e8f85029b1c6d5e2ed338dc472a5166a", size = 225068, upload-time = "2026-08-06T13:50:15.825Z" }, - { url = "https://files.pythonhosted.org/packages/c6/7e/37732ea80eebc30e976e4cdab15c190bc42d96959a42e38ddf6f8c60468f/coverage-7.15.4-cp315-cp315t-win_amd64.whl", hash = "sha256:288bde2a2d7ab6b6c2d7252fcde8b524387f2d970bdba9658fc6f8bbcaef0f9b", size = 225895, upload-time = "2026-08-06T13:50:17.928Z" }, - { url = "https://files.pythonhosted.org/packages/c6/08/1e00f7923eaaba45fb3d51dd794125fc766304b1df264f3a9c6557bfb30e/coverage-7.15.4-cp315-cp315t-win_arm64.whl", hash = "sha256:68be5e1de60ff13c9095bbec0e5a7fa45b33b101752215b91345ea1f61c4a278", size = 225213, upload-time = "2026-08-06T13:50:19.981Z" }, - { url = "https://files.pythonhosted.org/packages/b4/d9/e70c286c979378f061d8266e279b686ab0b0b688e1fe0af864684f23a77d/coverage-7.15.4-py3-none-any.whl", hash = "sha256:964730a1e9de9c0cf11be6a1a3c79ce419c34882842abd256086ba4698705e84", size = 214332, upload-time = "2026-08-06T13:50:22.192Z" }, +version = "7.15.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/76/d0/55fe630f4cf94e3fcba868240fad8c8cdd1f764e2a932f8926347e6ec4cd/coverage-7.15.2.tar.gz", hash = "sha256:3df60dc267f0a2ca23cb7a9ab1109c62b9335ffbf519fcfe167157c28c09b81d", size = 927741, upload-time = "2026-07-15T18:56:19.558Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7d/3a/54536704f507d4573bf9161c4d0dd3dd59b6d85e48c664e901b6844d8e33/coverage-7.15.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2f1ec6f304b156669cfde653b4e9a953f5de87e247ea02ac599bce0ab2744036", size = 221414, upload-time = "2026-07-15T18:53:51.941Z" }, + { url = "https://files.pythonhosted.org/packages/b6/d9/8ba925d29743e3577b21e4d8c11a702b76bc93c41e7fdfd1177af63d4b8d/coverage-7.15.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4d3361879d736f469f45723c11ea1a5bbdaf1f6928f0e632c940378b5aa9b660", size = 221913, upload-time = "2026-07-15T18:53:53.682Z" }, + { url = "https://files.pythonhosted.org/packages/09/54/a855f3aa0187f2b431ade4e4791b77b56282cfb5d201c83ec26a31b5b36a/coverage-7.15.2-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:c6a98d698f9e2c8008d0370ec7fc452ebfcc530002ae2d0061170d768b992589", size = 252332, upload-time = "2026-07-15T18:53:55.467Z" }, + { url = "https://files.pythonhosted.org/packages/8e/d3/13ac97b4370640ba3452fc8559b06cc2f479ce3ba4a0b632a73e44c38a7d/coverage-7.15.2-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d50dd325e18ec25bfcc10cd7f99b04df1ab9ec76b0918c260e60817ad0643dee", size = 254243, upload-time = "2026-07-15T18:53:57.055Z" }, + { url = "https://files.pythonhosted.org/packages/88/83/5eca144942d8d0659d3f55176517f4a59cdc65eefd17146a0770935a3ebd/coverage-7.15.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:67d7602480a47bdf5b675635403625553ebaa70d5a62a657c035149fd401cea0", size = 256352, upload-time = "2026-07-15T18:53:58.83Z" }, + { url = "https://files.pythonhosted.org/packages/4e/ba/d3db2e01a50fc88cdb4c0f19542bcf6f61489e34dc9aa3538413e2459a38/coverage-7.15.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cee0f89f4767a6057c8fbf168f8135f18be651300496086bd873e3189fed0487", size = 258313, upload-time = "2026-07-15T18:54:00.497Z" }, + { url = "https://files.pythonhosted.org/packages/78/b3/aba83416e9177df28e5186d856c19158c59fc0e7e814aaa61a4a2354ad1b/coverage-7.15.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a29ec5305a7335aacee2d799e3422e91e1c8a12474986e2b3b07e315c91be82f", size = 252449, upload-time = "2026-07-15T18:54:02.456Z" }, + { url = "https://files.pythonhosted.org/packages/6a/a5/4b00ecac0194431ab451b0f6710f8e2517d04cef60f821b14dec4637d575/coverage-7.15.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:48ccc6395958eda89093ecdc35644c86f23a8b23a7f4d44958812b721aad67c1", size = 254043, upload-time = "2026-07-15T18:54:04.072Z" }, + { url = "https://files.pythonhosted.org/packages/75/b6/cfa209b4313ee7f1b34da47efcd789ea51c024ad35af390e00f5a3c10a2e/coverage-7.15.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:81f382c5a94b434ec1f6da607edb904c76d7212e618cd4d1bc9f97bed4120ef5", size = 252107, upload-time = "2026-07-15T18:54:06.745Z" }, + { url = "https://files.pythonhosted.org/packages/36/67/e8cac5a6954038c98d7fe7eb9802afe7ab3ecb637bb7cc00e69b4148b56d/coverage-7.15.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:bbc808daf4f5cd567af8075ecc72d21c6dfef9a254709a621a84c217c935ebc0", size = 255873, upload-time = "2026-07-15T18:54:08.48Z" }, + { url = "https://files.pythonhosted.org/packages/2c/92/395cca9f330a86c3fe3471d73e2c102116c4c58fdc619dbbc125c6e93a54/coverage-7.15.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:a4c46b247b5d4b78f613bd89fea926d32b25c6cc61a50bd1e99ba310348f3dad", size = 251826, upload-time = "2026-07-15T18:54:10.083Z" }, + { url = "https://files.pythonhosted.org/packages/51/60/3e91b20295439652424f426b7086ec5bf4fbe3f604c73eda22b986c4fd6b/coverage-7.15.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:094dd37f3ef7b2da8b068b583d1f4c40f91c65197e16c52a71962d5d537fc5db", size = 252735, upload-time = "2026-07-15T18:54:11.878Z" }, + { url = "https://files.pythonhosted.org/packages/a5/eb/8c07839005e5e3c6b3877d3a6e2a80ce766589f31dd2b6882b78d59a7b8c/coverage-7.15.2-cp311-cp311-win32.whl", hash = "sha256:a63b9e190711134d581c4d703df5df09851b1acf99792c7aacbbe9f41f0283c9", size = 223500, upload-time = "2026-07-15T18:54:13.525Z" }, + { url = "https://files.pythonhosted.org/packages/2e/98/59d83c257cd59f0fbaf9d9ddb26b744a576760dfd1ae16e516408894a02b/coverage-7.15.2-cp311-cp311-win_amd64.whl", hash = "sha256:8bb9f4b4279187560796a4cdaca3b0a93dd97e48ee667df005f4ed9a97403688", size = 223973, upload-time = "2026-07-15T18:54:15.163Z" }, + { url = "https://files.pythonhosted.org/packages/ea/09/2d285c8bef5c4f695d120c1c96dc11715638aa8e134069f210bb6a62a9fe/coverage-7.15.2-cp311-cp311-win_arm64.whl", hash = "sha256:8c726b232659cbd2ae57ade46509eb068c9bd7a06df9fcbff6fe484870006934", size = 223519, upload-time = "2026-07-15T18:54:16.803Z" }, + { url = "https://files.pythonhosted.org/packages/6a/50/eb5bf42e531611a9f8d272556b1ed4de503f84a91413584094487cf69f8f/coverage-7.15.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:1adac78e5abc7c5438f7a209c9ca69d06542f0bf481d728b6989ea80b813fdf9", size = 221587, upload-time = "2026-07-15T18:54:18.439Z" }, + { url = "https://files.pythonhosted.org/packages/06/d1/da99af464c335d4e023a6efcd7ec30f63b88a43c93745154ab74ffb31cea/coverage-7.15.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b868acc62aa5de3be7a9d05c2333bf8359ca987e43f9cb30ff8fbda6a024ab73", size = 221943, upload-time = "2026-07-15T18:54:20.062Z" }, + { url = "https://files.pythonhosted.org/packages/5b/8a/13c42723d61ca447eafa18732e8141dd6a63f2732e1c7e1502c182dd88d7/coverage-7.15.2-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:6f6966fc30e6f06ca8f98fb0ce51eda6b111b3ee8d066a8b1ec9e77fa06ab55d", size = 253450, upload-time = "2026-07-15T18:54:21.765Z" }, + { url = "https://files.pythonhosted.org/packages/d7/29/99021303f98fbdcb63504b4d07bea4cc025b9b2dd907c4f07c85d50a0dab/coverage-7.15.2-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:68af907f595ab01a78f794932ff3bdf929c316d3000810d38dbc247129e26f8b", size = 256187, upload-time = "2026-07-15T18:54:23.4Z" }, + { url = "https://files.pythonhosted.org/packages/f9/a8/fd503715ed6ca9c5d742923aa5209257340b367a867b2ced0c7d4ba8a0b9/coverage-7.15.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:afa29e2eff3d5729267e2cb2fd4ce9d61c952932fb2694e34ccb5d9540c6a296", size = 257301, upload-time = "2026-07-15T18:54:25.183Z" }, + { url = "https://files.pythonhosted.org/packages/da/40/3f4b8fb409810036ebc2857d36adc0498c6e957b5df0290c5036b2e143f1/coverage-7.15.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bbf44513ceb1589e31948e20eafbde9deaface90e1a1afa5f5f77b4423d17ce6", size = 259562, upload-time = "2026-07-15T18:54:27.204Z" }, + { url = "https://files.pythonhosted.org/packages/0b/8a/9bdffbef47db77cce3d6b02a28f7e919b19f0106c4b080c2c2246040f885/coverage-7.15.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9deddf09eecb717b7f980414b43d90a5b22ff3967d2949ab29cb0aa83d9e9098", size = 253841, upload-time = "2026-07-15T18:54:29.134Z" }, + { url = "https://files.pythonhosted.org/packages/1b/1e/9031efde019d31a06646261fce6dfc5c3c74e951e27a71e5c9a424563178/coverage-7.15.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ae901f7e55ba405c84ee1cab3d3e962e4e871e4a2bcb9c90911adbd69b42ac5a", size = 255221, upload-time = "2026-07-15T18:54:31.142Z" }, + { url = "https://files.pythonhosted.org/packages/56/db/787acde872389fc84a9ef9d8cd1ccc658e391ab4cb5b28092a714426a394/coverage-7.15.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a0f47002c6eeb7c280228467a4cb0cc15ca2103a8421b986b2d3ec04a0f9bd8b", size = 253366, upload-time = "2026-07-15T18:54:32.886Z" }, + { url = "https://files.pythonhosted.org/packages/2f/9b/6f57bc4b93c842eef1695f8cdaf2318e35e7ba54f5ba80d84be213ab7858/coverage-7.15.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1cd7a5beb7af3e864a13b1f0fb26efd3695da43ef0daf71e586adfffaf34d5b2", size = 257434, upload-time = "2026-07-15T18:54:34.7Z" }, + { url = "https://files.pythonhosted.org/packages/88/26/b3186a21b2acc83e451118978905c81c7072c3333707804db09a78c096a2/coverage-7.15.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:97a5c5457a9fb1d6c4e06cfb5dc835871fbfb6a6a51addc9e925bdeff5ef7440", size = 252935, upload-time = "2026-07-15T18:54:36.548Z" }, + { url = "https://files.pythonhosted.org/packages/20/c2/c9f3376b2e717ea69ed7a6e9a5fcab968fb0b290db6cf4bd9a1fc7541b75/coverage-7.15.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0901cfe6c13bcd2302da4f83e884555d2a22bda6e4c476f09ef204ba20ca536e", size = 254807, upload-time = "2026-07-15T18:54:38.296Z" }, + { url = "https://files.pythonhosted.org/packages/f0/e1/dfc15401f4a8aaeb486e1ba3e9e3c40522a6e38bd0ecf0b3f29cb8082957/coverage-7.15.2-cp312-cp312-win32.whl", hash = "sha256:b171bdd71cb7ff792bf32e376173b0ace7e7963e7e57c58dfc42063a6a7174cd", size = 223641, upload-time = "2026-07-15T18:54:40.103Z" }, + { url = "https://files.pythonhosted.org/packages/91/40/81b6d809d320cd366ec5bdf8176575e897dcb8efe7fb4b489ef9e93e4d13/coverage-7.15.2-cp312-cp312-win_amd64.whl", hash = "sha256:582edc45c2040543fef83341be23c43024a3ab3ae0c2d8bc498a06282905ad40", size = 224172, upload-time = "2026-07-15T18:54:41.882Z" }, + { url = "https://files.pythonhosted.org/packages/ef/28/9f14ec438149f7de557f45518f09b4a7917b795cc37083aa7db482693f8c/coverage-7.15.2-cp312-cp312-win_arm64.whl", hash = "sha256:a638db90c61cd219aeee65e83a24fdaa57269a741ae0cf773309208ac862cee3", size = 223556, upload-time = "2026-07-15T18:54:43.674Z" }, + { url = "https://files.pythonhosted.org/packages/fc/d5/f8c838e6b7282976f7c918884b792df7a0c42c5bba5d99c60ad2d221d56d/coverage-7.15.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1121caa19159a38b5463eaae4b1e1fde81e525b15ecc5e000cd5b1a108f743a8", size = 221606, upload-time = "2026-07-15T18:54:45.448Z" }, + { url = "https://files.pythonhosted.org/packages/bf/37/97c926376364f66298cc44893b89cdf17b8bc406376497c4061ae4b8a8ff/coverage-7.15.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a300c6934e0989c327b9e8a1e110329da4641149f872bbe9f70168be66da76c1", size = 221982, upload-time = "2026-07-15T18:54:47.341Z" }, + { url = "https://files.pythonhosted.org/packages/b7/30/a36050a6e83c2135ee0776f452ca3948224befc6d7f26acecc082d0c106a/coverage-7.15.2-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:2617f8799d268fabdeef42a7e89ac3a23e1deee9025427db2df970f99a89a578", size = 252972, upload-time = "2026-07-15T18:54:49.2Z" }, + { url = "https://files.pythonhosted.org/packages/31/d3/06b5f1daf95f0f15ab05bd75f26ba5f3c8b33d0bb72f3aaa3cf41d1bad3a/coverage-7.15.2-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7dc2950a2992cd676d35c20ae63522836deeb034f08874699d14068710af3dc1", size = 255569, upload-time = "2026-07-15T18:54:51.098Z" }, + { url = "https://files.pythonhosted.org/packages/81/1c/9afb3f8de2b8d36960391c48559a2e3ff96594b58099f115921549ea8d0d/coverage-7.15.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9e36686f7a442185db2400b3df171aac520869faf9deb59df687d28659eda2a6", size = 256806, upload-time = "2026-07-15T18:54:53.145Z" }, + { url = "https://files.pythonhosted.org/packages/64/d8/b989f96061a5e32d82fddd1b1b9ff48a7c8f8ae7606f0e80fd9de54b1e33/coverage-7.15.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7d29ca7bd67af6e12e74632d65f026eabc1364da5c254494cd914446a28a3ef7", size = 258936, upload-time = "2026-07-15T18:54:55.015Z" }, + { url = "https://files.pythonhosted.org/packages/b8/fa/f99771f5110457c7b511c1935ca49ddf288218eaa84322e028b9334146ae/coverage-7.15.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:db9c8438057e5b0f6a22a0af99c0c1d26b57fbbdbd1be5861ddb8f897fcc3a2d", size = 253178, upload-time = "2026-07-15T18:54:57.527Z" }, + { url = "https://files.pythonhosted.org/packages/f6/96/c098a6044d119c751ceede7be91035fa8310170ec24a6523aff72f0a5793/coverage-7.15.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:63022c4c8dec1d0342f05c3ede99842fe3d007689acc45e86f123a1746e4a026", size = 254934, upload-time = "2026-07-15T18:54:59.41Z" }, + { url = "https://files.pythonhosted.org/packages/b2/a2/1457b3a7a50c8d77500103b97a046db863e2f59a1cf6d2f814595f349885/coverage-7.15.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:6c0be82b4d4aa5b2704e08518e2252f3e3d110164bcca826816801052e48a7aa", size = 252898, upload-time = "2026-07-15T18:55:01.338Z" }, + { url = "https://files.pythonhosted.org/packages/6c/0e/76958874c471ecfcdde0d2b2747bb2c61bdbf34a40636f4ce9db9923e643/coverage-7.15.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4510fb9cdf6bb02dfa6af0be4a534b8102d086e22e4a33f8836df663da3d660d", size = 257056, upload-time = "2026-07-15T18:55:03.243Z" }, + { url = "https://files.pythonhosted.org/packages/7c/7c/3d7c4e3bf58baa40327dc7edc2272b17cf02299366d52763db1b0ca1556a/coverage-7.15.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:42ec3d989421b174a2ab607c1539f24127ad362757b7f1c0c0d7a2993f7eb37b", size = 252718, upload-time = "2026-07-15T18:55:05.029Z" }, + { url = "https://files.pythonhosted.org/packages/c8/b8/1cecffed9ce14fb25be9ba42d37b6bb61485c9a3ddd43cd3dde36b6087d8/coverage-7.15.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e8f91bce78e32343af184c3b7fa28fcf5a9e2641f4b6623d392038f804939188", size = 254490, upload-time = "2026-07-15T18:55:06.889Z" }, + { url = "https://files.pythonhosted.org/packages/6c/2c/42984561bc7f4c045dca67516a0c50ee5ef8d84352dbeb5559dc86c4823e/coverage-7.15.2-cp313-cp313-win32.whl", hash = "sha256:434e68d531858205895eb0d74b73d20b84260de426387d53c422a5acda2cf050", size = 223647, upload-time = "2026-07-15T18:55:08.941Z" }, + { url = "https://files.pythonhosted.org/packages/41/9f/39c7c9245efc583beddf89a87683574e663ed93637f3afb6cd7b88405676/coverage-7.15.2-cp313-cp313-win_amd64.whl", hash = "sha256:26c3b04a6377fd7c09800921fa934e3a17c0020439cd59df73e73ae1d4b6a78c", size = 224190, upload-time = "2026-07-15T18:55:10.789Z" }, + { url = "https://files.pythonhosted.org/packages/c7/de/3a2883cf8a213659280ef4b403059e17a9acaeb7fc7fd4105e1226ff2e6d/coverage-7.15.2-cp313-cp313-win_arm64.whl", hash = "sha256:3ed010aa1b69cda8e827aabfca9866216c980e2dca82ab9a78c5f83689964c8b", size = 223583, upload-time = "2026-07-15T18:55:12.678Z" }, + { url = "https://files.pythonhosted.org/packages/81/5f/aed265fd7a3551a394f36dfe41868aee709b7f95db4052205b4ad1563ac3/coverage-7.15.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:40f633c5c5fc783732f6312280122e859538fa24461235597c13d803ea9a108a", size = 221650, upload-time = "2026-07-15T18:55:14.527Z" }, + { url = "https://files.pythonhosted.org/packages/6b/2c/222ba12a545189017120f8eddfc1a0bd4616b47d5d4a8d99421edb2fe4c6/coverage-7.15.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:075560438765b7a2ef43bf7aa7758661b53d889df47f062a31bda6c1ade553a2", size = 221988, upload-time = "2026-07-15T18:55:16.674Z" }, + { url = "https://files.pythonhosted.org/packages/aa/38/304b5877ab46e6c290b4292cfcf3fe28245f0e5597cad7f6acc91fc7e0a4/coverage-7.15.2-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:25fd15dd40a0a2c51a500d664ca29053c09c3259d998407bf982b6e114696138", size = 253029, upload-time = "2026-07-15T18:55:18.856Z" }, + { url = "https://files.pythonhosted.org/packages/6c/58/821b533b8db9e44cf1d8a97bd525149ced40dde1d0093da02cb78e715244/coverage-7.15.2-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b9a6367e4aff723e8ee8190836836124284e8fcd4265e307c844010cfa074f3f", size = 255536, upload-time = "2026-07-15T18:55:21.027Z" }, + { url = "https://files.pythonhosted.org/packages/f1/f2/7aa06604c389d32ea7f0a6a988359a7eafc3cd3f8e7bc2e88cd2fdf0b877/coverage-7.15.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9854ca62c152874b2060772503535be2e8f53f70b8aaa7686b094888d872f984", size = 256881, upload-time = "2026-07-15T18:55:23.125Z" }, + { url = "https://files.pythonhosted.org/packages/a2/4f/1ef342339c7916d0096bc5888cc0f653882cc7bc8f897d5cb89143287c9b/coverage-7.15.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:913b6c56e110da40e035bbd168353bf7aaa2544a5eaccea5d98a4629aac156c7", size = 259196, upload-time = "2026-07-15T18:55:25.099Z" }, + { url = "https://files.pythonhosted.org/packages/fe/f4/7ed055d7a9c5ec13b161773a115a5ccc6b0081d568c31fad830806306cc7/coverage-7.15.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:aaccad4129d735a8a4d526f26929894c9a4e8ef7034566f210b176749d6906e3", size = 253036, upload-time = "2026-07-15T18:55:27.018Z" }, + { url = "https://files.pythonhosted.org/packages/14/79/ea82cca18c242a3a38b6c017da39726aa62dcb64aa635abf79b92009975c/coverage-7.15.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a164b50081fc7357331c4024ef4d17b78ba325f8380d05f5a69599a7e05257ee", size = 254887, upload-time = "2026-07-15T18:55:29.084Z" }, + { url = "https://files.pythonhosted.org/packages/a4/ba/a136db3c0d9562b00e10b72540dbf3a33cd3bc5b95060c9308e247494623/coverage-7.15.2-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:bfd341ccf78128e72c094bc70cc25b3ef309c33c7c2c66ba3ed4309549e02de1", size = 252852, upload-time = "2026-07-15T18:55:31.184Z" }, + { url = "https://files.pythonhosted.org/packages/17/17/ea334246b16b7d059953fad6fdefa11e33c68efbd3fe37b1098120a1fac2/coverage-7.15.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:1473b3ba8e7ee0f076117b1a72c23f579a2b9e2bb742f48a8d86ea27ca93f91a", size = 257128, upload-time = "2026-07-15T18:55:33.163Z" }, + { url = "https://files.pythonhosted.org/packages/ed/c3/074fb66d46d607855f710876b117cbda562c5ab08363528e78820449f937/coverage-7.15.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:17c432b5f73ad52ef46fb06019f6fa7c66ce381961cf0f7dfd1d3a4bd3a98145", size = 252668, upload-time = "2026-07-15T18:55:35.063Z" }, + { url = "https://files.pythonhosted.org/packages/e1/c1/f620850ada9b36435921c9a3a8057013422b1d964eb4bf37fe138724d192/coverage-7.15.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:77f0ef5011df53a4bd1b35211ab122287f8d9b8d7aa1c4553e5c2deb24b1d446", size = 254325, upload-time = "2026-07-15T18:55:37.125Z" }, + { url = "https://files.pythonhosted.org/packages/cc/31/a729ca3689404493af82ef8e6ff70bd88bdda8da89aeef6ca9b387aeb2b4/coverage-7.15.2-cp314-cp314-win32.whl", hash = "sha256:f653e5d7248c1191ec988a85c72edeab46c3ff44f90639a4ed4874ec0be90243", size = 223844, upload-time = "2026-07-15T18:55:39.078Z" }, + { url = "https://files.pythonhosted.org/packages/c6/83/5d809dc808fb1698c671f3e372259bb9158e64b7ea526fc6ab7de64de9fe/coverage-7.15.2-cp314-cp314-win_amd64.whl", hash = "sha256:9911f31aad8906abe337c271343485cf20df5e70df5d2f57f9f136e7b55f26bc", size = 224331, upload-time = "2026-07-15T18:55:41.346Z" }, + { url = "https://files.pythonhosted.org/packages/16/4e/35e488548e952795829e129995c4174df33bf432b591d1aa42c8d9e4e7ad/coverage-7.15.2-cp314-cp314-win_arm64.whl", hash = "sha256:e38def96ad59853824c97953fdcd2c320a84ba3ce99b417db78af8bb6c3db635", size = 223760, upload-time = "2026-07-15T18:55:43.518Z" }, + { url = "https://files.pythonhosted.org/packages/ed/49/dd2c86cd6374038f6e415fb5bfb86db5218553209c081384a020369dee79/coverage-7.15.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:835ec4e20b45f0a7f63ed78f94065aca00de033403df8377bfe8b9c6abc0a7be", size = 222384, upload-time = "2026-07-15T18:55:45.569Z" }, + { url = "https://files.pythonhosted.org/packages/d3/74/173ff17a1c0808e5a438f549f6f145d5ac7528f2791310b63523e3200ac7/coverage-7.15.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7466cc7ab6dc0db871d264bf99e8779f0917ee63d40730af0552f71535a6e072", size = 222647, upload-time = "2026-07-15T18:55:47.544Z" }, + { url = "https://files.pythonhosted.org/packages/84/f8/b8cba872162356fb44ac79c10309d987206a4461e32072fc29228dad7331/coverage-7.15.2-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:e370c12133095ff18432de8c044962be85a5a96d90c6fcbce8e17e76236d2328", size = 264013, upload-time = "2026-07-15T18:55:49.768Z" }, + { url = "https://files.pythonhosted.org/packages/ee/67/a807a7586d0b8cae485308ddd55756f0806c92f8e0b411bacbf23c48edf3/coverage-7.15.2-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fe41909c9515c3bfdb5f02c4d1f857dba322d9a9a1178069b91eea77889df63a", size = 266135, upload-time = "2026-07-15T18:55:51.941Z" }, + { url = "https://files.pythonhosted.org/packages/ce/67/cd78771dc985f7e4ebdcc82b1a96d9a932af9e806f01f2f91a89f4c72e80/coverage-7.15.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6aa28cfb6488e5453b5b762d65f73aa586380f6693a04d58078ce228a29b06c0", size = 268555, upload-time = "2026-07-15T18:55:54.065Z" }, + { url = "https://files.pythonhosted.org/packages/18/3e/10134cf81275188c58568f324fc74aedff32c63ca4d5bbc513a91944a6f0/coverage-7.15.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bcc0aae933921d03096f53b0b03eeb702129fd406dee59f08d2efacc68681fa5", size = 269674, upload-time = "2026-07-15T18:55:56.066Z" }, + { url = "https://files.pythonhosted.org/packages/75/4a/771b77de446cba985dc414bbc5844bd21604da05dbc044286df8318a48a7/coverage-7.15.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7c63387e21ab21f512c69c9756a8c7dadd322c7275edb064064433c9a09c3743", size = 263101, upload-time = "2026-07-15T18:55:58.107Z" }, + { url = "https://files.pythonhosted.org/packages/5f/b5/70a7011da15f4071943361183aefa27847f3e3aec4fd335f1cb3d3a622b1/coverage-7.15.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0e55510bc98ae943cece9e667a6c0fe94c6a92913720dea34243657a17993d0c", size = 266007, upload-time = "2026-07-15T18:56:00.468Z" }, + { url = "https://files.pythonhosted.org/packages/b4/0d/f9547e804ce7ad49646ffeffac26699510efbe6c0f751b66fdc960c4e825/coverage-7.15.2-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:2ff08701be2d1556fc78b326c80a3e8042da09352ecb3819105f8e386c8a3071", size = 263611, upload-time = "2026-07-15T18:56:02.615Z" }, + { url = "https://files.pythonhosted.org/packages/ac/59/f576a396659c0efd351f5c1544f67c3560e89c7761cabf7f65e412beeda5/coverage-7.15.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:38c9518b7103826c403a461544e3c2e77151e8676d06eaed85911a97e962584a", size = 267344, upload-time = "2026-07-15T18:56:04.622Z" }, + { url = "https://files.pythonhosted.org/packages/7c/5d/c2e4fce3579c0cb635024293f1a32bbe26df101b3e3a69f22243d1352b6c/coverage-7.15.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:dee88b1ed88587abd8c0269a1fc1f4cc77f7750d1dfde2869e2a123af420e67d", size = 262456, upload-time = "2026-07-15T18:56:06.641Z" }, + { url = "https://files.pythonhosted.org/packages/bb/dd/956287d69436b66094bc4b57ac2da71e43bfd2a5524e958900b9f582fcf8/coverage-7.15.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:2fbeeeecea279727f8ac16c8e1133ddfeee793e985c86ae343d6a5ce744eef8c", size = 264771, upload-time = "2026-07-15T18:56:08.795Z" }, + { url = "https://files.pythonhosted.org/packages/2c/5a/6f979530c2734c575de77cf58f5f28d51f7123a94b5030fd9156fe5f363c/coverage-7.15.2-cp314-cp314t-win32.whl", hash = "sha256:cb0fddaa6884be6aae36ced9544b5e90f7d5f03845a2853bf47a14953a4e8688", size = 224151, upload-time = "2026-07-15T18:56:10.856Z" }, + { url = "https://files.pythonhosted.org/packages/54/7e/27f6b2a74d484742f4017553e710b01e396b23d809df3e95ca0bb9a2824b/coverage-7.15.2-cp314-cp314t-win_amd64.whl", hash = "sha256:77f091ea3a9cc611cd29f433565476bc1936c084ac8eee00ea0e7e70c27e4199", size = 224981, upload-time = "2026-07-15T18:56:12.928Z" }, + { url = "https://files.pythonhosted.org/packages/b1/48/284863423aa474240f6842bd00d680da22f4e6ea2e466618ef7c9c9e69a9/coverage-7.15.2-cp314-cp314t-win_arm64.whl", hash = "sha256:6fc448c377d6eeb00a47c673494bd9bae29280ca53987e1869e67ebedfe20658", size = 224294, upload-time = "2026-07-15T18:56:15.156Z" }, + { url = "https://files.pythonhosted.org/packages/ec/82/32e3bd191d498e64f6f911ad55d14006a0861e54869d2d32452326399e65/coverage-7.15.2-py3-none-any.whl", hash = "sha256:eb6bcae8d1a9d305351ecb108232441d11c5cfe9de840a04388ba5d2db8d735c", size = 213375, upload-time = "2026-07-15T18:56:17.305Z" }, ] [[package]] @@ -365,7 +335,7 @@ dev = [ [package.metadata] requires-dist = [ - { name = "coverage", marker = "extra == 'dev'", specifier = "==7.15.4" }, + { name = "coverage", marker = "extra == 'dev'", specifier = "==7.15.2" }, { name = "cryptography", specifier = "==50.0.0" }, { name = "fastapi", specifier = "==0.141.1" }, { name = "httpx", specifier = "==0.28.1" }, @@ -375,7 +345,7 @@ requires-dist = [ { name = "pytest", marker = "extra == 'dev'", specifier = "==9.1.1" }, { name = "pyyaml", specifier = "==6.0.3" }, { name = "ruff", marker = "extra == 'dev'", specifier = "==0.16.1" }, - { name = "setuptools", marker = "extra == 'dev'", specifier = "==84.0.0" }, + { name = "setuptools", marker = "extra == 'dev'", specifier = "==83.0.0" }, { name = "uvicorn", specifier = "==0.52.1" }, ] provides-extras = ["dev"] @@ -756,11 +726,11 @@ wheels = [ [[package]] name = "setuptools" -version = "84.0.0" +version = "83.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6d/44/f5da03a8ef95d369145c5bb53050e7877c9f3d312e128605fd9504829143/setuptools-84.0.0.tar.gz", hash = "sha256:f4695c21257f0d9b537ec2692c941d02ee143b7cc1276941349a546573b2ef73", size = 1168449, upload-time = "2026-08-08T18:27:58.365Z" } +sdist = { url = "https://files.pythonhosted.org/packages/34/26/f5d29e25ffdb535afef2d35cdb55b325298f96debd670da4c325e08d70f4/setuptools-83.0.0.tar.gz", hash = "sha256:025bccbbf0fa05b6192bc64ae1e7b16e001fd6d6d4d5de03c97b1c1ade523bef", size = 1154254, upload-time = "2026-07-04T15:31:22.699Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/95/9c/c510029fc6ef33a6275cd2c5d3cecd6613dfd6aa401d57c54f1c18852ccf/setuptools-84.0.0-py3-none-any.whl", hash = "sha256:51a52592b3b99e102b609654876bd65f19f999935166d1352678931132b0c670", size = 818216, upload-time = "2026-08-08T18:27:56.719Z" }, + { url = "https://files.pythonhosted.org/packages/5d/40/e1e72872c6354b306daef1703549e8e83b4d43cfea356311bf722a043752/setuptools-83.0.0-py3-none-any.whl", hash = "sha256:29b23c360f22f414dc7336bb39178cc7bcbf6021ed2733cde173f09dba19abb3", size = 1008090, upload-time = "2026-07-04T15:31:20.885Z" }, ] [[package]] From 06866a8c9f341a263ce0283d3d0cbf39b7c175e3 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 21 Aug 2026 07:22:00 +0900 Subject: [PATCH 4/7] docs: correct official standards links --- docs/REFERENCES.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/REFERENCES.md b/docs/REFERENCES.md index cf1b627..778725d 100644 --- a/docs/REFERENCES.md +++ b/docs/REFERENCES.md @@ -110,10 +110,11 @@ normative final record in ADR 0001–0007. NIST SP 800-63B and SP 800-63C (June 2017; updated 2 March 2020) were withdrawn on 1 August 2025 and superseded by SP 800-63B-4 and SP 800-63C-4 (Final 31 July 2025). This bibliography cites the current finals opened at -https://csrc.nist.gov/pubs/sp/800/63/b/4/final, -https://csrc.nist.gov/pubs/sp/800/63/c/4/final, +https://csrc.nist.gov/pubs/sp/800/63/B/4/final, +https://csrc.nist.gov/pubs/sp/800/63/C/4/final, https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-63B-4.pdf, and -https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-63C-4.pdf. +https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-63C-4.pdf + Offline attachments under [`docs/papers/`](papers/README.md) may still hold the withdrawn 2017 texts; ADR 0001–0007 use the 2025 finals. From 0353001438efb060b85373c121f4d54dfd48e8c8 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 21 Aug 2026 07:22:49 +0900 Subject: [PATCH 5/7] docs: keep ADR metadata diff-clean --- docs/adr/0001-keycloak-hub.md | 2 +- docs/adr/0002-passwordless-local-accounts.md | 2 +- docs/adr/0003-identity-matching.md | 2 +- docs/adr/0004-desired-state-reconciliation.md | 2 +- docs/adr/0005-secret-ownership.md | 2 +- docs/adr/0006-user-operation-lock.md | 2 +- docs/adr/0007-automation-authority.md | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/adr/0001-keycloak-hub.md b/docs/adr/0001-keycloak-hub.md index e37e13e..cf35b72 100644 --- a/docs/adr/0001-keycloak-hub.md +++ b/docs/adr/0001-keycloak-hub.md @@ -1,7 +1,7 @@ # ADR-0001: Keep Keycloak and Keyverse as the ecosystem identity hub **Status:** Accepted -**Date:** 2026-08-09 +**Date:** 2026-08-09 **Last expanded:** 2026-08-18 ## Context diff --git a/docs/adr/0002-passwordless-local-accounts.md b/docs/adr/0002-passwordless-local-accounts.md index 11564fd..8974b03 100644 --- a/docs/adr/0002-passwordless-local-accounts.md +++ b/docs/adr/0002-passwordless-local-accounts.md @@ -1,7 +1,7 @@ # ADR-0002: Keep ecosystem-local accounts passwordless-first **Status:** Accepted -**Date:** 2026-08-09 +**Date:** 2026-08-09 **Last expanded:** 2026-08-18 ## Context diff --git a/docs/adr/0003-identity-matching.md b/docs/adr/0003-identity-matching.md index f76b7f4..94c7d9a 100644 --- a/docs/adr/0003-identity-matching.md +++ b/docs/adr/0003-identity-matching.md @@ -1,7 +1,7 @@ # ADR-0003: Use exact external subject, then verified email, then explicit link **Status:** Accepted -**Date:** 2026-08-09 +**Date:** 2026-08-09 **Last expanded:** 2026-08-18 ## Context diff --git a/docs/adr/0004-desired-state-reconciliation.md b/docs/adr/0004-desired-state-reconciliation.md index f06dc89..21b3559 100644 --- a/docs/adr/0004-desired-state-reconciliation.md +++ b/docs/adr/0004-desired-state-reconciliation.md @@ -1,7 +1,7 @@ # ADR-0004: Use side-effect-free preflight and re-observed desired-state reconciliation **Status:** Accepted -**Date:** 2026-08-09 +**Date:** 2026-08-09 **Last expanded:** 2026-08-18 ## Context diff --git a/docs/adr/0005-secret-ownership.md b/docs/adr/0005-secret-ownership.md index 5ff0a20..c9f167d 100644 --- a/docs/adr/0005-secret-ownership.md +++ b/docs/adr/0005-secret-ownership.md @@ -1,7 +1,7 @@ # ADR-0005: Separate portable configuration from deployment-private values **Status:** Accepted -**Date:** 2026-08-09 +**Date:** 2026-08-09 **Last expanded:** 2026-08-18 ## Context diff --git a/docs/adr/0006-user-operation-lock.md b/docs/adr/0006-user-operation-lock.md index 5bbaeab..b434fac 100644 --- a/docs/adr/0006-user-operation-lock.md +++ b/docs/adr/0006-user-operation-lock.md @@ -1,7 +1,7 @@ # ADR-0006: Share one user-operation lock across merge and SCIM full replacement **Status:** Accepted -**Date:** 2026-08-09 +**Date:** 2026-08-09 **Last expanded:** 2026-08-18 ## Context diff --git a/docs/adr/0007-automation-authority.md b/docs/adr/0007-automation-authority.md index ceed058..ac553bb 100644 --- a/docs/adr/0007-automation-authority.md +++ b/docs/adr/0007-automation-authority.md @@ -1,7 +1,7 @@ # ADR-0007: Separate autonomous development from review, merge, and release authority **Status:** Accepted -**Date:** 2026-08-09 +**Date:** 2026-08-09 **Last expanded:** 2026-08-18 ## Context From 8077aa46e120ea5977464f2e611d44ab44bab695 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 21 Aug 2026 23:29:26 +0900 Subject: [PATCH 6/7] docs: add product technical gap baseline --- CHANGELOG.md | 3 + DOCUMENTATION.md | 3 +- README.md | 1 + docs/TRACEABILITY.md | 1 + .../product-technical-gap-baseline.md | 68 ++++++++ docs/product-technical-gap-baseline.md | 160 ++++++++++++++++++ tests/test_documentation_contract.py | 2 + 7 files changed, 237 insertions(+), 1 deletion(-) create mode 100644 docs/doctoring/product-technical-gap-baseline.md create mode 100644 docs/product-technical-gap-baseline.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 78fa94a..23819a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -64,6 +64,9 @@ Keep a Changelog, and releases use semantic versioning. 2026-07-28, RFC 9207 callback-issuer validation, and RFC 9068 JWT access-token header, claim, signature, and algorithm rejection evidence; runtime acceptance remains unimplemented. +- Added the product/technical gap baseline and its APA 7th doctoring record, + including the current exact-head PR/Issue inventory and explicit + `gap-not-claimed` runtime and release boundaries. - Relying-party deployment controllers now send validated, secret-free metadata to Keyverse desired-state PUT instead of applying client representations directly to Keycloak; confidential credential placement remains a separate diff --git a/DOCUMENTATION.md b/DOCUMENTATION.md index 6f7f97a..150342c 100644 --- a/DOCUMENTATION.md +++ b/DOCUMENTATION.md @@ -14,6 +14,7 @@ Keyverse already has strong feature-specific specifications, doctoring, federati | Test strategy | [`docs/TEST_STRATEGY.md`](docs/TEST_STRATEGY.md) | | Operability/recovery/release | [`docs/OPERABILITY.md`](docs/OPERABILITY.md) | | Requirements/evidence traceability | [`docs/TRACEABILITY.md`](docs/TRACEABILITY.md) | +| Product and technical gap baseline | [`docs/product-technical-gap-baseline.md`](docs/product-technical-gap-baseline.md) and [`docs/doctoring/product-technical-gap-baseline.md`](docs/doctoring/product-technical-gap-baseline.md) | | Architecture decisions | [`docs/adr/README.md`](docs/adr/README.md) | | Federation onboarding | [`docs/federation-onboarding.md`](docs/federation-onboarding.md) | | RP onboarding | [`docs/rp-onboarding.md`](docs/rp-onboarding.md) | @@ -34,4 +35,4 @@ Keyverse already has strong feature-specific specifications, doctoring, federati - **external-system** — Keycloak/ADFS/LDAP/external OIDC/HR/IGA behavior not implemented by Keyverse itself. - **planned** — accepted target without executable implementation. -Open PR #72 OIDC RP claim mapper profile and PR #74 hourly GitHub API remediation remain active-PR until merged. Keyverse's current protected-main desired-state/reconciliation capabilities are documented independently from those changes. \ No newline at end of file +Open PR #72 OIDC RP claim mapper profile and PR #74 hourly GitHub API remediation remain active-PR until merged. Keyverse's current protected-main desired-state/reconciliation capabilities are documented independently from those changes. diff --git a/README.md b/README.md index dabd1ff..2c022e7 100644 --- a/README.md +++ b/README.md @@ -161,6 +161,7 @@ use two-word-or-longer snake_case names (`idp_config_entries`, | [`docs/adr/`](docs/adr/README.md) | Accepted architecture decisions (0001–0008 on this branch) | | [`docs/REFERENCES.md`](docs/REFERENCES.md) | APA 7th bibliography for ADR 0001–0007 | | [`docs/doctoring/`](docs/doctoring/) | Feature-specific standards interpretation | +| [`docs/product-technical-gap-baseline.md`](docs/product-technical-gap-baseline.md) | Current buyer-visible product and technical gap register | | [`docs/papers/`](docs/papers/README.md) | Offline copies of selected primary sources | | [`docs/operations/`](docs/operations/) | Operator runbooks, including hourly product development | | [`ARCHITECTURE.md`](ARCHITECTURE.md) | Runtime topology and trust boundaries | diff --git a/docs/TRACEABILITY.md b/docs/TRACEABILITY.md index 77d1dee..c337485 100644 --- a/docs/TRACEABILITY.md +++ b/docs/TRACEABILITY.md @@ -28,6 +28,7 @@ | contextual-orchestrator Keyverse identity and tenant boundary | ADR-0008; deployment-owned OIDC validation plus downstream scope RBAC and org/workspace ABAC | PR #109 `32ba3a9` adds the injected verifier seam; stacked PR #110 `8607eba` requires `VerifiedIdentity`, rejects boolean-only authorization, binds workflow/evaluation/batch resources to secret-free tenant context, and denies cross-tenant or ownerless reads; main remains unchanged until both protected PRs merge | active-PR | | sidecar anonymous-access boundary | ADR-0008; private service-boundary and least-privilege policy | newsdom-api protected `develop` `3d0426b` (PR #595) fail-closed token gate, startup credential registry, explicit anonymous opt-in, review-fixed authenticated examples/healthcheck/401 contract, and pypdf Trivy remediation; Keyverse-aware gateway evidence remains required for exposure | implemented-main | | 100% production statement/branch/docstring | CWL quality contract | CI/pytest/interrogate | implemented-main | +| product and technical gap baseline | PRD/TRD/ADR/operability and live exact-head evidence | [`docs/product-technical-gap-baseline.md`](product-technical-gap-baseline.md) plus [`docs/doctoring/product-technical-gap-baseline.md`](doctoring/product-technical-gap-baseline.md) | active-PR | ## Research, standards, and operations records diff --git a/docs/doctoring/product-technical-gap-baseline.md b/docs/doctoring/product-technical-gap-baseline.md new file mode 100644 index 0000000..2c7e69b --- /dev/null +++ b/docs/doctoring/product-technical-gap-baseline.md @@ -0,0 +1,68 @@ +# Product-technical gap baseline doctoring record + +**Date:** 2026-08-21 +**Scope:** Keyverse product, trust-boundary, PR queue, and release evidence + +## Interpretation + +This baseline separates deterministic repository evidence from live protocol, +consumer authorization, deployment, and release acceptance. Source/tests can +prove validators, reconciliation, locking, and documentation contracts; they +cannot prove a controlled passwordless login, downstream token acceptance, +production sizing, or immutable release provenance without an approved runtime +lane. Missing evidence is therefore `gap-not-claimed`, not synthetic success. + +The mapper policy remains closed: `role`, `org`, and `workspace` are issuer-side +claims, not tenant authorization. Consumers must independently validate issuer, +signature/JWKS, expiry, audience, resource, tenant, purpose, and RBAC. The +LineageWeave profile keeps `org` as one opaque tenant key and `workspace` as one +child namespace; ambiguous membership denies before authorization. + +## Current evidence interpretation + +- The protected-main head observed for this snapshot is + `ce207dfd42975db61c82a5963e206fc1db14ac2b`. +- The #112 root stack is at `31dd486cb97ca215da451151f618a954a07b0ea5` with + hosted checks pending and `REVIEW_REQUIRED`; local 100% evidence does not + replace hosted checks or independent approval. +- #104 was normally restacked onto #112 at `c623a3d8df6e0f6da0e9623b23e3178e0f0296f0`. + Its documentation-only conflict resolution preserved both CHANGELOG entries; + fresh hosted checks are required. +- Central `.github#1203` has a cancelled scheduler predecessor and a queued + retry. Cancellation is normal concurrency behavior; the observed age and + evidence do not satisfy D1–D5 emergency bypass criteria. +- Open issues #114, #102, #99, #71, and #2 remain tracked. No issue is treated + as implemented-main evidence merely because a design PR exists. + +## Standards interpretation + +OIDC exact issuer and audience validation, RFC 8725 token validation, RFC 9700 +authorization-code and PKCE guidance, RFC 8707 resource indicators, RFC 9728 +protected-resource metadata, RFC 9068 JWT access-token requirements, and RFC +9207 authorization-response issuer comparison are interpreted as consumer or +resource-server acceptance requirements where applicable. Keycloak mapper +configuration remains projection evidence only. + +No new frontend behavior is claimed by this baseline. A future UI change must +record its Figma File ID and Storybook scene/edge-event, accessibility, +interaction, performance, responsive, typography/color, animation, form, +navigation, and chart acceptance in the owning ADR. + +## Verification rule + +Every refresh must re-query the exact PR head, base, open review threads, formal +review decision, required CheckRun conclusions, and merge state. A changed head +invalidates prior evidence. Protected merges remain normal PR merges; guarded +force merge is permitted only after all D1–D5 and emergency acceptance criteria +are independently proven. No bypass, force push, direct protected push, fake +status, or self-approval is part of this record. + +## APA 7th references + +- OpenID Foundation. (2014). *OpenID Connect Core 1.0*. https://openid.net/specs/openid-connect-core-1_0-18.html +- Internet Engineering Task Force. (2020). *JSON Web Token best current practices* (RFC 8725). https://www.rfc-editor.org/rfc/rfc8725.html +- Lodderstedt, T., Bradley, J., Labunets, A., & Fett, D. (2025). *Best current practice for OAuth 2.0 security* (RFC 9700). https://www.rfc-editor.org/rfc/rfc9700.html +- Internet Engineering Task Force. (2018). *OAuth 2.0 authorization server metadata* (RFC 8414). https://doi.org/10.17487/RFC8414 +- Internet Engineering Task Force. (2020). *Resource indicators for OAuth 2.0* (RFC 8707). https://doi.org/10.17487/RFC8707 +- Internet Engineering Task Force. (2025). *OAuth 2.0 protected resource metadata* (RFC 9728). https://doi.org/10.17487/RFC9728 +- Model Context Protocol. (2026, July 28). *Authorization*. https://modelcontextprotocol.io/specification/2026-07-28/basic/authorization diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md new file mode 100644 index 0000000..fb48a23 --- /dev/null +++ b/docs/product-technical-gap-baseline.md @@ -0,0 +1,160 @@ +# Keyverse product and technical gap baseline + +**Evidence snapshot:** 2026-08-21T14:24:31Z (UTC) +**Repository:** `ContextualWisdomLab/keyverse` +**Protected-main head observed:** `ce207dfd42975db61c82a5963e206fc1db14ac2b` +**Status:** live inventory and gap register; not a release acceptance record + +This baseline joins the product, architecture, ADR, standards, operations, +and exact-head GitHub evidence into one executable backlog. It distinguishes +protected-main evidence from open-PR work, accepted contracts, and claims that +remain intentionally unverified. + +## Product and authority boundary + +Keyverse is a standalone and embeddable identity control plane for CWL, Naruon, +and sibling products. It owns passwordless-first Keycloak policy, federation +and directory preflight/reconciliation, account unification, SCIM lifecycle, +relying-party desired state, audit, and safe deployment operations. + +Downstream applications own token signature/issuer/audience validation, +tenant/resource/purpose ABAC, and bounded RBAC. A Keycloak mapper receipt is +issuer-side configuration evidence, never proof that a relying party accepts a +token or enforces authorization. + +## Evidence vocabulary + +| Classification | Meaning | +|---|---| +| `implemented-main` | Source and representative tests are on protected `main`. | +| `active-PR` | Work exists only in an open PR and is not released evidence. | +| `active-issue` | An open issue records a product or operational gap. | +| `accepted-contract` | An ADR or standard defines policy; runtime acceptance may still be absent. | +| `gap-not-claimed` | The repository makes no success claim until stronger evidence exists. | + +Queued, cancelled, skipped-required, stale, predecessor-head, and +rate-limited checks are not successful evidence. Formal approval must bind to +the exact current head and satisfy the latest-pusher and independent-review +rules. + +## Capability and buyer acceptance map + +| Capability | Current maturity | Buyer-visible boundary | +|---|---|---| +| Passwordless local identity | `implemented-main` | Realm validators and tests protect WebAuthn/passwordless policy; live login remains separate evidence. | +| Federation and LDAP preflight | `implemented-main` | Validators are side-effect-free; external bind/discovery and apply remain separate. | +| Account merge and SCIM full replacement | `implemented-main` | Verified identity matching, tombstones, audit, and shared merge/PUT locking are covered on main. | +| SCIM `PATCH active=false` lock parity | `active-PR` | PR #113 is not protected-main evidence until its current head passes all gates and merges. | +| Closed RP mapper profile | `implemented-main` / `accepted-contract` | Canonical `role`, `org`, and `workspace` claims remain closed; consumers must prove their own authorization. | +| Real login and token acceptance | `gap-not-claimed` | No live controlled passwordless browser flow, token exchange, downstream ABAC/RBAC, or revocation acceptance is claimed. | +| Standalone Compose/Helm operation | `implemented-main` / `gap-not-claimed` | Repository validators exist; deployment secret/configuration, rollback, and immutable artifact evidence remain required. | +| Product loop and protected merge | `active-PR` | The scheduler and review path must bind every decision to a current exact head. | +| Release artifact acceptance | `gap-not-claimed` | Version, immutable image digest, SBOM/provenance, rollback, and exact-main regression are still release gates. | + +## Current exact-head PR inventory + +This table was queried from the live GitHub state at the snapshot time. Counts +exclude informational CodeRabbit/Devin contexts and count only CheckRun +success, skipped, or non-terminal results. + +| PR | Scope | Base | Exact head | Checks | Gate / next safe action | +|---:|---|---|---|---|---| +| [#113](https://github.com/ContextualWisdomLab/keyverse/pull/113) | SCIM deactivation shared lock | `ce207dfd42975db61c82a5963e206fc1db14ac2b` | `9bd33ee0d00ef1874fd5efabac3462f678a256ed` | 21 success / 8 skipped / 1 pending | `REVIEW_REQUIRED`; obtain exact-head independent approval and finish the pending review gate. | +| [#112](https://github.com/ContextualWisdomLab/keyverse/pull/112) | Account-unification lockfile and stacked contract updates | `ce207dfd42975db61c82a5963e206fc1db14ac2b` | `31dd486cb97ca215da451151f618a954a07b0ea5` | 0 success / 7 skipped / 14 pending | `REVIEW_REQUIRED`; local evidence is complete, but hosted Checks and independent approval are pending. | +| [#104](https://github.com/ContextualWisdomLab/keyverse/pull/104) | ADR 0001–0007 and operator README expansion | `31dd486cb97ca215da451151f618a954a07b0ea5` | `c623a3d8df6e0f6da0e9623b23e3178e0f0296f0` | 0 success / 0 skipped / 4 pending | Restacked normally; wait for fresh Checks and independent review before merging into the #112 stack. | +| [#103](https://github.com/ContextualWisdomLab/keyverse/pull/103) | Hierarchical authorization, login helper, and PATs | `ce207dfd42975db61c82a5963e206fc1db14ac2b` | `77b8f4ea9995329f1c55b916d110b460b4bc7649` | 20 success / 8 skipped / 1 pending | `REVIEW_REQUIRED`; retain fail-closed security boundary and obtain current approval. | +| [#101](https://github.com/ContextualWisdomLab/keyverse/pull/101) | Coupled Python dependency updates | `ce207dfd42975db61c82a5963e206fc1db14ac2b` | `50dd9c96cab5c230f775685e8baea939fba390dd` | 22 success / 8 skipped / 0 pending | `REVIEW_REQUIRED`; revalidate against the final lockfile stack and obtain approval. | +| [#100](https://github.com/ContextualWisdomLab/keyverse/pull/100) | LineageWeave account-derived RP profile | `ce207dfd42975db61c82a5963e206fc1db14ac2b` | `ede8075f82bb082b7d992b824992bf44792f744e` | 18 success / 7 skipped / 2 pending | `REVIEW_REQUIRED`; downstream issuer/audience/tenant acceptance remains unclaimed. | +| [#83](https://github.com/ContextualWisdomLab/keyverse/pull/83) | Remove runtime application RPs from portable realm | `ce207dfd42975db61c82a5963e206fc1db14ac2b` | `dd1ab7444a75342b42e3af013ccda6d1dbfb359d` | 22 success / 8 skipped / 0 pending | `REVIEW_REQUIRED`; confirm exact-head approval and latest-pusher policy before merge. | + +The central coordination PR [`.github#1203`](https://github.com/ContextualWisdomLab/.github/pull/1203) +is open at exact head `babb411e5132f67a665e302eb492da69f7d91afb` against +`731af58e954901c4f1cc853231c592abb1eaf617`. Its `scan-pr-queue` run +`32487969901` was cancelled by the normal concurrency scheduler; successor run +`32488287515` is queued. This is not a source failure or D1–D5 emergency +evidence, so no bypass or guarded force merge is allowed. + +## Open issue inventory + +| Issue | Signal | Classification | Required outcome | +|---:|---|---|---| +| [#114](https://github.com/ContextualWisdomLab/keyverse/issues/114) | MCP-compatible OAuth authorization for headless agents | `active-issue` | Independently review the design, then prove a real resource-bound client flow before runtime implementation. | +| [#102](https://github.com/ContextualWisdomLab/keyverse/issues/102) | Hierarchical authorization plane and PATs | `active-PR` | Prove tenant/resource fail-closed behavior and current-head security review. | +| [#99](https://github.com/ContextualWisdomLab/keyverse/issues/99) | Orphaned federation and product-loop identities | `active-issue` | Preserve the registry recurrence detector and central coordination evidence. | +| [#71](https://github.com/ContextualWisdomLab/keyverse/issues/71) | Remove runtime application RPs from portable import | `active-PR` | Merge #83 only after exact protected evidence. | +| [#2](https://github.com/ContextualWisdomLab/keyverse/issues/2) | Central IdP and external-IdP federation | `accepted-contract` | Complete approved-environment acceptance without weakening preflight boundaries. | + +## Buyer-visible gap order + +### G0 — Protected queue convergence + +The repository must distinguish current, reviewed, passing artifacts from stale +or coupled proposals. The loop is inventory, review disposition, focused fix, +exact-head local and hosted checks, independent approval, protected merge, merge +SHA verification, and re-listing. Never self-approve, force-push, admin-merge, +publish fake status, or reuse predecessor evidence. + +### G1 — Controlled real login and authorization acceptance + +In an approved environment, prove discovery/issuer, JWKS signature and allowed +algorithm, authorization-code + PKCE `S256`, passwordless browser login, token +`iss`/`sub`/`aud`/time claims, logout, tenant/resource ABAC, role/scope RBAC, +cross-tenant denial, and verifier-unavailable fail-closed behavior. An +unavailable issuer stays `unavailable`; it is never replaced with a synthetic +success. + +### G2 — Downstream tenant semantics + +For `lineageweave-web`, `org` is one opaque external tenant key and `workspace` +is one child namespace. Ambiguous or missing membership denies before ABAC/RBAC; +membership changes require a new token or session. Generic tenant claims must +not be added to the closed mapper profile. + +### G3 — SCIM concurrency and database evidence + +After #113, prove real concurrent PATCH/merge behavior on protected main. For +production storage, add PostgreSQL migration/rollback, tenant-qualified +constraints, concentrated-tenant skew measurements, partition/index decisions, +backup/restore, and recovery evidence. Local SQLite tests are not that proof. + +### G4 — MCP resource authorization + +The design-only ADR requires Keycloak authorization code + PKCE, exact redirects, +RFC 8707 resource binding, RFC 9728 protected-resource metadata, RFC 9207 +callback issuer comparison, RFC 9068 JWT validation, revocation, and negative +evidence. Runtime MCP acceptance remains `gap-not-claimed`. + +### G5 — Release and module acceptance + +On exact protected main, complete regression and controlled deployment +acceptance, publish immutable image digest plus SBOM/provenance, and prove +rollback. A green feature PR is not a release. + +## Loop and design boundary + +The hourly PR steward may advance only trusted same-repository PRs with exact +head, independent approval, and required Checks. The hourly product loop may +create at most one bounded draft product-gap PR only after the open queue is +empty and protected-main evidence is healthy. GitHub review/check waiting is not +a reason to stop independent review, documentation, or test design, but queued +results are never promoted to success. + +This repository has no current frontend change in this baseline. Therefore no +Figma file or Storybook inventory is claimed. If a future buyer gap changes a +web surface, its ADR must record the Figma File ID, design tokens, reusable +components, Storybook scene/edge events, and accessibility/interaction/ +performance/responsive/form/navigation/chart acceptance before implementation +is claimed. + +## APA 7th references + +- OpenID Foundation. (2014). *OpenID Connect Core 1.0*. https://openid.net/specs/openid-connect-core-1_0-18.html +- Internet Engineering Task Force. (2020). *JSON Web Token best current practices* (RFC 8725). https://www.rfc-editor.org/rfc/rfc8725.html +- Lodderstedt, T., Bradley, J., Labunets, A., & Fett, D. (2025). *Best current practice for OAuth 2.0 security* (RFC 9700). https://www.rfc-editor.org/rfc/rfc9700.html +- Internet Engineering Task Force. (2018). *OAuth 2.0 authorization server metadata* (RFC 8414). https://doi.org/10.17487/RFC8414 +- Internet Engineering Task Force. (2020). *Resource indicators for OAuth 2.0* (RFC 8707). https://doi.org/10.17487/RFC8707 +- Internet Engineering Task Force. (2025). *OAuth 2.0 protected resource metadata* (RFC 9728). https://doi.org/10.17487/RFC9728 +- Model Context Protocol. (2026, July 28). *Authorization*. https://modelcontextprotocol.io/specification/2026-07-28/basic/authorization + +Interpretations and evidence boundaries are maintained in +[`docs/doctoring/product-technical-gap-baseline.md`](doctoring/product-technical-gap-baseline.md). diff --git a/tests/test_documentation_contract.py b/tests/test_documentation_contract.py index c5c0657..6d8ab7b 100644 --- a/tests/test_documentation_contract.py +++ b/tests/test_documentation_contract.py @@ -17,6 +17,8 @@ "docs/TEST_STRATEGY.md", "docs/OPERABILITY.md", "docs/TRACEABILITY.md", + "docs/product-technical-gap-baseline.md", + "docs/doctoring/product-technical-gap-baseline.md", "docs/adr/README.md", "README.md", "AGENTS.md", From fdf62541a0732adbee5faf0da1c4773d2f9bc37f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 21 Aug 2026 23:31:29 +0900 Subject: [PATCH 7/7] docs: bind baseline to successor head --- docs/doctoring/product-technical-gap-baseline.md | 4 +++- docs/product-technical-gap-baseline.md | 6 ++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/doctoring/product-technical-gap-baseline.md b/docs/doctoring/product-technical-gap-baseline.md index 2c7e69b..1e4bd57 100644 --- a/docs/doctoring/product-technical-gap-baseline.md +++ b/docs/doctoring/product-technical-gap-baseline.md @@ -27,7 +27,9 @@ child namespace; ambiguous membership denies before authorization. replace hosted checks or independent approval. - #104 was normally restacked onto #112 at `c623a3d8df6e0f6da0e9623b23e3178e0f0296f0`. Its documentation-only conflict resolution preserved both CHANGELOG entries; - fresh hosted checks are required. + the baseline addition then advanced it to successor + `8077aa46e120ea5977464f2e611d44ab44bab695`; fresh hosted checks are required + for the successor and neither head is protected-main evidence. - Central `.github#1203` has a cancelled scheduler predecessor and a queued retry. Cancellation is normal concurrency behavior; the observed age and evidence do not satisfy D1–D5 emergency bypass criteria. diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md index fb48a23..167cb21 100644 --- a/docs/product-technical-gap-baseline.md +++ b/docs/product-technical-gap-baseline.md @@ -74,6 +74,12 @@ is open at exact head `babb411e5132f67a665e302eb492da69f7d91afb` against `32488287515` is queued. This is not a source failure or D1–D5 emergency evidence, so no bypass or guarded force merge is allowed. +This baseline is itself carried by #104. The table records that PR at the +pre-baseline snapshot head `c623a3d8df6e0f6da0e9623b23e3178e0f0296f0`; adding +this baseline advanced it normally to successor `8077aa46e120ea5977464f2e611d44ab44bab695`. +The successor's hosted Checks and review state must be read from the live PR, +and neither snapshot is protected-main evidence. + ## Open issue inventory | Issue | Signal | Classification | Required outcome |