Sub-Daily Ingest Workflow
The four-hour capture loop keeps Slack, Gmail, X bookmarks, and local sessions close enough to current that the wiki can act as operational memory instead of a stale archive.
Trigger
Run this workflow when Kevin asks whether Slack, email, or X are being ingested every few hours, when scripts/check-freshness.ts flags the sub-daily loop, or when a scheduler audit touches source-compile, email-sync, or slack-sync.
The live jobs are intentionally staggered:
| Job | Owns | Cadence | Durable state |
|---|---|---|---|
source-compile |
X bookmarks, enriched bookmark research, local AI sessions, repeated agent friction | Every 4 hours | automations.source-compile, syncs.x_bookmarks, compile.last_bookmark_* |
email-sync |
Gmail collection, signatures, people/company/action-item extraction | Every 4 hours | automations.email-sync, syncs.email |
slack-sync |
Slack activity review, action items, project decisions, durable chat facts | Every 4 hours | automations.slack-sync, syncs.slack |
Invariant
Each run must separate collection from promotion:
- Collect fresh source material into
raw/. - Write an output report under
outputs/<date>/<slug>/. - Promote only durable facts into the wiki.
- Update
state.jsonwith ISO timestamps for sub-daily freshness. - Append
wiki/log.md.
If collection fails, the run still writes a blocker report and automation timestamp, but it must not update the corresponding syncs.* timestamp. That keeps freshness honest.
Run Order
- Check freshness:
npx tsx scripts/check-freshness.ts - Confirm live scheduler state when auditing:
sqlite3 ~/.codex/sqlite/codex-dev.db "SELECT id, name, status, rrule, next_run_at FROM automations WHERE id IN ('source-compile-2','email-sync','slack-sync');" - For source compile, follow Source Compile Workflow.
- For Gmail, follow Email Sync Workflow.
- For Slack, follow Slack Sync Workflow.
- Re-run freshness and verify the three jobs either advanced state or reported blockers.
Promotion Matrix
| Source signal | Promote to wiki? | Target |
|---|---|---|
| New tool, paper, repo, demo, API change, or repeated X bookmark cluster | Yes, when it changes future behavior | tool/project/concept page plus relevant hub |
| Email from a real person with durable project, career, or action context | Yes, when person/company/project context changes | person, career, project, or decision page |
| Slack decision, launch status, blocker, or repeated workflow | Yes, when it should be remembered outside Slack | project, decision, workflow, skill, or log page |
| Signature request, reply-needed email, transient Slack nudge | Usually output-only | raw digest + output action list |
| Auth failure, missing browser session, empty inbox, no new bookmarks | No | output report with No durable wiki update needed or blocker |
State Writes
Use ISO timestamps for the sub-daily automation keys:
{
"syncs": {
"x_bookmarks": "2026-06-30T18:00:00.000Z",
"email": "2026-06-30T18:00:00.000Z",
"slack": "2026-06-30T18:00:00.000Z"
},
"automations": {
"source-compile": "2026-06-30T18:00:00.000Z",
"email-sync": "2026-06-30T18:00:00.000Z",
"slack-sync": "2026-06-30T18:00:00.000Z"
}
}
Do not use date-only values for four-hour jobs. Date-only state makes check-freshness unable to distinguish a current run from a 23-hour-old run.
Closeout
The sub-daily workflow is complete only after the source-specific workflow reports either a successful collection/promotion run or a blocker/no-op. Do not advance all three jobs as a batch unless all three were actually checked. If any durable wiki page changed, run Build Index Workflow and QMD Embed Workflow. If a workflow, skill, scheduler prompt, or generated pack changed, follow Generated Surface Contract before declaring the loop current.
Failure Handling
| Failure | Correct behavior |
|---|---|
| Gmail OAuth missing or expired | Run or request npx tsx scripts/sync-email.ts --auth; do not update syncs.email until collection succeeds. |
| Slack browser session missing | Record the blocker, leave syncs.slack stale, and keep the automation active. |
| X bookmark sync fails | Record the command/error, leave syncs.x_bookmarks or compile.last_bookmark_* unchanged, and avoid partial wiki promotion. |
| Wiki promotion is ambiguous | Write output-only, say No durable wiki update needed, and leave a follow-up row. |
| Scheduler prompt drifts from automation file | Update the live scheduler to read the canonical automations/<slug>.md contract. |
Validation
After changing this loop:
npx tsx scripts/doctor.ts --only automations
npx tsx scripts/build-index.ts
qmd update && qmd embed
npx tsx scripts/check-freshness.ts
The healthy state is not necessarily all green. Immediately after creating a new sub-daily job, freshness should keep flagging it until the first successful run writes state. That is correct fail-visible behavior.
Run Contract
This workflow follows Workflow Run Contract: name the sources, write output or no-op proof, promote only durable facts, update state only when the run really completed, refresh generated surfaces when durable pages or skills change, and log user-visible work.
Timeline
- 2026-07-01 | Added shared workflow-run and generated-surface closeout expectations so the four-hour loop cannot look fresh when only part of the source family completed. Source: User request, 2026-07-01
- 2026-06-30 | Created as the coordinating workflow for the four-hour Slack/Gmail/X ingest loop and its state/writeback contract. Source: User request, 2026-06-30;
automations/slack-sync.md;automations/email-sync.md;automations/source-compile.md