Commit Graph

858 Commits

Author SHA1 Message Date
shitratgit[bot] bc8c995b65 feat(slack): dispatch ShitRat work from lc and cc channels 2026-08-05 16:36:55 +00:00
shitratgit[bot] b739996b0e fix(infra): native typesense snapshots, convex LAN forwarder, MTU 8192 receipts 2026-07-31 13:23:36 -07:00
shitratgit[bot] f1cb2bc72f fix(system-bus): gate k8s work behind service placement on flagg 2026-07-31 12:26:22 -07:00
shitratgit[bot] 43d39a3684 fix(search): normalize channel timestamps 2026-07-30 00:26:41 -07:00
shitratgit[bot] ca3f4d9636 fix(memory): recover and quarantine capture backlog 2026-07-30 00:26:33 -07:00
shitratgit[bot] 0b9b2fce49 docs(search): record Typesense debris cut 2026-07-29 19:27:45 -07:00
shitratgit[bot] 60d5333730 fix(search): retire Typesense run projections 2026-07-29 19:10:22 -07:00
shitratgit[bot] b3149dc226 fix(memory): decouple capture auth from Typesense 2026-07-29 17:22:13 -07:00
shitratgit[bot] e5ac65901f fallback: batch the spray, redact private bodies — telegram-quiet step
First outage message immediate, later inputs join a rolling 10-min
batch summary (sources + counts, urgent/critical non-private subjects
only). Front/email/slack NEVER contribute raw text on fallback.
Recovery cancels the pending summary and leaves canonical inputs for
the gateway. Per-input fallback.delivered receipts preserved. 07-27
burst replay: 33 raw sends become 6 summaries, zero private bodies.
212 gateway tests re-run green by steering.
2026-07-29 16:33:09 -07:00
shitratgit[bot] 15f378e8fb notify: kind tagging enforced at the boundary — telegram-quiet step
notify send warns on missing --kind and defaults to receipt (review
making it required 2026-08-12). check-system-health tags alert/receipt/
digest, check-email tags ask, voice canary + NAS soak timers tagged,
redis-backup already correct via sendHardAlert. Judgment prompt names
kind as primary routing evidence. Remaining untagged: external
cli/notify callers, check-gateway-health, pane-schedule. 66 targeted
tests re-run green by steering. Correction: audit had 140 kind-less
messages (119 was the priority count).
2026-07-29 16:30:49 -07:00
shitratgit[bot] 7152c95870 external delivery canary — telegram-quiet step
gateway/external-delivery-canary: host-only Inngest function, weekly
Tue 09:17 PT, GATEWAY_EXTERNAL_CANARY_MODE=off by default so
registration is inert. Proves both paths (immediate delivery.confirmed
within 10min SLO; quiet aggregate close-deliver to one visible digest),
snapshots pending-input age + aggregate deadlines from the canonical
log, 0600 JSONL receipts + OTEL pass/fail, failure alarm via the
mechanical pane scheduler (no gateway judgment, no listeners, never
restarts). First live run is a separate approved action. Tests re-run
green by steering.
2026-07-29 16:09:23 -07:00
shitratgit[bot] 9f82eb32a0 gateway incident latch — telegram-quiet step
New @joelclaw/gateway-incident-latch reconstructs (source, anomalyId)
incident state from canonical gateway.decision.recorded receipts; Redis
out of the truth path. Stream tool rewrites incident-tagged decisions:
opens deliver + aggregate, identical repeats join, one material change
per PT day, resolution close-delivers once, reopens link follows.
Producer contract documented in docs/gateway.md + messaging skill.
Audited pulse replay: 15 DMs (12 transitions + 3 digests) from 42
inputs. 45 tests re-run green by steering; companion joelclaw-api
boundary change committed separately.
2026-07-29 16:03:35 -07:00
shitratgit[bot] 5227b8554e fix(memory): bound capture authentication lookup 2026-07-29 16:00:09 -07:00
shitratgit[bot] ca66a98f7d gateway health checker: remove autonomous restart — telegram-quiet step
The checker diagnoses, writes an idempotent operator-action receipt,
emits gateway.health.operator_action.requested via the OTEL outbox, and
pushes gateway.health.degraded with automaticRestart:false. 72h audit
showed 2 auto-restarts, both errored, 0 successes. Only remaining
restart callers are operator-invoked (cli gateway.ts --restart flag,
cutover-rollback script). Tests re-run green by steering.
2026-07-29 15:54:26 -07:00
shitratgit[bot] 581c32568b fix(pi): detach session capture from turn lifecycle 2026-07-29 15:43:47 -07:00
shitratgit[bot] 6dae9285cf system-health: per-component incident latch + daily digest — telegram-quiet step
check-system-health now latches (component, anomalyId) incidents in
Redis and DMs only open/material-change/resolved transitions; every
observation still lands in OTEL and the daily aggregate; one digest at
00:05 PT; all-green silent. Replay of the audited 285-check window:
10 DMs where 72 were sent. New systemHealthDailyDigest function
registered — NOT live until the system-bus host reloads (registration
is not routing). 42 targeted tests re-run green by steering; worker
reports 794/794 package suite.
2026-07-29 15:38:21 -07:00
shitratgit[bot] 144ec06a55 notify wait reads canonical terminal events — telegram-quiet step
Flow projection gains fallback.delivered and aggregate-closure
terminals; notify wait resolves the flow from the event log with the
Redis correlation key as cache only; producer correlation preserved
through decision execution. Fixes false deliveryProven:false on
confirmed deliveries (daily-flow receipts 07-25..07-29). 31 targeted
tests green, verified by steering. Step + ledger recorded in
aihero-support .brain/projects/telegram-quiet.
2026-07-29 15:29:35 -07:00
shitratgit[bot] e65e02dd5c feat(gateway): steer delivery to the dropped-ball bar with a morning digest
roles/gateway.md carries the settled bar: a ping means waiting-on-Joel
and aging, important first-notice breakage, or an answer he asked for.
One incident one thread, one ping per crossing, worker receipts never
ping, runbook cadence is evidence. Ack-first applies to addressed
inbound only; ambient gets observe and zero outbound. Morning digest
beat: idempotent 07:30 LA wake with registry readback in the driver.
2026-07-29 14:23:27 -07:00
shitratgit[bot] 5b84fd0b3a fix(gateway): ambient inbound observes instead of acking
Transport stamps addressed/ambient on inbound.received at ingress: reply
to a gateway flow, @mention, or DM is addressed; Slack channel messages
without a mention are ambient. The plugin refuses deliver/close-deliver
on ambient without a prior escalate receipt, requires observe/escalate
as the first ambient decision, and scopes ack-first to addressed only.
Joel told the gateway 'my slack messages are not commands unless noted'
on 2026-07-27 and prose lost; this is the tooth.
2026-07-29 14:02:51 -07:00
shitratgit[bot] a9bab79f89 feat(gateway): delivery-volume meter pages once when judgment drifts loose
Daily 08:00 cron counts delivery.requested + fallback.delivered over 24h.
At 24/day (2x the 12/day settled target) it pages once with top sources;
a Redis NX latch resets only after a full day back at target. Stable
notification eventId keeps Inngest retries from double-paging.
2026-07-29 13:28:06 -07:00
shitratgit[bot] 55aa80a32e fix(gateway): leave a receipt when the heartbeat fiber covers a blocked pass
The heartbeat fiber logs only on verdict change, so the one moment worth seeing
— it refreshing the key while the work pass sits inside a 300s poke — left no
trace at all. The fix was invisible in production exactly where it matters.

A work pass older than the TTL can only mean it is blocked, so refreshes taken
in that window now carry coveringBlockedPass and how stale the pass had gone.
Still silent the rest of the time; the receipt log is 25MB per five days and
does not need every ordinary tick twice.
2026-07-27 13:59:48 -07:00
shitratgit[bot] 819bc64aeb fix(gateway): the heartbeat means alive, not idle
The transport reads gateway:agent:heartbeat to answer one question: will a
live agent session pick this message up? The driver was answering a different
one — "is the session idle this instant" — and withholding the key otherwise.

Two failure modes, both measured on the live receipt log 2026-07-27:

1. The pass that wrote the key was the same pass that blocked on promptAgent.
   The poke deadline is 300s; the key lives 60s. 51 of the last 200 pokes
   outlived the TTL, leaving the gateway invisible 12.6% of the day — precisely
   while it was busiest. Fixed by giving the heartbeat its own fiber that takes
   its own fresh observation and never blocks on a poke.

2. Every ordinary busy turn read as an outage, so the transport delivered raw
   text and bypassed the agent's judgment. Fixed by heartbeatVerdict(), which
   withholds only on real evidence: no session, degeneration, a failed poke,
   an in-flight retire, or a session that has stopped answering entirely.

The last of those is what keeps this honest. A mid-turn session is vouched for
only within unresponsiveGraceMs (default = poke deadline), so a wedged session
still loses the key and the kill drill still has something to catch. A retired
session drops the key in the same pass that stops it, never on pre-kill
evidence.

Also fixes the restart gap: a driver restarting mid-turn used to score the
running session unresponsive on sight and withhold, which is why restarting the
driver guaranteed a raw-delivery window.

45 driver tests pass (7 new), 252 across gateway + driver.
2026-07-27 13:57:23 -07:00
shitratgit[bot] d4a5ad49cc fix(front): announce VIP mail only — the digest can have the rest
Restoring the webhook's notify seam restored its behavior too: every
inbound email announced individually. Seven landed on Joel's phone inside
a minute, ads included, while aggregation was still cold.

check/email-triage already batches inbound mail into one "N emails need
you" digest, so per-message announcement was always the redundant half.
The reader now announces inbound VIP senders only. Everything else is
still indexed and still reaches Joel through the digest; it just does not
ring the phone.
2026-07-27 09:19:16 -07:00
shitratgit[bot] bc223c64bd fix(gateway): stop the noise machine — aggregation, Front mail, health ticks
Six days of the message event log (3978 events, the gateway's whole life)
say the same thing three ways: it talks more and Joel talks less.
Deliveries went 15/day on cutover to 76/day; Joel's own messages went
36/day to 3/day.

Three causes, two of them mine from 2026-07-25.

1. The 4h session retire stranded every open aggregate. A batch lives in
   the agent's head, so the successor can never close one it did not
   open. Aggregation stopped dead at 07-25 21:00 and never resumed across
   11 retires — and aggregation was absorbing 694 of 844 health-check
   inputs. shouldRetire now waits for an empty batch set, bounded by
   aggregateGraceMs so a wedged batch cannot keep a rotting session alive.

2. The Front reader emitted only channel/message.received (indexing).
   front-message-received-notify listens for front/message.received, so
   from 07-23 12:13 every inbound email was indexed and none announced.
   The reader now emits both, inbound only, with a Redis claim so the
   5-minute overlap window cannot ping twice.

3. check-system-health was 844 of 1321 gateway inputs — the loudest
   speaker by 6x — because the only suppressor was "does a task exist".
   Joel was told about Front Projection 46 times in 40 hours. It now
   notifies on transitions of the degraded set, with a 6h reminder for
   real outages and one message on recovery.

Also retries Front 200s with unparseable bodies instead of burning an
Inngest attempt on "Unexpected end of JSON input".

11 new tests. Pre-existing 4 failures in system-bus are unrelated and
unchanged (verified against a clean tree).
2026-07-27 09:11:38 -07:00
shitratgit[bot] dbae426c9f fix(beats): identify a lane by its brief, not by a label others rewrite
The beat bypass reused a lane by matching its pane label. It never matched.
The `herdr-name-sync` pi extension renames a worker's pane to the pi session
name on every turn, so the "⏰ Campaign Pulse" label this code wrote became
"📈 Campaign Pulse" (and later "📡 Campaign Pulse") within one turn. Every
firing then failed to find its own lane and opened a new pane: ten Campaign
Pulse panes in ten tabs in [jc] scheduled beats, hourly, exactly the sprawl the
bypass exists to prevent.

A lane is now keyed by its brief path — the schedule names it and never changes
it — and the pane id is persisted in a Redis hash. Matching goes registry
first, label only as a fallback for lanes created before the registry existed.

Lesson worth keeping: a display label is not an identity. Anything cosmetic can
be rewritten by another process, and here one deliberately does.

Also makes SpawnBeatPorts.runCommand optional with a real default, so callers
can supply only the registry ports.
2026-07-26 18:12:24 -07:00
shitratgit[bot] 9fab44a12c fix: cron+debounce retry storm, and a Redis backup that lied for two months
content-sync declared both a cron trigger and a debounce. In Inngest v1.28.0 an
accepted debounce makes the scheduler retry the cron item, so it looped — 124
scheduling errors today, ramping from 8/hr to 54/hr. Split the concerns: a
cron-only scheduling function emits content/seed.requested, and the debounced
worker stays event-driven. Burst collapse is preserved.

nas-backup shelled out to kubectl, which is not installed on flagg and never
was. It failed 174 times while its runs reported COMPLETED. The last real Redis
backup is 2026-05-16 — dump-20260516.rdb, 115,761 bytes, REDIS0012 header. Two
months of green checkmarks over nothing. It now fails loudly through the shared
incident latch and resolves only on a confirmed backup.

The transport is NOT settled: panda has exhausted its ephemeral ports (16,008
TIME_WAIT of a 16,384 range, 9,753 to its own k8s API), so nothing there can
open an outbound TCP socket, including kubectl. The ssh path stays in-tree but
is not deployable until that is fixed. An in-cluster CronJob is the proposed
durable answer.
2026-07-26 16:12:23 -07:00
shitratgit[bot] a424dbccb6 fix(front): read conversations, not the events feed our token cannot see
The reader shipped against GET /events. Our token cannot read it, so every
scheduled run failed and the watermark never advanced — Front stayed exactly as
stale as it was with dead webhooks, just louder in the worker log:

  front events 403: Missing required scopes: [events:*:read]

Probing the live token (status codes only, never the value): /events 403,
/conversations 200, /inboxes 200, /me 200. So the key is fine and the endpoint
choice was wrong. vip-email-backfill.ts already reads Front the working way.

Rebuilt on GET /conversations + GET /conversations/{id}/messages, which need
nothing new from Joel. The event contract, watermark, overlap, bounded lookback
and pagination, backoff, and per-run telemetry are unchanged, and timestamp is
still Front's real created_at so overlapping polls keep producing stable
document ids. The dead /events path is deleted rather than left beside it.

A 403 now fails loudly naming the endpoint and the missing scope. This one was
invisible for an hour because a scope error looked exactly like "no new
messages" — the same way a dead webhook looks like a quiet inbox.
2026-07-25 14:56:49 -07:00
shitratgit[bot] 70db4dfc50 test(system-bus): allow deliberate dual-role registration by name
The worker-role uniqueness test has been red on main, flagging
webhook-subscription-dispatch-generic as a duplicate. It is not an accident:
commit 2810c4d6 registered it in both roles on purpose, because "the Front
incident proved cluster-only registration never runs on flagg."

Deleting the test would give up catching the accidental duplicates it exists
for, so the intentional ones are allowlisted by name instead, and a second test
asserts every allowlisted id really is registered in both roles — a stale
entry is how the next accident would hide.

packages/system-bus now runs green (768 pass), so a real regression is visible
instead of being buried under known-red tests.
2026-07-25 14:56:14 -07:00
shitratgit[bot] 30e1d65cbe fix(agent-loop): an empty story diff is not proof of no changes
getStoryDiff runs `git diff main...HEAD`. When the project is ON main that
compares a branch to itself: exit 0, empty output. The try succeeded, so the
HEAD~1 fallback never ran and the caller got "".

That matters because the reviewer and judge consume this diff as evidence. An
empty string reads identically as "this story changed nothing" and "I could not
read what changed" — so a story could pass review on no evidence at all.

Empty output now falls through to the single-commit diff instead of being
returned as a result. The no-main-ref case still falls through as before.

Found via three long-red tests in packages/system-bus, which turn out to have
been reporting a real bug rather than being broken themselves.
2026-07-25 14:56:14 -07:00
shitratgit[bot] 156c8dc261 chore(front): hourly reader cadence, per Joel
Joel, via the gateway on Telegram 2026-07-25T21:31Z: "we don't need 5min email
crons, every hour or two is more than enough."

Overlap stays at 5 minutes — that is the re-read window for clock skew and late
indexing, independent of poll cadence, and it stays conservative under hourly
runs. Authored by a gateway-dispatched worker; steering-reviewed.

Note: the Front freshness threshold is 60m, so an hourly poll sits right at it.
Webhooks remain the fast path; if they stay dead the threshold wants raising
rather than the cadence dropping back.
2026-07-25 14:48:59 -07:00
shitratgit[bot] 75f813074b feat(front): read Front with our own key instead of trusting their webhooks
Front's inbound projection died at 2026-07-21T18:44Z and stayed dead for ~98
hours. Nothing downstream was broken — front-notify, channel-message-ingest,
and the Typesense projection all still worked. The webhooks simply stopped
arriving. A webhook is a delivery promise somebody else makes; a reader is ours.

Adds a cron reader (*/5) that pulls Front with FRONT_API_TOKEN and emits the
same `channel/message.received` event the webhook path already emits, so
classification, thread aggregation, gateway context, and the health freshness
probe all keep working unchanged. A durable Redis watermark tracks progress with
5 minutes of deliberate overlap — re-reading slightly is free, missing a message
is not — bounded to a 7-day lookback and 15 pages per run so one pass cannot
walk all of Front. Retryable statuses back off rather than hammering.

The webhook stays as the fast path. Two sources feeding one seam is the point.

That required fixing idempotency, which was only theoretical: channel-message-
ingest derives its document id by hashing the message fields including
`timestamp`, and front-notify passed `Date.now()` — receipt time, not message
time. The same Front message ingested twice produced two different documents.
Harmless when a webhook fires once; ruinous for a poller that overlaps on
purpose. Both producers now pass Front's real message created_at, so a given
message has a stable id no matter which path delivers it or how often.

Pre-existing and NOT introduced here: `webhook-subscription-dispatch-generic` is
registered in both index.host.ts and index.cluster.ts, so the worker-role id
uniqueness test is already red on main.
2026-07-25 14:15:31 -07:00
shitratgit[bot] 4ab277d0e2 fix(driver): measure session age from the session, not from driver memory
Session age was tracked as first-sighting in driver memory, so restarting the
driver handed the running session a fresh 4h lease. Caught on the live restart:
a session that had been up since 2026-07-22 reported sessionAgeMs 0.

That is backwards — a driver restart is exactly when a rotting session most
needs retiring. Age now comes from the Claude transcript named by the herdr
agent session id, falling back to first sighting only when it cannot be read.

The live session reads 3.5 days old against a 4h limit.
2026-07-25 13:52:44 -07:00
shitratgit[bot] 2ee90b1fba fix(gateway): stop falling back while the agent is alive
185 fallbacks since cutover, 77 on 2026-07-25 alone, while the Claude gateway
process had been up since Wednesday. 161 of the 185 sources went on to receive
a real gateway decision anyway — so Joel got the raw dump first for nothing.

The gate was `EXISTS gateway:agent:heartbeat`: a key-presence check, not
liveness. A key missing for one poll during a normal session cycle read
identically to a dead loop. Worse, every receipt recorded heartbeatStaleForMs
as the configured TTL constant rather than measured staleness, so nobody could
tell a blip from an outage after the fact.

Now the receipt carries real measured staleness, a momentary gap has to
actually persist before the loop is declared down, and routine machine noise
falls back as one line and a pointer instead of a wall of health markdown.
Repeated identical fallbacks coalesce.

The safety property is unchanged and deliberately so: fallback still never
depends on the agent being healthy enough to report its own health. That
circularity is exactly what the kill drill exists to catch.
2026-07-25 13:47:28 -07:00
shitratgit[bot] a899ee1271 feat(driver): retire the gateway session before it rots
Two long sessions degenerated into repeated "court" tokens — one reply emitted
it 32,000 times — at ~394k and ~886k context on --model opus --effort low,
after running for days. The brief's own Vocabulary always said a cycle should
"retire at ~150k tokens or a quiet moment." Nothing implemented it: the driver
had no age or size signal at all, so the session ran until the model broke.

Adds two retire paths:

- Clean age retire (default 4h, GATEWAY_MAX_SESSION_AGE_MS, 0 disables) fires
  only when the session is healthy, idle, and has no unhandled work. Retiring
  with queued work or an outstanding poke would defeat the point.
- Forced retire on degeneration. Repeated-token collapse is poison — once it
  appears the model re-samples it — so this one preempts queue state. The
  detector is deliberately boring: the known court burst, or one token
  dominating a short recent window.

Wall-clock age is the honest signal. A scraped token count would be a fragile
guess about someone else's internals.

This exposed a real gap in the successor spawn: it returned early whenever a
pane already carried the target label, assuming that pane held a live session.
After a retire the pane exists with nothing in it, so nothing would have
relaunched. Spawn now checks for a live agent and relaunches in place, still
never opening a second pane for the same label.
2026-07-25 13:47:10 -07:00
shitratgit[bot] 94a8c8d27e fix(cli): messages audit falls back to the canonical Convex stream
`joelclaw messages audit` has failed fleet-wide since cutover with
MESSAGE_JOURNAL_CONFIG_MISSING. The diagnosis "provision the reader
credentials" was wrong: the legacy ClickHouse journal has no listener on flagg
at all, and the events the command wanted were sitting in Convex the whole
time — the same stream `messages trace` already reads successfully.

Audit now falls back to the canonical stream when the legacy journal is
unconfigured, and labels which source answered. A journal failure that is not
a missing-credential problem still surfaces as an error rather than being
quietly papered over.

Verified live: audit --since 6h returns 100 events with source "convex".
2026-07-25 13:42:28 -07:00
shitratgit[bot] 08a2f77e1e fix(health): stop Front Projection paging as critical; snapshot the replica source
Carries three gateway-worker changes that were reviewed and left uncommitted:

- Front Projection drops out of CRITICAL_COMPONENTS. A stale inbox projection
  is worth knowing about and is not worth forcing overall health critical and
  paging Joel. Health alerts were the single largest producer in the message
  stream (740 of 1083 machine messages) and the largest fallback source.
- serve.ts runs the function-health registration check at startup, so the
  assertion still runs when the durable function's own registration was lost
  while Inngest was unavailable.
- replicate-critical-db.sh freezes one source generation before hashing it.
  The builder renames critical.db atomically, so reading the live path twice
  could pair the old file's checksum with the new file's built_at.

Also lands the three investigation briefs those findings came from.
2026-07-25 13:29:19 -07:00
shitratgit[bot] 36d181433a fix(system-bus): keep voice transcripts out of the repo working tree
mlx_whisper always writes a transcript file, and without --output-dir it
writes to the worker's cwd. The system-bus worker runs from packages/system-bus,
so every Telegram voice note left the contents of Joel's message there as a
UUID-named .txt. One was sitting untracked in the working tree.

Writes the side-effect file beside the audio it came from instead. stdout is
still what the function returns.
2026-07-25 13:29:19 -07:00
shitratgit[bot] 5c7d7bfd06 fix(system-bus): scheduled beats spawn mechanically, not through the gateway
A pane.schedule.due event with verb:spawn already names its own brief, its
own prompt, and its own cadence. It was reaching the comms gateway as
evidence anyway, and the gateway re-executed it by dispatching a worker —
copying the brief path into a worker file and opening a tab. Every hour,
forever. That is a mechanical spawn wearing an LLM costume, and it produced
most of the pane sprawl.

verb:spawn now executes into a stable herdr beat lane, keyed by the brief's
title, reusing an idle or done pane and refusing to stack a second one while
the lane is working. verb:wake and verb:revive keep the gateway due-signal
path untouched. The beat still reports its result through notify, so the
gateway keeps deciding whether Joel hears about it — it just stops being the
thing that runs it.

Beat lanes never land in the gateway's own workspace. That workspace is
resolved from the pane hosting the gateway loop rather than a hardcoded
herdr id, so the guard survives a herdr restart.

Authored by a gateway-dispatched worker; steering-reviewed.
2026-07-25 13:29:06 -07:00
shitratgit[bot] f4c7560ad7 fix(gateway): executor reads a close-deliver rewrite from either field
Five close-deliver decisions on cutover day were recorded and never sent.
Four of them carried their text on decision.rewrite while the executor only
read payload.rewrite, so Joel simply never received those messages; the
transport log shows the skips.

The executor now reads either location, preferring the top level. The plugin
validator still requires the top-level field, so new receipts stay canonical
while old and off-spec ones still reach Joel — a message he should have
received outranks schema purity about where the agent put it.

The executor had no test file at all. Adds one covering both rewrite
locations, precedence, the no-text skip, and non-delivery passthrough.
2026-07-25 13:29:06 -07:00
shitratgit[bot] 87d0ab002b feat(system-bus): trigger MEGA journey watch from GitHub workflow_run webhook
New host function mega-journey-watch: on github/workflow_run.completed
for MEGA-DOT-DEV/mega-dev pushes to main, run one tick of the repo's
journey-watch.sh (post-merge Playwright buy-flow proof on flagg). The
script owns sha dedupe, single-flight, retry-once flake law, evidence
preservation, and joelclaw notification; a 30-minute LaunchAgent poll
stays as the delivery backstop.
2026-07-24 17:55:30 -07:00
shitratgit[bot] d5743c518c feat(health): assert function registration and recent runs
Registration success proves nothing: on 2026-07-24 a PUT returned
modified:false while three crons were completely dead (upstream v1.27.0 dropped
Function.EnvID and minted unrunnable queue items). Verify runs, not registration.

Startup + hourly check: every serve.ts function must exist in Inngest (bounded
PUT retries with backoff and readback if not), and every cron function must have
a run newer than 2x its real cadence — parsed from the actual expression, so
offsets like the ai-hero reconciler 17 * * * * are handled, not assumed :00.
Alerts name the function, its cron, and its last successful run, through the
shared incident latch with resolve-on-recovery.
2026-07-24 11:51:59 -07:00
shitratgit[bot] b31dc22716 fix(chains): assert-successor command + dispatcher retry persistence
Two chain deaths this week, two causes:

- 2026-07-21 campaign-pulse: the spawned run died on a WebSocket error before
  arming its heir. Fire-and-forget arming left no trace. `joelclaw wake
  assert-successor` now re-reads the registry, requires exactly one future
  successor, and alerts by chain name when verification fails.
- 2026-07-22 daily-flow (schedule 853efe38): the dispatcher logged "attempt 1 of
  3" and dropped the schedule. Pending schedules now survive failures 1-2 for the
  reconciler to re-emit; only exhaustion clears and alerts, exactly once.
2026-07-24 11:51:42 -07:00
shitratgit[bot] a6b4336b4e fix(system-bus): read Front app-webhook message from event target
Application webhooks wrap the same Front Event object as Rules webhooks,
so the message resource lives at payload.target.data — the parser was
reading the event wrapper and producing empty messages, freezing Front
Projection ingestion since 2026-07-21.

Also hardens text extraction (text -> blurb -> stripped HTML body ->
attachment placeholder) and quarantines events with no usable text via
front/message.quarantined instead of emitting blanks.
2026-07-23 10:09:18 -07:00
shitratgit[bot] aae0091410 gateway health: slim transport writes last-heartbeat.ts; health check probes pid+heartbeat freshness, not the dead WS port — authored by gateway-dispatched workers, steering-reviewed 2026-07-22 10:05:53 -07:00
shitratgit[bot] c02edae276 gateway: medium effort + trimmed boot snapshot after degenerate generation loop ("court" repetition) 2026-07-22 09:43:49 -07:00
shitratgit[bot] 1ce0d02383 memory: retro-writer condenser uses infer task "summary", not "summarization" 2026-07-22 09:28:18 -07:00
shitratgit[bot] 97f3bb76d4 message-event-stream: Inngest consumer for the append-only log
Registers message/event-consumer on the host worker (event + cron triggers),
adds the message/event.consume.requested event type, a live proof script, and
the Brain work receipt. Convex deploy of the backing schema is still blocked;
step status set to blocked with receipts.
2026-07-22 09:28:18 -07:00
shitratgit[bot] cc410bf260 cli: add canonical observer target commands 2026-07-22 09:25:30 -07:00
shitratgit[bot] aa7c05e76a gateway: sonnet 5 low effort for the loop — token generation was the latency floor (scan measured 8ms); amends opus call on speed evidence 2026-07-21 16:47:59 -07:00
shitratgit[bot] eea082c35b gateway: conversational fast path — advanceAfter one-call replies, low effort, 5s driver cadence 2026-07-21 16:35:56 -07:00