Finish Phase 22: the half of Petal that works with the tunnel down

Grammar lite, the false-friend list, the daily invitation and the offline
miscollocations — the four remaining §5–§6 items, all client-side and all
alive on a box that cannot reach the model.

The offline collocations forced a schema change. `type` had been doubling
as the answer to "which engine found this" — `mechanics` meant offline —
and that stops being true the moment an offline rule proposes a
collocation. Migration 0013 adds `source` (llm | local) and every pass now
scopes its DELETE by engine; without it the coach silently wiped every
offline chunk on the page. Existing rows backfill by type, so a pre-0013
collocation row is claimed as the coach's, which it was: the offline list
did not exist yet.

The rule pack is hand-curated rather than mined, and the entries left out
are the point — `married with` is wrong until "married with children",
`arrive to` wants at or in depending on the noun. A pack running on every
keystroke must not correct correct writing.

Claude-Session: https://claude.ai/code/session_016y6gyuHkQXPiEuW8RGQyua
This commit is contained in:
prosolis
2026-07-27 15:05:55 -07:00
parent e9b8595456
commit 1bbc8fc8d3
20 changed files with 1678 additions and 50 deletions
+11 -1
View File
@@ -106,7 +106,11 @@ type Suggestion struct {
Explanation string `json:"explanation"`
Type string `json:"type"` // grammar | phrasing | idiom | clarity | voice | collocation
Status string `json:"status"` // pending | accepted | rejected
CreatedAt time.Time `json:"created_at"`
// 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.
Source string `json:"source"` // llm | local
CreatedAt time.Time `json:"created_at"`
}
// Suggestion type and status values, mirrored from the schema CHECK constraints.
@@ -119,6 +123,12 @@ const (
SuggestionTypeCollocation = "collocation"
SuggestionTypeMechanics = "mechanics" // deterministic rule-based pass (no LLM)
// Who proposed it. The offline rule pack ('local') runs on every edit inside
// the browser and survives a VPN-down box; the model ('llm') adds the long
// tail when it is reachable.
SuggestionSourceLLM = "llm"
SuggestionSourceLocal = "local"
SuggestionStatusPending = "pending"
SuggestionStatusAccepted = "accepted"
SuggestionStatusRejected = "rejected"