The proxy cache held every GET on the whole service for 60 seconds, and these APIs accept writes. Kong's proxy cache has no invalidation on write: a POST that changes a product passes through the same gateway, and the gateway goes on serving the value it captured before it, for up to a minute, with nothing in the response saying so. It produced a real wrong answer. An agent woken by a stock event read the figure from before that event and reported, in one sentence, "68 in stock, above the threshold" and "it crossed the threshold, from 68". The manifest is kept, unreferenced, carrying the reason — so reinstating it is a decision rather than a rediscovery. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
workforce-api
Kong routing/policy layer for the workforce slice of
classic-models-api (see /Users/stephanetailland/git/classic-models-api),
split out per the DDD decomposition in ../kong-konnect-deployment-strategy.md.
The backend stays a single monolith — only Kong does the domain split.
Structure mirrors ../sales-api/../payments-api — single MCP route from
the start, not ../catalog-api's original two-route pattern (see
../STATUS.md section 18).
Two resources, both with a REST surface and MCP tools:
Office(single keyofficecode) — full CRUD + anemployeessub-resource.Employee(single keyemployeenumber) — full CRUD +reports(direct reports, self-referential FK) andcustomers(assigned sales rep's customers) sub-resources.
| Route | Method | Backend path |
|---|---|---|
/workforce/v1/offices/ |
GET, POST | /classic-models/api/v1/offices/ |
/workforce/v1/offices/{officecode}/ |
GET, PUT, PATCH, DELETE | /classic-models/api/v1/offices/{officecode}/ |
/workforce/v1/offices/{officecode}/employees/ |
GET | /classic-models/api/v1/offices/{officecode}/employees/ |
/workforce/v1/employees/ |
GET, POST | /classic-models/api/v1/employees/ |
/workforce/v1/employees/{employeenumber}/ |
GET, PUT, PATCH, DELETE | /classic-models/api/v1/employees/{employeenumber}/ |
/workforce/v1/employees/{employeenumber}/reports/ |
GET | /classic-models/api/v1/employees/{employeenumber}/reports/ |
/workforce/v1/employees/{employeenumber}/customers/ |
GET | /classic-models/api/v1/employees/{employeenumber}/customers/ |
/workforce-mcp |
GET, POST, DELETE | MCP streamable-http |
Auth & MCP tool ACLs — the one domain where customer gets zero access
Both viewsets use ReferenceDataPermission
(authentication/permissions.py): read_only/product_manager/support
get read access, writes are admin-only, and — unlike every other
domain built this session — customer is not in the allow-list at
all, not even for reads. Verified directly against the permission
class, not assumed: this is genuinely internal staff/organizational data,
and the backend enforces that explicitly. All 7 read tools
(list-offices, get-office, get-office-employees, list-employees,
get-employee, get-employee-reports, get-employee-customers) have ACL
[admin, read_only, product_manager, support] — no customer. Both write
tools (create-office, create-employee) are admin only.
openid-connect on both surfaces (workforce-api REST audience,
mcp-workforce MCP audience), RFC 8693 exchange chain
MCP → workforce-api → classic-models-api. IdP: local Keycloak.
Structure
Same dev/ + kong/rest/ + kong/mcp/ layout as sales-api. Deploy key
and broker secret reused/mirrored from catalog-api and the live broker
client, same pattern as every domain since payments-api.