stats-sync
Sync caezium/stats fork with upstream exelban/stats. Rebases henry/history-mcp onto upstream/master, stops on any conflict, runs the feature-presence audit + Swift tests + Debug build, then halts at a pre-install checkpoint and waits for the user to say "install". Use when the user says "/stats-sync", "sync stats", or asks to update the Stats fork.
- License 许可
- MIT
- 所属仓库
- caezium/skills
- 最近更新
- 2026-08-24
- Stars
- ★ 3
- Forks
- 0
01 前言 Front Matter
- name
- stats-sync
- description
- Sync caezium/stats fork with upstream exelban/stats. Rebases henry/history-mcp onto upstream/master, stops on any conflict, runs the feature-presence audit + Swift tests + Debug build, then halts at a pre-install checkpoint and waits for the user to say "install". Use when the user says "/stats-sync", "sync stats", or asks to update the Stats fork.
- allowed-tools
- Bash,Read,Edit,Write
03 SKILL.md 正文
Stats Sync
Routine for keeping the caezium/stats fork up to date with
exelban/stats. The fork's value is everything in henry/history-mcp:
a long-running history DB, an MCP query server, a SwiftUI History view,
configurable retention/tier policy, reader energy gating, and the test
scaffold. None of that exists upstream — every commit on
henry/history-mcp is load-bearing and must survive every sync.
Conflict policy: stop on every conflict
Unlike dayflow-sync there is no per-commit yaml and no auto-skip class.
The fork has only ~15 commits and they're all features — there's nothing
safe to drop. On any rebase conflict: stop with full context and ask the
user how to resolve.
Pre-install checkpoint
After rebase + audit + tests + Debug build pass, the skill always
stops with a glanceable summary and waits for the user to reply
"install" (or "abort"). It never auto-touches /Applications/Stats.app
because the user's ~/Library/Application Support/Stats/lldb is real
data — multiple gigabytes of their accumulated history — and a botched
install loses it.
Context
- Local clone:
~/Desktop/stats - Fork remote (
origin):https://github.com/caezium/stats - Upstream remote (
upstream):https://github.com/exelban/stats - Upstream's default branch is
master(notmain— it's an older project) - Integration branch:
henry/history-mcp— this is what the user builds from. The fork has no equivalent of dayflow's "main mirrors upstream" pattern; we only track upstream by SHA in state. - State directory:
~/.claude/skills/stats-sync/state/last-synced-upstream.txt— last upstream/master SHA processedlast-synced-date.txtlast-skipped-commits.log— empty in practice (we never skip)
Steps
1. Sanity check
cd ~/Desktop/stats
git remote -v
git status --short
git branch --show-current
df -h ~ | tail -1
Required state before proceeding:
originpoints tohttps://github.com/caezium/stats.gitupstreampoints tohttps://github.com/exelban/stats.git- Working tree clean (no uncommitted changes, no untracked source files —
.claude/worktrees are fine) - Current branch is
henry/history-mcp(if not, ask the user before switching — they may be mid-feature on something else) - Free space on
~≥ 4 GB (Stats's lldb can be 1–2 GB and Xcode needs room for module cache + build_dist)
If the working tree is dirty, stop and ask. If disk < 4 GB, suggest the user clean their Spotify/swiftpm caches; do not delete anything automatically. The Stats lldb is precious.
2. Fetch upstream
cd ~/Desktop/stats
git fetch upstream --prune
git fetch origin --prune
Compare git rev-parse upstream/master against
~/.claude/skills/stats-sync/state/last-synced-upstream.txt.
If equal, nothing changed upstream — tell the user in one line and
exit. No rebase, no audit, no checkpoint.
3. Show what's new upstream
SINCE=$(cat ~/.claude/skills/stats-sync/state/last-synced-upstream.txt)
git log "$SINCE..upstream/master" --oneline --no-merges
git log "$SINCE..upstream/master" --no-merges --shortstat | tail -5
Summarize in plain English ("Upstream added N commits since vX.Y.Z —
mostly bug fixes for the GPU module" / similar). Also note any new tags
(git tag --contains upstream/master minus the previous synced tag).
Don't dump the full log into the user's reading view.
4. Rebase henry/history-mcp onto upstream/master
cd ~/Desktop/stats
git checkout henry/history-mcp
git rebase upstream/master
On any conflict:
- Capture context:
git status # which files CONFLICT_SUBJECT=$(git log -1 --format='%s' REBASE_HEAD) echo "Conflict during: $CONFLICT_SUBJECT" - For each conflicted file, show:
- Our hunks (
<<<<<<<to=======) - Their hunks (
=======to>>>>>>>) - Upstream's nearby intent:
git log upstream/master -p -1 -- <file>
- Our hunks (
- Always stop and surface the conflict to the user. Never
git rebase --skip— every commit onhenry/history-mcpis a feature that must survive. - After the user dictates the resolution:
git add <files>,git rebase --continue. If the rebase chains into another conflict, repeat from (1).
Common conflict files to flag with extra care (these are where the fork's most load-bearing patches live):
Kit/module/reader.swift— base Reader class withtick(),selfPersists,lastReadAt,historyCadenceSeconds,popupClosedIntervalMultiplier. Upstream often re-edits this.Modules/CPU/readers.swift— FrequencyReader Taskdeferfix.Modules/Net/main.swift+Modules/Net/readers.swift—selfPersists: truewiring on Net readers + setup() overrides for multiplier=1.Stats/Views/AppSettings.swift— History storage UX.Kit/plugins/DB.swift(entirely fork-introduced, conflicts unlikely unless upstream adds something at the same path).
If git rebase --abort becomes necessary, do it and stop. Don't try
again with -Xours/-Xtheirs — wrong-default merges silently destroy
features.
After a clean rebase:
git push origin henry/history-mcp --force-with-lease
5. Feature-presence audit
Every grep below must match. If any FAILS, a patch got lost during the rebase — stop and report which one. Don't push, don't proceed.
| Feature | Verify by |
|---|---|
| History DB exists | [ -f Kit/plugins/DB.swift ] |
| NetTierPolicy + tier configurability | grep -q "public struct NetTierPolicy" Kit/plugins/DB.swift |
| Drop sentinel for past-hour-tier rows | grep -q "dropSentinel" Kit/plugins/DB.swift |
| LLDB compactRange | grep -q "compactRange" Kit/lldb/lldb.h && grep -q "CompactRange" Kit/lldb/lldb.m |
| Hourly maintenance calls compactRange | grep -q "compactRange()" Kit/plugins/DB.swift |
| ttlDaysOverride test hook | grep -q "ttlDaysOverride" Kit/plugins/DB.swift |
| QueryServer / MCP | [ -f Kit/plugins/QueryServer.swift ] && grep -q "case \"/info\"" Kit/plugins/QueryServer.swift |
| QueryServer staleness | grep -q "age_seconds" Kit/plugins/QueryServer.swift |
| History view | [ -f Stats/Views/HistoryView.swift ] |
| History view: 30 d / 90 d range chips | grep -q '"30d"' Stats/Views/HistoryView.swift && grep -q '"90d"' Stats/Views/HistoryView.swift |
| Stride-sampled top-N | grep -q "findTimeSeriesSampled" Stats/Views/HistoryView.swift |
| Staleness warning chip | grep -q "staleReaderCount" Stats/Views/HistoryView.swift |
| Reader tick gating | grep -q "popupClosedIntervalMultiplier" Kit/module/reader.swift && grep -q "internal func tick" Kit/module/reader.swift |
| Reader 60 s history cadence | grep -q "historyCadenceSeconds" Kit/module/reader.swift |
| Reader selfPersists flag | grep -q "selfPersists" Kit/module/reader.swift |
| Reader lastReadAt diagnostics | grep -q "lastReadAt" Kit/module/reader.swift |
| FrequencyReader defer fix | grep -q "defer { self.isReading = false }" Modules/CPU/readers.swift |
| Net selfPersists wiring | grep -q "selfPersists: true" Modules/Net/main.swift |
| Net readers opt out of gate | grep -q "popupClosedIntervalMultiplier = 1" Modules/Net/readers.swift |
| History storage settings UX | grep -q "Keep detailed data for" Stats/Views/AppSettings.swift && grep -q "Keep summary data for" Stats/Views/AppSettings.swift |
| Option lists | grep -q "HistoryDetailedDaysOptions" Kit/types.swift && grep -q "HistorySummaryDaysOptions" Kit/types.swift |
| Test scaffold present | [ -f Tests/HistoryDB.swift ] |
| ReaderTickTests present | grep -q "final class ReaderTickTests" Tests/HistoryDB.swift |
Run these as a single bash block; collect failures into a list. Any non-zero exit means a patch got lost in the rebase.
6. Run the Swift tests
cd ~/Desktop/stats
xcodebuild -project Stats.xcodeproj -scheme Stats \
-configuration Debug -destination 'platform=macOS' \
-derivedDataPath /tmp/stats-sync-build \
CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO \
test 2>&1 \
| grep -E "error:|Test Case.*(failed)|TEST (SUCCEEDED|FAILED)|Executed [0-9]+ test" \
| tail -15
Expected: ** TEST SUCCEEDED ** with all tests passing (currently 27
total: 17 HistoryDBTests + 3 RAMTests + 7 ReaderTickTests; this number
will grow over time).
Note the trailing ** TEST FAILED ** line that sometimes appears
after a successful test run is from the test host binary trying to
open the live lldb while running Stats holds its LOCK. That's a
pre-existing nuisance, not a real failure — verify by the
Executed N tests, with 0 failures count just above it.
If real test failures appear: stop, don't push, don't install. Show the failure to the user.
7. Debug build (final verification before the install checkpoint)
cd ~/Desktop/stats
xcodebuild -project Stats.xcodeproj -scheme Stats \
-configuration Debug -destination 'generic/platform=macOS' \
-derivedDataPath /tmp/stats-sync-build \
CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO \
build 2>&1 \
| grep -E "error:|BUILD (SUCCEEDED|FAILED)" \
| tail -10
Expected: ** BUILD SUCCEEDED **. The test step (6) already builds
everything but a separate build invocation catches errors that only
surface for the non-test target.
If the build fails:
- Errors in files our patches touched → patch needs updating. Stop.
- Errors in files only upstream touched → upstream regression in this release. Tell the user; usually means waiting for the next upstream patch or porting a workaround.
8. Pre-install checkpoint
Always stop here. Do NOT proceed to step 9 unless the user replies
"install" or similar. This is the hard gate before
/Applications/Stats.app is touched.
Present a glanceable summary in this shape:
Stats sync — ready to install? (<upstream version tag>, <N> upstream commits)
Rebase: clean / <N> conflicts manually resolved
Conflicts on: <file paths if any>
Audit (~22 grep checks): all pass / N failed
Tests: ** TEST SUCCEEDED ** — <N> executed, 0 failures
Debug build: ** BUILD SUCCEEDED **
What changed upstream:
- <one bullet per upstream commit, summarized — e.g. "GPU fan curve fix">
- <if there's an upstream version tag in this range, name it>
Currently installed: <bundle version of /Applications/Stats.app, if installed>
Will be installed: Debug build from /tmp/stats-sync-build
Reply "install" to proceed with quit-Stats + install-to-/Applications
+ live-endpoint verify.
Reply "abort" to leave the rebased branch on the remote without
touching the live app.
If anything looks off (audit failure, suspicious upstream commit touching reader/DB code), explicitly recommend "abort" or "investigate first" rather than "install".
9. (On user "install") Quit + install + verify
Run only after the user explicitly says "install".
# 1. Quit currently-running Stats
osascript -e 'tell application "Stats" to quit' 2>/dev/null
sleep 3
pkill -9 -f Stats.app/Contents/MacOS/Stats 2>/dev/null || true
sleep 1
# 2. Replace /Applications/Stats.app
rm -rf /Applications/Stats.app
cp -R /tmp/stats-sync-build/Build/Products/Debug/Stats.app /Applications/Stats.app
xattr -cr /Applications/Stats.app
# 3. Launch
open /Applications/Stats.app
sleep 6
PID=$(pgrep -fl Stats.app/Contents/MacOS/Stats | head -1 | awk '{print $1}')
echo "Stats running: pid=$PID"
# 4. Free build cache
rm -rf /tmp/stats-sync-build
Note: no data backup is needed. The lldb format is forward-compatible
within this fork's schema — every commit on henry/history-mcp preserves
the on-disk layout. If a future commit ever changes that, add a backup
step here before it ships.
Verify the live endpoints (gives confidence the install survived):
sleep 4 # let QueryServer come up
curl -s http://127.0.0.1:9276/health
curl -s http://127.0.0.1:9276/info \
| python3 -c "
import sys, json
d = json.load(sys.stdin)
readers = d.get('readers', [])
stale = [r for r in readers if r.get('age_seconds') is not None and r['age_seconds'] > 300]
print(f'prefixes={len(d[\"prefixes\"])}, readers={len(readers)}, stale_at_startup={len(stale)}')
"
Expected:
/healthreturns{"ok":true,"app":"Stats","port":9276}/inforeturns at least 10 prefixes;stale_at_startupmay be a few (some readers don't fire until a popup-open event), no warning yet.
If /health fails to respond after 8 seconds total, stop and report —
the QueryServer didn't come up. Probable causes: port collision, the
build is missing the QueryServer file (which would have failed step 5),
or auto-update tried to validate the install and bailed (the unsigned
build doesn't carry a team ID — see the alert message from past
sessions).
10. Bump state markers
After successful end-to-end run (install succeeded + endpoints verified):
git -C ~/Desktop/stats rev-parse upstream/master \
> ~/.claude/skills/stats-sync/state/last-synced-upstream.txt
date +%Y-%m-%d > ~/.claude/skills/stats-sync/state/last-synced-date.txt
State marker advances ONLY after install succeeds AND endpoints respond. Aborts, conflicts, audit failures, test failures, build failures, and QueryServer no-shows all leave the marker at the previous SHA so the next run retries from the same base.
11. Final report
Tell the user, under 200 words:
- Upstream version + N commits merged
- Conflicts: how many, which files (if any)
- Audit: pass/fail
- Tests: pass count
- Build: succeeded
- Install: bundle version, QueryServer responsive at the expected port
- Anything notable in the upstream changes (e.g. "GPU module gained a new metric — visible in your charts next time the GPU module is reloaded")
Failure modes
- Working tree dirty → ask the user before anything.
- Not on
henry/history-mcp→ ask, don't auto-switch. - Disk < 4 GB → stop, suggest cache cleanup, don't auto-delete.
- Rebase conflict → stop with full context, never
--skip. - Feature-presence audit failure → stop, name the missing patch.
- Test failures → stop, don't push, surface the failure.
- Build failure → stop, don't push, classify (our files vs upstream).
- No upstream changes → exit early, one-line report.
- Pre-install checkpoint → always stops here; not a failure.
/healthno-show after install → stop, report; the install binary may be broken or port-collided.
04 同仓库更多 Skills
writing-for-agents
Writing documents for agents. Use when creating or editing skills, or modifying AGENTS.md or CLAUDE.md.
wrangler
Cloudflare Workers CLI for deploying, developing, and managing Workers, KV, R2, D1, Vectorize, Hyperdrive, Workers AI, Containers, Queues, Workflows, Pipelines, and Secrets Store. Load before running wrangler commands to ensure correct syntax and best practices. Biases towards retrieval from Cloudflare docs over pre-trained knowledge.
workers-best-practices
Reviews and authors Cloudflare Workers code against production best practices. Load when writing new Workers, reviewing Worker code, configuring wrangler.jsonc, or checking for common Workers anti-patterns (streaming, floating promises, global state, secrets, bindings, observability). Biases towards retrieval from Cloudflare docs over pre-trained knowledge.
wizard
Generate an interactive bash wizard that walks a human through steps only they can perform. Use when provisioning infrastructure, setting up credentials or CI secrets, walking an unfamiliar third-party dashboard, or running a one-off migration or cutover. Don't invoke this for steps the agent can perform itself.
why
Use for 'why does X work this way', 'why we picked Y', design rationale, regressions, postmortems, or data-backed thresholds. Discovers available evidence sources, searches each relevant category, and returns a cited read on decisions and tradeoffs. Use how for runtime behavior.
week-prep
Generate a briefing with calendar, tasks, and attention flags. Use for "week prep", "briefing", or "what's on this week".