DOCUMENTATION HUB·LEVEL_2

Nightly Cron Runbook — Documentation Engine vs API Narrate

REF_PATH: operations-support/nightly-cron-runbookSOURCE: APP_DOCUMENTS_DB

Nightly Cron Runbook — Documentation Engine vs API Narrate

One-page ops reference for Ironframe’s two separate “narrate” pipelines. They run on a staggered cadence so documentation sync and filesystem state settle before exposure-threshold narrate reads live telemetry.


Staggered pipeline (default)

Time (local Windows)Time (Vercel UTC)JobEntry
03:0003:00Documentation sync + OSINT + governance memoscripts\cron_narrate_scheduled.ps1
(30 min settle)(30 min settle)Filesystem, glossary, and DB state finalize
03:3003:30GRC triad narrate + briefing-queue draft + exposure alertsscripts\cron_narrate_api_scheduled.ps1bin\cron_narrate.ps1 or Vercel /api/cron/narrate

Why stagger: When both jobs coincided at 03:00, narrate could evaluate INTERNAL_ALERT_EXPOSURE_THRESHOLD_CENTS against telemetry while the doc engine was still mutating docs and related state. Shifting API narrate to 03:30 guarantees a finalized post-update environment.

Register local tasks:

powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\register-nightly-cron-tasks.ps1

At a glance

Doc Engine (Windows)API Narrate (Core / Vercel)
PurposeRefresh app documentation from git deltas; OSINT sweep; governance memo via Cursor CLIPersist Governance Frame Triad snapshot + board narrative in Postgres
Primary outputdocs/qa/complete-feature-glossary.md, agent-written memos in logGovernanceFrameTriadSnapshot, CronJobArtifact, briefing-queue draft
SchedulerWindows Task \Ironframe Daily Documentation Engine03:00 localWindows Task \Ironframe GRC Narrative Hydration03:30 local; Vercel 30 3 * * * UTC
Entry scriptscripts\cron_narrate_scheduled.ps1scripts\cron_narrate.ps1scripts\cron_narrate_api_scheduled.ps1bin\cron_narrate.ps1 · app/api/cron/narrate/route.ts
Log filescripts\cron_narrate.loglogs\cron_narrate_log.txt (local) · Vercel function logs
Needs app running?No (Cursor CLI only)Yes — Core on :3000 (local) or deployed preview/prod

1. Windows Documentation Engine

Task settings (staggered baseline)

SettingDoc engineGRC API narrate
Task name\Ironframe Daily Documentation Engine\Ironframe GRC Narrative Hydration
TriggerDaily 03:00 localDaily 03:30 local
Actionscripts\cron_narrate_scheduled.ps1scripts\cron_narrate_api_scheduled.ps1
Start inC:\Users\Dereck\ironframe-livesame

Legacy single-task installs may only have the doc engine at 03:00. Re-register with scripts\register-nightly-cron-tasks.ps1 to add the 03:30 narrate task.

SettingValue (doc engine)
Run asInteractive user (Dereck)
LogonInteractive only
PowerStop on battery; no start on batteries

Implication: If the PC is asleep, logged out, or on battery at 03:00, the full pipeline may run later (e.g. after wake/login). Task Scheduler may still show an early-morning last-run time while cron_narrate.log timestamps reflect the actual execution window.

Environment variables

VariableRequiredSourcePurpose
CURSOR_API_KEYYesUser env vars or .env.localHeadless Cursor CLI auth
(from .env.local / .env)OptionalDotenv import in scriptSupabase, DB, etc. if agent touches them

Loaded by Import-ProjectDotEnv from .env.local then .env.

Pipeline phases (scripts/cron_narrate.ps1)

  1. Git delta — writes daily_code_diff.txt (diff vs ~24h ago, excludes docs/).
  2. Writer — Cursor agent updates docs/qa/complete-feature-glossary.md.
  3. Ironintel / Ironwatch — live OSINT sweep (Irongate-sanitized).
  4. Ironlogic / Irontally — corporate governance memo (ALE baselines, compliance drift).

Artifacts & logs

PathMeaning
scripts\cron_narrate.logAuthoritative success/fail log (timestamps in -05:00 local)
daily_code_diff.txtInput delta for Writer phase
docs\qa\complete-feature-glossary.mdPrimary doc output

Doc engine — success criteria

Pass when the log shows, in order:

[timestamp] cron_narrate.ps1: starting (project root: ...)
[timestamp] Cursor agent auth: API key configured.
[timestamp] daily_code_diff.txt generated successfully (... bytes).
[timestamp] Invoking Narrative Architect for internal code changes...
[timestamp] Invoking Ironintel & Irongate for live morning OSINT sweep...
[timestamp] Invoking Ironlogic & Irontally for Corporate Governance Memo...
[timestamp] cron_narrate.ps1: complete.

Fail when you see ERROR: lines (missing CURSOR_API_KEY, CLI not found, agent exit code ≠ 0).

Doc engine — manual smoke

cd C:\Users\Dereck\ironframe-live
powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\cron_narrate.ps1
Get-Content .\scripts\cron_narrate.log -Tail 20

Doc engine — Task Scheduler check

schtasks /Query /TN "\Ironframe Daily Documentation Engine" /FO LIST /V |
  Select-String "Last Run|Last Result|Next Run|Status|Task To Run"
schtasks /Query /TN "\Ironframe GRC Narrative Hydration" /FO LIST /V |
  Select-String "Last Run|Last Result|Next Run|Status|Task To Run"
Last ResultMeaning
0Wrapper exited OK (confirm with log — see timing note above)
Non-zeroWrapper or script failed

2. API Governance Narrate (Core)

Schedule

HostCronRoute
Vercel (production/preview)30 3 * * * UTC (after 0 3 ironwatch heartbeat)/api/cron/narrate
Local (scheduled)03:30 localscripts\cron_narrate_api_scheduled.ps1bin\cron_narrate.ps1
Local (manual)On demandPOST your provisioned workspace URL

Vercel sends Authorization: Bearer <IRONFRAME_CRON_SECRET> automatically when the secret is configured in the project.

Environment variables

VariableRequiredPurpose
IRONFRAME_CRON_SECRETYesBearer auth on all cron routes
GOOGLE_API_KEY or GOOGLE_GENERATIVE_AI_API_KEYYesGemini narrate in narrateGovernanceTriad.ts
DATABASE_URLYesUpsert GovernanceFrameTriadSnapshot
IRONFRAME_CORE_ORIGINLocal wrapper onlyDefault your provisioned workspace URL (bin\cron_narrate.ps1)
SHADOW_PLANE_INGEST_TENANT_UUIDOptionalDefault tenant if header/query omitted
GEMINI_NARRATE_MODELOptionalOverride model (default gemini-2.5-flash)

Default tenant UUID: Medshield (5c420f5a-8f1f-4bbf-b42d-7f8dd4bb6a01) unless x-tenant-id header or ?tenantId= is set.

API narrate — success criteria

HTTP 200 JSON body:

{
  "ok": true,
  "tenantId": "...",
  "operationalDate": "2026-06-16",
  "snapshotId": "...",
  "artifactId": "...",
  "narrativeChars": 1234
}

Pass indicators:

  • Response includes non-empty snapshotId and artifactId.
  • Board context shows populated narrativeCache for that tenant:
curl.exe -s "your provisioned workspace URL `
  -H "x-ironframe-host-tenant-uuid: 5c420f5a-8f1f-4bbf-b42d-7f8dd4bb6a01" |
  ConvertFrom-Json | Select-Object -ExpandProperty narrativeCache

Fail indicators:

  • 401 — bad/missing IRONFRAME_CRON_SECRET.
  • 500 + "ok": false — usually missing Google API key or DB error (check Core logs).
  • Core not running — connection refused from bin\cron_narrate.ps1logs\cron_narrate_log.txt shows CRITICAL FAULT.

API narrate — manual smoke (local)

# Terminal 1 — Core must be up
$env:IRONFRAME_WORM_THREAT_EVENT_ENFORCED = "1"   # optional
npm run dev

# Terminal 2 — load secret from .env.local, then:
$secret = (Get-Content .env.local | Where-Object { $_ -match '^IRONFRAME_CRON_SECRET=' }) -replace '^IRONFRAME_CRON_SECRET=',''
curl.exe -s -X POST "your provisioned workspace URL `
  -H "Authorization: Bearer $secret" `
  -H "x-ironframe-host-tenant-uuid: 5c420f5a-8f1f-4bbf-b42d-7f8dd4bb6a01"

Or use the Windows wrapper (writes to logs\cron_narrate_log.txt):

powershell -NoProfile -ExecutionPolicy Bypass -File .\bin\cron_narrate.ps1
Get-Content .\logs\cron_narrate_log.txt -Tail 5

Expected wrapper line:

Success: Snapshot generated. snapshotId=... artifactId=...

3. Troubleshooting quick map

SymptomLikely pipelineAction
Glossary stale; no DB snapshotDoc engine only ranCheck scripts\cron_narrate.log; re-run scripts\cron_narrate.ps1
Board narrativeCache: nullAPI narrate never ran / failedSmoke POST /api/cron/narrate; verify GOOGLE_API_KEY + DB
Task “success” at 03:00 but log shows 07:00+Interactive-only / sleep / batteryEnable “Run whether user is logged on or not”, “Wake to run”, AC power
CURSOR_API_KEY is not setDoc engineSet User env var or .env.local
IRONFRAME_CRON_SECRET is missingAPI wrapper (bin\)Add to .env.local
Two different log filesBoth exist by designscripts\ = doc engine · logs\ = API wrapper

4. Pipeline cadence reference

LayerScheduleConfig location
Doc engine (local)03:00Windows Task \Ironframe Daily Documentation Engine
Ironwatch heartbeat (Vercel)0 3 * * * UTCvercel.json
GRC API narrate (local)03:30Windows Task \Ironframe GRC Narrative Hydration
GRC API narrate (Vercel)30 3 * * * UTCvercel.json/api/cron/narrate

To shift Vercel narrate to 04:00 UTC (longer settle), change vercel.json to "0 4 * * *" and local task to 04:00 in register-nightly-cron-tasks.ps1.

Do not chain bin\cron_narrate.ps1 immediately after cron_narrate.ps1 in the same script — that collapses the stagger and reintroduces race conditions on exposure threshold evaluation.


Last aligned to repo: staggered nightly pipeline (03:00 doc / 03:30 narrate), scripts/register-nightly-cron-tasks.ps1.