Skip to content

perf(people): bind Employment-separation evidence to no-store response policy #392

Open

Description

@seonghobae

Finding

Draft #317 currently classifies Employment-separation responses as governed commercial evidence after validating the JSON media type and response body semantics, but it does not validate the cache/privacy response policy.

The canonical People owner #64 emits every _send_json(...) response with Cache-Control: no-store and Vary: Authorization. A proxy, gateway, stale deployment, or alternate responder can therefore return the same 200/409 JSON envelope without the owner’s no-store/authorization-vary boundary and still be counted as governed buyer-path evidence.

For high-impact employment responses this is a material false-GREEN: the benchmark can certify latency/outcome evidence for a representation that does not preserve the canonical purpose-bound PII cache policy.

Owner evidence

  • services/people-api/src/orgmetra_people_api/http.py::_send_json emits content-type: application/json, cache-control: no-store, and vary: Authorization.
  • test(perf): add Employment separation buyer-path workload #317 employment_separation_buyer_path.js::parseJson currently requires only hasGovernedSeparationJsonMediaType(response.headers) before strict JSON parsing.

Repair contract

Test-first on #317:

  • accept canonical Cache-Control: no-store and Vary: Authorization case-insensitively by field name/token;
  • reject missing Cache-Control: no-store;
  • reject cache-permitting/conflicting policy such as public or s-maxage even when JSON/body semantics are otherwise valid;
  • require Authorization in Vary, while allowing additional valid Vary field names added by an intermediary;
  • reject malformed/collapsed ambiguous header representations rather than inferring safety;
  • integrate the cache-policy predicate before response body parsing so first-commit, replay, rejection, and contention share the same fail-closed transport boundary.

Do not weaken #317 latency/sample cardinality, do not claim hosted k6 GREEN from unit-level evidence, and keep this issue open until protected/integration acceptance carries the repair.

Activity

  1. seonghobae commented on Sep 15, 2026

    @seonghobae
    ContributorAuthor

    Current repair lineage is now materialized on Draft #317 without weakening the owner contract:

    • test-first contract: 079d2b737addb05961e9dbf32bce14a688118169;
    • response-policy validator: 839301222e19bec3976165093655d8ad7a6676d6;
    • buyer-path integration/current exact head: 79c5229717459ea6090d22652500484d9c5a0246.

    parseJson(response) now requires both the governed JSON media type and hasGovernedSeparationNoStorePolicy(response.headers) before strict body parsing, so first-commit, replay, rejection and contention all fail closed if the canonical cache/privacy policy is absent or ambiguous. The validator requires the current People owner representation (Cache-Control: no-store) and an explicit Authorization token in Vary; additional syntactically valid Vary field names remain allowed.

    This is deliberately stricter than generic HTTP necessity because the acceptance contract is binding to the current owner representation, not inventing a looser proxy-compatible substitute. RFC 9111 §5.2.2.5 makes no-store the operative anti-storage directive; RFC 9110 §12.5.5 notes that Authorization need not generally appear in Vary, but #64 intentionally emits it, so commercial evidence verifies the owner contract rather than treating RFC permissiveness as permission to drift.

    Focused boundary reproduction for the new predicate is 10/10 PASS. That is unit-level/focused evidence only. There is still no hosted exact-head k6 run, production-equivalent deployment, authenticated performance attestation, or measured p50/p95/p99/max on 79c522971.... Keep this issue open through protected/integration acceptance.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions