Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions LESSONS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Lessons learned

What went wrong or nearly went wrong in this repo, and what now stops it happening again. Kyt standard rule S12:
no lesson stays `open` for more than 14 days. Geordi reads this file every Monday.

**Add a row when:** an incident or near miss happens, a code-review finding scores 80 or more, a scanner finds
something real, a release step fails, or a Claude session hits a trap worth not hitting twice. Record it the same
day, as `open`, in plain words.

**Close it by choosing one status** (within 14 days):

| Status | Means | The last column must say |
|---|---|---|
| `check-added` | A test, CI step, hook or script now fails if it happens again | the file or job that fails |
| `template-changed` | The shared template in `kyt-dev-platform` changed, so every repo gets it | the template |
| `skill-changed` | A skill (for example `ship`) now does it differently | the skill and step |
| `advice-only` | No check is practical; the reason is written down | why not |
| `retired` | No longer applies | why |
| `open` | Recorded, not yet decided | (blank) |

Portfolio-wide lessons live in `kyt-dev-platform/lessons/PORTFOLIO-LESSONS.md`; a repo lesson that applies to every
repo is copied there by Geordi's weekly review.

L-001 to L-010 were backfilled on 16 Oct 2026 from the Kōrero project record (state file and build notes). Their
dates are when each was found. Statuses were checked against the code on 16 Oct; where no check could be found,
the row is `open`, so several start past the 14-day limit and need a decision.

| ID | Recorded | What happened | Lesson | Status | Check or change |
|---|---|---|---|---|---|
| L-001 | 2026-09-25 | The New Zealand English pass had never run for anyone on the default settings: it needs `en-NZ` and the default was `en`. A separate `nz_english_pass_enabled` (default on) looked like the switch and did nothing on its own (F13). | Test the shipped defaults, not only the feature with the right settings. | open | Fixed in 1.41 (`ensure_nz_locale_default`, `settings.rs`). No test that the pass runs under default settings was found on 16 Oct. |
| L-002 | 2026-09-25 | Meetings were lost or reverted when the user left the Meetings tab: Stop built the meeting inside the page, and a returning page saved its older copy over background results (B1–B3). | Background work writes through the store, never through the page that started it. | open | Fixed in 1.41 (`fix/meetings-background`). Whether a test covers it was not checked. |
| L-003 | 2026-09-25 | A 34-minute meeting produced no transcript: the microphone peaked at RMS 0.0073, under the fixed 0.010 speech gate, and nothing said so until after Stop (T1). | A fixed loudness gate fails quiet or distant microphones; check the signal while recording. | open | 1.41 reports it at Stop. The in-room plan (M1) replaces the fixed gate. |
| L-004 | 2026-09-25 | Release step B stopped with "uncommitted changes" in 1.41 and again in 1.42: the signed build rewrites `src-tauri/Cargo.toml` line endings (content identical). Restored by hand each time. | A build step must not leave the tree dirty; fix the cause, not each release. | open | A `.gitattributes` rule for `Cargo.toml`, or the release script restoring it, would close this. |
| L-005 | 2026-10-01 | A local `vite build` from `C:\Dev\korero` (capital D) fails on entry pages with inline styles; the folder on disk is `C:\dev`. | Build from the exact-case path. | advice-only | Vite's behaviour, not Kōrero's. The release scripts use `C:\dev\korero`; CI is unaffected. |
| L-006 | 2026-10-01 | Critical, found in review: "Move existing recordings" could write the meetings store after a load error, replacing the user's meetings (R-02). | Never write a store that failed to load. | open | Fixed in 1.42 (`storeLoadError` guards). Whether a test covers it was not checked. |
| L-007 | 2026-10-02 | Cloud AI provider keys are never stored in Windows Credential Manager: `keyring` is built without a platform feature, so it uses a mock store, and the migration then blanks the plaintext key. Keys are lost on restart (F15). Ollama is unaffected. | A secure-storage dependency needs a test that a value survives a restart. | open | Still present on 16 Oct (`keyring = "3.6"`, no platform feature). |
| L-008 | 2026-10-08 | Notes for long meetings covered only the first 48,000 characters on 8 of 35 meetings, and lost every flagged moment, with no warning. | Never cut input silently: either handle all of it or say in the output what was left out. | open | Fixed on `fix/1.44` (`notes_parts.rs`, 13 tests). Becomes `check-added` when 1.44 merges. |
| L-009 | 2026-10-08 | Audio brief failed with "voice engine not found" on the main laptop from the first build with PRIV-1 until 1.44: removing a developer path removed the only lookup that found the engine. | When a lookup path is removed, give its replacement (a setting) and a health check in the same change. | open | Fixed on `fix/1.44` (engine folder setting). |
| L-010 | 2026-09-25 | On the main laptop, local `main` tracks `origin`, which is upstream Handy, not `korero`; a bare `git push` targets the wrong repo. | Always push with `git push korero <branch>`. | open | One-line fix: `git branch -u korero/main main` on each machine. |
| L-011 | 2026-10-16 | The first in-room plan listed `pyannote-rs` as a fallback engine. The M0 probe found it does not compile against Kōrero's `ort` (29 errors), ignores the model's speaker-change output and drifts about 60 ms per 10 s. | Compile a dependency and read its source before naming it in a plan, even as a fallback. | advice-only | `app-dev-pm` Phase 1.5 already requires probing claims; this was a planning slip, caught by M0 before any app code. |
| L-012 | 2026-10-16 | M0's first scores were worse than calling everyone one speaker. The cause was the CAM++ embedding model packaged with `pyannote-rs`: it ran without error and gave stable output, but told AMI speakers apart no better than chance, even on headset audio. The harness was checked first (reference timing, feature scale, window type), then models were compared on clean labelled turns; WeSpeaker ResNet34-LM separated them 82–100% of the time. | A model that runs is not a model that works: score it on labelled data, and check the harness before trusting a bad number. | open | Plan M2 adds a check that scores the shipped models on the AMI test set and fails above the agreed error. |
21 changes: 21 additions & 0 deletions docs/DECISIONS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Kōrero: decision log

The one decision log for Kōrero: decisions that change how Kōrero is built, released or behaves, oldest first.
Started 16 Oct 2026. D-001 to D-006 are backfilled from the project record (`KORERO_STATE.md` and the build notes
in the Kōrero claude.ai project), where some carried older labels (shown in brackets); those labels are retired.

- **Status:** `Proposed` (written down, not decided; binds nothing), `Accepted`, `Superseded by D-NNN` or
`Withdrawn`. The code follows `Accepted` rows only.
- **Adding a row:** it goes in the same PR as the change it records. Take the next free number on `korero/main`
when the PR is ready to merge, not when drafting.
- **Product order** (what to build next) lives in `docs/PRODUCT.md`, not here.

| # | Date | Status | Decision | Why | Alternatives rejected | Revisit when |
|---|---|---|---|---|---|---|
| D-001 | 2026-09-25 | Accepted | **Kōrero is built and released from the fork** (`nkeating-cmd/korero`, branch `main`). The overlay-and-patches build in `korero-build-tooling` (`korero-build.ps1`, `korero-ship.ps1`) is not used for releases. (Was "D8".) | Measured 23 Sept: the overlay was at 1.38.0 and none of the 26 files v1.40 changed matched it; `korero-ship.ps1` would force-push `korero-fork` over `main`. 1.41.0 shipped from the fork. | Overlay plus patches on upstream Handy: keeps upstream close, but the landing debt was already larger than the features. | A rebase onto upstream Handy is attempted (191+ commits behind at the time). |
| D-002 | 2026-09-25 | Accepted | **New Zealand English is the default** (`default_selected_language = "en-NZ"`). A one-time step moves an inherited `en` to `en-NZ` and records that it ran, so a user who switches it off keeps their choice. (Was "D12"; finding F13.) | The NZ pass (macrons, NZ place names and spelling) only runs under `en-NZ`, and the default was `en`, so it had never run for anyone on the default, including both of Nic's machines. | Leave `en` and add a prompt: depends on users noticing a setting they did not know mattered. | A user reports the NZ pass changing text they did not want changed. |
| D-003 | 2026-10-02 | Accepted | **Calls to a local Ollama model use Ollama's own chat API with the context sized to the request**, not its OpenAI-compatible endpoint; thinking is off for Ollama (1.42). | Ollama's default context is 4k tokens on GPUs under 24 GB and the OpenAI endpoint cannot raise it, so long meetings got notes from part of the transcript, silently. gemma4:12b timed out with thinking on. | Tell users to raise the context in Ollama: invisible to most users and not checked by the app. | Ollama's OpenAI endpoint gains a context setting; another local runtime is added. |
| D-004 | 2026-10-02 | Accepted | **Before a local model reads a long transcript, the speech model is freed** (local model, at least 4,000 characters, nothing recording, unload not set to Never; never waits). Nic chose this ("option A") for 1.43. | Both models together did not fit the graphics card on the main laptop. Cost: Parakeet reloads in about 4 s, mostly hidden behind the next recording. | Keep both loaded: out-of-memory on 12 GB cards. Smaller notes model: worse notes. | A machine with ample video memory is the norm, or the reload cost becomes noticeable. |
| D-005 | 2026-10-08 | Accepted | **Notes for a meeting over 48,000 characters are written in parts and combined** (split at speaker turns, at most 8 parts); shorter meetings are unchanged. Nic: "go with reading in parts". | 8 of 35 meetings on the main laptop had notes from the start only, and lost every flagged moment. | A bigger context window: needs more video memory than the target machines have, and quality falls off on long inputs. | Ask needs whole-meeting answers (it still reads one part and says so). |
| D-006 | 2026-10-08 | Accepted | **`wasapi` was upgraded from 0.23 to 0.25 to clear a security advisory**, rather than recording an exemption. Nic's choice during the 1.44 publish gates. | An exemption leaves the advisory in place; the upgrade's side effect (Windows manifest embedding in test binaries) was fixable. | Exempt the advisory in `cargo deny`. | — |
| D-007 | 2026-10-16 | Accepted | **In-room speaker separation runs in Kōrero's own code on its existing ONNX runtime** (`ort 2.0.0-rc.12`), after Stop, on the CPU, never live: pyannote segmentation-3.0 (MIT) read for speaker changes, not only speech; WeSpeaker ResNet34-LM speaker embeddings (CC BY 4.0, attribution in the third-party notices); clustering of the whole meeting on segments of 1.5 s or more, clusters under 20 s of speech folded into the nearest one, short fragments attached by voice. Plan: `KORERO_1.45_IN-ROOM-SPEAKERS-PLAN_2026-10-16.md` (v2). (Approved by Nic 16 Oct 2026: "Execute D007".) | M0 probe, 16 Oct, on four AMI test meetings recorded by one far-field room microphone (92 minutes): mean error 23.1% with a 0.25 s collar, 28.1% strict, against 22.4% strict for pyannote's own pipeline on all 16 test meetings; right number of people in 2 of 4 meetings (one too many in one, two too few in the other). About 80 s of CPU time per hour of audio on 4 threads. It builds in the same binary as `transcribe-rs` with one `ort`. | `pyannote-rs` 0.3.4: does not compile against `ort` rc.12 (29 errors); reads the model only as a speech detector; its clock drifts 60.6 ms per 10 s; its packaged CAM++ embedding model told AMI speakers apart no better than chance, even on headset audio (57.5% error with the right model, 80.5% as published). `sherpa-onnx`: a second ONNX runtime beside `ort`. Telling the clusterer the number of people: worse (31.5%) than finding it. The notes model guessing speakers: invents attributions. | Results on Nic's own room recordings differ from AMI; `knf-rs` (C++ build, `ndarray` 0.16) is replaced by Rust feature code. |
Loading
Loading