docs+config: continuous research end to end, and a feed a subscriber can play
deploy / test (push) Successful in 4m58s
deploy / build (push) Successful in 58s

CLAWMATES_PUBLIC_URL was unset, so the podcast feed built every episode
URL from the localhost default: valid XML that no podcast app could
play. Set in the repo override and on prod (https://clawmates.work);
the feed now carries real enclosure URLs for both episodes.

Addendum 7 records the four defects found working on this recipe — only
one of which the plan predicted — and the evidence for each.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01WZb5A2kfVfjpdwSochkuHz
This commit is contained in:
Omar Sobh
2026-09-22 13:39:40 -05:00
co-authored by Claude Opus 5
parent 785a7e7b0e
commit 620d1bfc5f
2 changed files with 64 additions and 0 deletions
@@ -133,6 +133,10 @@ services:
# TypeSafe Jev — shadow skill triage (cm-api skill_triage.rs). Optional: # TypeSafe Jev — shadow skill triage (cm-api skill_triage.rs). Optional:
# unset, the triage records nothing. Server-side only; never a mission's. # unset, the triage records nothing. Server-side only; never a mission's.
TYPESAFE_API_KEY: ${TYPESAFE_API_KEY:-} TYPESAFE_API_KEY: ${TYPESAFE_API_KEY:-}
# Where this deployment is reachable from OUTSIDE. The podcast feed
# builds every episode URL from it, so unset means a feed whose items
# all point at localhost — valid XML that no subscriber can play.
CLAWMATES_PUBLIC_URL: ${CLAWMATES_PUBLIC_URL:-http://localhost:8080}
# ElevenLabs GenFM, for rendering the Continuous Research episode. # ElevenLabs GenFM, for rendering the Continuous Research episode.
# #
# SERVER-SIDE ONLY, deliberately. It is NOT in # SERVER-SIDE ONLY, deliberately. It is NOT in
+60
View File
@@ -560,3 +560,63 @@ accidents and obvious cases, which is what its own header has always said.
**Also worth keeping:** measure efficacy and utility as a pair. Every gate **Also worth keeping:** measure efficacy and utility as a pair. Every gate
scenario already asserts the mission still delivered; that is the utility scenario already asserts the mission still delivered; that is the utility
half, and it should stay mandatory for any future rule. half, and it should stay mandatory for any future rule.
## Addendum 7 — 2026-09-22, continuous research end to end
`docs/TEMPLATE-MATURITY.md` graded `continuous_research` "runs live,
unguarded" — the recipe with the most moving parts and no standing check.
Working on it found four defects, only one of which the plan predicted.
**The digest never reached the vault.** Paper notes auto-merge (`library.rs`
calls `auto_merge::try_merge`); the digest did not, because nothing on the
mission path ever called it. `ContinuousResearch/` on `main` ended at
**2026-08-18** while every run since produced `analysis.md`, `script.md` and
`episode.json` onto a branch nobody merged. `mission_delivery` now accrues
such a branch behind three limits — `AdditiveOnly` (re-reads the diff against
the remote base, refuses any M/D/R), the phase's own judge verdict, and a pure
`accrues_automatically()` naming exactly one recipe. **Proven:** the scenario's
`analysis.md is on main (6717 bytes)`, where the same path returned 404 before.
**The script parsed to zero turns, forever.** The baseline run the plan put
first found this instead of the race it was written for: the worker was
healthy, found the script in the checkout, and `parse_script` returned nothing
— so it skipped, every two minutes, with no episode and no tombstone. The
skill asks for `HOST:`; the writer, producing markdown, wrote `**HOST:**`, and
`split_once(':')` yields `**HOST` which fails the all-uppercase test. The
parser now accepts emphasis around the label and leaves emphasis *inside*
speech alone. Had the baseline been skipped, the vault fallback would have
shipped as a "fix" for a race that was not happening.
**The renderer raced a reaper.** It read the script from a checkout deleted 30
minutes after completion; `record_unrenderable`'s own message pointed at the
vault as manual recovery. It now takes the vault — default branch, then the
delivery branch. **Proven live:** `checkout is gone; rendering from the vault
(clawmates/mission-01a0c9c4-cf8ba78d:…/script.md)`.
**A tombstone was permanent.** `NOT EXISTS (podcast_episodes)` meant a day that
gave up could never be reconsidered: mission `01a0c9c4` was tombstoned at
16:17:39, four minutes before the build that could render it started at
16:21:58, and would have stayed silent on a script sitting on a branch.
Tombstones now expire after a backoff each attempt refreshes — recovers the
same day a cause is fixed, without the every-two-minutes churn.
**Result — the first complete passes this pipeline has made.** Two episodes,
`419s`/`6,708,231 bytes` and `401s`/`6,422,347 bytes`, rendered by
`elevenlabs/eleven_flash_v2_5`, served at `HTTP 200 audio/mpeg`, in a feed that
now advertises `https://clawmates.work` — `CLAWMATES_PUBLIC_URL` was unset, so
every enclosure URL pointed at `localhost:8080`: valid XML no subscriber could
play.
**The guard** (`verify-mission-delivery.sh continuous-research`): digest on
`main`, manifest triage fields, evidence spread above `SATURATED_BELOW` (live:
**0.89**), analysis naming every harvested paper, `episode.json` obeying the
recipe's own 1070 char rule, and a rendered episode. Its first run failed the
episode assertion while the pipeline was fine — it checked the instant the
mission completed, asserting the worker is *fast* rather than that it works. It
now waits for a verdict and says how long it waited.
**Process note.** One commit was pushed off a `cargo test … | grep …` whose
exit code came from grep, so two failures passed into a `&&` chain that
committed and pushed. Nothing bad shipped — the failures were `PoolTimedOut`
from Docker Desktop being down locally, and CI on a real Postgres was green —
but the gate did not gate. Use `set -o pipefail`, or run cargo bare.