Two reports from live dictation in fa.
The locale trigger vocabulary listed only verbs that ask for a note ("بنویس",
"یادداشت کن"). Naming the field itself — "توی یادداشتها بنویس", "در توضیحات" —
reported no commentTrigger, and the resolver discards a comment without one, so
the note was silently dropped. Added the field-name shape to fa, en and nl.
Where the dictated note ends was never stated, so the model answered it
inconsistently: "توی توضیحات بنویس سلام چطوری و بفرست برای لابراتوآر سانشاین"
sometimes put the lab dispatch inside the comment. Added a no-overlap rule — a
clause reported as a lab, tooth, prosthesis or deadline is an instruction and
must not appear in comment as well — with that sentence as a worked example.
Both rules go in the prompt and in the JSON-schema field descriptions, because
the schema description is what the model reads while filling the field. No code
enforcement for the second: stripping an extracted clause out of free-speech
note text by string matching is guesswork, and the review sheet shows the note
before it is saved.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Five migrations were pending, not one: this dev database had not been
migrated since the prosthesis overhaul landed on master. Used migrate deploy
rather than migrate dev so no drift prompt could offer a reset, and counted
the DELETE first — 0 rows matched against 2 total.
Seeded translations verified: 7 categories and 5 subcategories in fa, en and
nl, with crown resolving to روکشها. Both login accounts survived.
Also records that .gitea/workflows run no test, lint or typecheck step at
all, which is why two spec-file type errors reached the branch unnoticed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Authored by the /orchestrate builder agent, committed unrepaired so the
fixes that follow are reviewable against it.
Backend: replaces the flat prosthesisDefaultType/prosthesisOverrides wire
shape with a prosthesis: ProsthesisAssignment[] list whose targets can be a
tooth or a jaw; adds resolveAssignmentTarget / classifyTypeCode /
resolveProsthesisAssignment for leaf-vs-category classification, region
validity with mixed-region deferral, and assignmentIndex on unresolved
items; adds PROSTHESIS_CATEGORY and PROSTHESIS_SUBCATEGORY to
CatalogEntityKind with a migration and seeded fa/en/nl translations; and
rewrites the extraction prompt to render the catalog as a tree.
Frontend: merged "teeth and prosthesis" row, stack preview through the
existing applyLeafToJobs, three chip-fold paths, rewritten applyVoiceResult
and voiceForEditor, and the two carried-forward recording fixes — the
container fallback that refused Safari and the render gate that never
checked isMediaRecorderSupported().
Adds Vitest for the frontend's pure helpers, and updates CLAUDE.md.
Gate was green: backend 16 suites / 209 tests, nest build, prisma validate;
frontend 37 Vitest tests, tsc --noEmit, next build.
KNOWN DEFECTS, fixed in the commits that follow:
- VoiceReviewSheet.tsx:169 — a picked tooth chip is dropped on Apply
- VoiceReviewSheet.tsx:213 / TreatmentWorkspace.tsx:2215 — decision 41's
type-row lock is missing, so unticking it saves prosthesis lab rows on a
non-prosthesis detail
Reviewed on the correctness lens only; regression-risk never ran. The
migration was validated but never applied.
Spec: docs/specs/voice-treatment-entry/spec.md
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The Gap-check phase halted the run on one blocking gap and reported four
notes. Nothing was built.
Blocking: the prompt cannot offer a prosthesis category the model can name,
because no category label exists anywhere the backend can read —
CatalogEntityKind covers only TREATMENT_TYPE, PROSTHESIS_TYPE and
LAB_WORKFLOW_STEP. Adds PROSTHESIS_CATEGORY and PROSTHESIS_SUBCATEGORY as
catalog entities with seeded fa/en/nl translations, rather than sending bare
codes that would read untranslated on the locale this feature exists for.
Also closed: a chip that resolved to a jobless tooth and was then discarded
by the jobless-tooth rule, so the tap did nothing; the undefined region check
for a category whose leaves span crown and arch; the wrong endpoint path in
§3; and the availability-endpoint contradiction in §11.
Corrects two counts of my own: 5 subcategories, not 4 (night_guard was
missed), and the disjointness test now asserts against the live catalog
rather than a number written in prose.
Decisions 47-50. Work items 18-19 added to the ledger.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The treatment form was overhauled after voice v1 merged (a3c14a1, 7f92e73,
72f885d, 5d3597f): one prosthesis type per tooth became stacked jobs,
jaw-level appliances and a category tree. Voice still compiled against it
but could no longer express it, and in two places wrote data the form
itself refuses.
Rewrites the extraction contract (§5), the resolver rules and the
unresolved-reason table (§6), the review sheet (§7) and verification
(§12), and records decisions 34-46. Adds the repos: block and a progress
ledger so the task resolves from the branch.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>