feat(sense): close content_type enum and hydrate facet enum from runtime
Tighten talent/sense.schema.json so Gemini's response_json_schema
enforcement closes two surfaces that previously accepted garbage:
- content_type: replace 8-value enum (which included `mixed`) with the
17-value enum derived from think.activities.DEFAULT_ACTIVITIES minus
the schedule-only ids, plus the `idle` sentinel. A drift-detector
test in tests/test_sense_schema.py compares the on-disk enum to the
derivation, so adding/removing an activity will fail loudly.
- facets[].facet: replace the open `{type: string, minLength: 1}` with
a sentinel enum `[__RUNTIME_FACETS__]` that is hydrated per-call in
think.talents._execute_generate from think.facets.get_facets(),
filtered by the slug regex `^[a-z][a-z0-9_-]*$`. When the journal
has no valid facets the sentinel falls back to {type: string,
minLength: 1} so the schema remains satisfiable.
Hydration is a deep-copy, idempotent, read-only transform that lives
in think.talent. The on-disk schema and `get_talent("sense")["json_schema"]`
both still equal the raw file (existing
test_sense_loaded_json_schema_matches_on_disk_schema invariant holds);
hydration happens once before the primary provider call and is reused
for the fallback.
talent/sense.md is updated to match: the content_type field doc lists
all 17 values, the "Facet assignment rules" paragraph drops the
hardcoded pseudo-facets (`meetings`, `learning`, `technical-work`)
that were never real facet IDs, and Rule #5 stops referencing the
dropped `mixed` value.
No data migration. Existing on-disk records with content_type=mixed or
pseudo-facet entries remain valid documents; future runs simply emit
schema-conformant values.