URL migration map — Branch D Wave 1 (API namespace refactor)
This document is the authoritative source for the API surface rename
landed in Branch D Wave 1. Wave D2 (frontend) and Wave D3 (docs +
samples) consume it.
Conventions
All paths are prefixed with /api/v1 (omitted from the table for
brevity).
Where a handler is registered at two URLs (top-level + nested),
the row notes "dual-route".
"Body-shape change" rows mark cases where the handler's request body
no longer carries fields that are now path parameters.
Top-level namespace map
New namespace
Source modules (merged or re-prefixed)
/auth/
auth.py
/contracts/
contracts.py, semantic_bindings*.py (re-parented), semantic_field_mapping.py (re-parented), semantic_proposals.py (dual)
/proposals/
semantic_proposals.py
/suggestions/
semantic_suggestions.py
/metadata/
metadata.py, metadata_entities.py
/semantic/
Sub-routers: ontologies/, concepts/, discovery/, ingestion/, context/, health/, export/, plus retained governance/, lineage/, provenance/, reviews/, activity/, namespaces/, query/, dashboard/
/pipeline/
pipeline.py, pipeline_configs.py, pipeline_runs.py, extractors.py, runs.py
/quality/
quality.py, validation.py, validation_jobs.py, tracking.py
/editor/
All editor_*.py
/ops/
templates.py, events.py, settings.py, audit.py (audit-export), evolution.py, reconciliation.py, schemas.py (codegen), schema_import.py, docs.py
/auth (unchanged)
Old URL
New URL
Notes
POST /auth/login
POST /auth/login
POST /auth/logout
POST /auth/logout
GET /auth/me
GET /auth/me
/contracts
Old URL
New URL
Notes
POST /contracts/create-from-artifacts
POST /contracts/create-from-artifacts
POST /contracts/create-mixed
POST /contracts/create-mixed
GET /contracts
GET /contracts
GET /contracts/{contract_id}
GET /contracts/{contract_id}
PUT /contracts/{contract_id}
PUT /contracts/{contract_id}
DELETE /contracts/{contract_id}
DELETE /contracts/{contract_id}
GET /contracts/versions/{contract_name}
GET /contracts/versions/{contract_name}
/contracts/{contract_id}/fields/{field_path}/binding (re-parented)
These move from /semantic/bindings/... to per-contract nested routes.
Handlers stay byte-identical; the URL is the only change. Query
parameters contract_id, field_path, concept_id continue to be
read off the request, but on the new routes contract_id and
field_path are taken from the path.
Old URL
New URL
Notes
GET /semantic/bindings/events
GET /contracts/{contract_id}/fields/{field_path}/binding/events
Body-shape: contract_id + field_path now from path (query still accepted for back-compat list-all variant — see notes).
GET /semantic/bindings/state
GET /contracts/{contract_id}/fields/{field_path}/binding/state
concept_id still a query param.
POST /semantic/bindings/transitions
POST /contracts/{contract_id}/fields/{field_path}/binding/transitions
Body still carries to_state, actor, reason, concept_id, contract_version.
POST /semantic/bindings/accept
POST /contracts/{contract_id}/fields/{field_path}/binding/accept
Body-shape: contract_id + field_path removed from body; concept_id + others retained.
POST /semantic/bindings/remap
POST /contracts/{contract_id}/fields/{field_path}/binding/remap
Body-shape: same.
POST /semantic/bindings/mark-technical-only
POST /contracts/{contract_id}/fields/{field_path}/binding/mark-technical-only
Body-shape: same.
POST /semantic/bindings/annotate
POST /contracts/{contract_id}/fields/{field_path}/binding/annotate
Body-shape: same.
/contracts/{contract_id}/field-mapping (re-parented)
Old URL
New URL
Notes
GET /semantic/field-mapping/contracts/{contract_id}/semantic-health
GET /contracts/{contract_id}/field-mapping
Renamed from "semantic-health" to "field-mapping" coverage.
GET /semantic/field-mapping/contracts/by-name/{name}/{version}/semantic-health
GET /contracts/by-name/{name}/{version}/field-mapping
Same shape, renamed.
POST /semantic/field-mapping/parse
POST /contracts/field-mapping/parse
Utility under contracts.
POST /semantic/field-mapping/validate
POST /contracts/field-mapping/validate
Utility under contracts.
/contracts/{contract_id}/proposals (dual-route)
Old URL
New URL
Notes
GET /semantic/proposals/contract/{contract_id}
GET /contracts/{contract_id}/proposals AND GET /proposals/by-contract/{contract_id}
Dual-route registration. Handler unchanged.
/contracts/{contract_id}/audit (per-contract audit slice)
This is not yet registered as a distinct route — there is no
existing handler for per-contract audit beyond the global stream. Wave
D2 may surface this from /ops/audit-export?contract_id=.... No
change required for Wave D1.
/proposals (was /semantic/proposals)
Old URL
New URL
Notes
POST /semantic/proposals/
POST /proposals
GET /semantic/proposals/
GET /proposals
POST /semantic/proposals/link
POST /proposals/link
GET /semantic/proposals/{proposal_id}
GET /proposals/{proposal_id}
GET /semantic/proposals/contract/{contract_id}
GET /proposals/by-contract/{contract_id} dual-route w/ /contracts/{contract_id}/proposals
Two registered URLs, same handler.
PUT /semantic/proposals/{proposal_id}/status
PUT /proposals/{proposal_id}/status
POST /semantic/proposals/{proposal_id}/merge
POST /proposals/{proposal_id}/merge
POST /semantic/proposals/{proposal_id}/validate
POST /proposals/{proposal_id}/validate
/proposals/{proposal_id}/reviews (from /semantic/reviews)
Old URL
New URL
Notes
POST /semantic/reviews/
POST /proposals/{proposal_id}/reviews
Body-shape: proposal_id now from path (was in body).
GET /semantic/reviews/proposal/{proposal_id}
GET /proposals/{proposal_id}/reviews
GET /semantic/reviews/proposal/{proposal_id}/status
GET /proposals/{proposal_id}/reviews/status
/suggestions (was /semantic/suggestions)
Old URL
New URL
Notes
GET /semantic/suggestions
GET /suggestions
GET /semantic/suggestions/{suggestion_id}
GET /suggestions/{suggestion_id}
GET /semantic/suggestions/{suggestion_id}/review-events
GET /suggestions/{suggestion_id}/review-events
POST /semantic/suggestions/{suggestion_id}/accept
POST /suggestions/{suggestion_id}/accept
POST /semantic/suggestions/{suggestion_id}/dismiss
POST /suggestions/{suggestion_id}/dismiss
POST /semantic/suggestions/{suggestion_id}/request-changes
POST /suggestions/{suggestion_id}/request-changes
POST /semantic/suggestions/accept-batch
POST /suggestions/accept-batch
POST /semantic/suggestions/dismiss-batch
POST /suggestions/dismiss-batch
POST /semantic/suggestions/request-changes-batch
POST /suggestions/request-changes-batch
Contract JSON Schema storage routes were renamed for clarity (DB table json_schemas, model JsonSchemaModel).
Old URL pattern
New URL pattern
Notes
/metadata/schemas
/metadata/json_schemas
list + POST store
/metadata/schemas/{contract_name}/{contract_version}
same path under /metadata/json_schemas/
GET schema
/metadata/schemas/{contract_name}/{contract_version}/complete
same under /metadata/json_schemas/
GET with rules merged
/metadata/metadata[/...]
/metadata/metadata[/...]
unchanged
/metadata/coercion-rules[/...]
/metadata/coercion-rules[/...]
unchanged
/metadata/validation-rules[/...]
/metadata/validation-rules[/...]
unchanged
/metadata/artifacts
/metadata/artifacts
unchanged
/metadata/coercion-functions
/metadata/coercion-functions
unchanged
/metadata/validation-functions
/metadata/validation-functions
unchanged
/metadata/{entity_type} (catch-all)
/metadata/{entity_type}
unchanged
/metadata/owners[/...] (and 5 siblings)
/metadata/owners[/...] (and 5 siblings)
unchanged
/semantic — sub-namespaces
/semantic/ontologies (renamed from /semantic/schemes + versioning)
Old URL
New URL
Notes
GET /semantic/schemes
GET /semantic/ontologies
GET /semantic/schemes/{scheme_id}
GET /semantic/ontologies/{scheme_id}
POST /semantic/schemes
POST /semantic/ontologies
PUT /semantic/schemes/{scheme_id}
PUT /semantic/ontologies/{scheme_id}
GET /semantic/schemes/{scheme_key}/rdf
GET /semantic/ontologies/{scheme_key}/rdf
POST /semantic/schemes/rdf
POST /semantic/ontologies/rdf
POST /semantic/versioning/versions
POST /semantic/ontologies/versions
GET /semantic/versioning/versions/{contract_id}/history
GET /semantic/ontologies/versions/{contract_id}/history
GET /semantic/versioning/versions/{contract_id}/latest
GET /semantic/ontologies/versions/{contract_id}/latest
POST /semantic/versioning/branches
POST /semantic/ontologies/branches
GET /semantic/versioning/branches/{contract_id}
GET /semantic/ontologies/branches/{contract_id}
POST /semantic/versioning/diff
POST /semantic/ontologies/diff
POST /semantic/versioning/merge
POST /semantic/ontologies/merge
GET /semantic/schema/version
GET /semantic/ontologies/schema-version
renamed for clarity
GET /semantic/schema/export
GET /semantic/ontologies/schema-export
renamed for clarity
GET /semantic/schema/relationship-types (and CRUD)
GET /semantic/ontologies/relationship-types
+ sibling CRUD verbs
GET /semantic/schema/concept-types (and CRUD)
GET /semantic/ontologies/concept-types
+ sibling CRUD verbs
GET /semantic/schema/tiers (and CRUD)
GET /semantic/ontologies/tiers
+ sibling CRUD verbs
GET /semantic/schema/cardinality-rules
GET /semantic/ontologies/cardinality-rules
/semantic/concepts (graph + reasoning consolidated)
Old URL
New URL
Notes
GET /semantic/graph/concepts/{concept_id}/ancestors
GET /semantic/concepts/{concept_id}/ancestors
GET /semantic/graph/concepts/{concept_id}/descendants
GET /semantic/concepts/{concept_id}/descendants
GET /semantic/graph/concepts/tree
GET /semantic/concepts/tree
GET /semantic/graph/concepts/{concept_id}/fields
GET /semantic/concepts/{concept_id}/fields
GET /semantic/graph/concepts/by-name/{name}/fields
GET /semantic/concepts/by-name/{name}/fields
GET /semantic/concept-graph/concepts/{concept_id}/relationships
GET /semantic/concepts/{concept_id}/relationships
POST /semantic/concept-graph/concepts/relationships
POST /semantic/concepts/relationships
DELETE /semantic/concept-graph/concepts/relationships/{rid}
DELETE /semantic/concepts/relationships/{rid}
PUT /semantic/concept-graph/concepts/relationships/{rid}
PUT /semantic/concepts/relationships/{rid}
GET /semantic/reasoning/{concept_id}
GET /semantic/concepts/{concept_id}/lineage
re-homed reasoning under concepts
GET /semantic/rdf/concepts/export
GET /semantic/concepts/export
RDF I/O folded under concepts
POST /semantic/rdf/concepts/import
POST /semantic/concepts/import
GET /semantic/rdf/concepts/{name}
GET /semantic/concepts/by-name/{name}
renamed to disambiguate
POST /semantic/rdf/concepts
POST /semantic/concepts
PUT /semantic/rdf/concepts/{name}
PUT /semantic/concepts/by-name/{name}
renamed to disambiguate
DELETE /semantic/rdf/concepts/{name}
DELETE /semantic/concepts/by-name/{name}
renamed to disambiguate
GET /semantic/rdf/ontology/{contract_id}/export
GET /semantic/ontologies/{contract_id}/export
moved to ontologies
POST /semantic/rdf/ontology/{contract_id}/validate-shacl
Removed — use POST /semantic/governance/validate
SEM-1
POST /semantic/rdf/ontology/import-jsonld
POST /semantic/ontologies/import-jsonld
POST /semantic/rdf/ontology/{contract_id}/validate-semantic
POST /semantic/ontologies/{contract_id}/validate-semantic
/semantic/discovery (no change)
Old URL
New URL
Notes
GET /semantic/discovery/suggest
GET /semantic/discovery/suggest
GET /semantic/discovery/synonyms
GET /semantic/discovery/synonyms
GET /semantic/discovery/cross-contract
GET /semantic/discovery/cross-contract
Old URL
New URL
Notes
POST /semantic/ingestion/import
POST /semantic/ingestion/import
POST /semantic/ingestion/import-all
POST /semantic/ingestion/import-all
POST /semantic/kb/ingest-text
POST /semantic/ingestion/kb-text
POST /semantic/kb/ingest-upload
POST /semantic/ingestion/kb-upload
POST /semantic/extraction/runs
POST /semantic/ingestion/runs
GET /semantic/assets
GET /semantic/ingestion/assets
GET /semantic/assets/{asset_id}
GET /semantic/ingestion/assets/{asset_id}
POST /semantic/assets
POST /semantic/ingestion/assets
POST /semantic/assets/{asset_id}/link-ingest-run
POST /semantic/ingestion/assets/{asset_id}/link-ingest-run
GET /semantic/ingest-runs
GET /semantic/ingestion/runs
merged with extraction/runs (different verb path)
GET /semantic/ingest-runs/{run_id}
GET /semantic/ingestion/runs/{run_id}
POST /semantic/ingest-runs
POST /semantic/ingestion/runs
GET /semantic/ingest-runs/{run_id}/status
GET /semantic/ingestion/runs/{run_id}/status
PATCH /semantic/ingest-runs/{run_id}/status
PATCH /semantic/ingestion/runs/{run_id}/status
GET /semantic/ingest-runs/{run_id}/summary
GET /semantic/ingestion/runs/{run_id}/summary
Note: POST /semantic/extraction/runs and POST /semantic/ingest-runs
are kept as separate handlers (different request semantics) but
both surface at POST /semantic/ingestion/runs only one can win at
register time. We resolve by putting extraction at
/semantic/ingestion/extraction-runs and keeping the asset-lineage
runs handler at /semantic/ingestion/runs. Wave D2 must use
extraction-runs for the extraction-run start endpoint and runs
for the asset-lineage ingest-run CRUD.
/semantic/context (no change)
Old URL
New URL
Notes
GET /semantic/context
GET /semantic/context
GET /semantic/context/concepts/{concept_id}
GET /semantic/context/concepts/{concept_id}
GET /semantic/context/contracts/{contract_id}/fields/{field_path:path}
(unchanged)
GET /semantic/context/contracts/{contract_id}
GET /semantic/context/contracts/{contract_id}
GET /semantic/context/query
GET /semantic/context/query
/semantic/health (no change)
Old URL
New URL
Notes
GET /semantic/health
GET /semantic/health
GET /semantic/health/issues/{fingerprint}/history
GET /semantic/health/issues/{fingerprint}/history
POST /semantic/health/issues/history
POST /semantic/health/issues/history
/semantic/export (single unified export endpoint)
Old URL
New URL
Notes
POST /semantic/export/obsidian
POST /semantic/export/obsidian
GET /semantic/export/neo4j
GET /semantic/export/neo4j
GET /semantic/export/formats
GET /semantic/export/formats
GET /semantic/export/graph
GET /semantic/export/graph
/semantic — retained sub-namespaces
Old URL
New URL
Notes
POST /semantic/governance/validate
POST /semantic/governance/validate
POST /semantic/lineage/upstream
POST /semantic/lineage/upstream
POST /semantic/lineage/downstream
POST /semantic/lineage/downstream
POST /semantic/lineage/path
POST /semantic/lineage/path
POST /semantic/lineage/record
POST /semantic/lineage/record
GET /semantic/provenance/concepts/{cid}/origin
GET /semantic/provenance/concepts/{cid}/origin
GET /semantic/provenance/contracts/{cid}/grown
GET /semantic/provenance/contracts/{cid}/grown
GET /semantic/provenance/pipelines/{pid}/grown
GET /semantic/provenance/pipelines/{pid}/grown
GET /semantic/activity
GET /semantic/activity
GET /semantic/namespaces[/...]
GET /semantic/namespaces[/...]
unchanged (4 routes)
POST /semantic/query
POST /semantic/query
GET /semantic/dashboard/summary
GET /semantic/dashboard/summary
/pipeline
Old URL
New URL
Notes
POST /pipeline/run
POST /pipeline/run
POST /pipeline/configs (+ CRUD)
POST /pipeline/configs (+ CRUD)
unchanged
POST /pipeline/jobs (+ stats + cancel)
POST /pipeline/jobs (+ stats + cancel)
unchanged
POST /pipeline/runs
POST /pipeline/runs
GET /pipeline/runs/{run_id}/stream
GET /pipeline/runs/{run_id}/stream
GET /runs (+ stats, timeseries, {run_id})
GET /pipeline/runs/legacy (+ legacy/stats etc.)
rehomed under /pipeline/runs/legacy/* to avoid collision with /pipeline/runs/{run_id}
GET /pipelines (+ stats)
GET /pipeline/registry (+ /pipeline/registry/{name}/stats)
renamed for clarity vs /pipeline/configs
POST /extractors/test-connection
POST /pipeline/extractors/test-connection
POST /extractors/sample
POST /pipeline/extractors/sample
POST /ontology/configs (+ CRUD)
POST /pipeline/linkml-schema-configs (+ CRUD)
re-homed: pipeline-owned ontology snapshots
Note on /runs legacy: this is the legacy aggregated runs API (lists
all runs across pipeline + validation). It lives at /pipeline/runs/legacy/* —
Wave D2 should migrate UI off it incrementally.
/quality
Old URL
New URL
Notes
POST /quality/check
POST /quality/check
POST /quality/check/upload
POST /quality/check/upload
POST /quality/violations
POST /quality/violations
GET /quality/metrics
GET /quality/metrics
GET /quality/metrics/{metric_id}
GET /quality/metrics/{metric_id}
GET /quality/reports/{schema_id}
GET /quality/reports/{schema_id}
GET /quality/thresholds/{name}/{version}
GET /quality/thresholds/{name}/{version}
PUT /quality/thresholds/{name}/{version}
PUT /quality/thresholds/{name}/{version}
POST /validation/validate
POST /quality/validation/validate
re-homed: validation = quality concern
POST /validation/validate-batch
POST /quality/validation/validate-batch
POST /validation/jobs (+ CRUD)
POST /quality/jobs (+ CRUD)
re-homed
GET /quality/tracking (+ CRUD)
GET /quality/tracking (+ CRUD)
unchanged
/editor
All editor_*.py paths already canonical at /editor/.... No URL
changes — only internal restructuring of editor_knowledge_base.py
which currently exposes both /editor/kb/... and /editor/lineage/....
Old URL pattern
New URL pattern
Notes
/editor/kb/[...]
/editor/kb/[...]
unchanged
/editor/lineage[/...]
/editor/lineage[/...]
unchanged
/editor/concepts[/...]
/editor/concepts[/...]
unchanged
/editor/concept-graphs[/...]
/editor/concept-graphs[/...]
unchanged
/editor/drafts[/...]
/editor/drafts[/...]
unchanged
/editor/completeness/ontology/{sid}
/editor/completeness/ontology/{sid}
unchanged
/editor/search
/editor/search
unchanged
/editor/pipelines/run
/editor/pipelines/run
unchanged
/editor/field-name-suggestions
/editor/field-name-suggestions
unchanged
/layouts (router prefix)
/editor/layouts
rehomed: editor-owned layouts
/ops — new namespace
/ops/ consolidates infrastructure / cross-cutting concerns that are
not domain-specific.
Old URL
New URL
Notes
GET /audit/export
GET /ops/audit-export
per spec
GET /templates/schema
GET /ops/templates/json_schema
renamed for clarity
GET /templates/metadata
GET /ops/templates/metadata
GET /templates/coercion-rules
GET /ops/templates/coercion-rules
GET /templates/validation-rules
GET /ops/templates/validation-rules
GET /templates/contract-artifacts
GET /ops/templates/contract-artifacts
GET /templates/pipeline
GET /ops/templates/pipeline
GET /templates/pipeline/{filename:path}
GET /ops/templates/pipeline/{filename:path}
GET /templates/pipeline-artifacts
GET /ops/templates/pipeline-artifacts
GET /semantic/templates
GET /ops/domain-templates
renamed: domain seed templates
GET /semantic/templates/{template_id}
GET /ops/domain-templates/{template_id}
POST /semantic/templates/parse-nl
POST /ops/domain-templates/parse-nl
GET /folders/tree (+ CRUD)
GET /ops/folders/tree (+ CRUD)
GET /events/stream
GET /ops/events/stream
GET /settings/app-config
GET /ops/settings/app-config
public, no auth
GET /settings/runtime-overview
GET /ops/settings/runtime-overview
public, no auth
GET /settings/database-config
GET /ops/settings/database-config
POST /settings/test-database
POST /ops/settings/test-database
POST /settings/test-dlq
POST /ops/settings/test-dlq
POST /settings/dlq-stats
POST /ops/settings/dlq-stats
GET /settings/server-config (+ PATCH)
GET /ops/settings/server-config (+ PATCH)
GET /settings/quality (+ PUT)
GET /ops/settings/quality (+ PUT)
GET /settings/notifications (+ PUT)
GET /ops/settings/notifications (+ PUT)
POST /settings/notifications/test
POST /ops/settings/notifications/test
POST /evolution/check
POST /ops/evolution/check
POST /evolution/diff
POST /ops/evolution/diff
GET /evolution/{schema_name}/diff
GET /ops/evolution/{schema_name}/diff
GET /evolution/{schema_name}/check
GET /ops/evolution/{schema_name}/check
POST /evolution/{schema_name}/check
POST /ops/evolution/{schema_name}/check
POST /reconciliation/match
POST /ops/reconciliation/match
POST /schemas/generate
POST /ops/codegen/schemas
renamed for clarity (was "schemas/generate")
POST /schemas/convert
POST /ops/codegen/convert
POST /schema-import/ddl
POST /ops/schema-import/ddl
POST /schema-import/import
POST /ops/schema-import/import
POST /semantic/definitions/draft
POST /ops/definitions/draft
POST /docs/generate
POST /ops/docs/generate
POST /docs/section
POST /ops/docs/section
GET /docs/{contract_name}/{contract_version}
GET /ops/docs/{contract_name}/{contract_version}
Deletions (production routes removed)
Removed URL
Notes
GET /docs/playground-routes
per spec; previously surfaced OpenAPI subset for UI playground. Wave D2 must consume the canonical /openapi.json instead.
GET /schema-import/plugins
per spec; plugin discovery deemed an implementation detail. Wave D2 must remove any UI that hit it.
Dual-route handlers
The following handlers are registered at two URLs (same handler
body served at both):
proposals_by_contract
GET /api/v1/proposals/by-contract/{contract_id}
GET /api/v1/contracts/{contract_id}/proposals
No other handlers are dual-registered in Wave D1.
Body-shape changes (path params now carry data)
These routes moved their identifier from request body to path params.
Handlers were updated to read the value from Path(...) rather than
the request model. Where a back-compat shim could be cheap, the model
field was kept and ignored when the path value is present.
Handler
Field moved from body → path
accept_binding (semantic_bindings_actions)
contract_id, field_path
remap_binding
contract_id, field_path
mark_binding_technical_only
contract_id, field_path
annotate_binding
contract_id, field_path
create_transition (semantic_bindings)
contract_id, field_path
list_events (semantic_bindings)
contract_id, field_path (now mandatory)
get_state (semantic_bindings)
contract_id, field_path
create_review (semantic_reviews)
proposal_id
Out-of-scope (Wave D2 + D3 will consume this map)
Frontend (src/pycharter/ui/) — Wave D2 owns. UI fetchers must
be updated against this map.
Docs (docs/guides/, docs/reference/* except this file),
samples (samples/) — Wave D3 owns.
DB migrations — out of scope for Branch D.
VC-8 — Vocabulary consolidation (2026-07-12)
The ~37 semantic/editor routers collapsed into ten vocabulary
routers under /api/v1/vocabulary/* (vocabulary-consolidation ADR,
VC-8 — approved reversal of the T10 path-stability note; minor version
bump). Old paths return 404; there is no dual-accept window.
Old prefix
New prefix
/semantic/schemes
/vocabulary/schemes
/semantic/namespaces
/vocabulary/namespaces
/semantic/concepts
/vocabulary/concepts
/semantic/concepts/{id}/lineage (reasoning)
/vocabulary/insights/concepts/{id}/lineage
/semantic/search
/vocabulary/search
/semantic/discovery
/vocabulary/search/discovery
/semantic/health
/vocabulary/health
/semantic/dashboard
/vocabulary/health/dashboard
/semantic/activity
/vocabulary/health/activity
/semantic/governance
/vocabulary/governance
/semantic/ingestion
/vocabulary/extraction
/semantic/lineage
/vocabulary/insights/lineage
/semantic/context
/vocabulary/insights/context
/semantic/export
/vocabulary/insights/export
/semantic/provenance
/vocabulary/insights/provenance
/semantic/query
/vocabulary/insights/query
/semantic/resource-bindings
/vocabulary/bindings
/suggestions
/vocabulary/suggestions
/inbound/suggestions
/vocabulary/suggestions/inbound
/proposals
/vocabulary/governance/proposals
/editor/search
/vocabulary/search/universal
/editor/completeness
/vocabulary/health/completeness
/editor/concepts (POST create)
/vocabulary/concepts/quick-create
/editor/concepts/{key}/lineage
/vocabulary/concepts/{key}/lineage
/editor/concept-graphs
/vocabulary/concept-graphs
/editor/drafts
/vocabulary/drafts
/editor/field-name-suggestions
/vocabulary/suggestions/field-names
Unchanged: contract-scoped surfaces (/contracts/{id}/fields/{fp}/binding,
/contracts/{id}/proposals, /contracts/*/field-mapping), /ops/*
(schema import, domain templates, definitions, reconciliation, audit),
/editor/layouts, and /editor/pipelines/run.
The full route table is pinned by the OpenAPI snapshot gate
(tests/unit/test_openapi_snapshot.py +
tests/fixtures/openapi/routes.txt) — every surface change lands as a
reviewable fixture diff.