Applies to: Any coordination layer that monitors multiple repos and needs to communicate findings as issues on the appropriate target repo.
When monitoring detects a problem, communicate to the repo that owns the problem — not just to a local log. An issue on the right repo is worth more than a flag in a morning report.
Most monitoring-to-human communication looks like this:
Monitoring script runs → outputs flags → someone reads output → remembers to do something
This breaks at “someone reads output” and again at “remembers to do something.” The gap:
When monitoring detects a threshold breach or state change, it creates (or updates) an issue on the target repo — not on the coordination repo.
Monitor detects problem
↓
Determine target repo (from project mapping)
↓
Check: issue already exists for this problem?
├── NO → Create new issue (action recommendation)
└── YES → Add comment with current state update
↓
Log the communication (who was told, when, what)
An action recommendation is an issue created by the coordination layer on a project repo. It says:
It is NOT:
## [MONITOR] <brief description of finding>
**Detected:** <timestamp>
**Severity:** Critical / Warning / Info
**Pattern:** [link to relevant netstack pattern doc]
### What was detected
<one-paragraph description of what monitoring found>
### Risk
<what happens if this isn't addressed>
### Suggested action
<what the admin should do, with link to the pattern that explains how>
### Verification
Wip will auto-verify using: `<verification check description>`
When this check passes, this issue will be closed automatically.
---
*This issue was created automatically by monitoring. See [status-freshness-cron-pattern](https://netstack.org/docs/ops/monitor/status-freshness-cron-pattern/) for how this works.*
The coordination layer maintains a mapping: project code → repo → admin.
// repo-communication-map (lives in coordination repo)
const REPO_MAP = {
'cf-backup': { repo: '2cld/cf', admin: 'ghadmin@horseoff.com' },
'wf-backup': { repo: '2cld/wf', admin: 'ghadmin@horseoff.com' },
'sl-backup': { repo: '2cld/sl', admin: 'ghadmin@horseoff.com' },
'hwpc-rp': { repo: 'ns-account', platform: 'gitea', admin: 'nsadmin@horseoff.com' },
'netstack-doc': { repo: '2cld/netstack', admin: 'ho-wip' },
'wip-process': { repo: '2cld/wip', admin: 'ho-wip' },
};
Rules for target selection:
.wip-contract.md for admin contact per projectDon’t flood repos with duplicate issues. Before creating:
1. Search target repo for open issues with "[MONITOR]" prefix
2. If matching issue exists:
- Add a comment: "Still detecting this. Current state: <data>. Days open: N."
- If days open > escalation threshold → escalate (see below)
3. If no matching issue:
- Create new issue using template
| Days Open | Action |
|---|---|
| 0 | Issue created, admin notified |
| 3 | Comment added: “still open, gentle reminder” |
| 7 | Comment added: “7 days, needs attention” + mention admin |
| 14 | Escalate: notify higher admin or flag in coordination repo as blocked |
| 30 | Stale: label as stale-monitor, surface in weekly review for decision |
Not every monitoring flag warrants repo communication:
| Situation | Action Instead |
|---|---|
| Transient blip (check fails once, passes next run) | Log only, don’t create issue |
| Known planned downtime | Skip (coordinator marks planned downtime in state file) |
| Problem is already in an active Epic | Comment on existing Epic issue instead |
| Informational (no action needed) | Include in morning report, no issue |
The filter: Only create an issue when the monitoring state has been in warning/critical for >= 2 consecutive checks AND no existing issue covers it.
| Pattern | How this connects |
|---|---|
status-freshness-cron-pattern.md |
The cron triggers this pattern when state transitions to critical |
request-lifecycle.md |
A created issue IS a request. Verification + acknowledgment follow that lifecycle. |
pattern-workflow.md |
The issue links to the netstack pattern doc (action recommendation = “follow this pattern”) |
site-status-page-pattern.md |
Status page shows current state; issue tracks the fix work |
// create-alert-issue.js — called by status-freshness-cron when threshold breaches
async function createOrUpdateIssue(finding) {
const target = REPO_MAP[finding.checkId];
if (!target) { console.log(`No repo mapping for ${finding.checkId}`); return; }
// Search for existing monitor issue
const existing = await searchIssues(target.repo, `[MONITOR] ${finding.title}`);
if (existing) {
// Add status comment
await addComment(existing.number, [
`**Update (${new Date().toISOString()}):** Still detecting this.`,
`Current state: ${finding.status} (${finding.value})`,
`Days open: ${daysSince(existing.created_at)}`,
].join('\n'));
} else {
// Create new issue from template
await createIssue(target.repo, {
title: `[MONITOR] ${finding.title}`,
body: renderTemplate(finding, target),
labels: ['monitor', finding.severity],
});
}
// Log the communication
logCommunication(finding, target, existing ? 'commented' : 'created');
}
When the freshness cron detects recovery (critical → ok):
[MONITOR] issue on the target repoThis completes the cycle: detect → communicate → verify → close → acknowledge.