The garden learns which language a card is in, and read-aloud stops guessing
Phase 28 (c), the last of the phase. A word met inside a Portuguese document is a Portuguese card: migration 0018 mirrors documents.doc_lang onto vocab_words, set server-side from the ownership lookup capture was already making. Every card still reviews — filtering the queue to the half she is learning would drop the words she actually met. Read-aloud was the larger surprise. detectLang routed Han/kana to Chinese and everything else to en-US, so the zh pair was accidentally right and every Latin pair wrong. doc_lang now reaches the client read-only on the document JSON, and docLang(text, verdict) answers for a passage taken out of it — with the script test still winning, because quoted Chinese must never be spelled out one "Chinese letter" at a time. Claude-Session: https://claude.ai/code/session_01GJHNvirh7Hzhc9RL3HAvz7
This commit is contained in:
@@ -613,6 +613,31 @@ ALTER TABLE users ADD COLUMN direction TEXT NOT NULL DEFAULT 'learning_en'
|
||||
stmt: `
|
||||
ALTER TABLE documents ADD COLUMN doc_lang TEXT NOT NULL DEFAULT ''
|
||||
CHECK(doc_lang IN ('', 'en', 'pair'));
|
||||
`,
|
||||
},
|
||||
{
|
||||
// Which language a garden card is in — the same '' | 'en' | 'pair'
|
||||
// vocabulary as documents.doc_lang, and set from it: a word is captured
|
||||
// (or a phrase planted) out of a document, so the document's verdict is
|
||||
// the card's language. A card with no document keeps '', which reads as
|
||||
// English like every other empty here.
|
||||
//
|
||||
// The garden needed this the moment a document could be written in her
|
||||
// own language. Before Phase 28 every card was English by construction;
|
||||
// now a Portuguese lookup lands beside an English one with nothing to
|
||||
// tell them apart, and two surfaces get it wrong without the tag — the
|
||||
// review card's read-aloud (which would say a Portuguese word in a US
|
||||
// English voice) and the panel, where a mixed garden is illegible.
|
||||
//
|
||||
// Every card is reviewed regardless. Filtering the queue to the half she
|
||||
// is learning was the alternative and is wrong for the writer this is
|
||||
// for: the words she met while writing Portuguese are still words she
|
||||
// met, and a garden that quietly drops them is a garden that stops being
|
||||
// a record of her reading.
|
||||
name: "0018_vocab_lang",
|
||||
stmt: `
|
||||
ALTER TABLE vocab_words ADD COLUMN lang TEXT NOT NULL DEFAULT ''
|
||||
CHECK(lang IN ('', 'en', 'pair'));
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
+27
-22
@@ -34,15 +34,20 @@ type User struct {
|
||||
// (source of truth for the editor); `ContentText` is the flattened plain text
|
||||
// kept in sync on every save and fed to the LLM.
|
||||
type Document struct {
|
||||
ID string `json:"id"`
|
||||
UserID string `json:"user_id"`
|
||||
Title string `json:"title"`
|
||||
Content string `json:"content"` // Tiptap JSON
|
||||
ContentText string `json:"content_text"` // plain text for the LLM
|
||||
Tone string `json:"tone"` // target writing tone; steers LLM advice
|
||||
WordCount int `json:"word_count"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
ID string `json:"id"`
|
||||
UserID string `json:"user_id"`
|
||||
Title string `json:"title"`
|
||||
Content string `json:"content"` // Tiptap JSON
|
||||
ContentText string `json:"content_text"` // plain text for the LLM
|
||||
Tone string `json:"tone"` // target writing tone; steers LLM advice
|
||||
WordCount int `json:"word_count"`
|
||||
// DocLang is which language this document is written in — '' | 'en' | 'pair'
|
||||
// (migration 0017), written by the checkpoint pass and never by the client.
|
||||
// It reaches the client read-only, for the one decision the client has to
|
||||
// make on its own: which voice reads a selection aloud. '' means English.
|
||||
DocLang string `json:"doc_lang"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
|
||||
// PreserveHistory opts this document out of auto-snapshot pruning so its
|
||||
// full writing trail survives as authorship evidence (see the passport).
|
||||
@@ -110,15 +115,15 @@ const (
|
||||
// coordinates at render time (spec Note #6). `Replacement` is empty for `voice`
|
||||
// flags — those are awareness-only, with no correction to apply.
|
||||
type Suggestion struct {
|
||||
ID string `json:"id"`
|
||||
DocID string `json:"doc_id"`
|
||||
FromPos int `json:"from_pos"`
|
||||
ToPos int `json:"to_pos"`
|
||||
Original string `json:"original"`
|
||||
Replacement string `json:"replacement"`
|
||||
Explanation string `json:"explanation"`
|
||||
Type string `json:"type"` // grammar | phrasing | idiom | clarity | translate | voice | collocation
|
||||
Status string `json:"status"` // pending | accepted | rejected
|
||||
ID string `json:"id"`
|
||||
DocID string `json:"doc_id"`
|
||||
FromPos int `json:"from_pos"`
|
||||
ToPos int `json:"to_pos"`
|
||||
Original string `json:"original"`
|
||||
Replacement string `json:"replacement"`
|
||||
Explanation string `json:"explanation"`
|
||||
Type string `json:"type"` // grammar | phrasing | idiom | clarity | translate | voice | collocation
|
||||
Status string `json:"status"` // pending | accepted | rejected
|
||||
// Source names the engine that proposed the edit, not its family: an offline
|
||||
// rule and the model can both propose a collocation, and the writer is never
|
||||
// told which one spoke. It exists so each pass can replace its own rows.
|
||||
@@ -128,10 +133,10 @@ type Suggestion struct {
|
||||
|
||||
// Suggestion type and status values, mirrored from the schema CHECK constraints.
|
||||
const (
|
||||
SuggestionTypeGrammar = "grammar"
|
||||
SuggestionTypePhrasing = "phrasing"
|
||||
SuggestionTypeIdiom = "idiom"
|
||||
SuggestionTypeClarity = "clarity"
|
||||
SuggestionTypeGrammar = "grammar"
|
||||
SuggestionTypePhrasing = "phrasing"
|
||||
SuggestionTypeIdiom = "idiom"
|
||||
SuggestionTypeClarity = "clarity"
|
||||
// A span she wrote in her own language, rendered into English. Not a
|
||||
// correction — nothing was wrong with it — which is why it is its own type
|
||||
// rather than a clarity fix: the card is the pair model's flagship moment
|
||||
|
||||
Reference in New Issue
Block a user