Skip to content

Add an explanation timeline for every reminder - #1

Merged
renrenmimi merged 1 commit into
mainfrom
feat/explanation-timeline
Aug 31, 2026
Merged

Add an explanation timeline for every reminder#1
renrenmimi merged 1 commit into
mainfrom
feat/explanation-timeline

Conversation

@renrenmimi

Copy link
Copy Markdown
Owner

What

The gauge told you how tense a moment was, never why — and it fused the acoustic and language signals into one number before anyone could look at them. This adds a timeline that keeps the two families on separate tracks and states, for each reminder, which rule it met and how many points each signal contributed.

Two stacked tracks over the session:

  • Acoustic — loudness and pace, read from the waveform, no content involved.
  • Semantic — the model's tone read plus the local lexicon, read from the transcript.

Reminder markers sit across both. Every marker has a card giving the rule it satisfied (score ≥ 70 held 5s, or a high-risk phrase), the dominant driver, and the point contribution of each signal.

How the numbers hold up

The blend is linear, so the score above the 30-point baseline splits into exactly three additive terms:

score - BASE = (1-w)·(volume+speed) + (1-w)·keywords + w·(semantic - BASE)

Those are the terms the card prints. The rules side is scaled when it clamps at 100, and rounding remainders fold into the largest term, so the column adds up to the score it explains. A floored tick is left alone and labelled — its score was overridden, not blended.

Changes

  • fusion.ts produces a per-tick SignalBreakdown and blends it through a single shared fuseScore. The chart is drawn from the same numbers the score is made of, not from a second implementation. Scoring is bit-compatible — the existing 14 fusion tests pass untouched.
  • explain.ts is the new pure attribution module: contributions, dominant driver, evidence shares, track heights.
  • The session machine records each breathing pause and rewrite offer with the tick that justified it, captured at the transition so a later, calmer reading cannot rewrite the explanation.
  • --signal-acoustic becomes a real theme token; the tone gauge's hardcoded volume-ring hex now uses it.

Demo

/demo previously hardcoded its scores, which would have made its own explanation cards contradict themselves. It now declares canned inputs and runs the real fusion math. The arc is preserved (calm → hostile peak → intervention → recovery); peak moves 88 → 94 and the recap series follows.

Two bilingual fixes surfaced along the way: the opening tone rationale was English in both locales, and the dashboard claimed "AI resting · rules mode" while the script fed it tone reads on every beat.

Validation

  • 163 unit tests pass, including 20 new ones covering attribution, clamped and rules-mode reconciliation, and reminder provenance.
  • Playwright demo smoke passes (42.8s, zero API traffic).
  • Typecheck, lint, and build clean.
  • Verified in the browser in both locales: arithmetic reconciles on screen (30 + 19 + 36 + 9 = 94), and the timeline section shows no untranslated text in either language.

🤖 Generated with Claude Code

The gauge said how tense a moment was but never why, and it fused the
acoustic and language signals into a single number before anyone could
see them. This adds a timeline that keeps the two families apart and
states, for each reminder, the rule it met and the arithmetic behind it.

- fusion.ts now produces a per-tick SignalBreakdown and blends it through
  a single shared fuseScore, so the chart is drawn from the same numbers
  the score is made of rather than from a second implementation.
- explain.ts attributes the score above the 30-point baseline to loudness
  and pace, the local lexicon, and the model's tone read. The blend is
  linear, so the three terms reconcile exactly; the rules side is scaled
  when it clamps, and rounding remainders fold into the largest term.
- The session machine records each breathing pause and rewrite offer with
  the tick that justified it, captured at the transition so a later, calmer
  reading can never rewrite the explanation.
- The demo script now declares canned inputs and runs the real fusion math
  instead of hardcoding scores, which keeps its explanation cards honest.
  Its opening tone rationale was English in both locales; it is localized
  now, and the dashboard no longer claims the AI is resting while feeding
  tone reads.

Both locales carry the full string set, and the timeline section shows no
untranslated text in either.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Aug 31, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
tone-down Ready Ready Preview Aug 31, 2026 7:44pm

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d0e5e9daea

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +253 to +254
score: state.score,
signals: latestSignals(state),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Capture rewrite evidence when the request is triggered

When /api/rewrite takes longer than the two-second scoring interval, this records state.score and latestSignals(state) from the time the response arrives, not from the hostile score or keyword that initiated the request. If the speaker has calmed down meanwhile, the reminder card can claim that a low, unrelated tick triggered the rewrite; snapshot the triggering score/signals when requestRewrite starts and carry them in the event.

Useful? React with 👍 / 👎.

Comment on lines +250 to +255
reminders: withReminder(state, {
at: event.moment.at,
kind: 'rewrite',
score: state.score,
signals: latestSignals(state),
quote: event.moment.quote,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Record fallback rewrite reminders in the timeline

When the rewrite endpoint is unavailable, blocked by its breaker/budget, or rejects, ToneSuggestion still presents the local SUGGESTION_MAP rewrite, but no REWRITE_OFFERED event is dispatched. Because reminders are appended only through this event path, those user-visible rewrite reminders never appear in the new explanation timeline; emit a reminder when the fallback suggestion is actually shown as well.

Useful? React with 👍 / 👎.

@renrenmimi
renrenmimi merged commit 65555dd into main Aug 31, 2026
8 checks passed
@renrenmimi
renrenmimi deleted the feat/explanation-timeline branch August 31, 2026 19:54
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.

1 participant