CONTROL_PLANE_REF_NAME held the random suffix the Gateway operator gives
its auto-created mirror CR, looked up by hand. Replacing the Konnect org
recreated those CRs under new suffixes, so the ref named an object that
no longer existed and the domain's Kong entities sat Programmed=False.
Name the ${NAME}-cp mirror declared in k8s-platform's dataplane-template
instead — derived from NAME, so it stays valid across Gateway
recreations and Konnect org changes.
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.