A reader dates a book by the book. An inline 截至 <date> or (核验日期:<date>)
tells them nothing they cannot infer, and it decays: this cluster's clearest
case is the eight files that carried 截至 2026-05-18 while git showed they were
written 33 days earlier, one commit having later nudged the date a day for
visual consistency.
Two kinds of date deliberately survive, because they are not as-of stamps:
- Forward-looking deadlines. "Sonnet 5 的 $2/$10 是截至 2026-08-31 的介绍价"
states when a price expires; deleting that date would lose a fact.
- Ledger metadata. verified_at / expires_at / ttl_days in the volatile-facts
appendices are inputs to check_volatile_facts(), which fails the build when
they lapse. That is the right home for a verification date — machine-checked
and self-expiring, rather than prose nothing re-earns.
Event dates are untouched throughout (2026-06-09 GA, 2026-06-12 暂停). Where a
sentence needed the sequence, "截至 2026-07-09 官方模型页已恢复" became
"官方模型页此后已恢复", which keeps the ordering without the observation date.
FENCE_RE was `^\s*(`{3,}|~{3,})`, which matches leading whitespace but not
a blockquote marker. A fence written as `> ```python` was therefore never
recognised, so the quoted code was scanned as prose and CJK-adjacent `**`
inside it was reported as non-rendering emphasis. Since the emphasis check
became a blocking CI gate, that false positive could red-line a legitimate
push over a code sample in a callout.
This file documents itself as mirroring check 6 of the workspace-level
format_checker.py, whose FENCE_RE already handles up to three spaces of
indent plus any number of `>` prefixes. Adopting that pattern restores the
stated invariant.
Demonstrated before/after on a file holding the same line both inside and
outside a blockquoted fence: previously only the quoted copy was flagged
(exit 1); now neither is (exit 0), while a genuine prose defect is still
reported (exit 1), so the gate is not weakened. All 14 repos re-checked:
check_emphasis and check_project_rules pass on all 1,615 markdown files,
and the 14 copies remain byte-identical. A mutation-tested regression guard
now lives in the workspace repo at tests/test_blockquote_fence_emphasis.py.
The step ran `check_project_rules.py && check_emphasis.py` with more
commands on following lines. Under `bash -e` the left operand of `&&`
is an exempt context, so a rules failure did NOT abort the step: it
short-circuited past check_emphasis.py, execution continued to the next
line, and the step exited with that last command's status -- green.
Demonstrated:
bash -e -c 'false_check && emphasis
next_cmd' -> exit 0 (failure swallowed)
bash -e -c 'false_check
emphasis
next_cmd' -> exit 1 (correctly blocks)
So the repo's main content gate could fail while CI stayed green, and
today's new CJK emphasis gate was skipped entirely whenever it did.
Splitting the line restores fail-fast for both.
Swept all 14 repos: only this one and the other CI whose && line is
followed by further commands were affected; the 12 where the && list is
the last line of its run block already exit non-zero and are unchanged.
Verified: YAML parses, both checkers pass, repo test suite unchanged.
check_project_rules.py only checks fences + local links, so a bold marker that
CJK punctuation stops from rendering (e.g. `**(配比)**相互交织` — the closer is
preceded by punctuation and followed by a letter, so it can't close and the
asterisks print literally) would pass this repo's own CI. That class of bug had
to be swept out of every book by hand; nothing here was watching for it.
Adds check_emphasis.py — a standalone gate (identical across all books) that
runs CommonMark's flanking + process-emphasis rules and flags markers that
print literally or silently bold the wrong span. The ci.yaml content step now
runs it right after check_project_rules.py. Verified clean on this book.
The reason 640 collapsed lists shipped green is that no test exercised real
markdown->HTML rendering — the existing build_html_reader tests stub pandoc with
a fake, covering TOC/mermaid plumbing but never the markdown parse. This feeds
the exact bold-lead-in pattern through the real pandoc invocation the reader uses
and asserts the list survives as <ul>. Proven both ways: passes with the
extension, fails when it is removed.
The book style writes a bold label then a list with no blank line between them:
**触发分离的条件**:
- 输入序列长度 > 某阈值
Strict CommonMark (pandoc default -f markdown) requires a blank line before a
list, so it folded every such list into a single <p>. Measured 640 occurrences
across 13 books — troubleshooting checklists and diagnostic steps shipped in the
offline HTML reader as run-on paragraphs, while GitBook, GitHub, and the PDF all
rendered them correctly. The source was fine; only this reader was wrong.
Fix is the additive pandoc extension markdown+lists_without_preceding_blankline,
verified on real book content through real pandoc: the three bullets that
previously joined into one <p> now produce <ul><li>, with no effect on ordinary
paragraphs or on tables (pipe_tables stays enabled).
Neither step did what its name said, and together they meant no Dependabot PR
was ever auto-merged — the job died before reaching the merge step.
1. "Confirm required checks are configured" failed OPEN. It read branch
protection, which GITHUB_TOKEN cannot do (administration scope is not even
a valid permissions key), so gh api returned 403 JSON into REQUIRED and
`[ "$REQUIRED" -eq 0 ]` died with "integer expression expected". A failing
test inside an `if` condition is exempt from set -e, so the guard evaluated
false and let execution continue. A guard whose whole purpose is refusing
an unsafe merge silently passed whenever it could not check.
2. "Approve low-risk Dependabot PR" can never succeed: GitHub Actions is not
permitted to approve pull requests. It is also unnecessary — these repos do
not require reviews.
The real gate is now branch protection, which as of today requires BOTH
check-commit-identity and CI on every repo, and gh pr merge --auto cannot
complete until they pass. That is stronger than the removed probe, which only
checked that some required check existed.
This makes the 13 repos match oc_guide, which already had exactly this shape
and was the only one whose auto-merge was not broken.
prompt_engineering_guide asserted the old step ORDER; its test now asserts the
invariants that still hold — no self-approve attempt, and auto-merge gated on
the low-risk update-type allowlist.
The build script anticipated an older mdPress behaviour where a shared
--output made the site land in a sibling directory, then moved it back:
mdpress build --format site --output _site && rm -rf _site && mv _site_site _site
mdPress 0.8.1 writes straight into --output, so the sibling never appears and
the mv aborts the step:
✓ Generated site → .../docker_practice/_site/index.html
mv: cannot stat '_site_site': No such file or directory
Caught by a smoke test after 0.8.1 shipped, before any content push fanned it
out — this repo is the only one with a live copy of the workaround (the three
matches in other repos are inside gitignored _book/ build artifacts).
The site itself built fine; only the post-processing dance failed.
The mdPress PDF step drives headless Chrome, which intermittently dies with
"websocket url timeout reached" plus dbus/bus.cc:405 errors. Confirmed by the
verbatim signature in six runs across five repos on 2026-07-22 and 07-23 — most
recently it failed a docker_practice smoke test after mdPress had already
finished parsing 196 chapters and assembling the HTML, dying only at step 5/5.
It is infrastructure, not content: a rerun clears it every time.
oc_guide already solved this with tools/build_pdf.py (bounded retries, process-
scoped timeout cleanup, per-attempt logs) and is notably absent from the flake
victims. This applies the same idea to the other 13 repos with a much smaller
change: shadow mdpress with a retry function once per run block, so every call
shape — including the multi-line continuations — is covered without rewriting
42 heterogeneous call sites. `command mdpress` reaches the real binary.
Fails closed: after 3 attempts the function returns 1 and errexit fails the
step. Verified all three paths against a fake mdpress before rollout — first-try
success, success after two flakes, and 3x failure aborting the step without
printing the following command.
oc_guide is untouched (it has no `mdpress build` call). Converging the other 13
onto its richer build_pdf.py remains the better long-term shape.
CommonMark only treats a run of asterisks as an emphasis delimiter when it is
left/right-flanking. CJK punctuation counts as Unicode punctuation, so a closer
like `**数据(配比)**相互交织` is preceded by punctuation and followed by a
letter — it cannot close, and the page shows literal asterisks instead of bold.
Inserts the single space that book-rules 1.1 already asks for, on whichever
side was failing: 2 places across 2 file(s). No wording changed —
every diff here is whitespace-only. Verified against pandoc's gfm reader:
each span now renders as its own <strong>.
Per the author: mdPress should default to the latest release.
The cluster had drifted to three versions (0.7.10 x4, 0.7.11 x9, 0.7.14 x1),
and nine of those were pinned to bytes upstream had rebuilt under an existing
tag — the exact thing the harness move to 0.7.14 refused to do on principle.
Pinning also took CI down cluster-wide twice when a tag was rebuilt, because
the digest no longer matched. Latest is now 0.7.15; nothing was on it.
Each of the 43 install sites now resolves the newest release at build time by
following the /releases/latest redirect (no api.github.com call, so no
unauthenticated rate limit on shared runner IPs) and exports the version via
GITHUB_ENV.
Integrity is kept, not dropped: the archive is verified against that same
release's published checksums.txt, and a missing entry aborts the step rather
than passing silently. The honest trade-off is that this verifies the download
rather than pinning an immutable artifact — a rebuilt release is now followed
instead of failing the build. That is the intended behaviour here, since the
rebuild breakage was the problem being solved and upstream is the same author.
Tests updated in step: 12 suites asserted the literal MDPRESS_SHA256 as a
proxy for "this download is checksum-verified". They now assert checksums.txt,
which is where that guarantee lives.
An audit found the guard could miss commits it exists to catch.
1. SIGPIPE. Three inner checks used `printf ... | grep -q` under
`set -euo pipefail`. When grep -q matches early it exits, printf dies of
SIGPIPE, pipefail turns the pipeline non-zero, the `if` goes false, and the
offending commit is silently not reported — the guard then prints its
success line. This file already documents the hazard in a comment and had
fixed it for the two outer pre-filters; the three inner ones were left as
pipes. Now here-strings, like the rest.
Demonstrated on a synthetic revert-style commit (an AI trailer quoted near
the top, then a 200KB body): the pipe form MISSES it, the here-string form
detects it. A trailer in the conventional last position happens to survive
the pipe form, because grep must read to the end before matching — which is
why this never showed up in practice.
2. `chatgpt` was in BAD_NAME and BAD_ANY but not BAD_TRAILER, so a
Co-authored-by naming ChatGPT with an email outside anthropic.com/openai.com
was not caught. Confirmed by test, then added.
Verified before pushing this time: the amended script flags an AI-authored
commit, a large-body early trailer, and a ChatGPT trailer, and scans
claude_guide's full 345-commit history with zero false positives — that repo
has 46 commits with Claude or Anthropic in the subject, none of which are
identity or trailer hits.
A wedged Chrome/mdPress step held a harness runner for 27 minutes yesterday
(normal run: ~4 min) and ignored `gh run cancel`; it only stopped because that
job happened to carry timeout-minutes: 30. An audit found 66 of the 101 jobs
across the cluster had no timeout at all — the same hang there would have held
a runner for GitHub's 6-hour default.
Values come from measured run history, not guesses. Across ~120 successful
runs the slowest workflow tops out at 10 min (Update Preview Publications),
CI at 6.5, Update Preview PDF at 4.8:
30 min — jobs that run Chrome/mdPress/pandoc (3x the observed max, and the
value harness already used)
15 min — release, publish, deploy, check-link, chaincode-tests
10 min — dependabot auto-merge
Every value has at least 6x headroom over its job's observed maximum, so this
should never turn a slow-but-working run into a failure.
Verified: all 72 workflow files still parse, and all 101 jobs now carry an
integer timeout in range.
git.busybox.net is gone — every path under it 404s, including /busybox/ — so
the earlier sweep replaced the dead repo link with https://busybox.net/. That
is byte-identical to the 官网 bullet directly above it, leaving two bullets
with different labels and the same URL, and a 官方仓库 label describing a page
that is not a repository.
Points it at the official source-download index instead and relabels it
源码下载. Verified 200.
The guard added earlier could never trigger. tools/render_mermaid.py has no
nonzero exit path — its own docstring said "Exits 0 even if some/all fail
(non-fatal)" — so `if ! render_mermaid.py ...; then exit 1; fi` never ran its
body, and a build that rendered zero diagrams still passed. Reproduced by
running the script with no Chrome on PATH: it printed the fallback warning and
exited 0.
Adds a --strict flag that exits 1 when Chrome is missing or any diagram fails
to render, and passes it from every workflow invocation. The default stays
lenient so local mobile-reader builds keep working, which is what the exit-0
behaviour was actually for.
Verified both directions: with no Chrome, --strict exits 1 and the default
exits 0; against the real book with Chrome, --strict renders every diagram and
exits 0.
Follows the devDependency removal. The build has been mdPress-only since
1.7.0 and nothing referenced these:
- .vuepress/ (config.js + .gitignore). The mirror it configured is still
live, but no workflow in this repo builds or deploys it, so this was a
recipe nobody ran. Local .vuepress/dist/ (400 files of Feb-2026 build
output, never tracked) was removed from the working tree too — with its
.gitignore gone it would otherwise have shown up as untracked noise.
- docker-compose.yml: the vuepress-offline service, plus the now-unused
&mdpress-offline anchor that existed only to feed it.
- README: the 'docker run ... :vuepress' one-liner. That tag was last
pushed 2022-06-25, so readers following it got a four-year-old snapshot
of the book. 'mdpress serve' immediately above it still works.
- CODEOWNERS, .zhlintignore and check_project_rules.py entries for the
deleted directory, and the check-link allowlist entry for the mirror
(no content links to it).
The CHANGELOG entry recording the original CI removal is left alone — it
is history.
Verified: npm test passes (3 + 35), check_project_rules passes 202 files,
docker-compose.yml still parses with no dangling YAML aliases.
The build moved to mdPress in 1.7.0 ('移除遗留的 vuepress 构建,统一使用
mdpress'), but the VuePress devDependencies stayed. Nothing references them:
build/serve/start/pdf all call mdpress, and the only occurrence of vuepress
in code is '.vuepress' inside check_project_rules.py's ignore list. Pages
still ran 'npm ci', so every deploy installed the whole VuePress tree as
dead weight.
They were also the entire source of the repo's 91 Dependabot alerts. The
lockfile carried two copies of several packages; the vulnerable ones were
all VuePress's (dompurify 2.4.3, highlight.js 9.18.5, katex 0.15.6,
mermaid 9.4.3) while mermaid-cli's copies are above every patched
threshold. Removing the three devDependencies drops 16 of the 20 flagged
packages from the tree entirely and leaves the other four at safe versions.
.vuepress/config.js is deliberately kept — the mirror site it configures is
still live, and whether to archive it is a separate decision.
Verified: npm test passes (3 + 35 tests) and check_project_rules passes
202 files.
- 12.3: daemon.json has no top-level cgroup-driver key; use exec-opts
native.cgroupdriver (values cgroupfs/systemd) per official dockerd reference
- 12.2: USER namespace is not enabled by default; requires userns-remap
(aligns with 18.1)
- 14.1: cgroup v1 deprecation and kubelet default-fail happen in v1.35, not
v1.36 (per kubernetes.io cgroups doc)
- 15_etcd: maintenance window updated after etcd v3.7.0 release (2026-07-08)
to 3.6/3.7
- 3.9.6: fix dangling sentence order in registry migration note
- tools: add 'from __future__ import annotations' so PEP 604 annotations run
on Python 3.9 (fixes npm test)
Renders Mermaid to SVG via the workflow's Chrome, pandoc --mathml, assembles a
single self-contained responsive HTML reader, and attaches <repo>-<tag>.html to
the GitHub Release next to the PDF. Non-blocking (continue-on-error).