Skip to content

Commit e203c2b

Browse files
vdavezclaude
andcommitted
feat(shapes): capture every API-exposed field/expand + reverse-coverage gate
The conformance check only validated one direction — that the SDK's shape constants reference *allowed* fields. Nothing checked the reverse, so the hand-maintained explicit_schemas.py had silently fallen 200+ fields behind the API: 209 leaf fields, 41 nested expand branches, and 3 resources (naics, psc, mas_sins) the typed shape API couldn't request at all. Close all of them with a generated overlay (tango/shapes/generated_overlay.py, merged over the base by SchemaRegistry), with types resolved from live-API sampling rather than guessed. Code-object expands (set_aside, award_type, idv_type, …) — previously modeled inconsistently as str / bare dict — now uniformly resolve to {code, description} via the shared CodeDescription schema. Add a reverse-coverage gate (scripts/check_shape_coverage.py) that fails when Tango's shape trees expose anything the SDK misses and it isn't baselined. Offline against the vendored contract — no token, runs on forks — and wired into the lint.yml conformance job. Regenerate the overlay with scripts/generate_shape_overlay.py (from the vendored contract + contracts/observed_shape_types.json, no API key); refresh the observations with scripts/probe_shape_types.py (maintainer-run, needs a key). Verified: 428 tests pass, mypy clean, ruff clean, conformance passes, coverage gate at 0, and previously-uncapturable shapes now validate end-to-end while bad fields are still rejected. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent dc9a2d3 commit e203c2b

10 files changed

Lines changed: 9878 additions & 0 deletions

File tree

.github/workflows/lint.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ name: Linting
1111
# vendored contract against the tango repo's HEAD and emits a staleness
1212
# notice (never a failure — tango HEAD may carry unreleased changes).
1313
# Refresh the vendored contract with scripts/refresh_contract.py.
14+
# - Reverse shape-coverage is a HARD gate: check_shape_coverage.py fails when
15+
# Tango's shape trees expose a field/expand the SDK schema doesn't capture and
16+
# it isn't in contracts/shape_coverage_baseline.json. Also offline (vendored
17+
# contract), so it runs on forks. Burn the baseline down with the generator.
1418
on:
1519
workflow_dispatch:
1620
push:
@@ -68,6 +72,13 @@ jobs:
6872
- name: Check SDK filter/shape conformance (vendored contract)
6973
run: uv run python scripts/check_filter_shape_conformance.py
7074

75+
- name: Check reverse shape coverage (Tango exposes -> SDK captures)
76+
# Complements the conformance check with the OTHER direction: fails when
77+
# Tango's shape trees expose a field/expand the SDK schema doesn't capture
78+
# and it isn't in contracts/shape_coverage_baseline.json. Also offline
79+
# against the vendored contract — no secrets, works on forks.
80+
run: uv run python scripts/check_shape_coverage.py
81+
7182
# --- Staleness notice (best-effort, never fails the job) ---------------
7283
- name: Determine token availability
7384
id: gate

CHANGELOG.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
88
## [Unreleased]
99

1010
### Added
11+
- **Reverse shape-coverage: the SDK now captures every field and expand the API
12+
returns.** The conformance check only validated one direction — that the SDK's
13+
shape constants reference *allowed* fields. Nothing checked the reverse, so the
14+
hand-maintained `tango/shapes/explicit_schemas.py` had silently fallen ~200+
15+
fields behind the API: 209 leaf fields, 41 whole nested expand branches, and 3
16+
resources (naics, psc, mas_sins) the typed shape API could not request at all.
17+
A new generated overlay (`tango/shapes/generated_overlay.py`, produced by
18+
`scripts/generate_shape_overlay.py` and merged over the base by `SchemaRegistry`)
19+
closes all of them, with types resolved from live-API sampling rather than
20+
guessed. Notable now-shapeable data: contract/IDV acquisition attributes
21+
(`fair_opportunity_limited_sources`, `subcontracting_plan`, …), `contracts.officers`
22+
and `period_of_performance`, the full `organizations` hierarchy
23+
(`obligation_rank`, `l1..l8_fh_key`, `budget_appropriation`, `children`/`parent`),
24+
`otas`/`otidvs` `transactions`, and the deep `vehicles.awardees[.orders]` tree.
25+
Code-object expands (`set_aside`, `award_type`, `idv_type`, …), previously
26+
modeled inconsistently as `str`/bare `dict`, now uniformly resolve to
27+
`{code, description}`.
28+
- **Reverse shape-coverage gate.** `scripts/check_shape_coverage.py` walks Tango's
29+
shape trees (from the vendored contract) against the SDK schemas and fails when
30+
the SDK misses anything the API exposes and it isn't in
31+
`contracts/shape_coverage_baseline.json`. Offline against the vendored contract —
32+
no token, runs on forks — and wired into the `lint.yml` conformance job.
33+
Regenerate the overlay with `scripts/generate_shape_overlay.py` (from the vendored
34+
contract + `contracts/observed_shape_types.json`, no API key); refresh the type
35+
observations with `scripts/probe_shape_types.py` (maintainer-run, needs a key).
1136
- **Contract-first conformance system.** The canonical API filter/shape
1237
contract is now vendored at `contracts/filter_shape_contract.json` (refresh
1338
with the new `scripts/refresh_contract.py`), so the conformance check runs

0 commit comments

Comments
 (0)