v1.3.0
Added
-
DIBBS, exclusions, and SBIR/STTR endpoint support. Six endpoint families
Tango shipped in v4.16–v4.18 had no SDK support at all — no models, no methods.
Addedlist_dibbs_rfqs/get_dibbs_rfq,list_dibbs_rfps/get_dibbs_rfp,
list_dibbs_awards/get_dibbs_award,list_exclusions/get_exclusion,
list_sbir_topics/get_sbir_topic, and
list_sbir_solicitations/get_sbir_solicitation, with all 85 filter params,
shape schemas, andShapeConfigdefaults. New models:DibbsRfq,DibbsRfp,
DibbsAward,Exclusion,SbirTopic,SbirSolicitation.Two API behaviors are worth knowing.
is_open(DIBBS) and
is_currently_excluded(exclusions) are derived at query time, so filter with
theopen/activekwargs rather than shaping on those fields. And DIBBS
total_contract_priceis the order total repeated on every line item —
never sum it across rows; deduplicate on award + delivery-order number first. -
Reverse shape-coverage: the SDK now captures every field and expand the API
returns. The conformance check only validated one direction — that the SDK's
shape constants reference allowed fields. Nothing checked the reverse, so the
hand-maintainedtango/shapes/explicit_schemas.pyhad silently fallen ~200+
fields behind the API: 209 leaf fields, 41 whole nested expand branches, and 3
resources (naics, psc, mas_sins) the typed shape API could not request at all.
A new generated overlay (tango/shapes/generated_overlay.py, produced by
scripts/generate_shape_overlay.pyand merged over the base bySchemaRegistry)
closes all of them, with types resolved from live-API sampling rather than
guessed. Notable now-shapeable data: contract/IDV acquisition attributes
(fair_opportunity_limited_sources,subcontracting_plan, …),contracts.officers
andperiod_of_performance, the fullorganizationshierarchy
(obligation_rank,l1..l8_fh_key,budget_appropriation,children/parent),
otas/otidvstransactions, and the deepvehicles.awardees[.orders]tree.
Code-object expands (set_aside,award_type,idv_type, …), previously
modeled inconsistently asstr/baredict, now uniformly resolve to
{code, description}. -
Reverse shape-coverage gate.
scripts/check_shape_coverage.pywalks Tango's
shape trees (from the vendored contract) against the SDK schemas and fails when
the SDK misses anything the API exposes and it isn't in
contracts/shape_coverage_baseline.json. Offline against the vendored contract —
no token, runs on forks — and wired into thelint.ymlconformance job.
Regenerate the overlay withscripts/generate_shape_overlay.py(from the vendored
contract +contracts/observed_shape_types.json, no API key); refresh the type
observations withscripts/probe_shape_types.py(maintainer-run, needs a key). -
Contract-first conformance system. The canonical API filter/shape
contract is now vendored atcontracts/filter_shape_contract.json(refresh
with the newscripts/refresh_contract.py), so the conformance check runs
out of the box — locally, in CI, and on forks — with no tango checkout or
access token.scripts/check_filter_shape_conformance.pygained three new
checks on top of filter coverage: staleness (an SDK filter argument the
API no longer accepts is an error — it would silently no-op), types
(each argument's annotation is validated against the contract's
schema_version: 2per-filter type metadata), and a known-gaps baseline
(contracts/conformance_baseline.json) that downgrades accepted missing
params from errors to warnings so backlog is tracked instead of silent. A
new--suggestflag prints ready-to-paste typed parameter scaffolds for
any missing filters. The first run against the current API surface found 7
real coverage gaps, now baselined:keyon contracts/IDVs/OTAs/OTIDVs,
cageon entities,idon forecasts, andopportunity_idon
opportunities. -
TangoValidationErrornow exposes the API's structured validation details
directly:.issues(the list of{"path": ..., "reason": ...}entries the
server returns for shape errors) and.available_fields(the endpoint's
valid field set, when included). Both were previously reachable only by
digging through.response_data. (#45)
Changed
- The CI conformance job (
lint.yml) now runs unconditionally against the
vendored contract instead of silently skipping when
TANGO_API_REPO_ACCESS_TOKENis absent; the token is only used for a
best-effort staleness notice comparing the vendored contract to tango HEAD.
scripts/pr_review.pylikewise defaults its conformance step to the
vendored contract (override withTANGO_CONTRACT_MANIFEST). - 400 error messages now name the rejected field(s) and reason when the API
returns structuredissues— e.g.
Invalid request parameters: Invalid shape: tradeoff_process (unknown_field)
instead of justInvalid request parameters: Invalid shape. (#45)
Fixed
docs/WEBHOOKS.mdlisted only five of the eight webhook alert types. It
was missingalerts.exclusion.match,alerts.dibbs_rfq.match, and
alerts.dibbs_rfp.match. No SDK change was needed — event types are served by
the API and never hardcoded, so the new types already worked — but the
hand-written list had gone stale. Also documents two things that surprise
people: DIBBS awards are not alertable, and nothing fires when a record
merely lapses (an exclusion hitting its termination date, or an RFQ/RFP
passing its close date, emits no event). A production smoke test now asserts
every live event type appears in the doc, so the list cannot silently fall
behind again.- Refreshed the vendored API contract, which had gone stale by seven
resources. It tracked 25 resources against Tango's current 32, so the
conformance and coverage checks were validating against out-of-date truth and
could not see DIBBS, exclusions, SBIR, orbudget/accountsat all. Refreshing
it also surfaced 72 additional fields on existing resources, now covered.
Nested routes are keyed with a slash (budget/accounts), which silently broke
the oldbudget_accountsmapping — both keys are now accepted. check_filter_shape_conformance.pyno longer reports false stale params.
It resolved SDK arguments to API params only through an explicit
api_param_mappingdict, so methods that express the translation as tuple
tables —("account_title__icontains", account_title)and
range_filters = (("apportioned", apportioned, apportioned_gte, ...))— looked
like they exposed dozens of params the API rejects. They do not:
list_budget_accountscorrectly sendsapportioned__gte,fiscal_year__lte,
andaccount_title__icontains. The checker now understands both tuple forms
(inAssign,AnnAssign, and inlineforiterables), with an explicit
api_param_mappingstill taking precedence. This letbudget/accountsbe
conformance-checked for the first time; its genuinely missing lookup variants
(agency_code__in,bureau_name__icontains, …) are now baselined.