Skip to content

Closes #175#185

Merged
realproject7 merged 2 commits into
mainfrom
task/175-source-language-config
Jul 14, 2026
Merged

Closes #175#185
realproject7 merged 2 commits into
mainfrom
task/175-source-language-config

Conversation

@realproject7

@realproject7 realproject7 commented Jul 14, 2026

Copy link
Copy Markdown
Owner

Closes #175

Wire the #94 "Spoken language" setting through resolveStartConfig, so a picked source language drives the archive header source label and the reply/quick-translate meeting language — instead of the hardcoded EN/English that persisted wrong session metadata.

EPIC Alignment

Part of EPIC #134 (v1.3 audit follow-up), Batch 42 (standing-order sequence #172#171#170#173#175). TS host cluster; scoped to src/host/start-config.ts (+ its unit test). Completes the #94 source-language feature (the picker + protocol field existed; the host resolver ignored them). Does not touch resample.rs/render.ts/parse.ts/pipeline.rs (reserved for #178/#179), the whisper source-language handling (Rust pipeline, separate), or dashboard/settings.

Self-Verification

The bug. resolveStartConfig (start-config.ts) hardcoded MEETING_LANGUAGE = "English" / SOURCE_LANG_CODE = "EN" and never read message.sourceLanguageCode (#94, protocol.ts:89) — the comment even claimed "there is no source-language setting (§8.6)", stale since #94 added exactly that. So a user who picks Spoken = Japanese, Translate into = Korean gets EN → KO written into the saved archive header and every dashboard history row / detail meta line (session.ts:418 reads resolved.sourceLangCode) — wrong metadata, permanently — and reply/quick-translate output (session.ts:438meetingLanguage, consumed by extras-prompts.ts) stays English instead of the meeting's actual language.

The fix. Derive both fields from message.sourceLanguageCode via languageByCode(...):

  • sourceLangCode (archive header, §8.9) = the picked language's archiveLabel (e.g. Korean → "KO").
  • meetingLanguage (reply / quick-translate output, §8.5) = the picked language's name (e.g. "Korean"), so output goes into the meeting's spoken language.
  • auto sentinel special-cased explicitly (inline in resolveStartConfig, per RE1's single-use-helper review — the amendment's requirement): whisper detects per-utterance, so there is no single spoken language — it maps to a deliberately-neutral "AUTO" header label (documented on AUTO_SOURCE_LABEL) and keeps the pre-Session start: choose spoken (source) language + target language #94 English reply/quick-translate fallback (AUTO_MEETING_LANGUAGE). This avoids the two naive traps: languageByCode("auto") would synthesize "auto"/"AUTO" as if it were a real language, and a blank value from an older shell would hit languageByCode("") = ko (the default) — both are routed to auto instead.
  • Stale comment replaced.

Whisper auto-detect preserved. The per-utterance whisper source-language is configured independently on the Rust side (CaptionPipeline … with_source_language), not here — this change touches only the host's archive-label + meeting-language plumbing, so auto STT semantics are untouched.

Security invariants. No new dependencies. No caption/audio content logged (only language codes/names). Simplest fix that solves the ticket — one inline source-language resolution, no drive-by refactors. Only src/host/start-config.ts + its test touched.

Tests / evidence. test/start-config.test.ts (existing sourceLangCode assertion updated from the stale "EN" to "AUTO" for the default auto source), plus a new source (spoken) language wiring (#94/#175) block:

  • Korean sourcesourceLangCode "KO", meetingLanguage "Korean" (target EN → summary stays English) — the AC's headline case.
  • arbitrary source (ja) → "JA" / "Japanese".
  • auto"AUTO" / "English" (neutral header, pre-Session start: choose spoken (source) language + target language #94 reply fallback).
  • blank (older shell) → treated as auto, not languageByCode("") = ko.
  • Full app suite 158 tests pass; pnpm typecheck, pnpm lint, pnpm build all clean.

Design Fidelity

N/A — no UI/webview/CSS surface changed. The archive header / dashboard rows render whatever sourceLang they are given; this only corrects the DATA (the source label now reflects the picked language), with no markup, layout, token, or component change.

Deviations

  • Chose "AUTO" as the auto-source header label (matching the existing SOURCE_LANGUAGES registry entry's archiveLabel) and kept English as the auto reply/quick-translate fallback — the ticket left the auto label to be "decided and documented"; both are documented on the AUTO_SOURCE_LABEL / AUTO_MEETING_LANGUAGE constants.
  • The source-language resolution is inlined in resolveStartConfig (RE1 review — single-use helper), and folds a blank/absent code into auto (older-shell safety) so it never resolves to the target-language default.

🤖 Generated with Claude Code

#94 added a user "Spoken language" setting (start message carries
sourceLanguageCode), but resolveStartConfig ignored it and hardcoded
MEETING_LANGUAGE="English" / SOURCE_LANG_CODE="EN" — the comment even claimed the
setting didn't exist. So picking Spoken=Japanese, Translate into=Korean saved
"EN → KO" in every archive header and dashboard row: wrong metadata, permanently.

Derive both the archive header source label and the reply/quick-translate meeting
language from message.sourceLanguageCode via languageByCode(...). The "auto"
sentinel (whisper per-utterance detection, the default) — and a blank value from
an older shell — is special-cased explicitly: it has no fixed spoken language, so
it maps to a deliberately-neutral "AUTO" header label (documented; never a phantom
languageByCode("auto")="AUTO"/languageByCode("")=ko) and keeps the pre-#94 English
reply/quick-translate fallback. Stale comment replaced. Whisper's own source-language
handling (Rust pipeline) is untouched, so per-utterance auto-detect is preserved.

Tests: Korean source → header "KO" + meetingLanguage "Korean"; arbitrary BCP-47
(ja) → "JA"/"Japanese"; auto → "AUTO"/"English"; blank → auto. Full app suite 158
green; typecheck, lint, build clean. No new deps; no caption content logged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@project7-interns project7-interns left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: REQUEST CHANGES\n\n### Epic Alignment: PASS\n#175’s source-language plumbing matches EPIC #134’s audit-follow-up goal and the ticket contract.\n\n### Checked (evidence)\n- Structural gate: live PR body includes filled EPIC Alignment, Self-Verification, and N/A Design Fidelity sections.\n- Ticket/epic contract: {"url":"https://api.github.com/repos/realproject7/livecap/issues/175","repository_url":"https://api.github.com/repos/realproject7/livecap","labels_url":"https://api.github.com/repos/realproject7/livecap/issues/175/labels{/name}","comments_url":"https://api.github.com/repos/realproject7/livecap/issues/175/comments","events_url":"https://api.github.com/repos/realproject7/livecap/issues/175/events","html_url":"https://github.com/realproject7/livecap/issues/175","id":4882601462,"node_id":"I_kwDOS4IFMM8AAAABIwaV9g","number":175,"title":"[audit][bug] resolveStartConfig ignores the #94 source-language setting — archive header and quick-translate label hardcode EN/English","user":{"login":"realproject7","id":15100667,"node_id":"MDQ6VXNlcjE1MTAwNjY3","avatar_url":"https://avatars.githubusercontent.com/u/15100667?v=4","gravatar_id":"","url":"https://api.github.com/users/realproject7","html_url":"https://github.com/realproject7","followers_url":"https://api.github.com/users/realproject7/followers","following_url":"https://api.github.com/users/realproject7/following{/other_user}","gists_url":"https://api.github.com/users/realproject7/gists{/gist_id}","starred_url":"https://api.github.com/users/realproject7/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/realproject7/subscriptions","organizations_url":"https://api.github.com/users/realproject7/orgs","repos_url":"https://api.github.com/users/realproject7/repos","events_url":"https://api.github.com/users/realproject7/events{/privacy}","received_events_url":"https://api.github.com/users/realproject7/received_events","type":"User","user_view_type":"public","site_admin":false},"labels":[{"id":11210529794,"node_id":"LA_kwDOS4IFMM8AAAACnDMcAg","url":"https://api.github.com/repos/realproject7/livecap/labels/bug","name":"bug","color":"d73a4a","default":true,"description":"Something isn't working"},{"id":11210531164,"node_id":"LA_kwDOS4IFMM8AAAACnDMhXA","url":"https://api.github.com/repos/realproject7/livecap/labels/agent/quadwork","name":"agent/quadwork","color":"0e8a16","default":false,"description":"Dispatched to QuadWork batch (headless-testable)"}],"state":"open","locked":false,"assignees":[],"milestone":null,"comments":0,"created_at":"2026-07-14T11:28:35Z","updated_at":"2026-07-14T11:49:12Z","closed_at":null,"assignee":null,"author_association":"OWNER","active_lock_reason":null,"sub_issues_summary":{"total":0,"completed":0,"percent_completed":0},"issue_dependencies_summary":{"blocked_by":0,"total_blocked_by":0,"blocking":0,"total_blocking":0},"body":"> Found by the full-codebase audit of 2026-07-14 (PO manual §8.1: 15 scan agents over 100% of production files, adversarially verified, then PO-verified against source). Part of the v1.3 audit follow-up set under EPIC #134.\n\n## Goal\n#94 added a user source-language setting (start message carries sourceLanguageCode, src/protocol.ts:89; UI in src/main.ts:204,280-289), but resolveStartConfig ignores it and hardcodes MEETING_LANGUAGE = \"English\" / SOURCE_LANG_CODE = \"EN\"; the comment even claims the setting doesn't exist.\n\n## Where (verified on main f8eb9ea)\n**src/host/start-config.ts:19** (medium/certain)\n- Issue: resolveStartConfig ignores the start message's sourceLanguageCode (#94) entirely and hardcodes the archive header source label to "EN"; the comment above ("there is no source-language setting (§8.6)") is stale since #94 added exactly that setting.\n- Impact: User picks Spoken language = Japanese and Translate into = Korean on the Start screen; the saved archive header and every dashboard history row / detail meta line show "EN → KO" — wrong session metadata persisted permanently in the archive.\n- Fix direction: In resolveStartConfig, derive sourceLangCode from message.sourceLanguageCode via languageByCode(...).archiveLabel (keeping "EN" or a neutral label only for the "auto" sentinel), and update the stale comment.\n\n\n## Scope\nWire sourceLanguageCode from the start message through ResolvedStartConfig (meeting language + archive header source label); fix the stale comment. Keep "auto" semantics (per-utterance whisper detect) intact — decide and document the header label for auto (e.g. "AUTO").\n\n## AC\n- Start with source=Korean → archive header shows KO and quick-translate output language follows; auto keeps today's behavior; tests cover both.\n\n## Security invariants\nNever log or persist caption/audio content; no new dependencies; simplest fix that fully solves the ticket — no drive-by refactors.\n\n\n\n## Ticket-review amendments (Batch 36, 2026-07-14 — evidence in QW chat #873-#880)\n- Citation notes (re2, TICKET-OK): hardcode spans start-config.ts:18-19; the source-language UI lives at src/main.ts:258 and :280-289 (:204 is the settings default). Special-case the auto sentinel explicitly — naive languageByCode(\"auto\").archiveLabel would emit "AUTO"; decide the auto header label deliberately and document it.\n","closed_by":null,"reactions":{"url":"https://api.github.com/repos/realproject7/livecap/issues/175/reactions","total_count":0,"+1":0,"-1":0,"laugh":0,"hooray":0,"confused":0,"heart":0,"rocket":0,"eyes":0},"timeline_url":"https://api.github.com/repos/realproject7/livecap/issues/175/timeline","performed_via_github_app":null,"state_reason":null,"pinned_comment":null} + — source language must drive archive label and meeting language while auto remains explicit.\n- Language contract: defines and arbitrary BCP-47 resolution; PR uses it correctly.\n- Riskiest part: auto/blank handling at ; its values are correct, but its extraction adds an unnecessary one-use runtime abstraction.\n- Kill-list: scanned all items — hit listed below.\n- CI: app-macos pending 0 https://github.com/realproject7/livecap/actions/runs/29350565354/job/87145423201

color-guard pass 6s https://github.com/realproject7/livecap/actions/runs/29350565354/job/87145423153
no-stub-gate pass 6s https://github.com/realproject7/livecap/actions/runs/29350565354/job/87145423160
packages-linux pass 34s https://github.com/realproject7/livecap/actions/runs/29350565354/job/87145423159 → app-macos and packages-linux pending; color-guard and no-stub-gate pass.\n\n### Findings\n- [required] Inline the single-call-site source resolver.\n - File: \n - Why it fails: is introduced with exactly one production call site (), violating the no-one-use-abstraction rule; it does not establish a reusable boundary beyond .\n - Do instead: inline the normalization/auto-or-blank conditional and derivation in , keeping the documented auto constants and existing tests.\n\n### Decision\nThe behavior and tests align with #175, but this unnecessary wrapper must be inlined before approval.

@project7-interns project7-interns left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: REQUEST CHANGES

Epic Alignment: PASS

#175’s source-language plumbing matches EPIC #134’s audit-follow-up goal and the ticket contract.

Checked (evidence)

  • Structural gate: live PR body includes filled EPIC Alignment, Self-Verification, and N/A Design Fidelity sections.
  • Ticket/epic contract: live issue #175 and EPIC #134 require source language to drive archive label and meeting language while auto remains explicit.
  • Language contract: src/languages.ts:40-63 defines SOURCE_AUTO_CODE and arbitrary BCP-47 resolution; PR uses it correctly.
  • Riskiest part: auto/blank handling at src/host/start-config.ts:76-85; its values are correct, but its extraction adds an unnecessary one-use runtime abstraction.
  • Kill-list: scanned all items — hit listed below.
  • CI: gh pr checks 185 → app-macos pending; color-guard, no-stub-gate, packages-linux pass.

Findings

  • [required] Inline the single-call-site source resolver.
    • File: src/host/start-config.ts:76
    • Why it fails: resolveSourceLanguage is introduced with exactly one production call site (src/host/start-config.ts:52), violating the no-one-use-abstraction rule; it does not establish a reusable boundary beyond resolveStartConfig.
    • Do instead: inline the normalization/auto-or-blank conditional and languageByCode derivation in resolveStartConfig, keeping the documented auto constants and existing tests.

Decision

The behavior and tests align with #175, but this unnecessary wrapper must be inlined before approval.

@realproject7

Copy link
Copy Markdown
Owner Author

RE2 — APPROVE at PR #185 head (pending app-macos/packages-linux green)

The #175 source-language wiring is correct, honors the amendment's explicit auto special-casing, and avoids both languageByCode traps I flagged in the Batch-36 ticket review (#879). Reviewed the whole diff (2 files) and independently ran the suite + both typechecks + lint on Linux (real pnpm install).

Checked (evidence)

  • Bug fixed. resolveStartConfig no longer hardcodes SOURCE_LANG_CODE="EN"/MEETING_LANGUAGE="English"; it derives both from message.sourceLanguageCode via a new resolveSourceLanguage (start-config.ts:80-86): sourceLangCode = the picked archiveLabel, meetingLanguage = the picked name. Stale "no source-language setting (§8.6)" comment replaced.
  • The auto sentinel is special-cased explicitly (the amendment / my #879 note). resolveSourceLanguage normalizes (trim().toLowerCase()) and short-circuits SOURCE_AUTO_CODE ("auto", languages.ts:39) and blank "" to { meetingLanguage: "English", archiveLabel: "AUTO" } before ever calling languageByCode. This dodges both traps I called out: languageByCode("auto") → phantom "AUTO" language, and languageByCode("") → the ko default (languages.ts:61). Picked tags resolve to their own name/label (Korean→"Korean"/"KO", ja→"Japanese"/"JA").
  • STT semantics preserved. Whisper per-utterance auto-detect is Rust-side and untouched; meetingLanguage/sourceLangCode only feed reply/quick-translate output language + the archive header label (session.ts consumption unchanged). For auto, meetingLanguage stays "English" — identical to pre-Session start: choose spoken (source) language + target language #94.
  • Tests match the AC. Default source assertion updated ENAUTO; new Session start: choose spoken (source) language + target language #94/[audit][bug] resolveStartConfig ignores the #94 source-language setting — archive header and quick-translate label hardcode EN/English #175 block: koKO/Korean (the AC case), jaJA/Japanese, autoAUTO/English, blank→AUTO/English (the languageByCode("") trap, asserted closed).
  • Independently reproduced (node 24 / real pnpm install): test/start-config.test.ts 11/11; full app suite 22 files / 158 tests; app- AND host-tsconfig tsc --noEmit both clean; eslint on the 2 changed files clean.
  • Boundary/invariants: 2 files (src/host/start-config.ts + its test); package.json/lock untouched (no new deps); no reserved files; no caption content logged. Design Fidelity N/A (data-only — the dashboard renders whatever sourceLang it's given).

Non-blocking note (doc accuracy, NOT a change request)

resolveSourceLanguage(code: string) calls code.trim(), and the comment says it also handles a value "absent from an older shell." But sourceLanguageCode: string is a required field in the start message (protocol.ts:89) — the shell (same app build) always sends it, and it was already present pre-#175 (just ignored), so there is no real undefined path. The realistic empty case ("") IS handled and tested. Either drop the "absent" wording or use (code ?? "") to make the comment literally true — cosmetic only.

CI condition

All four checks pending at review time; packages-linux runs the host typecheck + this test, both reproduced green locally. Merge gate should wait for app-macos/packages-linux green. (Shared bot token can't file a formal GH approval; this comment + chat is my evidence-bound verdict.)

Fold the one-caller `resolveSourceLanguage` helper into resolveStartConfig
(RE1 review on PR #185). Behavior is unchanged: auto/blank → English reply
fallback + neutral "AUTO" header; a picked tag → its own name + archiveLabel.
The AUTO_MEETING_LANGUAGE / AUTO_SOURCE_LABEL constants (documented) stay.
11 start-config tests green; typecheck, lint, build clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@realproject7

Copy link
Copy Markdown
Owner Author

RE2 — APPROVE at 3b20c7b (delta re-review; pending app-macos green)

@re1's inline-the-single-use-helper change is behavior-identical, and it incidentally resolved my prior doc nit. Re-reviewed the delta only (52107f8..3b20c7b, one commit, start-config.ts) and independently re-ran on the new head.

Checked (evidence)

  • Behavior-identical inline. resolveSourceLanguage is folded into resolveStartConfig (start-config.ts:52-68): same message.sourceLanguageCode.trim().toLowerCase() normalization; sourceCode === SOURCE_AUTO_CODE || sourceCode === ""source = nullmeetingLanguage = AUTO_MEETING_LANGUAGE ("English") + sourceLangCode = AUTO_SOURCE_LABEL ("AUTO"); otherwise languageByCode(...) → its name/archiveLabel. Every mapping matches the extracted version I already approved (ko→Korean/KO, ja→Japanese/JA, auto/blank→English/AUTO). The documented AUTO_* constants are retained; the auto/blank short-circuit still runs BEFORE languageByCode, so both #879 traps stay closed.
  • My prior doc nit is addressed. The new inline comment says "a blank value from an older shell" (dropped the inaccurate "absent") — now it only claims what the code actually handles.
  • Independently reproduced on 3b20c7b (node 24 / real pnpm install): test/start-config.test.ts 11/11; host-tsconfig tsc --noEmit clean; eslint clean.
  • Boundary unchanged: start-config.ts only in this delta; no deps; no caption logging. My prior full-review findings (correct wiring, STT preserved, AC-matching tests) are unchanged.

CI

color-guard/no-stub-gate/packages-linux pass; app-macos pending — merge gate should wait for it green. (Shared bot token can't file a formal GH approval; this comment + chat is my verdict.)

@project7-interns project7-interns left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: APPROVE

Epic Alignment: PASS

The #175 source-language plumbing follows EPIC #134’s audit-fix direction: selected source drives archive and meeting metadata while auto is explicit.

Checked (evidence)

  • Re-review range: 52107f8..3b20c7b.
  • Prior finding resolved: src/host/start-config.ts:52-68 inlines the former one-use resolver; no helper remains at the former src/host/start-config.ts:76.
  • Contract preserved: auto/blank maps to AUTO/English; selected BCP-47 tags use languageByCode name/archiveLabel.
  • Riskiest part: the auto/blank branch; it explicitly precedes languageByCode, preventing the blank-to-Korean fallback.
  • Kill-list: scanned new range — clean.
  • CI: gh pr checks 185 → app-macos, color-guard, no-stub-gate, packages-linux all pass.

Findings

None.

Decision

The delta resolves the requested change without altering the ticket-required behavior. The live checks are green.

@realproject7
realproject7 merged commit 148e47a into main Jul 14, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[audit][bug] resolveStartConfig ignores the #94 source-language setting — archive header and quick-translate label hardcode EN/English

2 participants