Coherence Weekly Rollup #2
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Coherence Weekly Rollup | |
| on: | |
| schedule: | |
| - cron: '0 9 * * 1' # Monday 9:00 UTC | |
| workflow_dispatch: | |
| inputs: | |
| coherence_root: | |
| description: 'Coherence root directory (default: coherence)' | |
| required: false | |
| default: 'coherence' | |
| type: string | |
| telemetry_out_dir: | |
| description: 'Override telemetry output directory (leave blank for auto)' | |
| required: false | |
| default: '' | |
| type: string | |
| commit_results: | |
| description: 'Commit results to repo (set false for protected branches)' | |
| required: false | |
| default: 'true' | |
| type: string | |
| permissions: | |
| contents: write | |
| jobs: | |
| rollup: | |
| name: Weekly Rollup | |
| runs-on: ubuntu-latest | |
| env: | |
| COHERENCE_ROOT: ${{ inputs.coherence_root || 'coherence' }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Resolve output directory | |
| id: resolve | |
| run: | | |
| ROOT="${COHERENCE_ROOT}" | |
| OVERRIDE="${{ inputs.telemetry_out_dir }}" | |
| if [ -n "$OVERRIDE" ]; then | |
| OUT_DIR="$OVERRIDE" | |
| elif [ "$ROOT" = "coherence" ]; then | |
| OUT_DIR="coherence/telemetry" | |
| else | |
| SANITIZED=$(echo "$ROOT" | tr '/' '_') | |
| OUT_DIR="telemetry_out/${SANITIZED}" | |
| fi | |
| echo "out_dir=${OUT_DIR}" >> "$GITHUB_OUTPUT" | |
| mkdir -p "$OUT_DIR" | |
| - name: Validate root | |
| run: | | |
| if [ ! -f "${COHERENCE_ROOT}/intel/assumptions.yaml" ]; then | |
| echo "::warning::No assumptions.yaml found at ${COHERENCE_ROOT}/intel/assumptions.yaml" | |
| fi | |
| if [ ! -d "${COHERENCE_ROOT}/drift" ]; then | |
| echo "::warning::No drift directory found at ${COHERENCE_ROOT}/drift/" | |
| fi | |
| - name: Generate Rollup | |
| env: | |
| TELEMETRY_OUT_DIR: ${{ steps.resolve.outputs.out_dir }} | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const fs = require('fs'); | |
| const root = process.env.COHERENCE_ROOT; | |
| const outDir = process.env.TELEMETRY_OUT_DIR; | |
| const today = new Date(); | |
| today.setHours(0, 0, 0, 0); | |
| const warnDate = new Date(today); | |
| warnDate.setDate(warnDate.getDate() + 14); | |
| // ── Assumptions ── | |
| let totalActive = 0; | |
| let expiredCount = 0; | |
| let nearExpiryCount = 0; | |
| const expiredList = []; | |
| const nearExpiryList = []; | |
| const asmPath = `${root}/intel/assumptions.yaml`; | |
| if (fs.existsSync(asmPath)) { | |
| const content = fs.readFileSync(asmPath, 'utf8'); | |
| // Parse assumptions from YAML. | |
| // Handles: empty files, assumptions: [], indented lists, multi-line statements. | |
| const blocks = content.includes('- id:') | |
| ? content.split(/^\s*- id:/m).slice(1) : []; | |
| for (const block of blocks) { | |
| const id = (block.match(/^\s*(.+)/m) || ['', ''])[1].trim(); | |
| const status = (block.match(/^\s*status:\s*(.+)/m) || ['', ''])[1].trim(); | |
| const expires = (block.match(/^\s*expires:\s*(.+)/m) || ['', ''])[1].trim(); | |
| // Handle multi-line statements (> or | YAML folding) | |
| let statement = ''; | |
| const stmtMatch = block.match(/^\s*statement:\s*(.+)/m); | |
| if (stmtMatch) { | |
| const val = stmtMatch[1].trim().replace(/^["']|["']$/g, ''); | |
| if (/^[>|][-+]?$/.test(val)) { | |
| const lines = block.split('\n'); | |
| const idx = lines.findIndex(l => /^\s*statement:/.test(l)); | |
| const keyIndent = (lines[idx].match(/^(\s*)/)||['',''])[1].length; | |
| const parts = []; | |
| for (let i = idx + 1; i < lines.length; i++) { | |
| const indent = (lines[i].match(/^(\s*)/)||['',''])[1].length; | |
| if (lines[i].trim() && indent <= keyIndent) break; | |
| if (lines[i].trim()) parts.push(lines[i].trim()); | |
| } | |
| statement = parts.join(' '); | |
| } else { | |
| statement = val; | |
| } | |
| } | |
| if (status !== 'active') continue; | |
| totalActive++; | |
| if (!expires) continue; | |
| const exp = new Date(expires + 'T00:00:00'); | |
| const asmId = id.startsWith('ASM-') ? id : `ASM-${id}`; | |
| if (exp < today) { | |
| expiredCount++; | |
| expiredList.push({ id: asmId, expires, statement }); | |
| } else if (exp <= warnDate) { | |
| nearExpiryCount++; | |
| nearExpiryList.push({ id: asmId, expires, statement }); | |
| } | |
| } | |
| } | |
| // ── Drift ── | |
| let openDriftCount = 0; | |
| const openDrifts = []; | |
| const driftDir = `${root}/drift`; | |
| if (fs.existsSync(driftDir)) { | |
| const driftFiles = fs.readdirSync(driftDir) | |
| .filter(f => /^DRIFT-[\d-]+\.md$/.test(f)); | |
| for (const df of driftFiles) { | |
| const content = fs.readFileSync(`${driftDir}/${df}`, 'utf8'); | |
| const statusMatch = content.match(/## Status\s*\n+\s*(.+)/); | |
| if (statusMatch) { | |
| const st = statusMatch[1].trim().toLowerCase(); | |
| if (st === 'open' || st === 'in_progress') { | |
| openDriftCount++; | |
| const sevMatch = content.match(/## Severity\s*\n+\s*(.+)/); | |
| const sev = sevMatch ? sevMatch[1].trim() : 'unknown'; | |
| openDrifts.push({ id: df.replace('.md', ''), severity: sev }); | |
| } | |
| } | |
| } | |
| } | |
| // ── Score ── | |
| let score = null; | |
| const scorePath = `${root}/telemetry/coherence_score.json`; | |
| if (fs.existsSync(scorePath)) { | |
| try { score = JSON.parse(fs.readFileSync(scorePath, 'utf8')); } catch {} | |
| } | |
| // ── Write rollup JSON ── | |
| const dateStr = today.toISOString().split('T')[0]; | |
| const rollup = { | |
| date: dateStr, | |
| assumptions: { total_active: totalActive, expired: expiredCount, near_expiry: nearExpiryCount }, | |
| drift: { open: openDriftCount }, | |
| score: score ? score.score : null, | |
| }; | |
| fs.writeFileSync(`${outDir}/weekly_rollup.json`, | |
| JSON.stringify(rollup, null, 2) + '\n'); | |
| // ── Write top risks ── | |
| const risks = []; | |
| // Sort: expired assumptions first, then near-expiry, then open drift | |
| for (const a of expiredList) { | |
| risks.push({ priority: 1, label: 'EXPIRED', detail: `${a.id} — ${a.statement} (expired ${a.expires})` }); | |
| } | |
| for (const a of nearExpiryList) { | |
| risks.push({ priority: 2, label: 'EXPIRING', detail: `${a.id} — ${a.statement} (expires ${a.expires})` }); | |
| } | |
| for (const d of openDrifts) { | |
| risks.push({ priority: d.severity === 'critical' ? 1 : 3, label: `DRIFT/${d.severity}`, detail: d.id }); | |
| } | |
| risks.sort((a, b) => a.priority - b.priority); | |
| const lines = [ | |
| `# Top Risks — ${dateStr}`, | |
| '', | |
| ]; | |
| if (risks.length === 0) { | |
| lines.push('No active risks. All assumptions current, no open drift.'); | |
| } else { | |
| lines.push('| Priority | Type | Detail |'); | |
| lines.push('|----------|------|--------|'); | |
| for (const r of risks.slice(0, 10)) { | |
| lines.push(`| ${r.priority === 1 ? 'HIGH' : r.priority === 2 ? 'MED' : 'LOW'} | ${r.label} | ${r.detail} |`); | |
| } | |
| } | |
| lines.push(''); | |
| fs.writeFileSync(`${outDir}/top_risks.md`, lines.join('\n')); | |
| - name: Commit Rollup | |
| if: ${{ inputs.commit_results != 'false' }} | |
| env: | |
| TELEMETRY_OUT_DIR: ${{ steps.resolve.outputs.out_dir }} | |
| run: | | |
| git config user.name "github-actions[bot]" | |
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
| git add "${TELEMETRY_OUT_DIR}/weekly_rollup.json" "${TELEMETRY_OUT_DIR}/top_risks.md" | |
| git diff --staged --quiet && echo "No changes to commit" && exit 0 | |
| git commit -m "chore: weekly coherence rollup [skip ci]" | |
| git push |