Commit Graph

11 Commits

Author SHA1 Message Date
Joseph Doherty 0d3bbf4272 test: T58 coverage gaps (truncation, update/close paths) (T80.5)
Three gaps left by T58's initial test coverage:

* test_key_quote_truncation_at_200_chars — exercises the 200-char hard
  slice in _build_key_quotes_suffix so any future change to the
  truncation strategy (ellipsis, word boundary, etc) trips the test.
* test_thread_detection_update_candidate_emits_thread_updated —
  pins the ``update`` action emission shape (thread_id, summary,
  last_referenced_scene_id).
* test_thread_detection_close_candidate_emits_thread_closed — pins
  the ``close`` action emission shape (thread_id, closed_at).

No production change; pure coverage add.
2026-04-26 21:50:55 -04:00
Joseph Doherty b91a5e9293 fix: thread_closed uses chat-clock time, not wall clock (T80.4)
T58 stamped emitted ``thread_closed`` events with
``datetime.now(timezone.utc).isoformat()``. The rest of the close
pipeline (memories.chat_clock_at, scene_closed.ended_at, edge writes)
uses the chat's in-world clock. Threads must agree so timeline
reconstruction stays consistent under time skips and replay.

Read ``chat["time"]`` (already loaded for the per-POV path) and pass
it through as ``closed_at``. Falls back to UTC now only when chat_state
has no clock yet — defensive; chat_created always seeds it.

Adds test_thread_closed_uses_chat_clock_time.
2026-04-26 21:50:04 -04:00
Joseph Doherty 9d06eaf57a fix: log swallowed exceptions in detect_threads try/except (T80.3)
The broad ``except Exception`` around detect_threads silently dropped
programmer errors (wrong kwargs, import-time failures, etc), making
diagnostics painful. Log at DEBUG with full exc_info so the failure
surfaces in local logs without breaking the close pipeline's
failure-tolerant contract.

Adds test_detect_threads_failure_is_logged using caplog.
2026-04-26 21:49:17 -04:00
Joseph Doherty dae481eb92 fix: scope thread detection transcript to closing scene (T80.2)
apply_scene_close_summary fed detect_threads the chat-wide last-50
turns. When a chat has accumulated multiple scenes' worth of dialogue,
that bleeds prior-scene turns into the second close's classifier prompt
and risks mis-attributing threads (closing one that opened earlier,
re-opening one that already closed).

Add an optional ``since_event_id`` kwarg to ``_read_recent_dialogue``
that lower-bounds by event_log id, plus a ``_scene_opened_event_id``
helper that resolves the scene-open event for a given scene_id. Wire
both into the thread-detection call site so its scene_transcript
holds only the closing scene's turns. The per-POV summarizer keeps the
chat-wide approximation it had before — that's intentional.

Adds test_thread_detection_uses_scene_scoped_transcript.
2026-04-26 21:48:44 -04:00
Joseph Doherty d123684f9a fix: guard scene close key-quote suffix against re-close bloat (T80.1)
Re-running apply_scene_close_summary on the same scene previously caused
recursive bloat: _build_key_quotes_suffix sourced quote text from
memories.pov_summary, which after the first close already carried a
"Key quotes:" suffix. The next close would then quote the quotes,
nesting deeper each time.

Strip any existing suffix from candidate text before truncating to
200 chars in the suffix builder, and from the fresh classifier output
before composing the new value in _summarize_and_apply_for_witness so
the rewrite replaces rather than stacks.

Adds test_scene_close_re_run_does_not_double_suffix.
2026-04-26 21:46:20 -04:00
Joseph Doherty a781732ee6 feat: meanwhile summary digest surfaces to next you-scene (T65) 2026-04-26 20:59:35 -04:00
Joseph Doherty 343f305587 feat: significance-driven quote retention + thread emission on close (T58) 2026-04-26 20:18:34 -04:00
Joseph Doherty 13c23fd898 feat: LLM-merged group meta-summary (T70) 2026-04-26 17:07:12 -04:00
Joseph Doherty 4e240347b4 feat: per-POV summaries on close for each present witness 2026-04-26 16:06:05 -04:00
Joseph Doherty 5aab98e4d7 fix: classifier robustness — schema in prompt, retries, kickoff fallback
The kickoff parse-and-confirm route was 500-ing intermittently because
Hermes-3 + Featherless's response_format={"type":"json_object"} only
guarantees JSON output, NOT a particular schema. The model was inventing
its own field names (sceneTime, entities, settingDetails) instead of
the KickoffParse fields, causing Pydantic validation to fail on both
classify() retries.

Three changes:

1. Include the Pydantic JSON schema in the system prompt so the model
   knows exactly which keys to produce. Affects every classify() call
   (kickoff parse, turn parse, scene-close detect, significance,
   state-update, scene summarize). Strip ```json fences if the model
   wraps its output. Bump retries 2 → 3 (model is stochastic; one extra
   attempt closes most of the remaining gap).

2. parse_kickoff() now passes a default empty KickoffParse so the
   route degrades to a fillable form instead of 500 when the classifier
   ultimately fails. The confirm form is the human-in-the-loop; an
   empty form is strictly better UX than a stack trace.

3. Tests updated: bumped canned-failure arrays from 2 → 3 entries to
   match the new attempt count; renamed kickoff test from
   "raises_when_classifier_fails_twice" to
   "falls_back_to_empty_when_classifier_fails" reflecting the new
   degraded-but-usable behavior.

Verified live with all 3 sample bots (maya/eli/sam) — kickoff route
returns 200 across multiple attempts. Full suite: 168 passed.
2026-04-26 15:03:13 -04:00
Joseph Doherty b5175aefaa feat: per-POV summary and edge summary update on scene close 2026-04-26 13:53:12 -04:00