Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 18 additions & 5 deletions .github/workflows/backlog-hygiene.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,14 @@ jobs:
exit 0
fi

changed="$(git diff --name-only "$BASE_SHA" "$HEAD_SHA")"
# THREE-dot, deliberately. The two-dot form asks "how do these two trees differ", which
# includes everything main gained since this PR branched -- as a REVERSE delta on paths the
# PR never touched. Any main-side change to docs/BACKLOG.md (the archive move being one
# large one) would then be credited to every open PR with an older base, and this gate would
# pass while enforcing nothing. Three-dot asks the question actually being posed: what did
# THIS PR change relative to the merge base. `ci.yml` already uses three-dot for the same
# question. fetch-depth: 0 above guarantees the merge base is present.
changed="$(git diff --name-only "$BASE_SHA...$HEAD_SHA")"
touches_code=false
case "$changed" in
*messagefoundry/*|*ide/*|*messagefoundry_webconsole/*) touches_code=true ;;
Expand All @@ -73,17 +80,23 @@ jobs:
exit 0
fi

if printf '%s\n' "$changed" | grep -qx 'docs/BACKLOG.md'; then
echo "OK — PR claims '$claim', touches code, and updates docs/BACKLOG.md."
# The item's banner lives in docs/BACKLOG.md while the item is open, and moves verbatim into
# docs/archive/backlog/ once it is retired. Updating a retired item's banner is a legitimate
# satisfaction of this rule, so both locations count -- otherwise the one PR shape that
# correctly amends an archived item fails a REQUIRED check with no way to comply.
if printf '%s\n' "$changed" | grep -qE '^(docs/BACKLOG\.md|docs/archive/backlog/.+\.md)$'; then
echo "OK — PR claims '$claim', touches code, and updates the backlog item namespace."
exit 0
fi

n="$(printf '%s' "$claim" | grep -oE '[0-9]+')"
cat >&2 <<EOF
ERROR: this PR says it implements '$claim' and changes engine/IDE code, but it does not
touch docs/BACKLOG.md.
touch docs/BACKLOG.md or any file under docs/archive/backlog/.

Update item #$n's status banner in the same PR. If the work is complete:
Update item #$n's status banner in the same PR — in docs/BACKLOG.md if the item is still
open, or in its docs/archive/backlog/ file if it has already been retired. If the work is
complete:

> ✅ **SHIPPED in <version> (<ADR / PR>).** <one line of evidence>

Expand Down
8 changes: 7 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -133,9 +133,15 @@ jobs:
# scripts/docs/backlog_status_check.py imports only argparse/re/sys/pathlib and has its own CLI.
# tests/test_backlog_status_check.py imports that SAME module, so there is one implementation and
# the unit tests keep covering its edge cases — this only adds the always-on invocation.
# --min-items is the anti-narrowing floor, and it is the point of this invocation as much as the
# banner check is. The item namespace spans docs/BACKLOG.md AND docs/archive/backlog/: retiring
# items moves them between those files, so every OTHER assertion here — one banner per item, no
# contradictions, no duplicates — is satisfied just as easily by scanning a remnant of the corpus
# as the whole of it. Without a floor, a change that stopped the archive being read would go green.
# Raise the number when the total legitimately grows; it must never be lowered to make CI pass.
- name: Backlog status invariant (ungated — see above)
if: runner.os == 'Linux'
run: python scripts/docs/backlog_status_check.py
run: python scripts/docs/backlog_status_check.py --min-items 277

# PySide6's offscreen platform plugin needs a few system libraries even
# headless. Linux-only; Windows runners need no equivalent.
Expand Down
4 changes: 2 additions & 2 deletions docs/AOAG-DEPLOYMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ cross-subnet listener:
> the store connection takes exactly one AG-aware keyword, and **it has shipped**:
> **`[store].multi_subnet_failover`** emits ODBC `MultiSubnetFailover=Yes` so the driver races the
> subnets instead of serially waiting out each one
> ([backlog #100](BACKLOG.md#100-multisubnetfailoveryes-opt-in-for-the-sql-server-store-connection-p2),
> ([backlog #100](archive/backlog/BACKLOG-CLOSED.md#100-multisubnetfailoveryes-opt-in-for-the-sql-server-store-connection-p2),
> shipped 2026-07-10; see [`CONFIGURATION.md`](CONFIGURATION.md)). It is **opt-in and defaults to
> `false`** — a deployment that sets nothing gets nothing. **Turn it on** for any multi-subnet AG.
>
Expand Down Expand Up @@ -451,7 +451,7 @@ two hospital engines and the DR engine. Give each an **identical config dir** an
engine**: every one of its ~7 commits/message now crosses the WAN to the AG primary (§3.3), and
there is **no automatic fail-back** to the hospital once an engine there returns — leadership stays
put until you deliberately move it (the §6 *Failback* runbook). **Leader preference IS built** —
[backlog #101](BACKLOG.md#101-cluster-leader-preference--non-promotable-standby-p2) shipped
[backlog #101](archive/backlog/BACKLOG-CLOSED.md#101-cluster-leader-preference--non-promotable-standby-p2) shipped
2026-07-12 ([ADR 0096](adr/0096-cluster-leader-preference-and-non-promotable-standby.md)): two
per-node `[cluster]` knobs, `acquire_delay_seconds` (handicaps take-over of an **expired** lease
only — renews are never delayed, so there is no two-leader window) and `promotable = false` (the
Expand Down
Loading
Loading