docs(08): create phase 8 cron reporting plans
- 08-01-PLAN.md: Archive script (DRY_RUN toggle) + daily report cron (skill-backed) - 08-02-PLAN.md: Weekly stale summary cron + weekly archive cron (no_agent) - ROADMAP.md: Updated Phase 8 plans count to 2 Covers CRON-01 (daily report), CRON-02 (stale archive script+cron), CRON-03 (Jira integration in daily/weekly reports).
This commit is contained in:
242
.planning/phases/08-cron-reporting/08-01-PLAN.md
Normal file
242
.planning/phases/08-cron-reporting/08-01-PLAN.md
Normal file
@@ -0,0 +1,242 @@
|
||||
---
|
||||
phase: 08-cron-reporting
|
||||
plan: 01
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified:
|
||||
- ~/.hermes/scripts/archive-stale-sessions.sh
|
||||
- ~/.hermes/archive/sessions/
|
||||
autonomous: true
|
||||
requirements:
|
||||
- CRON-01
|
||||
- CRON-02
|
||||
user_setup: []
|
||||
must_haves:
|
||||
truths:
|
||||
- "Archive stale sessions script exists at ~/.hermes/scripts/archive-stale-sessions.sh and is executable"
|
||||
- "Archive directory exists at ~/.hermes/archive/sessions/"
|
||||
- "Daily report cron job 'ngn-daily-report' is registered and fires at 09:00 SGT"
|
||||
- "Daily report loads both 'session' and 'jira-query' skills"
|
||||
artifacts:
|
||||
- path: "~/.hermes/scripts/archive-stale-sessions.sh"
|
||||
provides: "Deterministic no_agent script for session export + prune"
|
||||
min_lines: 35
|
||||
must_contain:
|
||||
- "DRY_RUN"
|
||||
- "hermes sessions export"
|
||||
- "hermes sessions prune --older-than 30 --yes"
|
||||
- path: "~/.hermes/archive/sessions/"
|
||||
provides: "Archive storage directory for stale session JSONL exports"
|
||||
- path: "Hermes cron DB (job: ngn-daily-report)"
|
||||
provides: "Daily report cron job entry in Hermes cron scheduler"
|
||||
key_links:
|
||||
- from: "archive-stale-sessions.sh"
|
||||
to: "hermes sessions export"
|
||||
via: "CLI call with positional output path"
|
||||
pattern: "hermes sessions export"
|
||||
- from: "archive-stale-sessions.sh"
|
||||
to: "hermes sessions prune"
|
||||
via: "CLI call with --older-than 30 --yes"
|
||||
pattern: "hermes sessions prune --older-than 30 --yes"
|
||||
- from: "ngn-daily-report cron job"
|
||||
to: "session skill"
|
||||
via: "--skill session in cron create"
|
||||
pattern: "skill session"
|
||||
- from: "ngn-daily-report cron job"
|
||||
to: "jira-query skill"
|
||||
via: "--skill jira-query in cron create"
|
||||
pattern: "skill jira-query"
|
||||
---
|
||||
|
||||
<objective>
|
||||
Create the archive script and register the daily report cron job.
|
||||
|
||||
**Purpose:** Lay the foundation for CRON-02 (stale session archive script) and implement CRON-01 + CRON-03 (daily report with Jira integration). The archive script is created with a dry-run toggle so the first archive fires in safe mode (export only, no prune). The daily report cron is a skill-backed job that loads the session and jira-query skills to enumerate active sessions, post Jira progress comments, and deliver a Telegram summary.
|
||||
|
||||
**Output:**
|
||||
- `~/.hermes/scripts/archive-stale-sessions.sh` — no_agent archive script with DRY_RUN toggle
|
||||
- `~/.hermes/archive/sessions/` — archive storage directory
|
||||
- Hermes cron job `ngn-daily-report` registered in gateway scheduler
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@/Users/bapung/.config/opencode/gsd-core/workflows/execute-plan.md
|
||||
@/Users/bapung/.config/opencode/gsd-core/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/PROJECT.md
|
||||
@.planning/ROADMAP.md
|
||||
@.planning/REQUIREMENTS.md
|
||||
@.planning/phases/08-cron-reporting/08-CONTEXT.md
|
||||
@.planning/phases/08-cron-reporting/08-RESEARCH.md
|
||||
@/Users/bapung/.hermes/config.yaml
|
||||
@/Users/bapung/.hermes/skills/ngn-agent/session/SKILL.md
|
||||
@/Users/bapung/.hermes/skills/ngn-agent/jira/SKILL.md
|
||||
@.planning/phases/07-main-session-skill/07-01-SUMMARY.md
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Create archive-stale-sessions.sh with dry-run toggle + archive directory</name>
|
||||
<files>
|
||||
~/.hermes/scripts/archive-stale-sessions.sh
|
||||
~/.hermes/archive/sessions/
|
||||
</files>
|
||||
<action>
|
||||
Create the archive directory and the no_agent archive script.
|
||||
|
||||
**Archive directory:**
|
||||
```bash
|
||||
mkdir -p ~/.hermes/archive/sessions
|
||||
```
|
||||
|
||||
**Script file:** Create `~/.hermes/scripts/archive-stale-sessions.sh` with:
|
||||
- `DRY_RUN=true` at top — safe default (export only, skip prune)
|
||||
- `set -euo pipefail` for strict error handling
|
||||
- `ARCHIVE_DIR="$HOME/.hermes/archive/sessions"` — archive location per D-12
|
||||
- `mkdir -p "$ARCHIVE_DIR"` — ensure directory exists
|
||||
- `TIMESTAMP=$(date +%Y%m%d_%H%M%S)` for unique filenames
|
||||
- `OUTPUT_FILE="$ARCHIVE_DIR/sessions-${TIMESTAMP}.jsonl"` — date-stamped per D-11 corrected (export uses positional output arg, no `--output` flag — per RESEARCH.md Pitfall 1 and CLI verification)
|
||||
- Step 1: `hermes sessions export "$OUTPUT_FILE"` — exports ALL sessions (no `--older-than` flag on export — per D-11 correction and RESEARCH.md §Critical finding)
|
||||
- Step 2: Only if `$DRY_RUN = false`, run `hermes sessions prune --older-than 30 --yes` (uses `--yes` not `--confirm` — confirmed by RESEARCH.md Pitfall 2 and `hermes sessions prune --help`)
|
||||
- Step 3: `hermes sessions stats` — show post-archive store state
|
||||
- Echo progress messages for Telegram delivery (stdout is delivered verbatim via `--deliver telegram`)
|
||||
- Print summary with session count and file size
|
||||
|
||||
**Critical corrections from research (must NOT repeat CONTEXT.md errors):**
|
||||
- ❌ `hermes sessions export --older-than 30d --output <path>` — DOES NOT EXIST. Use positional path only.
|
||||
- ❌ `hermes sessions prune --confirm` — DOES NOT EXIST. Use `--yes`.
|
||||
- ✅ Export all, prune separate: `hermes sessions export <path> && hermes sessions prune --older-than 30 --yes`
|
||||
|
||||
After creating the file, make it executable:
|
||||
```bash
|
||||
chmod +x ~/.hermes/scripts/archive-stale-sessions.sh
|
||||
```
|
||||
|
||||
**Dry-run verification:** Script's default mode is export-only (DRY_RUN=true). User manually reviews the first JSONL export before setting DRY_RUN=false to enable pruning. This satisfies the user's discretion requirement for safe initial testing.
|
||||
</action>
|
||||
<verify>
|
||||
<automated>
|
||||
test -f ~/.hermes/scripts/archive-stale-sessions.sh \
|
||||
&& test -x ~/.hermes/scripts/archive-stale-sessions.sh \
|
||||
&& test -d ~/.hermes/archive/sessions \
|
||||
&& grep -q 'DRY_RUN' ~/.hermes/scripts/archive-stale-sessions.sh \
|
||||
&& grep -q 'hermes sessions export' ~/.hermes/scripts/archive-stale-sessions.sh \
|
||||
&& grep -q 'hermes sessions prune --older-than 30' ~/.hermes/scripts/archive-stale-sessions.sh \
|
||||
&& grep -q '--yes' ~/.hermes/scripts/archive-stale-sessions.sh \
|
||||
&& grep -q 'set -euo pipefail' ~/.hermes/scripts/archive-stale-sessions.sh
|
||||
</automated>
|
||||
</verify>
|
||||
<done>
|
||||
Archive script exists at ~/.hermes/scripts/archive-stale-sessions.sh, is executable, and contains:
|
||||
- DRY_RUN=true toggle (safe default)
|
||||
- Export with date-stamped filename
|
||||
- Prune with `--older-than 30 --yes` (gated behind dry-run)
|
||||
- `set -euo pipefail` error handling
|
||||
- Progress echo statements for Telegram delivery
|
||||
Archive directory exists at ~/.hermes/archive/sessions/
|
||||
</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: Register daily report cron job (09:00 SGT, skill-backed)</name>
|
||||
<files>(Hermes cron DB — internal state)</files>
|
||||
<action>
|
||||
Register the daily report cron job using `hermes cron create`.
|
||||
|
||||
**Command (execute verbatim):**
|
||||
```bash
|
||||
hermes cron create \
|
||||
--name "ngn-daily-report" \
|
||||
--deliver telegram \
|
||||
--skill session \
|
||||
--skill jira-query \
|
||||
"0 9 * * *" \
|
||||
"Daily operational report — generate summary of active sessions and update their Jira tickets:
|
||||
|
||||
1. DISCOVER ACTIVE SESSIONS: Run 'hermes sessions export -' to get all sessions as JSONL. Use python3 to parse the JSONL and find sessions with last_active within the last 7 days — these are the active sessions. For each active session, record its id, title, and last_active timestamp. (Note: 'hermes sessions list' has NO --json flag — use export - for machine-readable output per RESEARCH.md Pitfall 3.)
|
||||
|
||||
2. FIND JIRA TICKETS: For each active session, use hindsight_recall with query 'session summary jira' to find the Jira ticket key from the session summary saved by Phase 7's session skill (Step 7, tier: session-summary). If hindsight_recall returns no results, search session messages from the export output for Jira ticket key patterns (PLATFORM-<digits>, AIOPS-<digits>, etc.) — per RESEARCH.md Pitfall 4. One session may have multiple Jira tickets (1-to-many mapping — D-04).
|
||||
|
||||
3. UPDATE JIRA TICKETS: For each active session with found Jira ticket key(s), add a progress comment via:
|
||||
ngn-jira POST '/rest/api/3/issue/<KEY>/comment' --body '{\"body\": \"Session activity update — Date: <today>, Last active: <last_active>. Session: <session_id>. Progress: See session transcript for details.\"}'
|
||||
IMPORTANT: Do NOT transition ticket statuses (D-05). Only add comments. Do NOT update tickets for stale sessions (D-15).
|
||||
|
||||
4. COMPOSE TELEGRAM SUMMARY: Create a structured message with sections:
|
||||
📋 ACTIVE SESSIONS — list each with title, last active timestamp
|
||||
🔄 JIRA UPDATED — list of ticket keys that received new comments
|
||||
Keep within Telegram's 4096 character limit.
|
||||
Do NOT include stale session info or ticket status transitions.
|
||||
|
||||
5. DELIVERY: Your response is automatically delivered to TELEGRAM_HOME_CHANNEL via --deliver telegram."
|
||||
```
|
||||
|
||||
**Key details (per locked decisions):**
|
||||
- Per D-01: Schedule `0 9 * * *` = daily at 09:00 SGT. Timezone is already SGT (+08, confirmed by date +%Z) — no TZ config needed per RESEARCH.md Pitfall 5.
|
||||
- Per D-06: `--deliver telegram` sends to TELEGRAM_HOME_CHANNEL (474440517)
|
||||
- Per D-17: Skill-backed cron (two `--skill` flags, no `--no-agent`)
|
||||
- Per D-18: Loads `session` skill (Phase 7) for session structure + `jira-query` skill (Phase 4) for Jira comment patterns
|
||||
- Per D-04/D-14: Updates ALL Jira tickets linked to active sessions (1-to-many mapping)
|
||||
- Per D-05/D-16: Comments only, no status transitions
|
||||
- Per D-07: Report includes active sessions list, last activity timestamps, Jira ticket keys updated
|
||||
</action>
|
||||
<verify>
|
||||
<automated>
|
||||
hermes cron list 2>&1 | grep -q ngn-daily-report
|
||||
</automated>
|
||||
</verify>
|
||||
<done>
|
||||
Daily report cron job `ngn-daily-report` is registered with:
|
||||
- Schedule: `0 9 * * *` (daily at 09:00)
|
||||
- Delivery: telegram (TELEGRAM_HOME_CHANNEL)
|
||||
- Skills: session + jira-query (skill-backed, no no-agent)
|
||||
- Prompt instructs agent to: enumerate active sessions via JSONL export, find Jira tickets via hindsight_recall, add progress comments via ngn-jira, compose Telegram summary
|
||||
</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<threat_model>
|
||||
## Trust Boundaries
|
||||
|
||||
| Boundary | Description |
|
||||
|----------|-------------|
|
||||
| Cron agent → Hermes session DB | Cron LLM agent reads session data (ids, titles, timestamps, transcripts) to compose reports |
|
||||
| Cron agent → Jira Cloud API | Cron LLM agent writes progress comments to Jira tickets via ngn-jira script |
|
||||
| no_agent script → Hermes session DB | Archive script reads (export) and writes (prune) session store directly |
|
||||
|
||||
## STRIDE Threat Register
|
||||
|
||||
| Threat ID | Category | Component | Disposition | Mitigation Plan |
|
||||
|-----------|----------|-----------|-------------|-----------------|
|
||||
| T-08-01 | Tampering | Daily report cron prompt | accept | Cron prompt is authored during plan execution and stored in Hermes cron DB — not user-controllable. No injection risk. |
|
||||
| T-08-02 | Information Disclosure | Archive JSONL files | mitigate | Archive directory (`~/.hermes/archive/sessions/`) is under `~/.hermes/` which has same protection as Hermes data dir. No world-readable permissions. |
|
||||
| T-08-03 | Information Disclosure | Cron output to Telegram | accept | Report summaries sent to user's DM channel only (TELEGRAM_HOME_CHANNEL). No PII in summaries — session IDs and Jira keys only. |
|
||||
| T-08-04 | Tampering | Session prune via no_agent script | mitigate | DRY_RUN=true by default — no prune until user explicitly sets false. Also `approvals.cron_mode: deny` in config adds an approval gate for destructive cron operations per RESEARCH.md Security Domain. |
|
||||
| T-08-05 | Elevation of Privilege | Jira comment injection | accept | Cron agent uses ngn-jira script which authenticates with JIRA_EMAIL + JIRA_API_TOKEN — bounded to the user's Jira permissions. Comments only, no transitions per D-05/D-16. |
|
||||
| T-08-SC | Tampering | Package installs | n/a | No packages installed — all tools are native Hermes CLI or existing scripts. |
|
||||
</threat_model>
|
||||
|
||||
<verification>
|
||||
1. `test -f ~/.hermes/scripts/archive-stale-sessions.sh && test -x ~/.hermes/scripts/archive-stale-sessions.sh` — script exists and is executable
|
||||
2. `test -d ~/.hermes/archive/sessions` — archive directory exists
|
||||
3. `grep -q DRY_RUN ~/.hermes/scripts/archive-stale-sessions.sh` — dry-run toggle present
|
||||
4. `grep -q 'hermes sessions export' ~/.hermes/scripts/archive-stale-sessions.sh && grep -q 'hermes sessions prune --older-than 30 --yes' ~/.hermes/scripts/archive-stale-sessions.sh` — correct CLI commands used
|
||||
5. `hermes cron list 2>&1 | grep -q ngn-daily-report` — daily report cron job registered
|
||||
6. `hermes cron run ngn-daily-report 2>&1` — test-run succeeds (schedules for immediate execution; verify no errors)
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- Archive script at ~/.hermes/scripts/archive-stale-sessions.sh is executable with DRY_RUN=true default
|
||||
- Archive directory at ~/.hermes/archive/sessions/ exists
|
||||
- Daily report cron job `ngn-daily-report` is registered and visible in `hermes cron list`
|
||||
- Daily report uses correct schedule (0 9 * * *), delivery (telegram), and skills (session + jira-query)
|
||||
- Test-run of daily report cron job completes without CLI errors
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
Create `.planning/phases/08-cron-reporting/08-01-SUMMARY.md` when done.
|
||||
</output>
|
||||
Reference in New Issue
Block a user