| name | Note Graph |
| description | Regenerate notegraph.json + docs/notegraph.md + docs/notegraph.html from memory/ and docs/, PR + notify only when the graph changes |
| var | |
| tags | ["meta","docs"] |
Today is ${today}. Regenerate the navigable link graph over memory/ + docs/. Skip notify and PR when nothing changed — silence on a stable corpus is the goal.
Steps
1. Fingerprint inputs and check for change
Build a fingerprint of every markdown file the extractor will scan (same set: memory/**/*.md + docs/**/*.md, excluding memory/logs/):
{
find memory docs -name '*.md' \
-not -path 'memory/logs/*' \
-not -path '*/node_modules/*' \
-not -path '*/.git/*' \
| sort | xargs sha1sum
sha1sum scripts/notegraph.mjs
} | sha1sum | awk '{print $1}' > /tmp/notegraph.fingerprint
Compare against memory/state/notegraph.json (key input_fingerprint). If identical:
- Exit silently. No notify. No PR. No file rewrite. Log a single line:
notegraph: no input change, skipping.
2. Run the extractor
node scripts/notegraph.mjs
The script writes:
notegraph.json
docs/notegraph.md
docs/notegraph.html
It prints a one-line summary like notegraph: 38 nodes · 147 hard · 12 soft · 1 orphans · 0 bundled — capture this for the notify message. bundled counts atomic notes (under memory/notes/, excluding daily/) whose body scores ≥3 on the bundle scorer — candidates for the reflect atomicity pass to split.
3. Detect meaningful change
Diff the generated notegraph.json against HEAD:notegraph.json. Compute:
node_delta = current stats.nodes − previous stats.nodes
edge_delta = current stats.edges − previous stats.edges
orphan_delta = current stats.orphans − previous stats.orphans
bundled_delta = current stats.bundled − previous stats.bundled
new_orphans = nodes with inDegree==0 && outDegree==0 in current that weren't orphans in previous
resolved_orphans = nodes that were orphans in previous but aren't now
new_bundled = nodes with bundled==true in current that weren't bundled in previous
resolved_bundled = nodes that were bundled in previous but aren't now
Set verdict_one_line in priority order:
new_bundled.length > 0 → ${new_bundled.length} new bundled note(s): ${new_bundled[0].id}…
new_orphans.length > 0 → ${new_orphans.length} new orphan(s): ${new_orphans[0]}…
node_delta > 0 and orphan_delta <= 0 and bundled_delta <= 0 → +${node_delta} notes wired in
edge_delta > 10 → +${edge_delta} new edges
- otherwise →
graph refreshed (${current.stats.nodes}n / ${current.stats.edges}e / ${current.stats.bundled}b)
If git diff --quiet notegraph.json docs/notegraph.md docs/notegraph.html reports no diff (extractor output deterministic), exit silently — update the fingerprint state, no PR.
4. Open PR
git checkout -b notegraph/${today} 2>/dev/null || git checkout notegraph/${today}
git add notegraph.json docs/notegraph.md docs/notegraph.html
git commit -m "notegraph: ${verdict_one_line}"
git push -u origin notegraph/${today}
gh pr create \
--title "notegraph: ${verdict_one_line}" \
--body "Auto-regenerated by the \`notegraph\` skill on ${today}.
Stats: ${current.stats.nodes} nodes · ${current.stats.hard} hard · ${current.stats.soft} soft · ${current.stats.orphans} orphans · ${current.stats.bundled} bundled
Delta: ${node_delta:+0} nodes, ${edge_delta:+0} edges, ${orphan_delta:+0} orphans, ${bundled_delta:+0} bundled
$( [ -n \"$new_orphans\" ] && echo \"**New orphans:** $new_orphans\" )
$( [ -n \"$resolved_orphans\" ] && echo \"**Resolved orphans:** $resolved_orphans\" )
$( [ -n \"$new_bundled\" ] && echo \"**New bundled atomic notes:** $new_bundled (split via reflect skill)\" )
$( [ -n \"$resolved_bundled\" ] && echo \"**Resolved bundled:** $resolved_bundled\" )
Interactive view: \`docs/notegraph.html\`. Static map: \`docs/notegraph.md\`."
5. Persist state and notify
Write memory/state/notegraph.json:
{
"input_fingerprint": "<from step 1>",
"last_run": "${today}",
"stats": { ... current.stats ... },
"last_verdict": "${verdict_one_line}",
"last_pr": "${pr_url}"
}
Notify (concise, one line):
./notify "*Notegraph updated* — ${verdict_one_line}. PR: ${pr_url}"
Skip the notify if verdict_one_line is the bland graph refreshed (…) form and there are no new/resolved orphans — those updates aren't worth a ping.
Sandbox note
This skill is pure local work — no outbound network from bash. node scripts/notegraph.mjs reads filesystem only, and gh pr create handles its own auth. No prefetch or post-process needed.
Exit modes
NOTEGRAPH_NO_CHANGE — fingerprint matched, silent exit (the common case on a stable corpus)
NOTEGRAPH_OK — regenerated + PR opened; notify only if verdict is interesting
NOTEGRAPH_NEW_ORPHAN — at least one new orphan detected; always notify
NOTEGRAPH_NEW_BUNDLED — at least one new bundled atomic note detected; always notify (operator or reflect should split)
NOTEGRAPH_ERROR — extractor crashed or git op failed; notify with the error and exit non-zero