content-pipeline¶
Hourly supply checks and bounded drafting, approval-gated distribution: research what the field is actually asking → write an engineering note → derive social posts and a short narrated video → publish at the best time. Requested by Angel 2026-09-11. Site: embeddedera-site · business: embeddedera-business · brand: ../brand/README
Current daily workflow — September 23 clarification¶
- Target seven complete future daily packages: one new website note, Facebook feed with exact clickable article URL, Facebook Story with a native article link, Instagram feed with bio CTA, and Instagram Story with a native article link. All share one note/date; Stories follow their platform feed after the article is verified live.
- Research and artwork preparation happen ahead. The assistant has standing authority for routine note/feed review. Approval queues a reviewed item for its assigned day; it must not immediately publish several website notes.
- This pipeline owns the website/feed stages; the separate Story chat owns Android execution. They share one editorial date and exact article identity. A complete future package needs approved bound inputs and accepted execution routes for all five stages. Published/viewer-verified are later delivery states, not prerequisites for preparing a future package.
- Implemented foundation: note approvals now reserve future dates and immutable source digests in the existing approval ledger. Source edits hold the item for new review; feed scheduling respects note date reservations. The Home page distinguishes this queue and research reserve from unverified full-package readiness.
publish-note approvenow queues; its internal release command requires an exact current-day claim. - Not yet end-to-end: no automatic note-release timer is enabled. Complete-package asset preparation/approval, post-deployment public verification, feed dispatch and Android Story publication/viewer evidence still need to be connected. The release runner is disabled by default. Do not describe this as seven prepared packages or a working unattended daily chain.
- Research refill at minute07 now targets seven committed unpublished notes. Already-live articles and old feed schedules do not count. This is an input reserve, not the complete-package count.
- Transition: prior approved feed jobs for September23–26 remain unchanged. The three notes approved by the owner on September23 were published immediately at06:14–06:16Eastern under the old behavior; leave them live and identify them as early-publication exceptions. Eight canceled legacy Postiz Stories stay canceled; the Android workflow is separate.
- Operator page: https://home.embeddedera.com/home/content . Source contract and remaining work:
assistant/docs/daily-content-queue.md. SOP-018v6.2 supersedes the older policy below.
Autonomous operation implementation plan — September 23¶
Detailed implementation contracts, ownership, fault tests and rollout: content-autonomy-implementation. This remains planning; no new publisher was enabled.
Owner requirement: no routine checking, approval taps, chat sessions or manual posting. Routine review authority covers notes, feeds and both Stories. This section is an implementation plan, not an assertion that the services below are connected or enabled.
- Extend the existing SQLite approval ledger into one parent daily package: note/date/exact URL, immutable reviewed note and media identities, both feed payloads, both native Story payloads, assigned times, actual reviewers and per-stage execution evidence. Keep prepared, submitted, published and verified distinct. Reuse Postiz for feeds and the existing Android project for Stories; no competing editorial calendar.
- Extend the existing refill controller to prepare all assets and run a separate substantive editorial/visual review. Check primary sources, duplicates, exact links, Instagram bio reachability, mobile readability and the vivid hardware style. Record assistant reviewer identity. Bound revision attempts; replace an unsuitable unpublished candidate with another reviewed candidate. Target seven complete future days, replenish on consumption and on periodic deficit checks. Source drafts alone do not satisfy the target. Respect measured AI capacity; no paid overflow.
- Connect a deterministic daily executor, independent of either chat: claim today's reviewed package durably, deploy one new note, verify its exact public identity, then submit the bound Facebook/Instagram feeds and hand each Story to Android after its corresponding feed is confirmed. A known failed submission can be retried within bounds; an uncertain result must first reconcile remote evidence. Persist claims before external writes; successful stages are never repeated to repair another stage.
- The Story project owns the controlled live adapter and viewer link inspection. The latest handoff records owner-approved real-note pilot payloads and standing delegated Story review authority. Finish technical pilot acceptance without asking again for unchanged content/time approval. Composition/discard evidence is not publication acceptance.
- Extend the independent delivery checker to reconcile all five stages, verify the exact article links, detect stale worker heartbeats and low complete-package reserve, and initiate bounded recovery. Serialize Android and heavy local work under existing resource limits. Escalate only unresolved actionable exceptions; keep routine success/recovery in the ledger. An external heartbeat monitor is needed to detect complete Plex/network loss; a monitor on Plex alone cannot establish that guarantee.
- Before enabling the whole chain, exercise isolated failure scenarios: reboot between claim and submission, provider timeout after acceptance, note deployment failure, changed artwork, expired authentication, partial platform outage and missed slot. Validate duplicate prevention and exact-link evidence in the approved live Story pilot, then enable the connected executor and observe real daily cycles. No claim of full autonomy or seven ready packages before evidence exists.
Fallback rules: before any public action, an unsuitable daily candidate may be replaced by a reviewed unscheduled reserve package with a recorded calendar decision. Once any stage is public, freeze the day's article identity and repair only missing stages. Do not silently move existing approved jobs, publish multiple notes to catch up, or substitute another article in a partially published day. A platform outage can prevent same-day completion; record it truthfully, recover within the established window and escalate only when necessary. Authentication challenges may still require owner action.
Why¶
One note a week is the cheapest durable marketing a shop like this has: each one is a page that answers a real search, a post for every platform, and raw material for a video. Doing it by hand never survives a busy month, so it has to run itself.
Shape (4 stages)¶
- Research (Mon 06:00) — read what people are asking this week in the places our clients and peers actually post, plus vendor obsolescence notices. Pick ONE topic that (a) matches a service we sell, (b) has real questions behind it, (c) we have not already written. Output: a topic brief with sources.
- Write (same run) — a 1,100–1,500 word note in the brand voice, saved to
src/content/notes/<slug>.mdon theapple-stylebranch, committed, pushed, deployed to the dev preview. ntfy to Angel with the preview link. - Approve — Angel reads it and replies "publish" (or edits). Then it is merged to
mainand goes live. Deliberate gate: the notes carry Angel's name and engineering reputation; an unreviewed factual error is expensive and permanent. One tap, not a workflow. - Distribute (on approval) — derive: LinkedIn post, X thread, Instagram caption + carousel text, YouTube/Shorts script; render a 60–90 s narrated video (local TTS + slides + captions); schedule each to its platform's best slot.
Sources for stage 1 (all free, no key unless noted)¶
- Reddit JSON (
/r/<sub>/top.json?t=week): PrintedCircuitBoard, embedded, AskElectronics, ECE, rfelectronics, KiCad, FPGA, diyelectronics. - Hacker News via Algolia API (
hn.algolia.com/api/v1/search_by_date?tags=story). - Stack Exchange API: electronics.stackexchange.com (300 req/day unauthenticated).
- EEVblog forum boards (scrape the recent-topics page).
- Vendor obsolescence/PCN feeds: ST, Microchip, TI, NXP product-change notices.
- Distributor lifecycle flags (Digi-Key API needs a key — optional later).
- Our own inbox: recurring questions from
[EE-requests (already labelled EmbeddedEra/Leads).
Publishing times (B2B, US Eastern — revisit with real analytics after 3 months)¶
| Platform | Slot |
|---|---|
| Site note | Tue 07:00 (gives Google the week to index before the weekend) |
| Tue or Wed 08:00–10:00 | |
| X | Tue–Thu 09:00 and a second post 13:00 |
| Tue–Thu 11:00 or 19:00 | |
| YouTube / Shorts | Thu 15:00 |
| Rule: one note a week, posts staggered across 3 days, never more than one post per platform per day. |
Video (stage 4) — free, local, no subscriptions¶
- TTS: Piper (open source, runs on CPU, good English voices;
en_US-ryan-highor similar). Alternative if a warmer voice is wanted later: a paid API — not free, avoid for now. - Render: ffmpeg. Slides generated from the note's headings (brand colours, JetBrains Mono), plus captions burned in (whisper.cpp for timing, or Piper's own phoneme timings).
- Output: 1080×1920 vertical for Shorts/Reels, 1920×1080 for YouTube.
- Angel's own bench footage (2026-09-19 shoot) becomes the b-roll pool.
Parked¶
- Video (2026-09-11, Angel's call): the narrated-video stage is built and works
(
render-note-video, Piper + PIL + ffmpeg, 48 kHz stereo, brand slides) but Angel does not want video for now. Leave the scripts in place; thevideoblock insocial.jsonis still generated and costs nothing. Revisit after the 2026-09-19 shoot, when real bench footage could carry it.
Visuals: carousel cards, designed in HTML¶
render-cards <social.json> [outdir] [--square] → hook card, one numbered card per point, a
closing card with the URL; 1080x1350 portrait (or --square), plus a combined PDF (LinkedIn
takes a PDF as a swipeable document post, which currently reaches furthest there).
How it works: the design is embeddedera-site-apple/tools/cards/carousel.html — real CSS
with Inter + JetBrains Mono from Google Fonts, a copper radial light, an SVG board-routing
pattern generated per card from a seed, a grain layer, and headline auto-fit measured by the
browser. tools/cards/render-cards.py injects the note's data, waits for fonts, and screenshots
each .card with headless Chromium (~/.local/share/shots-venv, Chromium in ~/.cache/ms-playwright).
About 6 seconds for a six-card set. To iterate on the design, open carousel.html?demo=1 in a
browser — it renders with demo data, no build step.
Why not Canva: the Connect API's autofill (fill a brand template from data and export) requires a Canva Enterprise organisation; paid plans get only a development trial. Free/Pro accounts can upload assets and export, but not the automation we need. Canva stays a fine manual tool for Angel if he wants one; it is not an automation target. Checked 2026-09-11 against canva.dev docs.
Superseded: render-note-carousel (the PIL version) still exists but is no longer called.
Diagrams in notes¶
render-diagram <data.json> <out.webp> and npm run diagrams (wired into npm run build).
Design: embeddedera-site-apple/tools/cards/diagram.html, four types — decision (tree with
labelled branches), process (numbered strip), compare (columns), stack (layers) — at wide
1600x900, square or tall. Definitions live in src/content/notes/_diagrams/<slug>/<name>.json,
output to public/notes/<slug>/<name>.webp, re-rendered only when the JSON is newer.
WebP, not PNG: a flat dark 3200x1800 diagram is ~2.2 MB as PNG and ~130 KB as WebP with no
visible loss. The notes keep their 99/100 performance score. render-diagram writes a PNG too if
the output path ends in .png.
Two diagrams shipped in the EOL note (decision tree + qualifying process).
Card types¶
render-card <data.json> <out.webp> → tools/cards/cards.html, one card per run, 1080x1350
(or square, or wide 1200x675 for link images). Types: quote (pulled sentence + role-only
attribution), spec (2–6 big figures with labels), myth (MYTH / ACTUALLY), photo (full-bleed
image + heavy scrim + headline). Samples and their JSON: ~/embeddedera-content/_samples/.
Photo rule learned 2026-09-11: the scrim had to be doubled to stay readable over a cluttered bench photo, and the sample shot shows third-party equipment brand names on screens and printers — the brand rule forbids naming equipment, so shots for publication must avoid legible brands. Add that to the 2026-09-19 shoot list: angle away from brand plates, or cover them.
Quote follow-ups¶
quote-drafts now runs two passes: new requests, then stalled threads — a warm nudge at 3–5 days
of client silence, a graceful close-out at 10–14, hard cap of two per thread ever, skips anyone
who declined or already replied. Still draft-only; send/reply/forward stay disallowed in the
wrapper. The follow-up branch has not yet run against a real aged thread — watch the first one.
Status¶
- 2026-09-11: notes section live on the site; first note published (microcontroller EOL).
- 2026-09-11: Stages 1+2 built.
~/.claude/routines/weekly-research.md(prompt) +~/.local/bin/weekly-research(wrapper), cron0 6 * * 1. The model only researches and writes ONE markdown file; the wrapper does git, build, deploy and ntfy, so a bad run cannot publish. Gates before commit: ≥700 words, frontmatter present, no standards claims, no prices, no banned words, site must build. Topic backlog at~/.claude/routines/note-backlog.md; it reads the existing note titles so it never repeats one. Lands on the DEV preview; Angel says "publish the note" to promote. - 2026-09-11: Stage 4a+4c built.
note-to-social <slug>→~/embeddedera-content/<slug>/withsocial.json,posts.txt(copy-paste ready, each piece labelled with its posting slot) and two MP4s. Prompt~/.claude/routines/note-to-social.md; validation in the wrapper rejects prices, standards claims, banned words, over-length X posts.render-note-video <social.json>does the video: Piper voiceen_US-ryan-high(~/.local/share/piper-venv, voices in~/.local/share/piper-voices), PIL slides in brand colours with the logo, ffmpeg assembly; 1080x1920 and 1920x1080. ~40 s to render a ~20 s video on this box. Tested on the EOL note. -
Stage 4b: Postiz (self-hosted, API) or direct platform APIs — Angel must create/connect the app credentials for LinkedIn, X, Instagram (Meta), YouTube.
-
2026-09-17: Postiz was re-enabled at Angel's request and is healthy at
social.embeddedera.com. Meta app registration is next: use EmbeddedEra Social Publisher, business portfolio EmbeddedEra LLC, and the two Postiz callback URLs recorded in map/objects/containers/postiz. The selected Instagram route is Facebook Business, using the same Meta app as the Facebook Page. Provider credentials remain blank until Angel creates the app; do not paste the app secret into notes or chat.
Approved workflow — 2026-09-17¶
- Keep existing website-note review and approval. Add a compact Content approvals entry on Home, leading to
/home/content; detailed previews and schedules belong there, not on the home dashboard. - Company destinations: Facebook Page feed and Story, Instagram feed and Story. Personal-profile sharing is deferred.
- Each social package includes note-derived branded feed and vertical Story artwork, captions, destination/account identities and scheduled times. Only explicit package approval authorizes scheduling; existing note approval alone is not approval of newly generated social copy/art.
- Backlog covers five currently published notes. Postiz contained zero posts when checked on 2026-09-17; three legacy social.json packages exist. Reconcile by note and destination rather than blindly recreating posts.
- Cadence changes must be reflected in pending social slots. Actual note drafting cron is daily at 06:00 server time; earlier weekly prose is historical. Approved jobs must not be silently moved.
- Failed/uncertain API writes need destination-specific state and reconciliation before retry, to prevent duplicates. See SOP-018 for the controlled operating procedure.
- Verified accounts: Facebook
embeddederaofficial(Page 254051434466101), Instagramembedded_era(17841465671705929), both active in Postiz. Installed Postiz v1.47.0 supportspost_type: postandstoryfor these providers.
Open¶
- AI art: OpenRouter credit is nearly empty and will NOT be topped up (owner 2026-10-13: use ChatGPT). The Codex (ChatGPT) image route is first since assistant 68359dd (2026-10-12; the ~8 min timeouts were a stdin hang, fixed) and worked on the 2026-10-13 Etsy run. Verify the next social-art run also goes through Codex; if Codex fails, art fails (no paid cascade).
- Bring the note
offerschema andcanonicalOfferHref/canonicalLinksfrom main (~/embeddedera-site1f36d62, 9dd61ff) toapple-style(~/embeddedera-site-apple, where drafts are written and previewed). Today the dev preview has no offer schema, so a draft that will fail production builds fine on dev (2026-10-11 release failure).note-release-preflightcatches it meanwhile. - Story jobs on the same day collide with the bounded emulator: on 2026-10-02 the Instagram Story run died when the Facebook Story run's 15-min emulator was killed under it (0-byte /sdcard/Pictures/story-pilot.png, "Previous pilot media requires reconciliation"). Space same-day Story jobs ≥16 min apart or stop the emulator after each run (story-publish-due / story-publisher). Also: Instagram Story link verification is FIXED (story-publisher 69f8d25, 2026-10-03; first
verifiedStory = 2026-10-02 IG, job e6fd9d7e758abfae, viaverify_story.py, a never-posting re-check available while a Story is live <24 h). Daily IG runs now self-verify; confirm tomorrow's log showsverification : {...observed_host...}. - Facebook Story link verification is blocked by Facebook, not by our code: the Page owner's view shows only "Your button will appear to viewers here." (non-clickable, hierarchy not dumpable) and the Page's own Story never appears in the home tray. Needs a NON-owner viewer: owner decision whether to (a) let the emulator's Facebook app switch to Angel's personal profile (profile switcher in the menu, switch back afterwards) or (b) sign a dedicated test viewer account into the emulator. Then add a viewer branch in
viewer_verification._facebook_owner_view. Task 293 Verify ("seven consecutive verified days per platform") cannot pass for Facebook until then. - Check 2026-10-01 09:05 Facebook Story (first real run on FB app 580.0.0.51.74) and 18:05 Instagram (first with the case-insensitive 'Unseen' tray fix → should reach 'verified'): ~/.local/state/cron-run/story-publish-due.log + ledger. Facebook's own 'Your story' tray entry still unverified on the new app; promo interstitial list may need entries.
- Watch the art similarity gate (#87): two topically close notes inside 14 days could fail all 3 daily attempts and stay held; if a note is held 'too similar to
' two days running, widen the composition set or lower SIMILAR_MAX (daily_library.py). Note openers now flow into the image prompt (openrouter_art._opening) — fine for our own reviewed notes, needs a guard if outside drafts ever enter. - Before daily_monitor.py goes on a timer: daily_package.readiness() (daily_package.py ~453) must treat a backlog-held approval day as filled, like daily_prepare.gap() does since #85, or it will raise a false
buffer_gap.
See ~/Notes/tasks.md — this section is now generated from the task register.
Delivery log¶
Moved out: the dated delivery log (2026-09-17 to 2026-09-27) is in content-pipeline-archive.
Social pause (2026-10-16) — resume steps¶
Owner paused Facebook and Instagram (feeds, Stories, scheduling, approval, notifications) until told otherwise; the person taking over posts them by hand. Website notes keep releasing on the existing schedule.
What is paused
- Flag ~/.config/social-paused: daily_package.py (social_paused(), _stage_status, claim) and daily_runtime.LedgerPort.due/pending skip every non-website stage; scripts/publish-note release skips the social prepare step (installed copy ~/.local/bin/publish-note updated). Tests: tests/test_daily_integration.py::test_social_pause_releases_website_and_skips_every_social_stage. Code is uncommitted in ~/assistant (see git status).
- Cron lines commented with # PAUSED-SOCIAL 2026-10-16: story-handoff, story-publish-due, story-preflight, content-approve-missing. Crontab before the change: ~/.local/state/social-pause/crontab.before-2026-10-16.
- User timers disabled: postiz-scheduler-watchdog, content-delivery-check, content-schedule-audit, content-review-prep.
- Six Postiz containers stopped and restart=no (volumes kept).
- Still running on purpose: content-daily-prepare, content-buffer-fill (stands down: exits 0 while the flag exists, 2026-10-17; its reserve is counted on FB/IG feed slots and needs the delivery report the paused content-delivery-check refreshes), content-daily-release (website), x-post-prompt (X 12:30), social-insights, marketing-snapshot (read-only metrics). Open question to the owner: pause those too?
Resume
1. rm ~/.config/social-paused
2. crontab -e: remove the # PAUSED-SOCIAL 2026-10-16 prefixes.
3. systemctl --user enable --now postiz-scheduler-watchdog.timer content-delivery-check.timer content-schedule-audit.timer content-review-prep.timer
4. cd ~/torrentvpn && docker update --restart=unless-stopped postiz postiz-postgres postiz-redis postiz-temporal postiz-temporal-es postiz-temporal-postgres && docker compose --profile social up -d postiz postiz-postgres postiz-redis postiz-temporal postiz-temporal-es postiz-temporal-postgres
5. Social stages for days posted by hand will show as unattempted; mark or retire those days first so they do not double-post.
Handoff / next
- Independent review (assistant:sonnet grader, 2026-10-16) found 3 issues: tests ignored by the live flag (fixed: autouse fixture in tests/conftest.py), schedule audit would show paused social slots as queued (fixed: reports missing/social_paused), resume note. Full -k 'daily or content' suite: 365 passed.
- On resume: days whose window passed during the pause are skipped as expired (intended, never posted late). Packages built by content-daily-prepare during the pause are fully reviewed, so the website claim is not blocked; only notes approved through the old publish-note path skip content.prepare and need Content approvals if social is wanted for them.
- Separate bug: 2026-10-16 website deploy failed (generate-licenses.mjs:87, missing license info for thermocouple-converter); note_queue row uncertain, URL 404, engine will not retry. Fix the license entry and reconcile the 10-16 row.
- The 7-day buffer (10-17..10-23) is already reviewed; new packages still generate social artwork (API spend). A social-free prepare mode is a possible follow-up.
Handoff 2026-10-23: self-healing pipeline (re-architecture)¶
Angel wants the website + social content pipeline fully automated with no owner intervention; re-architecting is OK.
- What failed (10/16-10/23): every alert watches "did the job crash / run". The failures were exit-0 jobs while the outcome degraded: content-buffer-fill exited 0 hourly for a week while the reserve drained to 2 notes; a rebase restamped four notes' dates with no error at all.
- Shape: one reconciler on the existing cron-run/ntfy plumbing, not a rewrite. Desired state: >=7 future days queued, today's note live, displayed date == release date, live index == repo. Each run picks the next bounded repair from a fixed menu of existing jobs (research, prepare, release retry, OG regen, validator retry) and pages only when the menu is exhausted.
- Rule: a job may not exit 0 on a skip without writing a machine-readable reason the reconciler reads.
- Guards: block force-push to main on the site repo; pauses (~/.config/social-paused etc.) carry an expiry and re-surface when it passes.
- Drift: ~/.local/bin copies drift from ~/assistant/scripts (content-buffer-fill synced 10/23; weekly-research still differs). Pick one source of truth with a deploy step.
- Boundaries: resuming social is the owner's call (paused 10/16 "until the owner says resume"); the Story/Android path has its own owner.
- Decided 2026-10-23 (Angel): Facebook/Instagram stay off, probably for good. Desired state is website-only: count the reserve on the website alone (the default, not a paused mode), stop building feed/Story packages and artwork, and drop the social stages from readiness. The social-paused flag is permanent, not a pause to expire. Update SOP-018 to match (bump version, sop-deploy).
Handoff 2026-10-24 (continue the 10-23 re-architecture)¶
- Done 10-23 late: research prompt states the offer limit (cause of the 23:37 failure). Verified: refill 23:52-00:05 rc 0, three drafts
status=ok(current-monitor at 1 mA, HX711 load-cell drift, e-paper ghosting),research_ready_days2 -> 4, deficit 3; next hourly runs should close it. content-daily-prepare still has to queue them (and will build unused social art for each). - Found: drafts need no owner approval:
content-daily-prepare(:37) AI-reviews and callsnote_queue.approveviadaily_bundle.py:89(one day per tick, so 5 days = ~5 h). Butsocial_paused()is NOT consulted indaily_prepare.py/daily_assets.py/daily_bundle.py: every new package still builds feed+Story art plus art-director/review calls (api-credits run content claude) that will never be used. - Next, in order: (1) website-only slice of the 10-23 decision: stop feed/Story/art in
daily_prepare/daily_bundle/daily_assets(OG card only); drop social stages fromcontent_schedule_audit.py+ Home readiness (today's buffer status is a permanent false red:missing_daily_slot social_paused,delivery_report_stale); make the website count the default incontent_buffer.py+ wrapper; extendtests/test_daily_integration.pysocial-pause test; Codex implements, Claude gates (SOP-025). (2) SOP-018 per SOP-000, bump,sop-deploy; map cardobjects/automation/schedules.md. (3) Then the reconciler (handoff 10-23 above). (4) Drift:~/.claude/routines/weekly-research.md(the live research prompt) is in no repo;weekly-researchdiffers between~/.local/binand~/assistant/scripts.
Handoff 2026-10-24 01:50 (after website-only + reconciler) — all verified 09:30, closed¶
- Done: website-only default (assistant 9cc9b41/c6ef17c),
content-reconcilehourly :52 (fd626ae, 4a79fbb), scripts symlinked from~/assistant/scripts(b4c0396, eecb52b), prepare rotation (18b028f), reviewer exact-URL prompt (82f6deb), test ledger leak fixed + 751 rows removed, SOP-018 v7.0 deployed. Full suite 2987 passed. App restarted. - Verify first (in order): (1) 07:00-09:00 release of 10-24
why-a-boost-converter...live (content-reconcile09:52 run green on today_live); (2)sqlite3 -readonly ~/.local/state/assistant/content-approval.sqlite3 "select due_date,slug,state from note_queue where due_date>=date('now') order by 1"grows ~1/h; (3)grep -c review_error ~/.local/state/content-daily/*-[0-9]-*.jsonstops rising (prompt fix); (4) incident #330 closes itself when reserve >=4. - Open (see Open list): reconciler 7-day lookback; buffer excludes failing drafts; push
~/assistantmain (owner).
Log¶
-
2026-11-04 continuation: owner approved the $10 cycle-only transfer. Applied content $50 / sales-copy $10 for cycle 2026-10-08, with automatic default restoration November 8 and total cap still $90.
~/.local/bincommit e7cc8d0,test_api_credits.pypasses. Live review resumed using claude-sonnet-5-5 ($0.060522); caught genuine ESP32-S3 attribution/PSRAM claims. Normal automatic revision completed as notes-queue d59b6ee ($0.062807); fresh independent review passed on claude-sonnet-5-5 with three fetched primary sources; immutable package queued for November 9. Reserve now five days. Livecron-run content-reconcileis green (five-day reserve warning only), with no missed releases or RSS date errors. Hourly refill/review remain enabled to rebuild the seven-day target. -
2026-11-04: Investigated stalled queue (incident #330): content API share $40.0244/$40 exhausted in cycle 2026-10-08→11-08, while total spend is $65.607/$90. No subscription-capacity fault. Assistant a75394b applied locally to main (not pushed): preserve the budget cause and next reset in preparation reports; use installed Claude CLI 2.1.318 instead of SDK-bundled CLI for reviewer alias. 56 targeted tests pass; live prepare confirmed explicit
Content API budget exhausted; resets 2026-11-08, zero attempts consumed, four future days remain queued. Owner asked whether to transfer $10 from unused sales-copy share to content for this cycle; no allocation changed and no model call made pending answer. Next: on approval, apply cycle-only allocation, run normal prepare/refill and outcome reconciler, verify queued reserve. - 2026-10-23: Angel: Facebook and Instagram stay off; not expected to be re-enabled. Pipeline target is website-only.
- 2026-10-23: Site showed five notes as "Oct 23" (released 10/19-10/23): a rebase on 10/23 restamped the committer date of four publish commits and
publishedDate.tsread%cs. Now reads author date%as(site59902bf, regression test inscripts/published-date.test.mjs); live index and RSS show 10/19-10/23. Rule: never rebase already-pushed publish commits; the date now survives it anyway. - 2026-10-23: Website reserve had drained to 2 notes (nothing queued after 10/25): since the 10/16 social pause,
content-buffer-fillexited every hour because the reserve was counted on FB/IG feed slots. Addedcontent_buffer.py --website-only(assistanta502bb4); the wrapper passes it while~/.config/social-pausedexists. On social resume it goes back to the feed-slot count automatically. - 2026-10-23: 23:37 refill failed: the researcher wrote a 220-char offer
line(wrapper limit 200, never stated in the prompt). Prompt~/.claude/routines/weekly-research.mdnow states <180 chars / label <=6 words (backup.weekly-research.md.bak-20261023). The buffer's incident cause line said "unexpected worktree changes" while the research log said "Offer line/label rejected" — that run used the pre-23:46 wrapper (retry commit 9206829 replaced it); the current awk extracts the right line. - 2026-10-24: Angel: keep working in the same chat past midnight; goal "so this won't happen again". Website-only slice sent to Codex (worktree
~/assistant/.claude/worktrees/website-only, brief: social opt-in via~/.config/social-enabled, text-only note review, website-only manifests, audits drop social). Then the outcome reconciler. - 2026-10-24: Website-only by default (assistant 9cc9b41, merged c6ef17c): social is opt-in via
~/.config/social-enabled(absent = website-only;social-pausedno longer read); prepare runs a text-only note review (source_accuracy, no_unsupported_shop_claims, topic_relevance), no artwork; new manifests have only thewebsitestage; legacy 10-24/10-25 five-stage packages still release their website stage; audits/buffer drop social. 2987 tests pass. App restarted. - 2026-10-24: Outcome reconciler
content-reconcile(assistant fd626ae+4a79fbb), cron :52 via cron-run: fail if <4 consecutive queued days, or no note queued in 26 h while <7 (stall), or today's note not live by 09:00, or a live RSS date != release date. First live run caught the real state: reserve 1, last queue 137 h ago, 6 drafts waiting. Status~/.local/state/content-reconcile/status.json. - 2026-10-24: Script drift closed: repo
~/assistant/scriptsis the source of truth;~/.local/bin/{content-buffer-fill,publish-note,weekly-research,content-daily-prepare,content-daily-release,content-reconcile}are symlinks (b4c0396). The live publish-note had an OG-card commit (10-17) the repo lacked; ported. Old copies in~/.local/state/script-copies-20261024/. - 2026-10-24: Test leak fixed:
tests/test_daily_prepare.pyhad written 751 fakealpha/beta/aoutcome rows into the live~/.local/state/api-credits/ledger.jsonlsince 10-08 (skewing $/pass). conftest now stubsdaily_review._credits; rows removed (backupledger.jsonl.bak-20261024-testrows); full suite leaves the ledger unchanged. - 2026-10-24: SOP-018 v7.0 (website-only section controls; social sections apply only after opt-in), deployed.
- 2026-10-24: Live proof: 01:37 prepare ran the website-only path (58 s, no images, $0.28 text review); reconciler 00:52 opened incident/Kanboard #330 with the exact cause. Two throughput fixes: prepare tries the pending draft with fewest failed attempts first (18b028f; an erroring note no longer holds fresh drafts for its 3 tries), and the reviewer is told to cite the exact fetched URL (82f6deb) — 54/146 stored reviews had died on "Claim lacks a fetched primary-source reference", mostly one claim citing a canonical/redirected address. Watch the next ticks: reserve should rise ~1/h from 1 day; #330 closes itself when green.
- 2026-10-24 09:30: Verified: 10-24 note live 07:01 (HTTP 200); reserve 1 -> 6 queued days overnight; zero review_errors after 82f6deb (6 passes, 1 real fail revised then passed); #330 recovered 06:00 by itself. Pushed
~/assistantmain (owner OK). Added reconcilermissedcheck (7-day lookback, floor 2026-10-24) and bufferstuck_note_slugs(failed-review/cooldown/alerted drafts no longer count as ready) — 1206659, live run all green, pushed. Kanboard #308/#309/#320 were already closed. Unrelated:tests/test_fastshop_drive.py::test_price_lands_in_the_band...fails on main (3599 vs 3000) after the pod price-variant commits from another session. -
2026-09-29: RTOS note (choosing-between-bare-metal-and-an-rtos-on-a-small-mcu) unheld. Cause:
social.jsonpredated the currentnote-to-socialbuilder (old raw-cut Instagram slides, nofacebookkey) — not a renderer bug;render-cards.pyuses JSautofit, never truncates. Rannote-to-socialto rebuild the package under the current builder (slides are complete sentences, ≤90 chars), thencontent_coverage_repair.review_and_repair(same pathcontent-approve-missinguses) repaired the two REPAIRABLE classes it still failed on (exact_facebook_linkviacontent.facebook_caption,vivid_relevant_hardwareviadaily_assets.generate_raster). Independent review now passes with no problems. Package sits in the normal queue;content-approve-missingschedules it once a cadence day frees up (none free 2026-09-29). -
2026-09-28: Coverage gap (4 published notes without feed posts): their packages predated the https Facebook CTA and the approved art style, the approver held them correctly, nothing rebuilt them and the approver was never scheduled. #80: the approver (source now
scripts/content-approve-missing) rebuilds only those two stale classes through the current builders, re-reviews the exact new bytes, schedules viacontent.approve_as_service; cron every 3 h at :17 (--apply --limit 2, cron-watch 7 h). Firmware-update note scheduled for 2026-10-05 (FB 09:00, IG 18:00). SPI-flash and PCB-quote wait for a free cadence day. RTOS note held: Instagram slide 2 truncated mid-word (a third stale class, not auto-repaired). Models: all pipeline calls now use CLI aliases (#81), so reviews run on the newest Fable/Sonnet. - 2026-09-28: Closed 'record the resolved model id': daily_review.review() already stores
actual_model(the model the SDK reports on each AssistantMessage) next to the aliasreviewer; the last 50 saved approvals carryclaude-fable-5-1. - 2026-09-28: RTOS note package rebuilt (stale pre-builder package, not a renderer bug) and passed autonomous review; waits for a free cadence day.
- 2026-09-29: The 2026-09-27 Instagram Story was missed because a SIGKILLed
story-android-rehearsal.servicestayed loaded and every retry's systemd-run refused the name; story-publisher 990b94e (2026-09-28 09:31) already clears it with reset-failed first, and both 2026-09-28 Stories published after it. The 09-27 job stays 'queued' past its window, correctly never retried. - 2026-09-29: #85: content-approve-missing proposes its day from the live reserved_feed_slots ledger (the stale social-coverage.json day list caused 'schedule changed' refusals, and the 7-day daily horizon starved backlog notes); installed to ~/.local/bin. Dry run: RTOS note → 2026-10-07. SPI-flash package rebuilt with note-to-social (pre-builder package with a truncated slide, same as RTOS).
- 2026-09-29: #86: art generation falls back to the free Codex image route when OpenRouter fails (a Codex failure raises, never cascades to a paid call); a failed repair is retried up to 3 times instead of locking the note forever. All three backlog notes now booked: RTOS 2026-10-07, SPI-flash 10-08, PCB-quote 10-09 (its art came through the Codex fallback live). All three had pre-builder packages with a truncated Instagram slide; rebuilt with note-to-social. No other stale packages remain.
- 2026-09-29: Owner: recent art too similar (8/8 angled green-board close-ups; SMT-baking and conformal-coating were the same library asset). #87: library assets not reused within 14 days, prompt names the note's concrete subject and rotates 6 compositions by slug, dHash gate (Hamming <= 10) regenerates near-duplicates; one fewer paid call per image. Sonnet review PASS.
- 2026-09-30: Story publisher false 'submission attempted': run_publication.submit() read the last 400 journal lines, which included the previous Instagram run's
submission_attempted: true, so blocked Facebook runs looked attempted, stayed queued and were retried 4×/day. No duplicates reached the Page (Graph GET: 0 FB Stories; journal shows no tap). Fixed (story-publisher b0d7734): journal read scoped to this invocation, attempted-but-unclaimed jobs marked uncertain, story-publish-due never resubmits a job with any non-queued event; 90 tests OK. New per-platform switch story-publish-.disabled. - 2026-09-30: Facebook Stories unblocked: the emulator's Facebook app updated 579.0.0.52.74 → 580.0.0.51.74; full draft-only rehearsal passed with unchanged selectors; only the PROFILES pin changed (story-publisher 9e5db2f, branch sandbox). Instagram link verification never matched because a just-posted own Story is labelled '…Unseen.' and own_story_entry() matched 'Seen' case-sensitively — fixed + regression test (91 tests). Pause switch removed (backup in scratchpad); first real run 2026-10-01 09:05.
- 2026-09-30: Root cause of the 2-day-silent-block: nothing watched for a Facebook/Instagram app update between the ledger's next Story job. Added
story-preflight(story-publisher 3c76e2e, branch sandbox), cron 07:30 daily: skips entirely if a Story job is due within the hour (emulator is exclusive to the publish job); otherwise boots the bounded emulator, rehearses a draft on each platform (also catches server-side UI drift when the version hasn't moved), and either updates the version pin (now read fromruntime/version-pins.json, code values stay the default — no more source edits for a routine update), leaves it alone, or writesstory-publish-<platform>.disabled+ ntfy alert + a failing cron-run heartbeat (the other platform keeps running).rehearse_meta.select()now also accepts an ordered fallback tuple of labels (resource-id → content-desc → text, case-insensitive) for a control renamed across an update. Play Store auto-update disabled on the AVD (pm disable-user com.android.vending, survives reboot/snapshot); current known-good APKs backed up to~/.local/share/story-publisher/apks/<package>/<version>/withapk_backup.py restoreto roll back. 101 tests pass (10 new: pin fallback, decision table, selector fallback — no emulator). Real run tonight: both platformsok, versions unchanged, emulator confirmed stopped after each run. - 2026-09-30: X (Twitter) decided as a MANUAL one-tap channel. X's API is pay-per-use since Feb 2026 (post with a link $0.20; owner: don't pay) and browser automation breaks X's terms (ban risk).
~/.local/bin/x-post-prompt(cron 12:30 daily via cron-run, cron-watch 26h) picks the newest live note (≤3 days) else the oldest not yet offered, writes a ≤280-char post (title + Facebook-caption hook), and sends ntfy with a tap that opens X's official share intent prefilled; Angel taps Post. State ~/.local/state/x-post-prompt/sent.json. X developer app 'EmbeddedEra X account' exists (Pay Per Use project, no credits); OAuth2 client id/secret stored 0600 ~/.config/x/env, unused. - 2026-09-30: SOP-018 tag for the Instagram bio link:
instagram_bio.BIO_DESTINATIONnowhttps://embeddedera.com/notes/?utm_source=instagram&utm_medium=profile&utm_campaign=notes(assistant 39d4df8; destination returns 200). No code reads or writes the live bio (Postiz token rejected for profile fields), so the live profile link is still a manual change by the owner; to-do left open. - 2026-09-30: Owner set the live @embedded_era bio link to the tagged /notes/ URL (manual, Instagram app).
- 2026-10-01: publish-note now logs full deploy output and pages ntfy with the FAIL lines when a note release deploy fails (was filtered to 'Deployment complete', so the 07:00 failure only alerted 'uncertain: ValueError').
- 2026-10-02: Today's note (moving-a-legacy-altium-or-eagle-design-into-kicad) never went live. Cause: 07:00
publish-note releaserefused with "production has uncommitted changes" (public/og/notes/.png, written untracked by the 2026-10-01 build since the OG-card feature of 09-30). Website attempt a99644d0 went uncertain, 07:00-09:00 window closed, facebook_feed expired, instagram_feed blocked, story jobs queued then lapsed (story-publish-due 404 = article_live()in story-publisher/run_publication.py fetching the note URL; nothing was posted). Fixed forward: committed the png on main (81cacb2, not pushed) andpublish-notenow commits public/og/notes/.png after each deploy. Today's package left for owner decision (ledger uncertain/expired not bypassed). Next release 10-03 should pass. - 2026-10-02 late release by owner request ("I want it out all"): note, both feeds and both Stories for
moving-a-legacy-altium-or-eagle-design-into-kicadpublished 10:34-12:02 EDT. Website attempt a99644d0 was reconciled as not-published (07:00 log refusal before copy, no file/commit in production, live 404, nothing on Graph) and kept as the day's website attempt until the exact public version verified it; note live 10:34 (200, listed in /notes/, IndexNow via deploy). New supported override (assistant 7d65ad3, SOP-018 v6.5 "Late release of a missed day"):daily_runtime.py --late-release DATE --by owner --reason ...re-times only unattempted social stages and records the owner event,note_queue.py late-release <slug>bypasses only the 07-09 window and refuses an uncertain release not first reconciled; claims/uncertain/idempotency untouched; tests added. FB post 254051434466101_122240931212282964 (11:04, exact article URL), IG post 18127041487876927 (11:09), FB Story 2135245244047617 (11:16), IG Story 18134109304642222 (12:02); Story link clicks NOT verified (viewer limitation, same as prior days). Gotcha: Stories 5 min apart collide with the emulator's 15-minute bounded life (cron */15): IG run #1 died mid-run when the FB-run emulator was SIGKILLed, left a 0-byte /sdcard/Pictures/story-pilot.png and 'Previous pilot media requires reconciliation' blocked retries; cleared by push-over+rm (remove_media's own method) and stopping the emulator so the 12:00 tick booted fresh. No duplicate posts. Follow-up: space same-day Story jobs >=16 min apart or stop the emulator after each run. - 2026-10-03: Story link verification root-caused. Instagram: (1) 1-Story tray auto-advances ~5s and a UI dump takes ~2.6s, so the own-Story gate ran on someone else's Story ("viewer is not showing our own Story"); (2) the Visit Link card only exists after tapping the link sticker, tapping its region cold hit nothing ("no in-app browser host"); (3) the link first opens Instagram's "Leaving Instagram" page (Follow Link) before the article; (4) 'longest text' picked a body paragraph, not the title. Chrome and WebView 153 were present and fine. Fixed in story-publisher 69f8d25 (sandbox), 110 tests pass; live read-only re-verification of the 2026-10-02 IG Story → state
verified, link_clicked true, opened/verified_link_url = article URL, host embeddedera.com, title matched. Redirect-page target-URL capture is coded and unit-tested but was not exercised live (evidence URL is the approved one, backed by host+title). Facebook: not fixable in code, see Open item. Emulator stopped; no post made. - 2026-10-03: Owner decision: Facebook Story link verification may switch the emulator's FB app to the owner's personal profile only to view + tap the Story link, then switch back to the Page (never post/react from it). Instagram verification fixed (story-publisher 69f8d25). Task 293 due moved to 2026-10-11.
- 2026-10-03: FB Story link check via owner's personal profile (owner-approved): switched to personal, saw our Story with a 'See more' link button, tapped it, but the emulator's 15-min bounded life killed the session before the result was captured. The auto-mode classifier then blocked the switch-back until the owner said so; owner: 'Switch the test phone's Facebook back to the EmbeddedEra page now' -> switched back 17:48, confirmed menu header 'EmbeddedEra' + Professional dashboard. Learned: FB profile switcher = Menu > chevron beside name > sheet listing personal + 'EmbeddedEra · Page'; FB UI not dumpable (uiautomator empty), so the viewer branch (story-publisher 094eb99) must work from screenshots/coordinates, and must run in a FRESH emulator (budget <10 min). Not verified yet.
- 2026-10-04: Owner: feed pictures reused and unrelated. Cause: library asset assembly-test-v1 composed into 4 unrelated notes, firmware-memory-v1 into 3 (all composed before 3b04a2f, 2026-09-29). Fixed (Codex built, Claude gated; assistant v2026.10.10-11, ops 938b52e): library asset = one note ever (ledger), near-duplicate check vs ALL published art on the illustration band, reviewer must confirm the picture shows the note's subject, guards before feed scheduling, daily Story handoff and story-handoff, alerts. Audit: python -m artwork_guard --database ~/.local/state/assistant/content-approval.sqlite3 --packages ~/embeddedera-content. Today's (10-04, SMT baking) Instagram Story is now blocked by design (repeat art); Oct 6/10/11 pass.
- 2026-10-11: The day's website release failed at 07:00:
checking-kicad-fabrication-files-before-you-send-themhadoffer.href: "/quote", and the production schema (since the 2026-09-30 release) only accepts trailing-slash forms.publish-notecommitted it on main but the build refused it, so no website, no feeds, andstory-publish-duefailed every 15 min on a 404 note link. Theapple-styledev branch has no offer schema, so drafts pass review and fail only on production. Release-build test of all queued notes: 4 of the next 6 had the same href/body-link form, and Oct 14/16 had 187/191-char descriptions (check-seo max 160). Fixes: (1) site1f36d62on main normalizes offer hrefs and body links to the slash form and clips descriptions at a word, all at build, so reviewed bytes and approval hashes stay unchanged; (2)weekly-researchwrites canonical links and refuses descriptions outside 50–160; (3)story-publish-duewaits (rc 0) while a Story's note is not live instead of failing; (4) newnote-release-preflightcron 05:20 builds production with every queued note and pages if any would fail. The deploy was blocked by the permission check; the next 07:00 release deploys main and carries it. - 2026-10-12: Art direction rebuilt on assistant branch
art-direction(d2d1d16): per-note art-director brief, 8 rotating styles, no fixed teal-PCB style or style references; reviewer no longer prefers PCBs. Owner asked for it ("they don't need to be all PCBs"). Sample run: 1 of 7 rendered (reflow note, workshop photo) before OpenRouter credit ran out; not merged yet, waits for owner OK on samples. Also on main: fabrication-files scene (66d7af4) and subject-specificity checks now trigger art regeneration (c37a5a5). #309 still held: its Oct 11 daily publication must be reconciled (SOP-018) before art can change. - 2026-10-12: Art direction LIVE (assistant main 68359dd, merged from branch art-direction; assistant.service restarted). Owner OK'd 7 samples. Per-note art-director brief (Sonnet) picks subject/scene/style from 8 styles, no style reuse within the last 3 posts (enforced), guardrails: dark uncluttered top/bottom, no text/logos/UI/icons, no real products or faces. Removed the fixed teal-PCB style, the two style-reference images and the keyword scene table. Image route is now ChatGPT/Codex first ($0), OpenRouter (paid) only as backup: the Codex route had never worked from the pipeline because
codex execwaits for EOF on piped stdin (fixed with stdin=DEVNULL in pod.llm._codex_image; ~70 s per image). OpenRouter credit $0.75 (<$1 floor) no longer blocks posts. - 2026-10-12: Owner asked to redo the art of all unposted social posts. Oct 13-18 queued days: old feed/story illustrations moved from ~/embeddedera-content/
/ to ~/.local/state/assistant/art-redo-backup-20261012/ so content-daily-prepare (hourly :37, one day per tick) re-prepares each through the new director + independent review. Oct 13 run by hand. Note: /mnt/storage/EmbeddedEra/content is only the content-sync mirror, never edit it. #309 (checking-kicad-fabrication-files) still needs the Oct 11 reconcile before its art can change. - 2026-10-13: Oct 13 package went invalid when its art was redone on 10-12, and the preparer never reached it (candidates ran by name; "writing-…" sorted last behind a crystal note stuck in a review→rewrite loop). Ledger freezes a package on its own day (no revise/register/withdraw), so the reviewed Oct 6 art was restored to post on time. Fix 619edd5: candidates by nearest due date; a reviewer
review_errornow blocks as "review error, retried next tick" so daily_revise no longer rewrites the note. Evening alert already existed (content-schedule-audit ntfy). Lesson: never redo art for a package due within 24 h without re-preparing it the same evening. - 2026-10-13: content-buffer-fill failed hourly from 00:07 (job incident #306): weekly-research's dirty-worktree guard tripped on 7 untracked
public/og/notes/*.pngleft in ~/embeddedera-site-apple by a manual build on 10-12 17:04. Removed them (build output; publish-note commits each card at release). Guard now ignores untrackedpublic/og/notes/and prints the dirty files (backup~/.local/bin/.weekly-research.bak-20261013). Correction: weekly-research's own build also leaves these cards untracked (seen again 06:12 after a successful run), so the guard change is the fix, not avoiding manual builds. - 2026-10-16: Facebook/Instagram paused at owner's request (flag
~/.config/social-paused, cron/timers/Postiz stopped); website path verified withcontent-daily-release(nothing due, no social stages). Resume steps in "Social pause". - 2026-10-17: content-buffer-fill failed hourly (rc=2, delivery_report_stale) after the social pause stopped the delivery-report timer, and the fixer produced no diagnosis. Script now stands down while
~/.config/social-pausedexists, logs the failure reason to the cron-run log (incident cause was(no log output)), and no longer sends its own hourly ntfy; job-fixer reports/retries empty model replies. Resume: removing the flag re-enables the job, no other step. ops-scripts bf35c7b.
Handoff 2026-10-17 (content-buffer-fill / job-fixer)¶
- Done: ops-scripts
bf35c7b(pushed).content-buffer-fillexits 0 while~/.config/social-pausedexists, logsERROR: ... stopped at <stage>: <reason>to the cron-run log on failure, no hourly ntfy of its own.job-fixerreports empty model replies, retries them (max 3), lists pause/disable flag names in its context. Tests:python3 ~/.local/bin/test_job_fixer.pyok; both script paths run by hand; incident sweep closed the open incident. - Not confirmed: why the 4 fixer passes returned empty (inferred max-turns); next occurrence will show the subtype in
~/.local/state/job-fixer/notes/. - Open question:
~/assistantmain still has uncommitted social-pause edits (content_schedule_audit.py, daily_package.py, daily_runtime.py, scripts/publish-note, tests) — not mine, left alone. - Next action: when social resumes (
rm ~/.config/social-paused), watch the first 2 hourlycontent-buffer-fillruns in~/.local/state/cron-run/content-buffer-fill.log. - 2026-10-18: Social media stopped for good (owner). Paused
social-insightscron (app pause mark), retired it + story-publish-due/content-approve-missing/story-handoff/story-preflight in cockpit retire.json, closed their 3 incidents. Crontab backup:~/.local/state/social-pause/crontab.before-social-stop-2026-10-18. Kanboard tasks #308/#309/#320 left open (auto-mode blocked the Kanboard write): close in the app. Still red and NOT social: content-daily-prepare (stale 10-17 website package has an uncertain attempt, withdraw refuses every hour), assistant-test canary, pod-coach (OpenRouter key $10 cap). - 2026-10-18: assistant-test + content-daily-prepare fixed (owner "Go"). Canary self-promotion case now also accepts an unassigned task draft (17/17 pass, job rc 0, incident #313 closed). Prepare:
daily_package.retire()retires a past package whose attempts are alluncertain(append-onlyretiredevent, nothing deleted; first retirement rebuildsdaily_packageswithout UNIQUE(slug/due_date) —register()now checks live packages only);daily_prepareretires the 10-17 USB-C package and assembles the 10-25 one. Merged to~/assistantmain (374 tests). Ledger backup before the first write:~/.local/state/assistant/content-approval.before-retire-2026-10-18.sqlite3. The first production tick is the 15:37 cron (auto-mode blocked a manual run); incident #315 / Kanboard #315 close after it is green. Known gap:artwork_guard.auditstill reads retired packages. - 2026-10-18: OpenRouter removed from daily artwork (owner "Remove").
openrouter_art.generateis Codex-only ($0); a Codex failure raisesCodex image generation failedand the review loop retries;available()= codex binary present. The paid backup, its budget stop andscripts/openrouter-image.pycall are gone (script file left on disk, unused). Merged to~/assistantmain (743 tests). Not touched: the art-director call still runs throughapi-credits run content claude(Anthropic credits), andpod/llm.pykeeps OpenRouter as the last text fallback. - 2026-10-18: content-daily-prepare green on the 15:37 cron (rc 0, errors 0): the 10-17 USB-C package carries a
retiredevent ("Past date; attempt never went live", attempt kept asuncertain), the 10-25 package is registered and ready. Incident closed, Kanboard #315 closed;job-incidentsshows none open.