You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 5e23b8b
Browse filesBrowse the repository at this point in the historyBrowse files
feat(contract-appeals): add the contract appeals resource
`list_contract_appeals` and `get_contract_appeal` cover `/api/contract_appeals/`, the Contract Disputes Act decisions of the civilian and defense boards of contract appeals, with all ten filters as named parameters and a 21-field `ContractAppeal` model whose fields are optional so a withheld `decision_text` stays absent rather than null.
The vendored contract moves to 4.26.0 so the conformance gate can see the new method, which also picks up two SLED fields the SDK had been rejecting client-side.
The shape schema is hand-curated because the overlay generator guesses four of these fields wrong, and the generated overlay wins at lookup time.
Copy file name to clipboardExpand all lines: CHANGELOG.md
+16Lines changed: 16 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -7,6 +7,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
8
8
## [Unreleased]
9
9
10
+
### Added
11
+
-**Contract Disputes Act appeals** (Tango API 4.26.0). Two methods over the new `/api/contract_appeals/` resource: `list_contract_appeals()` and `get_contract_appeal()`, plus a `ContractAppeal` schema and the `CONTRACT_APPEALS_MINIMAL` / `CONTRACT_APPEALS_COMPREHENSIVE` defaults. These are decisions of the Civilian Board of Contract Appeals (CBCA) and the Armed Services Board of Contract Appeals (ASBCA) — appeals of a contracting officer's final decision under an existing contract, which is a different dispute from a bid protest and shares no identifiers with `list_protests()`. All ten of the API's filters are explicit named parameters.
12
+
13
+
Three behaviors are documented on the methods and the model, because each is otherwise a support question. **`decision_text` needs an Enterprise plan, and below that tier the key is absent rather than null** — read it with `.get()`. **Searching that text is free at every plan**: `search=` matches inside the decision body and returns no fragment of it, so buying the body does not change what you can find. And **`docket` accepts a docket as it is cited** — a leading `CBCA` / `ASBCA` and `No.` / `Nos.` are ignored, while commas stay part of a docket (`7092-C(6682, 6765, 6767)`) rather than separating two.
14
+
15
+
Neither default shape names `decision_text`: the body runs to roughly 100K characters, so putting it in a default would make every fetch ask for something most callers cannot read. `ordering="rank"` requires a non-empty `search`, matching the API.
16
+
-**`alerts.contract_appeal.match` is an alertable event type.**`create_webhook_alert(query_type="contract_appeal", ...)` works. That method's `query_type` docstring now names the full current set and points at `list_webhook_event_types()` as the authority, since a list written into a docstring can only go stale.
17
+
18
+
### Changed
19
+
- Re-vendored `contracts/filter_shape_contract.json` (Tango API 4.26.0, schema_version 2, 49 resources) and regenerated `tango/shapes/generated_overlay.py` from it. Besides contract appeals this picks up two SLED fields the older contract could not see — `delisted_at` on a solicitation and `meta(jurisdiction_declared)` — which the API returns and the SDK schema was rejecting client-side. Both conformance gates report zero errors and zero new shape-coverage drift, and no baseline entries were added.
20
+
- The `ContractAppeal` shape schema is hand-curated in `tango/shapes/explicit_schemas.py` rather than left to the generated overlay. The generator falls back to name heuristics for a resource the live-API type probe has not sampled yet, and those heuristics get four of this resource's fields wrong: `docket_numbers` is a list, `listed` and `decision_date_repaired` are booleans, and `listing_year` is an integer.
21
+
22
+
### Documentation
23
+
- New **Contract Appeals** section in `docs/API_REFERENCE.md`, leading with what separates an appeal from a protest, plus the two new `ShapeConfig` constants in the shapes table.
24
+
- Added `alerts.contract_appeal.match` to the event-type listing in `docs/WEBHOOKS.md`.
Contract Disputes Act appeal decisions from the Civilian Board of Contract Appeals (CBCA) and the Armed Services Board of Contract Appeals (ASBCA).
1221
+
1222
+
**These are not bid protests.** A protest challenges an award or a solicitation before performance; an appeal here challenges a contracting officer's final decision under an existing contract — a claim, a termination, a differing-site-conditions dispute. Protests live at [Protests](#protests) and share no identifiers with this resource.
1223
+
1224
+
One row is one decision as the board's own listing publishes it, so several fields describe the listing rather than the dispute. `listed` goes false once the board's newest listing stops carrying the decision; the row is kept, never dropped.
1225
+
1226
+
### list_contract_appeals()
1227
+
1228
+
List appeal decisions with filtering and shaping.
1229
+
1230
+
```python
1231
+
appeals = client.list_contract_appeals(
1232
+
page=1,
1233
+
limit=25,
1234
+
shape=ShapeConfig.CONTRACT_APPEALS_MINIMAL,
1235
+
# Filter parameters (all optional)
1236
+
board=None,
1237
+
docket=None,
1238
+
appellant=None,
1239
+
judge=None,
1240
+
decision_type=None,
1241
+
decision_date_after=None,
1242
+
decision_date_before=None,
1243
+
listed=None,
1244
+
document_id=None,
1245
+
search=None,
1246
+
ordering=None,
1247
+
)
1248
+
```
1249
+
1250
+
**Filter Parameters:**
1251
+
-`board` - `"cbca"` or `"asbca"`; OR both with `"cbca|asbca"`
1252
+
-`docket` - Exact docket match, e.g. `"3288-R"`, `"59116"` or `"7092-C(6682, 6765, 6767)"`. A leading `CBCA` / `ASBCA` and `No.` / `Nos.` are ignored, so a docket can be pasted as it is cited. Commas are part of a docket, never a separator; OR several with `|`
1253
+
-`appellant` - Case-insensitive substring match on the appellant's name (min 2 characters)
1254
+
-`judge` - Case-insensitive exact match on the judge as the listing names them
1255
+
-`decision_type` - CBCA only: `"Decision"`, `"Dismissal"`, `"Order"`, `"Full Board Order"`, or the listing's own text. ASBCA rows carry no type
1256
+
-`decision_date_after` / `decision_date_before` - Decision date range (`YYYY-MM-DD`)
1257
+
-`listed` - Whether the board's newest listing still carries the decision
1258
+
-`document_id` - The board's own document id
1259
+
-`search` - Ranked full-text search over the appellant and the full decision text (min 2 characters); wrap in double quotes for a phrase
1260
+
-`ordering` - `decision_date` (the default, as `-decision_date`), `appellant`, `first_listed_at` or `rank`. `rank` requires a non-empty `search`
1261
+
1262
+
**Returns:**[PaginatedResponse](#paginatedresponse) with appeal-decision dictionaries
-`docket_numbers` is a list — the dockets with the board prefix stripped (`3288-R`, `59116`), and a consolidated appeal carries several. `docket_raw` keeps the listing's own text, and `docket_source` says where the dockets came from.
1292
+
-**Reading `decision_text` requires an Enterprise plan, and below that tier the key is absent rather than null** — read it with `.get()`, not `[...]`. Neither default shape names it: the body runs to roughly 100K characters, so asking for it on every row is rarely what you want.
1293
+
-**Searching that text is free at every plan.**`search=` matches inside the decision body and returns no fragment of it, so buying the body does not change what you can find.
1294
+
1295
+
---
1296
+
1217
1297
## State & Local (SLED)
1218
1298
1219
1299
State, local and education procurement — solicitations that never appear on SAM.gov because they were never federal. **Beta**: coverage is partial and grows one jurisdiction at a time.
|`CONTRACT_APPEALS_COMPREHENSIVE`|`get_contract_appeal`| The list fields plus docket_raw, docket_source, decision_date_repaired, decision_type_raw, listing_year, first_listed_at, listed, text_status, text_char_count (omits `decision_text`, which needs an Enterprise plan) |
0 commit comments