-
Notifications
You must be signed in to change notification settings - Fork 1
198 lines (186 loc) · 10.1 KB
/
Copy pathcontext-cadence.yml
File metadata and controls
198 lines (186 loc) · 10.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
name: context-cadence
# Weekly measurement of the agent-context surface (#118). Records one `baseline`
# telemetry row so regrowth, budget adherence and seam accrual accumulate as a
# per-repo series.
#
# It does NOT curate. Curation needs judgement — classify each section, verify
# each claim, decide what relocates where — and that stays agent-triggered,
# prompted by what these rows show. Rationale and the annotated template:
# curating-context/references/cadence.md
#
# Generated by install-cadence.sh. Re-run it rather than editing by hand.
#
# REQUIRES the ANTHROPIC_API_KEY repository secret. Without it --exact degrades
# to an offline estimate and record-telemetry.sh refuses the append, so the job
# records NOTHING, silently, every week. The credential is preflighted first.
on:
schedule:
- cron: '52 17 * * 5'
workflow_dispatch:
# contents: write because the job appends one JSONL line and pushes it. That is
# append-only telemetry, not code.
permissions:
contents: write
concurrency:
group: context-cadence
cancel-in-progress: false
jobs:
measure:
runs-on: ubuntu-latest
timeout-minutes: 10
# NOT continue-on-error. A red run here means "this repo is not measuring",
# which is true and worth seeing — the credential preflight exists to make
# that failure loud, and continue-on-error would restore the silence at the
# one level a human actually looks at. Drift is reported as ::warning::
# below and never fails the job, so red always means the mechanism broke,
# never that the surface grew.
steps:
# submodules: recursive is load-bearing — the skill is vendored under
# skills-vendor/ and reached through a symlink, which dangles without it.
# fetch-depth: 0 because the push path rebases when a human commit lands
# during the measurement, and rebasing on a depth-1 clone lacks the history
# to replay onto.
# @v5, not @v4: v4 targets Node 20, which runners now force onto Node 24
# with a deprecation warning on every run. Twelve repos are about to adopt
# this template, and sweeping twelve for a warning we could have not
# written is the avoidable version of the problem (#163).
- uses: actions/checkout@v5
with:
submodules: recursive
fetch-depth: 0
- name: Heal vendored symlinks
run: '[ ! -x .skills/doctor.sh ] || bash .skills/doctor.sh'
- name: Resolve the skill scripts
run: |
N=curating-context S=measure-context.sh SD=
for d in scripts ".claude/skills/$N/scripts" "skills/$N/scripts"; do
[ -f "$d/$S" ] && { SD="$d"; break; }
done
echo "SKILL_SCRIPTS=${SD:?curating-context scripts not found}" >>"$GITHUB_ENV"
# FIRST, not last: without a credential every later step does its work and
# the append is refused at the end.
- name: Preflight the credential
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
run: bash "$SKILL_SCRIPTS/measure-context.sh" --check-credential
# Exits 3 when there are new seams, which is a finding rather than a
# failure here — the count goes on the row either way.
#
# --base-ledger, NOT --base HEAD. On a clean checkout the policy file at
# HEAD and the one in the working tree are the same content, so the diff
# is empty and the one class that needs a base — moved-title — was zero
# in every scheduled run, in every repo, forever (#169). The ledger's
# newest repo_commit is the previous measurement, so the sweep spans the
# interval since it. With no such row the report SAYS the interval is
# empty rather than presenting a standing count as a week's accrual.
- name: Sweep the seams
run: |
bash "$SKILL_SCRIPTS/check-seams.sh" --base-ledger ".skills/context-metrics.jsonl" >/tmp/seams.txt 2>&1 || true
tail -20 /tmp/seams.txt
echo "SEAMS=$(sed -n 's/^seams: \([0-9]*\)$/\1/p' /tmp/seams.txt | tail -1)" >>"$GITHUB_ENV"
echo "SEAMS_ACKED=$(sed -n 's/^seams_acked: \([0-9]*\)$/\1/p' /tmp/seams.txt | tail -1)" >>"$GITHUB_ENV"
# Measured ONCE — the drift report below reads this file rather than
# re-running --exact, which would disagree with the row just recorded.
- name: Measure and record
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
run: |
bash "$SKILL_SCRIPTS/measure-context.sh" --exact >/tmp/ctx.json
bash "$SKILL_SCRIPTS/record-telemetry.sh" --baseline=scheduled \
--ledger ".skills/context-metrics.jsonl" \
${SEAMS:+--seams "$SEAMS"} ${SEAMS_ACKED:+--seams-acked "$SEAMS_ACKED"} \
--print-trend </tmp/ctx.json
- name: Commit the row
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
# Neither merge driver is a git built-in and the runner is a fresh
# clone, so without these two lines the .gitattributes entries are
# inert and the calibration files conflict as if unprotected (#192).
# `true` keeps the branch's ratio; the next --exact recomputes (#173).
git config merge.ours.driver true
# The counts file merges per row (#237): a collision keeps, per path,
# the row whose bytes match the file in the tree.
git config merge.context-counts.driver "bash \"$SKILL_SCRIPTS/merge-token-counts.sh\" %O %A %B %P"
# Staged separately, and the row is NOT tolerant of failure. One
# `git add` over both paths stages NOTHING when either is missing —
# it exits 128 on the unmatched pathspec — so `|| true` turned a
# missing ratio file into "no new row" and discarded the measurement
# silently, which is the failure this whole job exists to prevent.
git add -- ".skills/context-metrics.jsonl"
if [ -f .skills/context-token-ratio ]; then
git add -- .skills/context-token-ratio
fi
# The per-file calibration the same --exact run refreshes (#145).
# Unstaged it would be recomputed and discarded every week, and the
# estimators between runs would stay on the repo-wide ratio forever —
# a feature that writes a file nobody ever commits is a feature that
# does not exist.
if [ -f .skills/context-token-counts ]; then
git add -- .skills/context-token-counts
fi
if git diff --cached --quiet; then
echo "no new row — nothing to commit"
exit 0
fi
git commit -m "chore: weekly context measurement"
# A human push landing during the measurement makes this a
# non-fast-forward. The ledger is append-only JSONL, so rebasing is
# safe by construction; without it the week's row is simply lost.
# GITHUB_REF_NAME is authoritative; git branch --show-current is
# empty on a detached HEAD and `git push origin ""` fails opaquely.
BRANCH="${GITHUB_REF_NAME:-$(git branch --show-current)}"
# --- push ---
for attempt in 1 2 3; do
git push origin "HEAD:$BRANCH" && exit 0
echo "push rejected (attempt $attempt) — rebasing onto origin/$BRANCH"
# A failing rebase is fatal and must SAY so. As a bare command under
# bash -e it killed the step before this loop could retry or reach
# the error line below, making "3 attempts" really one.
git pull --rebase --autostash origin "$BRANCH" || {
echo "::error::rebase onto origin/$BRANCH failed — the row was not pushed."
# Name the file that actually conflicted rather than asserting it
# was the ledger. Blaming a ledger attribute that is present and
# correct sent the reader to the one file that was protected,
# while the calibration files were the unprotected ones (#173).
#
# \\` — escaped for BOTH layers. A single \` renders a live
# backtick into the workflow, where bash runs the attribute line
# as a command and the substitution eats the very filename this
# message exists to name (#171). The seams ::warning:: below has
# always had this right; this line did not.
git diff --name-only --diff-filter=U | sed 's/^/::error:: conflicted: /'
echo "::error::Re-run install-cadence.sh, then confirm with --check that"
echo "::error::every staged path carries a merge attribute:"
echo "::error:: \`.skills/context-metrics.jsonl merge=union\`"
echo "::error:: \`.skills/context-token-ratio merge=ours\`"
echo "::error:: \`.skills/context-token-counts merge=context-counts\`"
exit 1
}
done
echo "::error::could not push the measurement row after 3 attempts"
exit 1
# always(), so a failed push does not swallow the warnings. Those are the
# only output a human reads, and losing them on exactly the runs that went
# wrong inverts the intent.
- name: Report drift
if: always()
run: |
# always() makes this reachable when the measurement never ran — the
# missing-credential case, which is exactly the failure this design
# exists to make legible. A FileNotFoundError stacked on top of the
# real preflight error helps nobody.
if [ ! -f /tmp/ctx.json ]; then
echo "no measurement was taken — see the failing step above"
exit 0
fi
python3 - /tmp/ctx.json <<'PY'
import json, sys
p = json.load(open(sys.argv[1]))["policy"]
if p["over_budget"]:
print(f"::warning::{p['path']} is {p['tokens']} tokens against a "
f"{p['budget']} budget. Run `curate context` in this repo.")
PY
if [ "${SEAMS:-0}" -gt 0 ]; then
echo "::warning::$SEAMS unacknowledged cross-reference seam(s). Run \`curate context\`."
fi