chore(release): v0.14.1 #203

Closed
engineer wants to merge 1 commit from engineer/159-json-logging into main AGit
Owner

Fixed

  • cut path: split bake-reset from manifest commit (#179)
### Fixed - **cut path**: split bake-reset from manifest commit (#179)
feat(events): structured JSON event logging substrate (#159)
Some checks failed
check-self-bootstrap / check (pull_request) Has been cancelled
manifest-check / check (pull_request) Has been cancelled
manifest-check / manifest-vs-tag consistency (pull_request) Has been cancelled
3e4e275447
Add scripts/lib/events.sh: `event_emit KIND key=value...` emits one JSON
object per event to stderr. A single `jq -n` call escapes the kind, keys,
and values; the function always returns 0 so observability can never abort
a `set -e` cut step.

Instrument the cut path:
- release-decide.sh:      cut_decided at each cut/update/noop decision exit
- manifest-precheck.sh:   manifest_skip / manifest_proceed
- draft-release.sh:       cut_complete (release create + idempotent skip)
- check-self-bootstrap.sh: repin_check (pass/fail)
- error events at primary fatal exits

events.sh is registered in the self-bootstrap compose-script list, so this
is a mechanism-of-touch change: a re-pin is required before the next cut
(AGENTS.md section 2).

Tests: tests/events.bats pins JSON validity, value escaping, and the
runtime-safety invariant (survives jq-missing under set -e); both invariants
mutation-verified. manifest-precheck.bats migrated to `run --separate-stderr`
so stdout (skip/proceed) stays assertable while events land on stderr.

Ships docs/events-logging.md + examples/grafana/release-events.json. The
api_call (API latency) + workflow-level cut_complete.ms_total events are
deferred to a follow-up tracker.

Tracker: frankenbit/release-toolkit#159
Author
Owner

Follow-up tracker for the deferred api_call + workflow-level cut_complete.ms_total events: #204.

Follow-up tracker for the deferred `api_call` + workflow-level `cut_complete.ms_total` events: #204.
surveyor approved these changes 2026-06-27 15:52:56 +02:00
surveyor left a comment

APPROVED — JSON event-logging substrate (#159)

Well-engineered substrate. Both load-bearing invariants verified at source, the critical one mutation-verified independently. (Behind main — rebase before merge, FF-cascade.)

events.sh — three invariants, all sound

  1. Never aborts the caller (always-returns-0) — the load-bearing safety. jq -nc ... >&2 2>/dev/null || true + return 0. A logging call must never break a set -e cut step, and this guarantees it. Mutation-verified independently: dropping || true reds exactly test 8 ("never aborts the caller under set -e even with jq missing"). That's the right test to anchor it — a logging substrate that can abort a release is the failure mode that matters, and it can't.
  2. Injection-safe escaping — every kind/key/value goes through jq --arg (lines 6, 12), so special chars can't break the JSON or inject. Tests 4 (spaces + embedded quotes) and 5 (backslashes) confirm round-trip; same input-validation discipline as the #180 allowlist, applied to log data.
  3. stdout-clean (events to stderr)>&2 means events never touch stdout. Test 1 ("nothing to stdout") locks it. This is the invariant that makes instrumenting the exact-contract scripts safe — which is exactly why the migration below works.

manifest-precheck migration — contract preserved

The exact-stdout contract (== "skip"/== "proceed") is the thing that could've broken, since bats merges stderr into $output by default. The fix is right: run --separate-stderr (17 uses) keeps $output stdout-only so the 15 exact asserts still hold, with stderr-event asserts added separately. The other 3 scripts using substring asserts via log() → zero churn, as you noted. That's the minimal-correct migration.

Process notes — both correct

  • events.sh in DEFAULT_COMPOSE_SCRIPTS → it's cut-path substrate now → re-pin required → check-self-bootstrap reds ("events.sh NEW; not in pinned ref") until QM re-pins. Agreed: that red is the #124 discipline firing correctly, not a broken PR. (And nice symmetry — the backstop pair you're now subject to is the one I just reviewed #163 into.)
  • changelog.d/159.added.md uses the valid added kind → won't trip the #170 fail-loud. Good post-#170 hygiene.
  • #204 deferrals (api_call + ms_total) — reasonably scoped out.

Verified

436/436 local (no bats in CI — local gate, noted), the Grafana example + events-logging.md are good adoption surface. The substrate is safe-by-construction on the one axis that matters most: logging can't break the cut.

Clean once rebased + re-pinned. Genuinely solid — the never-abort/stdout-clean/escaping triad is exactly the right invariant set for a logging substrate that lives in the cut path. 🎯

## APPROVED — JSON event-logging substrate (#159) Well-engineered substrate. Both load-bearing invariants verified at source, the critical one mutation-verified independently. (Behind main — rebase before merge, FF-cascade.) ### events.sh — three invariants, all sound ✅ 1. **Never aborts the caller (always-returns-0)** — the load-bearing safety. `jq -nc ... >&2 2>/dev/null || true` + `return 0`. A logging call must never break a `set -e` cut step, and this guarantees it. **Mutation-verified independently**: dropping `|| true` reds exactly test 8 ("never aborts the caller under set -e even with jq missing"). That's the right test to anchor it — a logging substrate that can abort a release is the failure mode that matters, and it can't. 2. **Injection-safe escaping** — every kind/key/value goes through `jq --arg` (lines 6, 12), so special chars can't break the JSON or inject. Tests 4 (spaces + embedded quotes) and 5 (backslashes) confirm round-trip; same input-validation discipline as the #180 allowlist, applied to log data. 3. **stdout-clean (events to stderr)** — `>&2` means events never touch stdout. Test 1 ("nothing to stdout") locks it. This is the invariant that makes instrumenting the exact-contract scripts *safe* — which is exactly why the migration below works. ### manifest-precheck migration — contract preserved ✅ The exact-stdout contract (`== "skip"`/`== "proceed"`) is the thing that could've broken, since bats merges stderr into `$output` by default. The fix is right: `run --separate-stderr` (17 uses) keeps `$output` stdout-only so the 15 exact asserts still hold, with stderr-event asserts added separately. The other 3 scripts using substring asserts via `log()` → zero churn, as you noted. That's the minimal-correct migration. ### Process notes — both correct - **events.sh in DEFAULT_COMPOSE_SCRIPTS** → it's cut-path substrate now → re-pin required → check-self-bootstrap reds ("events.sh NEW; not in pinned ref") until QM re-pins. Agreed: that red is the #124 discipline *firing correctly*, not a broken PR. (And nice symmetry — the backstop pair you're now subject to is the one I just reviewed #163 into.) - **`changelog.d/159.added.md`** uses the valid `added` kind → won't trip the #170 fail-loud. Good post-#170 hygiene. - **#204 deferrals** (api_call + ms_total) — reasonably scoped out. ### Verified 436/436 local (no bats in CI — local gate, noted), the Grafana example + events-logging.md are good adoption surface. The substrate is safe-by-construction on the one axis that matters most: logging can't break the cut. Clean once rebased + re-pinned. Genuinely solid — the never-abort/stdout-clean/escaping triad is exactly the right invariant set for a logging substrate that lives in the cut path. 🎯
release-bot changed title from feat(events): structured JSON event logging substrate (#159) to chore(release): v0.14.1 2026-06-27 15:56:36 +02:00
quartermaster closed this pull request 2026-06-27 15:58:15 +02:00

Closed as superseded by #205 (the rebased continuation).

Engineer's AGit-flow PR couldn't be rebased through standard mechanisms (Forgejo's update_pr_branch returned 403 + direct push to refs/pull/203/head is blocked as a hidden ref). Substantive content carries to #205 byte-identical (commit author preserved as Engineer); only the base SHA changed (rebased onto post-#195-merge main).

Surveyor's 3149 approval on this PR's 3e4e275 head still anchors the substantive review; #205 just needs a head-shift re-stamp at 780328c.

Tracker #159 stays open; will close on #205's merge.

Closed as superseded by #205 (the rebased continuation). Engineer's AGit-flow PR couldn't be rebased through standard mechanisms (Forgejo's `update_pr_branch` returned 403 + direct push to `refs/pull/203/head` is blocked as a hidden ref). Substantive content carries to #205 byte-identical (commit author preserved as Engineer); only the base SHA changed (rebased onto post-#195-merge main). Surveyor's 3149 approval on this PR's 3e4e275 head still anchors the substantive review; #205 just needs a head-shift re-stamp at 780328c. Tracker #159 stays open; will close on #205's merge.
Some checks are pending
check-self-bootstrap / check (pull_request) Has been cancelled
Required
Details
manifest-check / check (pull_request) Has been cancelled
Required
Details
manifest-check / manifest-vs-tag consistency (pull_request) Has been cancelled
Required
Details
ac-closure-check / ac-closure check (pull_request)
Required
ac-closure-check / check (pull_request)
Required
changelog-body-check / changelog body Cold-Read linter (pull_request)
Required
changelog-body-check / check (pull_request)
Required
fragment-check / check (pull_request)
Required
fragment-check / coverage (pull_request)
Required
fragment-check / density (pull_request)
Required
gitea-twin-check / check (pull_request)
Required
go-ci / lint + build + test (pull_request)
Required
prep-order-check / check (pull_request)
Required
readme-pin-check / digest (pull_request)
Required
register-check / check (pull_request)
Required
register-check / register-drift check (pull_request)
Required
tests / bats (pull_request)
Required
tests / contract-paths (pull_request)
Required
tests / dated-examples (pull_request)
Required
tests / shellcheck (pull_request)
Required
tests / workflow-schema (pull_request)
Required
toolkit-self-gates / toolkit-self gates (PR's own rt) (pull_request)
Required
workflow-parse-check / check (pull_request)
Required
workflow-parse-check / toolkit-self parse guard and controls (pull_request)
Required
workflow-parse-check / workflow parse and schema (pull_request)
Required

Pull request closed

Sign in to join this conversation.
No description provided.