Skip to content

docs(register): C-343 registered (Tier 2) and C-317 RESOLVED by live drill - #431

Merged
Polichinel merged 3 commits into
developmentfrom
docs/register-deploy-lag
Aug 10, 2026
Merged

docs(register): C-343 registered (Tier 2) and C-317 RESOLVED by live drill#431
Polichinel merged 3 commits into
developmentfrom
docs/register-deploy-lag

Conversation

@Polichinel

Copy link
Copy Markdown
Contributor

Part of #421. Not a story — a production finding surfaced while sizing #423's blast radius.

What was observed

Four commands on the production host, output pasted by the operator:

~/.views-deploy-tag           v1.11.0
git log -1 --decorate         d72602b (tag: v1.10.0)      <- five days stale
site-packages/views_frames-   1.0.0.dist-info             <- the frozen version C-337 is about
git fetch --tags              * [new tag] v1.11.0         <- the tag was not even present

Three sources of truth disagreeing, for five days, with no error anywhere. The status page was green, the heartbeat was pinging, and the tag file read correctly. v1.11.0 was cut on 2026-08-03 for the sole purpose of raising the views-frames floor — and the floor on the box was still 1.0.0.

Why the in-script checkout does not rescue it — tested, not reasoned

refresh_pipeline.sh runs git checkout "$DEPLOY_TAG" while bash is executing that same file. A throwaway repo was built to find out what bash actually does, at 31 KB — nearly triple the real script's 11,592 bytes:

start: I am V1
TAIL EXECUTED FROM: V1        <- V2 was checked out at line 3 and never executed

Bash buffers the script and does not re-read it. Two consequences:

  1. A change to refresh_pipeline.sh itself takes effect one run later — a month, on a monthly cron.
  2. uv sync is never invoked by the pipeline, so a dependency change never lands, however often it runs.

The cause

Two documents describe deploying, and they contradict:

Source Says
refresh_pipeline.sh header "To deploy a new version: update ~/.views-deploy-tag on the server."
server_quickref.md §Deploy tag file plus git fetch --tags && git checkout <tag> plus uv sync

The wrong one is the one a person reads while inside the file they are editing. Fixed here — the header now says deploying is three steps, and gives both reasons the checkout below cannot substitute.

Tier 2, justified

Not Tier 3: this is not a maintainability cost, it is the server running code nobody believes it is running, with no error at any point. Not Tier 1: it does not itself corrupt data. What it does is silently prevent fixes from landing, converting every future correctness fix into one that may or may not be in effect. The trigger is not hypothetical — it fires at the next release, and #423 ships a refresh_pipeline.sh change, precisely the class that lags.

Blast radius, stated honestly

The first framing of this was "the first fails-green instance with production consequences." That was wrong, and checking before believing it showed why:

git diff v1.10.0..v1.11.0 --name-only   (excluding docs/reports/tests)
  .github/workflows/{ci,release-topology,serving-freshness}.yml
  pyproject.toml
  uv.lock

No src/ file. No pipeline script. Production imports four non-estimator symbols from views_frames (feature_frame.py:10-15). No number was ever wrong, and PyPI consumers were never exposed — the published wheel carried the correct floor throughout. The damage was to what could be claimed, not to what was produced. The corrected framing is what the entry says.

Remediated, and still open

The three documented steps ran live: uv sync moved views-frames 1.0.0 → 1.10.2 and views-datafactory 1.10.0 → 1.11.0, and FeatureFrame was then imported on the host — a ten-minor-version jump nobody has watched import is a belief, not an observation.

The entry stays OPEN. The fix was a human running commands; nothing prevents recurrence. The instrument — a pre-flight check comparing $DEPLOY_TAG against the HEAD the script started from, refusing to run on disagreement (fail-loud, ADR-011) — is proposed for #424 and deliberately not built here.

Verification

  • bash -n scripts/refresh_pipeline.sh → OK
  • ruff check . → All checks passed
  • docs/validate_docs.sh → PASSED
  • pytestexit 0
  • Register invariants: 343 IDs, 41 open, tier sum 41, header 1865 <= 3500, struck-through 115 unchanged, cluster reads "Ten"

🤖 Generated with Claude Code

Polichinel and others added 2 commits August 10, 2026 02:45
Observed on the production host, not read off the repo. The tag file
said v1.11.0, the working tree was at v1.10.0, views-frames was still
the frozen 1.0.0, and `git fetch --tags` reported v1.11.0 as a NEW tag.
Three sources of truth disagreeing for five days with no error anywhere
— and the release in question had been cut for the sole purpose of
raising that views-frames floor.

Two reasons the in-script `git checkout` does not rescue this, tested in
a throwaway repo at 31 KB rather than reasoned about:

  1. bash buffers the script and never re-reads it, so a change to
     refresh_pipeline.sh itself lands one run later — a month, on a
     monthly cron.
  2. `uv sync` is never invoked by the pipeline at all, so a dependency
     change never lands however often it runs.

Tier 2 with the justification stated: not maintainability, but the
server running code nobody believes it is running, with the status page
green and the heartbeat pinging. It does not corrupt data; it silently
prevents fixes from landing. The trigger fires at the next release, and
#423 ships a refresh_pipeline.sh change — exactly the class that lags.

Blast radius on this occasion was small and the entry says so: no src/
file and no pipeline script differs between the two tags, production
imports no views_frames estimator, and the PyPI wheel carried the right
floor throughout. The damage was to what we could claim.

Remediated live: uv sync moved views-frames 1.0.0 -> 1.10.2 and
views-datafactory 1.10.0 -> 1.11.0, and FeatureFrame was imported on the
host afterwards. Entry stays OPEN — nothing prevents recurrence. The
instrument (pre-flight tag-vs-HEAD check, fail-loud per ADR-011) goes to
#424, not here.

Also fixes the contradiction that caused it: the script header said
deploying was one step. It now says it is three, and why.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… alert (#427)

Epic #421 Story 6. The first entry in the fails-green cluster closed by
observation rather than by argument.

PR #359 added a /start ping on the theory that healthchecks.io would flag
a run that began and never finished — the OOM-kill case, where SIGKILL
bypasses both the ERR and EXIT traps so neither the success nor the /fail
ping ever fires. Nobody had watched that happen; the entry has read "OPEN
pending live grace-timeout drill" since July. Closing it because the ping
is sent would have been the cluster's own mistake: the ping firing was
never in doubt.

Drill: throwaway check at 5 min period / 1 min grace so the timeout was
observable in a minute instead of 30 days + 48 hours. Production
HEARTBEAT_URL and the production check untouched. One /start, then
nothing. Prediction recorded before the ping.

Observed: "is DOWN (success signal did not arrive on time, grace time
passed)", Last Ping Type: Started, 03:35:56 +0200. Detection latency for
an OOM kill drops from ~32 days to the grace window. Throwaway deleted.

Two things found that nobody was looking for. healthchecks.io's schedule
dialog documents the mechanism outright — "Grace Time: when a check is
late, OR has received a 'start' signal" — so it was vendor-documented all
along and simply never read. And the sample check reused for the drill
had sat grey, never red, for two months while permanently overdue: a
check that has never been pinged does not alert, so a monitor created and
never wired up is indistinguishable from a healthy one.

Not closed by this: the status page is still not regenerated on SIGKILL
(C-338's territory), and detection is not prevention (C-173).

Three header counts were wrong across three edits — resolved total,
summary-vs-entry 1:1, and struck-through. All three were caught by the
register guards, not by me.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Polichinel Polichinel changed the title docs(register): C-343 — writing the deploy tag is not deploying (Tier 2) docs(register): C-343 registered (Tier 2) and C-317 RESOLVED by live drill Aug 10, 2026
@Polichinel

Copy link
Copy Markdown
Contributor Author

Second commit added: C-317 RESOLVED (#427, epic #421 Story 6)

Kept on this branch rather than a separate PR because both changes rewrite the same header counts; two overlapping PRs on those lines would be worse to review than one.

The drill ran, and the mitigation works

PR #359 added a /start ping on the theory that healthchecks.io flags a run that began and never finished — the OOM-kill case, where SIGKILL bypasses both the ERR and EXIT traps so neither the success nor the /fail ping ever fires. That theory sat unverified since July: "OPEN pending live grace-timeout drill." Closing it because the ping is sent would have been this cluster's own mistake — the ping firing was never in doubt.

Method. Throwaway check, 5 min period / 1 min grace, so the timeout was observable in a minute rather than 30 days + 48 h. Production HEARTBEAT_URL and the production check untouched. One /start, then nothing.

Prediction, recorded before the ping: red within ~1 minute, e-mail arrives.

Observed:

"…is DOWN (success signal did not arrive on time, grace time passed)"
Last Ping Type: Started — 03:35:56 +0200

Last Ping Type: Started is the finding. Detection latency for an OOM kill drops from ~32 days to the grace window. Throwaway deleted; dashboard back to one check, green.

Two things found that nobody was looking for

healthchecks.io documented the mechanism all along. Its own schedule dialog reads "Grace Time — when a check is late, or has received a 'start' signal, how long to wait to send an alert." Weeks of "unverified" was resolvable by reading a tooltip.

A check that has never been pinged does not alert. The sample check reused for the drill had sat grey — never red — for two months while permanently overdue. A monitor created and never wired up is indistinguishable on the dashboard from a healthy one. That is monitoring.md §7 "Silence lies", found by accident.

Not closed by this

The status page is still not regenerated on SIGKILL, so status.html stays stale after an OOM kill until the next run — C-338's territory, not this. And detection is not prevention: C-173 (memory headroom) is what stops the kill.

The guards caught three of my errors

Resolving one entry needs four counts updated. I got three wrong and the register guards caught every one: the resolved total (299 → 300), the summary-row-to-detail-heading 1:1 match (I struck the row and not the heading), and the struck-through count (115 → 116). None was caught by me.

One more worth recording: the first full-suite run after this edit failed, and the background task notification reported "exit code 0" — because the command ended in echo, so the pipeline's exit status was the echo's. Reading the output file rather than trusting the notification is what caught it. Re-run unpiped: PYTEST_EXIT=0.

Verification

  • ruff check . → All checks passed
  • docs/validate_docs.sh → PASSED
  • pytest (exit code captured unpiped) → PYTEST_EXIT=0
  • Register: 343 IDs, 300 resolved, 40 open (0 T1, 3 T2, 11 T3, 20 T4, 6 deferred), 116 struck, header 1912 ≤ 3500, tier sum 40 == open count, cluster now nine

…#431)

/code-review medium and /review-diff on this PR, all findings fixed here.

Two stale counts, both already stale before this PR and made worse by
it: the fails-green cluster prose said "three of the eight" and "cheaper
than eight separate ones" while the table held nine, then ten. That is
C-336 happening inside the cluster section about C-336. The tally is now
qualitative on purpose — a number no guard protects will go stale again
— and C-336 gains the location, rather than a new concern ID.

One false claim of my own: C-343 said `git diff v1.10.0..v1.11.0` touches
"only pyproject.toml, uv.lock and three GitHub workflows". It spans 31
files. I had filtered out docs/reports/tests and then reported the
filtered result as the whole. The load-bearing part — no src/ file, no
pipeline script — is true; the framing was not. Corrected in both the
register and the changelog, in a paragraph whose subject is checking
before believing.

One self-invalidating anchor: the entry cited refresh_pipeline.sh at
11,592 bytes, which this same PR grows to 12,385. Now anchored to
v1.11.0 explicitly, so the number cannot rot.

And two from /review-diff on the shell comment. It claimed "the server
always runs a specific, tested version" eight lines above explaining
that this is the belief C-343 disproves — now scoped to the Python,
with a pointer forward. And it restated the three deploy commands that
already live in server_quickref.md, which is a second copy of the
procedure whose drift IS C-343. The restatement is gone; precedence is
stated instead: if the two ever disagree, the quickref wins.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Polichinel

Copy link
Copy Markdown
Contributor Author

Ritual complete — /code-review medium/review-diff/register-risk

Six findings, all real, all fixed in dd99e17. Four were factual errors in my own prose.

/code-review medium — 4 findings

Two stale counts, and the irony is the point. The fails-green cluster's prose said "three of the eight" and "cheaper than eight separate ones" while the table under it held nine entries, then ten. Already stale before this PR; this PR added C-343 and struck C-317 without correcting it. That is C-336 happening inside the cluster section about C-336. The tally is now deliberately qualitative — a number no guard protects will go stale again.

A false claim in C-343, in the paragraph about checking before believing. The entry said git diff v1.10.0..v1.11.0 touches "only pyproject.toml, uv.lock and three GitHub workflows." It spans 31 files. I had run the command with | grep -vE "^(docs|reports|tests)/" and then reported the filtered result as the whole diff. The load-bearing claim — no src/ file, no pipeline script — is true; the framing was not. Corrected in the register and the changelog.

A self-invalidating anchor. C-343 cited refresh_pipeline.sh at 11,592 bytes — which this same PR grows to 12,385. Now anchored to v1.11.0 explicitly, with the growth noted, so the number cannot rot.

/review-diff — 2 findings, verdict REVIEW (0 critical)

The shell comment contradicted itself in eight lines. Its opening still said "the server always runs a specific, tested version" — the exact belief the paragraph below explains is false. Now scoped to the Python the pipeline invokes, with a forward pointer.

The fix duplicated the procedure it was fixing. It restated the three deploy commands already in server_quickref.md. Two copies of a procedure free to drift apart is what C-343 is; a fix adding a third copy is the same bug. The restatement is gone, replaced by explicit precedence: if this comment and the quickref ever disagree, the quickref wins.

/register-risk — 0 new, 1 merged

The stale-count finding is C-336's mechanism at a new location, so per the dedup rule it became an addendum on C-336 rather than a new ID. Header counts unchanged.

Worth stating plainly, because it generalises: the register's numbers are guarded by five tests; its prose is not. This was caught by an agent counting table rows, not by any check.

Verification

  • bash -n scripts/refresh_pipeline.sh → OK
  • ruff check . → All checks passed
  • docs/validate_docs.sh → PASSED
  • pytest (exit captured unpiped, after the earlier notification-masking incident) → PYTEST_EXIT=0
  • Register: 343 IDs, 300 resolved, 40 open (0 T1, 3 T2, 11 T3, 20 T4, 6 deferred), 116 struck, header ≤ 3500, tier sum == open count

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant