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
98 changes: 98 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
name: CI

on:
pull_request:
push:
branches: [master]

permissions:
contents: read

concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: "1"

jobs:
core-and-ui:
name: Core and UI regression tests
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Check out source
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false

- name: Install Rust
uses: dtolnay/rust-toolchain@4cda84d5c5c54efe2404f9d843567869ab1699d4
with:
toolchain: stable
components: clippy

- name: Cache Cargo
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-${{ runner.arch }}-cargo-ci-${{ hashFiles('Cargo.lock') }}

- name: Test platform-independent core
run: cargo test -p prompter-core --all-targets --locked

- name: Lint platform-independent core
run: cargo clippy -p prompter-core --all-targets --locked -- -D warnings

- name: Test frontend session behavior
# GitHub's hosted Linux image includes Node; these tests use only its
# built-in test runner and need no package install or browser service.
run: node --test crates/app/ui/*.test.cjs

- name: Test speech-helper process lifecycle
run: |
rustc --edition=2021 --test crates/app/src/process.rs -o "$RUNNER_TEMP/linecheck-process-tests"
"$RUNNER_TEMP/linecheck-process-tests"

native-macos:
name: Native macOS build and tests
runs-on: macos-latest
timeout-minutes: 30
env:
MACOSX_DEPLOYMENT_TARGET: "13.0"
steps:
- name: Check out source
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false

- name: Install Rust
uses: dtolnay/rust-toolchain@4cda84d5c5c54efe2404f9d843567869ab1699d4
with:
toolchain: stable

- name: Cache Cargo
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-${{ runner.arch }}-cargo-ci-${{ hashFiles('Cargo.lock') }}

- name: Compile native speech helper
run: |
set -euo pipefail
triple="$(rustc -vV | sed -n 's/^host: //p')"
mkdir -p crates/app/binaries
swiftc -O -target "$(uname -m)-apple-macos13.0" scripts/speech-recognizer.swift \
-o "crates/app/binaries/speech-recognizer-$triple"

- name: Compile and test the desktop app
# Exercises Rust/Tauri integration without signing credentials, a
# notarization account, a microphone, or an interactive app session.
run: cargo test -p prompter-app --locked
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ Single-file vanilla JS/HTML/CSS. No framework, no build step. Communicates with
## Key conventions

- **No sibling repos**: Linecheck builds on its own (it no longer depends on `minutes-core`).
- **Diagnostics**: `open -n -W -a Linecheck --args --transcribe-file <audio> --script <md> --out <json>` runs the post-session check on any recording (speech access belongs to the app, so it can't run from a shell). Each live run's recognizer stream is in `~/.linecheck/recording.jsonl`; replay it with `cargo run -p prompter-core --example replay`.
- **Diagnostics**: `open -n -W -a Linecheck --args --transcribe-file <audio> --script <md> --out <json>` runs the post-session check on any recording (speech access belongs to the app, so it can't run from a shell). With **Save diagnostic replay** enabled (off by default), the latest run's script and recognizer stream are in `~/.linecheck/recording.jsonl`; replay it with `cargo run -p prompter-core --example replay`.
- **Content protection**: `contentProtected: true` in tauri.conf.json prevents screen capture of the window.
- **Script format**: `.script.md` — annotated markdown with optional YAML frontmatter, `> PAUSE:` and `> BRANCH:` directives. See SPEC.md.
- **File paths**: Scripts in `~/meetings/scripts/`, compliance reports in `~/meetings/consults/`, settings in `~/.linecheck/settings.json`.
Expand Down
25 changes: 20 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,16 @@ Allow microphone and speech recognition access when the first session starts. No
2. Put the reading line just under your camera (`[` `]`) and set the column width (`,` `.`).
3. Press **Start Session** (or Space) and start talking. Linecheck holds the line you're reading at the reading line.
4. It waits at `PAUSE` points. At `BRANCH` points it follows whichever answer you start reading, or you can click one.
5. Click **End**. You get a compliance report and coaching notes, re-checked against the full recording.
5. Click **End**. You get a compliance report and coaching notes, re-checked against the full recording. The report distinguishes live tracking, a check in progress, a completed recording check, and a failed check.

**Pause** freezes the prompter; the microphone continues listening and recording. **End** stops the microphone and closes the recording before reporting. Timer mode is labeled explicitly and scrolling alone never counts as speech evidence.

## Private by design

- Speech recognition runs on your Mac with Apple's on-device recognizer. Audio never leaves the machine.
- The session recording is used once, for the post-session check, then deleted (unless you choose to keep it).
- Reports and transcripts are written as `0600` files in `~/meetings/consults/`.
- Reports and transcripts are created privately (`0600`) in `~/meetings/consults/` and replaced atomically. New report and session directories use `0700` permissions.
- Saving the raw script and recognized speech for diagnostic replay is **off by default**. Enable **Save diagnostic replay** in Session options only when debugging; that local file remains until you delete it. Previously saved replay files are not automatically deleted.
- The window is hidden from screenshots and screen sharing by default. Toggle it in Session options (`H`) or from the menu-bar icon.

## Script format
Expand Down Expand Up @@ -90,7 +93,9 @@ Linecheck writes a Markdown report to `~/meetings/consults/`:
- Lines not delivered, restarts, and off-script stretches
- Coaching insights on pacing, coverage, pause discipline, and section balance (computed from the session data, no LLM)

Live tracking can miss things, so after you end the session Linecheck re-transcribes the whole recording, aligns it against the script again, and rewrites the report from that pass.
Live tracking can miss things, so after you end the session Linecheck re-transcribes the whole recording, aligns it against the script again, and rewrites the report from that pass. If recognition fails, times out, or exceeds the alignment resource limits, the live report stays intact and the failed check is shown explicitly.

Coverage is an estimate of script delivery, not a determination of clinical, legal, or regulatory compliance. A covered section has at least one matched main-line sentence; it does not mean every line was delivered. Adherence counts the words in covered sentences, so it is a sentence-level estimate rather than a verbatim count of matched spoken words. Optional branch words are not included in the main-line adherence denominator. The live transcript contains recognition snapshots, while a successful recording check saves a separate full-recording transcript.

## How the tracking works

Expand Down Expand Up @@ -128,8 +133,14 @@ Requires Rust, the Tauri CLI (`cargo install tauri-cli`), and the Xcode command
# Build the app (Swift helper + Tauri bundle), and optionally install it
./scripts/build.sh --install

# Run the tests (the core crate also builds and tests on Linux)
cargo test
# Run pure-core tests on any platform
cargo test -p prompter-core --all-targets --locked

# UI regressions (Node 18+, no npm dependencies)
node --test crates/app/ui/*.test.cjs

# Native app integration tests (macOS, after building the Swift helper)
cargo test -p prompter-app --locked
```

```
Expand Down Expand Up @@ -163,6 +174,10 @@ Other tools can hand scripts to Linecheck three ways:

See [INTEGRATION.md](INTEGRATION.md).

## Engineering direction

See [the engineering assessment](docs/ENGINEERING-REVIEW.md) for this upgrade's scope, remaining limits, and the next steps toward a measured, evidence-based tracking engine.

## Related

Linecheck shares the `~/meetings/` folder convention with [Minutes](https://github.com/silverstein/minutes), an open-source, local-first conversation memory for Claude Code, Codex, and other MCP clients.
Expand Down
Loading
Loading