fix(think): make schema-invalid generate output a terminal error master
_execute_generate previously wrote the model output to output_path whenever it was truthy and then emitted finish. get_use_end_state() classifies finish as a clean talent.complete, so a local model that emitted schema-violating JSON could both corrupt the output artifact and count as a successful daily completion. Cloud providers enforce schemas server-side, so this primarily affects the local provider path, which is the default engine direction. The new pre-write gate validates the candidate that would actually be written, using the load-bearing predicate output_path and result and not _output_valid_for_schema(result, ...). Using _schema_validation_clean here would have short-circuited on the raw-text advisory and fired for pulse and steward, whose post-hooks repair unparseable raw output into a valid default; that would turn graceful degradation into a terminal error. _schema_validation_clean is unchanged and keeps its one caller in the clean-provenance predicate, where the conservative raw short-circuit is correct. The output_path and result prefix is intentional. It excludes the story post-hook talents conversation, work, and event, which return an empty string on every path because their real output is a merge into the activity record. The error event includes schema_validation verbatim when present, but that field describes the raw provider text rather than the rejected post-hook candidate. It can therefore read valid: true when a hook produced invalid output. The human error message derives from errors[0] only when the raw text itself was invalid; otherwise it uses a generic fallback. schema_invalid now joins DETERMINISTIC_FAILURE_REASON_CODES so the existing threshold-2 backoff applies. The first schema-invalid run still gets a normal re-dispatch, so a stochastic local model that recovers next cadence is not penalized; only a talent that fails twice consecutively backs off. The set comment now reflects that these include high-recurrence stochastic failures, since "will crash identically" was already false for no_output. Fenced JSON is now an honest failure. tests/test_markdown_fence_strip.py::test_generate_json_output_not_stripped used to assert silent success for a fenced JSON payload; it now asserts terminal schema_invalid while preserving the original invariant that output: json is never fence-stripped. JSON fence-stripping remains a deliberate follow-up, not part of this change. The _output_valid_for_schema warning no longer says cached, since that was already wrong at two of its three call sites. _execute_with_tools also documents that no cogitate talent declares output: json or a schema today, and any future gate there should mirror the generate path. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>