Skip to content

feat: Taxomind contract from its OpenAPI document (M09) - #1

Merged
petbrueck merged 2 commits into
stagingfrom
claude/bold-lamport-mz7zfk
Oct 21, 2026
Merged

petbrueck merged 2 commits into
stagingfrom
claude/bold-lamport-mz7zfk

Conversation

@petbrueck

Copy link
Copy Markdown
Contributor

Implements M09 from docs/plans/modernization-2026-10.md, starting from staging at c7b9c53.

What changed

Taxomind is a FastAPI service and serves its OpenAPI document at /openapi.json, with a response model on every endpoint classiflow uses. So this PR follows the OpenAPI path of the plan:

  • lib/contracts/taxomind.openapi.json is a snapshot of that document. It was taken from Taxomind f7a9e91 with FastAPI 0.121.3 and Pydantic 2.12.4, the versions in its requirements-prod.txt. npm run taxomind:types regenerates taxomind.openapi.ts from it with openapi-typescript 7.13.0 (devDependency). openapi-typescript declares a typescript ^5 peer dependency, so an overrides entry lets it use the repo's TypeScript 6.
  • lib/ai-labeling.ts calls Taxomind through openapi-fetch 0.17.0. The bearer token, the 3 min / 30 s timeouts and the best-effort cancel are unchanged. Labeling and learning requests are typed by the generated LabelingRequest / LearningRequest. startAIJob('/taxonomies', …), monitorAIJob and cancelAIJob keep signatures the taxonomy routes can call unchanged, so those routes are untouched.
  • lib/contracts/taxomind.ts parses every response with zod: the job id of each creation response (jobId for /learn, job_id otherwise), the status enum and the labeling result. Each schema is checked against the generated type with satisfies, so a regenerated contract that renames or retypes a field fails typecheck. I checked this by changing the generated types by hand. lib/ai-results.ts moved into this file as parseLabelingResults.
  • Contract test: the stub in tests/integration/ai-job-monitor.test.ts converts the document's schemas with z.fromJSONSchema. It checks every request classiflow sends and every reply the stub itself gives, and answers a request that breaks the contract with a 422, as Taxomind would. New cases:
    • malformed replies: not JSON, missing or unknown status, start response without a job id, refused start
    • partial and empty results
    • cancellation on Taxomind's side
    • taxonomy sync through the sync-ai route, and cancel
    • sourceSlug = AI_SOURCE_SLUG--<workspaceId>
    • leading zeros and the unknown codes

Behaviour changes (all follow from the contract)

  • A malformed status response counts as a failed status request: backoff, then the existing poll timeout.
  • The status aliases complete, success and error are no longer accepted. Taxomind's enum is pending | running | completed | failed | canceled.
  • A job Taxomind reports as canceled is now marked failed with that reason, in both the monitor and the taxonomy sync. Before, it was polled until it timed out.
  • parseLabelingResults expects the contract's sentenceId and string nodeCode. The sentence_id alias and numeric codes are dropped: Taxomind's Pydantic models cannot emit either, and a stringified number would lose leading zeros.

Still open (recorded in the plan)

  • W07 idempotent start: Taxomind requires batchId but creates a new job for every request (uuid4) and does not deduplicate.
  • Taxonomy delete notification: /taxonomies accepts only action: 'create', so the delete route's action: 'delete' gets a 422 and aiNotified is always false.
  • Typed taxonomy requests: the /taxonomies payload is still untyped, because it is built in the taxonomy routes, which were outside this task's scope. Taxomind also ignores the description and synonyms the sync route sends.

Checks

  • npm run typecheck: clean
  • npm run lint: 0 errors (the 24 warnings were already there)
  • npm test: 48 passed
  • npm run test:integration on PostgreSQL 16: 77 passed
  • npm run build: succeeds
  • Mutation checks: disabling the canceled branch, or dropping batchId from the payload, makes the contract tests fail.
  • The lockfile change only adds entries; npm ci succeeds. Re-running npm on Linux would rewrite the Windows-generated peer/dev flags, so only the new package entries were merged in.

User management, prisma/ and migrations are not touched.


Generated by Claude Code

Taxomind (FastAPI) serves its OpenAPI document at /openapi.json. A snapshot
(Taxomind f7a9e91) is committed in lib/contracts/; `npm run taxomind:types`
generates the types with openapi-typescript, and lib/ai-labeling.ts calls
Taxomind through openapi-fetch. It keeps the bearer token, the timeouts and
the best-effort cancel.

Every response is parsed in lib/contracts/taxomind.ts with zod schemas that
are checked against the generated types: the job id per resource (`jobId`
for /learn), the status enum and the labeling result. parseLabelingResults
moved there from lib/ai-results.ts and now expects the contract's
`sentenceId` and string `nodeCode`. A malformed status response counts as a
failed request (backoff), and a job Taxomind reports `canceled` fails with
that reason instead of being polled until it times out.

The stub service in tests/integration/ai-job-monitor.test.ts checks every
request and reply against the document (z.fromJSONSchema). It adds cases for
malformed and partial replies, Taxomind-side cancellation, taxonomy sync
through the sync-ai route, sourceSlug, leading zeros and the unknown codes.

W07's idempotent start stays open: Taxomind does not deduplicate batchId.
The plan also records that /taxonomies rejects the delete route's
`action: 'delete'`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PXfhpzKa2n5fw4nyZnTn2h
Resolves the import conflict in tests/integration/ai-job-monitor.test.ts:
fixtures now use the owner client from ./helpers (staging), plus the
contract test's imports.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PXfhpzKa2n5fw4nyZnTn2h
@petbrueck
petbrueck merged commit dac4a14 into staging Oct 21, 2026
3 of 4 checks passed
@petbrueck
petbrueck deleted the claude/bold-lamport-mz7zfk branch October 21, 2026 21:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants