Skip to content

feat: check the map body's two stated budgets - #452

Merged
rubenhensen merged 4 commits into
mainfrom
dobby/map-budget-445
Sep 23, 2026
Merged

rubenhensen merged 4 commits into
mainfrom
dobby/map-budget-445

Conversation

@dobby-coder

@dobby-coder dobby-coder Bot commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

What this does

Implements #445: two checks on issue #247's stated budgets, which #442
rewrote and capped but left unenforced.

  • scripts/map-budget.sh <path-to-body-file>: checks a body file against
    the 153,600-byte (150 KiB) total budget (## Notes) and the 400-byte
    per-entry cap on ## Decisions so far (that section's own header
    comment). Both are UTF-8 byte counts. Exit 0 means within budget, 1 a
    real finding, 2 could not determine; the three are never conflated.
  • scripts/map-budget-test.sh: an offline self-test, 13 cases, including
    the em-dash case (400 characters, over 400 bytes), a heading-level typo
    that must not satisfy the section check, and a permanent known-good
    fixture.
  • scripts/testdata/map-247-body.md: a byte-for-byte snapshot of Wayfinder: fleet remediation — enforce the seams, kill silent failure (2026-07 audit) #247's
    real body, taken via gh issue view 247 -R encryption4all/postguard --json body -q .body on 2026-09-23 (68,356 bytes). A snapshot, not a
    live copy.
  • One new assertion in pg-core/tests/ci_wiring.rs,
    the_map_budget_checkers_self_test_runs_in_ci, modeled on
    the_wasm_package_checkers_self_test_runs_in_ci per the issue's
    pre-flight amendment.

Why this assertion is RED on this branch

dobby-coder has no workflows: write, so it cannot push under
.github/workflows/. The new ci_wiring.rs assertion expects
build.yml's ruleset-drift job to run scripts/map-budget-test.sh
alongside its four existing self-tests; that step is posted below for a
maintainer to apply, not pushed here. This is deliberate, not an oversight:
see the issue's §3 and the test's own doc comment.

git diff --name-only origin/main...HEAD -- .github/workflows/ is empty on
this branch. Confirmed nothing else regressed: every other check on this PR
is green except the four cascading from this one via the test job's
matrix (no fail-fast: false) -- Test workspace (pg-ffi), (pg-cli),
(pg-pkg) and (cryptify) all show cancelled at the API level, not a
second failure. Nothing here is fixable from this container; this is the
"cannot be fixed from here" case, not an oversight left unaddressed.

This PR's two comments below are a ready-to-apply handover, not just the
diffs in this description: each posts the full resulting file (so a
maintainer reviews exactly what will run with the repo's secrets, not a
diff) followed by one gh api -X PUT command that computes its own current
sha and needs no checkout to run.

CI on this PR

Test workspace (pg-core) fails on exactly the one assertion above (18/19
pass). build.yml's test job is a crate matrix with no fail-fast: false, so that one failure cancels its still-running matrix siblings.
Test workspace (cryptify), Test workspace (pg-cli) and Test workspace (pg-pkg) show red/cancelled as a result, not because of anything they ran
themselves; the same cancellation pattern is present on the pre-title-fix
run too, so it is not something this push introduced. Wire compat, the
ruleset's one required check, is green. The PR title also failed
Conventional Commit on open (no type prefix) and has since been
retitled; that check is green now.

Revised since review

Three findings from review of this PR.

Two fixed in 1295b8a:

  • scripts/map-budget.sh's BODY_BUDGET_BYTES was 150000 (decimal-KB
    math), while its own comment claimed that figure came from Wayfinder: fleet remediation — enforce the seams, kill silent failure (2026-07 audit) #247's
    header. The header only says "keep the body under 150 KB", never an
    exact byte count -- and that same sentence states GitHub's hard cap as
    an exact binary-KB conversion (262,144 = 256*1024). Read the same
    way, "150 KB" is 153,600 bytes, not 150,000 -- a body sized
    150,000-153,600 bytes was being flagged over budget despite being
    within the issue's actual intended budget. Constant, comment,
    scripts/map-budget-test.sh, and this description all updated to
    match.
  • The posted (not committed here -- no workflows: write) map-budget.yml
    patch's dedup step read gh api repos/.../comments --jq '.[-1].body'
    with no pagination, so once Wayfinder: fleet remediation — enforce the seams, kill silent failure (2026-07 audit) #247 crosses the API's default 30-comment
    page size the dedup silently stops seeing the true most recent comment,
    reintroducing the edit-storm spam it exists to prevent. The patch below
    now paginates and round-trips the comment body through base64 -- a
    plain tail -n 1 on unencoded, multi-line jq output would grab only
    the last line of the last page's comment, not the whole last comment.
    Verified locally against a stubbed gh returning a multi-page,
    multi-line response. Also revised in the matching patch comment on
    check the map body's two stated budgets: 150 KB total, 400 bytes per index entry #445.

Fixed in e1710db:

  • scripts/map-budget.sh's heading-existence check (grep -qF '## Decisions so far') and the awk section-extractor just below it (^## Decisions so far) disagreed on what counts as the heading: the existence check is
    unanchored, so it matched the string anywhere in a line, including inside
    a heading-level typo like ### Decisions so far; the extractor is
    anchored and never matches that line, so the section came back empty and
    every entry inside it was silently skipped. A body with that typo and a
    450-byte entry inside it exited 0 "OK" -- the exact "reported as 0 when
    it can't parse" case the module comment disclaims. Anchored the existence
    check the same way as the extractor (grep -qE '^## Decisions so far').
    Added a committed regression case to scripts/map-budget-test.sh; the
    existing 12 cases are unaffected.

The two workflow patches (posted on #445, not committed here)

Both are also posted as a comment on the issue, per §4 of the ticket, and as
two ready-to-apply comments on this PR itself (full file + one-paste gh api
command each): build.yml,
new map-budget.yml.

(a) build.yml: adds the self-test step to ruleset-drift, after the
existing five.

--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -515,3 +515,7 @@ jobs:
       - name: Test the ruleset-drift reporter
         shell: bash
         run: scripts/ruleset-drift-report-test.sh
+
+      - name: Test the map-budget checker
+        shell: bash
+        run: scripts/map-budget-test.sh

(b) A new .github/workflows/map-budget.yml: the live check, gated on
the wayfinder:map label, triggered on issues: edited (plus
workflow_dispatch), never a push. Writes github.event.issue.body to a
file via env:, never interpolated into run:. Runs
scripts/map-budget.sh as a bare command; on exit 1 comments on the issue
(deduped against the most recent comment); on exit 2 prints ::error:: and
comments nothing. Not wired into any required context.

diff --git a/.github/workflows/map-budget.yml b/.github/workflows/map-budget.yml
new file mode 100644
index 0000000..f18cd53
--- /dev/null
+++ b/.github/workflows/map-budget.yml
@@ -0,0 +1,93 @@
+name: Map budget
+#
+# Checks #247's two stated budgets on every edit to its body (#442, #445):
+# the whole body <=153,600 bytes (150 KiB), every "Decisions so far" entry
+# <=400 bytes. See scripts/map-budget.sh for the full rule, and why both
+# are byte counts rather than character counts.
+#
+# `issues: edited`, not a schedule: the event being budgeted is the body
+# changing, and that event exists. No `push` trigger either -- a push says
+# nothing about an issue body.
+#
+# Deliberately NOT a required context. It watches an issue body, which no PR
+# can fix, so making it required would block every merge on an edit nobody
+# on that PR could act on.
+
+on:
+  issues:
+    types: [edited]
+  workflow_dispatch:
+
+jobs:
+  map-budget:
+    name: Map body stays within budget
+    runs-on: ubuntu-latest
+    # The label, not the issue number, so a second wayfinder map is covered
+    # for free and no number is hardcoded in two places. `workflow_dispatch`
+    # carries no `issue`, so a manual run of this workflow as it stands
+    # always evaluates false and skips -- that is #445's decided shape, not
+    # an oversight; wiring a manual re-run to a specific issue would need an
+    # `issue_number` input this ticket does not ask for.
+    if: contains(github.event.issue.labels.*.name, 'wayfinder:map')
+    permissions:
+      contents: read
+      issues: write
+    steps:
+      - uses: actions/checkout@v4
+
+      - name: Write the issue body to a file
+        env:
+          # Never interpolated into a `run:` block: the body is a Markdown
+          # document full of backticks and $(...) shell examples, and
+          # interpolating it would execute them.
+          ISSUE_BODY: ${{ github.event.issue.body }}
+        run: printf '%s' "$ISSUE_BODY" >"$RUNNER_TEMP/map-body.md"
+
+      - name: Check the map body's budgets
+        env:
+          GH_TOKEN: ${{ github.token }}
+          GH_REPO: ${{ github.repository }}
+          ISSUE_NUMBER: ${{ github.event.issue.number }}
+        run: |
+          # `if cmd; then ... else code=$?; fi` rather than
+          # `out=$(cmd); code=$?`: this step has no `shell:` key, so it runs
+          # as `bash -e {0}`, and a bare command substitution's own non-zero
+          # exit would abort the step before a following `code=$?` line was
+          # ever reached (#429, #422). `if`'s condition is exempt from `-e`,
+          # so this survives the failure -- but `$?` must be captured as the
+          # *first* thing in the `else` branch: bash resets `$?` to 0 for the
+          # `if` statement itself once the `then` branch is skipped, so a
+          # `code=$?` placed after `fi` instead would always read 0.
+          if output=$(scripts/map-budget.sh "$RUNNER_TEMP/map-body.md" 2>&1); then
+            echo "map-budget: OK -- within both stated budgets"
+            exit 0
+          else
+            code=$?
+          fi
+
+          if [[ $code -eq 2 ]]; then
+            echo "::error::scripts/map-budget.sh could not determine the map body's budgets (exit 2) -- the checker did not run to completion, this is not a finding"
+            printf '%s\n' "$output"
+            exit 2
+          fi
+
+          # Only 1 (a real finding) remains. Dedupe against the most recent
+          # comment so an edit storm does not spam the issue with the same
+          # finding repeatedly. `--paginate` walks every page -- without it,
+          # a plain `.[-1]` reads only the API's first page (30 comments by
+          # default), and once #247 crosses that count the dedup silently
+          # stops seeing the true most recent comment, reintroducing the
+          # edit-storm spam this step exists to prevent. The body
+          # round-trips through base64 because `--paginate` applies the
+          # `--jq` filter once per page and prints one result per page: a
+          # multi-line comment body would otherwise span multiple output
+          # lines, and `tail -n 1` would grab only its last line rather than
+          # the whole last comment.
+          last_comment=$(gh api --paginate "repos/$GH_REPO/issues/$ISSUE_NUMBER/comments" --jq '.[-1].body // "" | @base64' | tail -n 1 | base64 -d)
+          if [[ $last_comment == "$output" ]]; then
+            echo "map-budget: over budget, but the most recent comment already says so -- not reposting"
+          else
+            gh issue comment "$ISSUE_NUMBER" --body "$output"
+          fi
+          printf '%s\n' "$output"
+          exit 1

Verification

  • bash scripts/map-budget-test.sh: 13/13 pass, offline, no GH_TOKEN.
  • cargo test --manifest-path pg-core/Cargo.toml --features test,rust,stream:
    91 passed, 1 failed (the_map_budget_checkers_self_test_runs_in_ci,
    the expected RED assertion).
  • cargo fmt --manifest-path pg-core/Cargo.toml --all -- --check: clean.
  • cargo clippy --manifest-path pg-core/Cargo.toml --all-targets --features test,rust,stream -- -D warnings: clean. The bare --all-targets
    command from the issue's acceptance list needs the same test,rust,stream
    features the test command uses; that gap is pre-existing on origin/main
    and unrelated to this change.
  • scripts/map-budget.sh scripts/testdata/map-247-body.md exits 0.
  • Fail-open proof against a copy of the real fixture:
    • +90,000 bytes → exit 1, names 158356 against the 153600 budget.
    • one 401-byte - [ entry inside the section → exit 1, names 401 and
      the entry text.
    • the same entry placed after the section (past ## Not yet specified)
      → exit 0.
    • ## Decisions so far heading deleted → exit 2.
    • ## Decisions so far replaced with ### Decisions so far (heading-level
      typo) plus a 450-byte entry inside it → exit 2, not the 0 it returned
      before e1710db.
    • nonexistent path → exit 2.
  • Fail-open proof on the ci_wiring.rs assertion itself: applied the
    build.yml patch above locally (uncommitted) → cargo test --test ci_wiring goes green (19/19); deleted just the new step → reds again on
    exactly the_map_budget_checkers_self_test_runs_in_ci; reverted the local
    edit entirely (git checkout -- .github/workflows/build.yml).
  • Simulated the live workflow's bash locally against a stubbed gh: within
    budget → exit 0, no gh calls; over budget → exit 1, comment posted;
    over budget with an identical most-recent comment → exit 1, no repost;
    undetermined → exit 2, ::error::, no gh calls at all.

Part of #247.

🤖 Generated with Claude Code

Adds scripts/map-budget.sh, checking #247's body against its own stated
150,000-byte total budget and 400-byte-per-entry cap on "Decisions so
far", plus scripts/map-budget-test.sh pinning its 0/1/2 exit contract
offline. Graduated from #442's rewrite, which stated the numbers but
left them unchecked.

pg-core/tests/ci_wiring.rs gets one new assertion that build.yml's
ruleset-drift job runs the checker's self-test. It is RED on this
branch: dobby-coder has no workflows: write, so the build.yml step and
the new map-budget.yml live check are posted on #445 for a maintainer
to apply, not pushed here.

Part of #247.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@dobby-coder
dobby-coder Bot requested a review from rubenhensen September 23, 2026 07:42
@dobby-coder dobby-coder Bot changed the title check the map body's two stated budgets feat: check the map body's two stated budgets Sep 23, 2026

@dobby-coder dobby-coder Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: request-changes (posted as a COMMENT review — GitHub does not allow REQUEST_CHANGES/APPROVE on a PR authored by this same dobby-coder identity, so the event type below is a technical fallback, not the actual verdict).

Rule check: 5 rules bound this task (atomic-commits, code-comments, draft-pull-requests, no-summary-issues, test-suite-before-submit) — all compliant. Single well-described commit, comment density in the two new scripts matches their established neighbours (scripts/ruleset-drift.sh, scripts/wasm-package-check.sh), the PR is a draft, no summary issue was created, and the full pg-core suite plus fmt/clippy ran clean apart from the one documented, expected-red ci_wiring assertion.

Review findings: one bug in the committed script, one bug in the posted-but-not-committed map-budget.yml patch (can't be fixed by pushing here — no workflows: write — but the patch text itself, posted in this PR's description and on #445, can and should be revised before a maintainer applies it), and one non-blocking nit.

  • .github/workflows/map-budget.yml (posted as a patch in the PR description / on #445, not part of this diff): its dedup step reads gh api repos/$GH_REPO/issues/$ISSUE_NUMBER/comments --jq '.[-1].body // ""' with no --paginate/per_page, so it only ever sees the API's first page (30 comments). #247 already has 26 comments and is the project's active running map; once it crosses 30, .[-1] silently stops being the true most-recent comment and the dedup this step exists for breaks, reintroducing the edit-storm spam it was built to prevent. Please revise the posted patch (in the PR body and the #445 comment) to paginate or use per_page + a proper "last page" fetch before a maintainer applies it.

The scripts/map-budget.sh comment/constant mismatch below is the blocking finding; the nit on ci_wiring.rs is informational only.

Comment thread scripts/map-budget.sh Outdated
set -euo pipefail

# 150,000 bytes, stated in #247's "## Notes" header comment.
readonly BODY_BUDGET_BYTES=150000

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BODY_BUDGET_BYTES=150000's comment (line 48) claims "150,000 bytes, stated in #247's own 'Notes' header comment" — but the header comment (gh issue view 247 --json body) only ever says "keep the body under 150 KB", never an exact byte figure. That same header sentence states GitHub's hard cap as an exact 262,144 bytes (= 256*1024), i.e. binary-KB math; under that same convention 150 KB reads as 153,600 bytes, 3,600 more than this constant. A body sized 150,000–153,600 bytes would be flagged over-budget here despite plausibly being within the issue's actual intended budget. Either fix the comment to stop claiming the header states an exact byte count (it's this script's own decimal-KB interpretation), or use 153600 if binary was intended. The same ambiguity applies to the out-of-scope 16,000-byte "Notes" figure mentioned at line 20 of this file.

fn the_map_budget_checkers_self_test_runs_in_ci() {
let job = job(&workflow(BUILD_WORKFLOW), "ruleset-drift", BUILD_WORKFLOW);
let steps = steps(&job);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit, not blocking: the_map_budget_checkers_self_test_runs_in_ci is the one cargo-test failure (18/19 pass) — fully expected and documented in both the test's own doc comment and the PR description: dobby-coder can't push .github/workflows/*.yml, so the build.yml step this asserts on is posted as a patch instead of committed. Needs a maintainer to apply the posted build.yml patch; no action needed here.

…header

#247's "## Notes" header comment says "keep the body under 150 KB", not an
exact byte figure, but that same sentence states GitHub's hard cap as an
exact binary-KB conversion (262,144 = 256*1024). Reading "150 KB" the same
way gives 153,600 bytes, not the 150,000 this script assumed -- a body
sized 150,000-153,600 bytes was being flagged over budget despite being
within the issue's actual intended budget.

Also fixes the comment that overclaimed the header states an exact byte
count.

Found in review of #452.
@dobby-coder

dobby-coder Bot commented Sep 23, 2026

Copy link
Copy Markdown
Contributor Author

Workflow handover (1/2): build.yml

dobby-coder has no workflows: write, so this can't be pushed to this branch from here — it needs a maintainer to apply it directly. This is the only thing keeping Test workspace (pg-core) red (the_map_budget_checkers_self_test_runs_in_ci, 18/19 pass otherwise); every other red/cancelled check on this PR is that one failure cascading through the test job's matrix (no fail-fast: false), not a separate problem.

The change is four lines appended to the end of the ruleset-drift job: a new self-test step for scripts/map-budget.sh, same shape as the four self-test steps already there. Full resulting file below, so you're reviewing exactly what would run with the repo's secrets, not a diff:

Full contents of .github/workflows/build.yml after the change
on:
  push:
    branches:
      - main
  pull_request:
    # `edited` included for base retargets (e.g. a stacked PR's base merging):
    # paths-filter's verdict depends on the base, and without `edited` a stale
    # verdict stays attached to the unchanged head sha.
    types: [opened, synchronize, reopened, edited]
    branches:
      - main
  workflow_dispatch:

name: Continuous integration

jobs:
  # sonarqube:
  #   name: SonarQube
  #   runs-on: ubuntu-latest
  #   continue-on-error: true
  #   steps:
  #     - uses: actions/checkout@v4
  #       with:
  #         fetch-depth: 0  # Shallow clones should be disabled for a better relevancy of analysis
  #     - name: SonarQube Scan
  #       uses: SonarSource/sonarqube-scan-action@v6
  #       env:
  #         SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
  # The matrix keys on the crate directory rather than a `pg-` suffix, because
  # `cryptify` does not carry that prefix. This renames the check contexts from
  # `Test workspace (core)` to `Test workspace (pg-core)`; nothing required
  # points at them today (only the two `Wire compat` contexts are required), but
  # a ruleset added later must use the new names.
  test:
    name: Test workspace
    strategy:
      matrix:
        crate: [pg-core, pg-pkg, pg-cli, pg-ffi, cryptify]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
      - if: ${{ matrix.crate == 'pg-core' }}
        run: cargo test --manifest-path pg-core/Cargo.toml --features test,rust,stream
      # cryptify declares no features of its own, so `--all-targets` (what its
      # own CI ran) is the equivalent invocation.
      - if: ${{ matrix.crate == 'cryptify' }}
        run: cargo test --manifest-path cryptify/Cargo.toml --all-targets
      - if: ${{ matrix.crate != 'pg-core' && matrix.crate != 'cryptify' }}
        run: cargo test --manifest-path ${{ matrix.crate }}/Cargo.toml --all-features

  format:
    name: Format workspace
    strategy:
      matrix:
        crate: [pg-core, pg-pkg, pg-cli, pg-ffi, cryptify]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
      - run: cargo fmt --manifest-path ${{ matrix.crate }}/Cargo.toml --all -- --check

  clippy:
    name: Clippy workspace
    strategy:
      matrix:
        crate: [pg-core, pg-pkg, pg-cli, pg-ffi, cryptify]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
        with:
          components: clippy
      - if: ${{ matrix.crate == 'pg-core' }}
        run: cargo clippy --manifest-path pg-core/Cargo.toml --all-targets --features test,rust,stream -- -D warnings
      - if: ${{ matrix.crate == 'cryptify' }}
        run: cargo clippy --manifest-path cryptify/Cargo.toml --all-targets -- -D warnings
      - if: ${{ matrix.crate != 'pg-core' && matrix.crate != 'cryptify' }}
        run: cargo clippy --manifest-path ${{ matrix.crate }}/Cargo.toml --all-targets --all-features -- -D warnings

  test-wasm-browsers:
    name: Run wasm tests in browsers
    strategy:
      matrix:
        include:
          - browser: chrome
            os: ubuntu-latest
          - browser: firefox
            os: ubuntu-latest
          - browser: safari
            os: macos-latest
    runs-on: ${{ matrix.os }}
    # The suite is 16 tests and finishes in ~25s; a passing job is 2-6 minutes,
    # almost all of it `cargo install wasm-pack` and the release build. 12
    # minutes is ~2x the slowest observed success. The previous budget was 20
    # minutes inside a `nick-fields/retry` wrapper, which on 2026-09-03 turned
    # three wedged safari sessions into ~22-minute jobs with ~17 minutes of
    # silence each (#416).
    timeout-minutes: 12
    env:
      # ~6x the slowest single test (~5s). This is a per-test timer running
      # inside the browser page, so it does NOT bound a wedged safaridriver
      # session -- that is what `timeout-minutes` above is for. It was 120,
      # which is both 24x the slowest test and, at 16 tests, a 32-minute
      # worst case that the job budget could never reach.
      WASM_BINDGEN_TEST_TIMEOUT: 30
    steps:
      - uses: actions/checkout@v4
      - name: Install
        run: cargo install wasm-pack
      - if: ${{ matrix.browser == 'firefox' }}
        run: sudo apt update && sudo apt install firefox
      # A plain `run:`, not `nick-fields/retry`. The wrapper retried genuine
      # non-zero exits (hiding a real failure behind a second roll) and did
      # NOT retry a timeout, which is the only shape this job has ever hit
      # -- `retry_on: error` excludes timeouts, so `max_attempts: 2` never
      # engaged. Dropping it also removes the literal backslash the action
      # input required (`--\${{ matrix.browser }}`), which survived into the
      # resolved command and only worked because a shell stripped it.
      - name: Run wasm-pack browser tests
        run: wasm-pack test --release --headless --${{ matrix.browser }} ./pg-wasm

  # ---------------------------------------------------------------------------
  # Wire compatibility: HEAD-sealed containers must open with published readers
  # (#251 / #260). #261 adds a wire-compat-js job that downloads the artifact
  # this job uploads.
  #
  # The path filter is a step, not an `on: paths:` key, on purpose: a job that
  # is skipped by a path filter never reports, so a required check would sit
  # pending forever on PRs that do not touch the wire surface. This shape always
  # reports and only does the expensive work when it needs to.
  #
  # On `push` the filter is the wrong instrument and is bypassed (#299). It
  # diffs only the push that triggered it, so a commit that reached `main`
  # without this gate ever running is never re-checked afterwards -- and the job
  # still reports green, because "filter said no" and "gate passed" are the same
  # success. That is not hypothetical: #297 changed seven files under
  # `pg-compat/**` and merged during the 2026-08-06 Actions outage with no run
  # at all, and the three `main` runs after it (506144c2, c1682ba7, 1238389b)
  # each reported `Wire compat: success` with `Seal`/`Open` both skipped. So on
  # a push the gate always does the real work: whatever is on `main` is what
  # gets sealed and opened, however it got there.
  # ---------------------------------------------------------------------------
  wire-compat-rust:
    name: Wire compat (published pg-core)
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: read # paths-filter reads the PR's changed files
    # wire-compat-js (#261) opens the same bytes with the published npm readers.
    # It downloads the artifact the seal step below produces, so it gates on the
    # same outcome the upload gates on rather than re-running the path filter and
    # drifting out of step with it.
    outputs:
      sealed: ${{ steps.seal.outcome }}
    steps:
      - uses: actions/checkout@v4
      # SHA-pinned as the one action new to this repo; the rest follow the
      # file's existing floating-ref convention.
      - uses: dorny/paths-filter@7b450fff21473bca461d4b92ce414b9d0420d706 # v4.0.2
        id: changes
        with:
          filters: |
            wire:
              - 'pg-core/**'
              - 'pg-wasm/**'
              - 'pg-compat/**'
              - 'pg-compat-js/**'
              # pg-core has no lockfile of its own: the bytes HEAD seals are a
              # function of the ROOT lockfile (a bincode-next/ibe/serde bump
              # touches only these two files). Root Cargo.toml also holds the
              # exclude list that keeps pg-compat on crates.io pg-core.
              - 'Cargo.lock'
              - 'Cargo.toml'
              - '.github/workflows/build.yml'

      # One decision, read by every step below, so the push override cannot be
      # applied to the seal but forgotten on the open.
      - name: Decide whether to do the real work
        id: gate
        shell: bash
        env:
          WIRE_CHANGED: ${{ steps.changes.outputs.wire }}
        run: |
          if [[ "$WIRE_CHANGED" == "true" || "$GITHUB_EVENT_NAME" == "push" ]]; then
            echo "run=true" >> "$GITHUB_OUTPUT"
          else
            echo "run=false" >> "$GITHUB_OUTPUT"
          fi

      - if: steps.gate.outputs.run == 'true'
        uses: dtolnay/rust-toolchain@stable
        with:
          components: clippy, rustfmt

      - name: Seal the sample set with HEAD
        id: seal
        if: steps.gate.outputs.run == 'true'
        shell: bash
        run: |
          cargo run --locked -p pg-core --features stream --example seal-samples \
            -- "$RUNNER_TEMP/wire-compat-artifacts"

      # Hand-off to wire-compat-js (#261). Uploaded before the reader runs so a
      # red gate still leaves the bytes that broke it.
      - name: Upload the sample set
        if: steps.seal.outcome == 'success'
        uses: actions/upload-artifact@v4
        with:
          name: wire-compat-artifacts-${{ github.event.pull_request.head.sha || github.sha }}
          path: ${{ runner.temp }}/wire-compat-artifacts
          retention-days: 7

      - name: Open it with published pg-core
        if: steps.gate.outputs.run == 'true'
        shell: bash
        env:
          PG_COMPAT_ARTIFACTS: ${{ runner.temp }}/wire-compat-artifacts
        run: cargo test --manifest-path pg-compat/Cargo.toml --locked

  # pg-compat is outside the workspace, so the per-crate fmt/clippy matrices do
  # not cover it. Kept separate from wire-compat-rust on purpose: a new stable
  # rustc lint must not red a required check named after wire compatibility,
  # nor pre-empt the seal/read steps that answer it.
  pg-compat-lint:
    name: Lint pg-compat
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: read
    steps:
      - uses: actions/checkout@v4
      - uses: dorny/paths-filter@7b450fff21473bca461d4b92ce414b9d0420d706 # v4.0.2
        id: changes
        with:
          filters: |
            wire:
              - 'pg-compat/**'
              - 'Cargo.lock'
              - 'Cargo.toml'
              - '.github/workflows/build.yml'
      - if: steps.changes.outputs.wire == 'true'
        uses: dtolnay/rust-toolchain@stable
        with:
          components: clippy, rustfmt
      - name: Format pg-compat
        if: steps.changes.outputs.wire == 'true'
        run: cargo fmt --manifest-path pg-compat/Cargo.toml --all -- --check
      - name: Clippy pg-compat
        if: steps.changes.outputs.wire == 'true'
        run: cargo clippy --manifest-path pg-compat/Cargo.toml --all-targets --locked -- -D warnings

  # ---------------------------------------------------------------------------
  # Public-API semver gate (#253). Fails a PR that breaks pg-core's or pg-wasm's
  # public API without declaring the break.
  #
  # Versions here are bumped by release-plz, not by the PR that makes the
  # change, so "declared" means the conventional-commit `!` marker that
  # release-plz turns into a major bump. The marker is read off the PR title
  # into SEMVER_RELEASE_TYPE; `edited` is already in this file's trigger list,
  # so adding the `!` to the title re-runs the gate.
  #
  # Same always-reports shape as wire-compat-rust: the path filter is a step,
  # not an `on: paths:` key, so a required check never sits pending.
  # ---------------------------------------------------------------------------
  semver-checks:
    name: Public API semver
    if: github.event_name == 'pull_request'
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: read # paths-filter reads the PR's changed files
    steps:
      # pg-wasm is not published to crates.io, so its baseline is origin/main
      # rather than a registry version, and the full history has to be here.
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - uses: dorny/paths-filter@7b450fff21473bca461d4b92ce414b9d0420d706 # v4.0.2
        id: changes
        with:
          filters: |
            api:
              - 'pg-core/**'
              - 'pg-wasm/**'
              - 'Cargo.toml'
              - 'Cargo.lock'
              - 'scripts/semver-checks.sh'
              - 'scripts/semver-checks-test.sh'
              - '.github/workflows/build.yml'

      # Stubs cargo, so it needs no toolchain and runs in under a second. It
      # covers the 100-vs-101 exit-code mapping the gate depends on: 100 is a
      # semver violation, 101 is the tool or the build failing.
      - name: Test the gate script
        if: steps.changes.outputs.api == 'true'
        shell: bash
        run: ./scripts/semver-checks-test.sh

      - if: steps.changes.outputs.api == 'true'
        uses: dtolnay/rust-toolchain@stable
        with:
          targets: wasm32-unknown-unknown

      # Pinned release binary rather than `cargo install` (a five-minute build)
      # or a fourth third-party action. Bump the version and the digest together.
      - name: Install cargo-semver-checks
        if: steps.changes.outputs.api == 'true'
        shell: bash
        env:
          VERSION: 0.49.0
          SHA256: 72f6834d75d28a66e02c9fd6a230ce901bb30eee6067b85867a97445df040e4a
        run: |
          curl -sSfL -o "$RUNNER_TEMP/cargo-semver-checks.tar.gz" \
            "https://github.com/obi1kenobi/cargo-semver-checks/releases/download/v${VERSION}/cargo-semver-checks-x86_64-unknown-linux-gnu.tar.gz"
          echo "${SHA256}  $RUNNER_TEMP/cargo-semver-checks.tar.gz" | sha256sum -c -
          tar -xzf "$RUNNER_TEMP/cargo-semver-checks.tar.gz" -C "$HOME/.cargo/bin" cargo-semver-checks

      # The PR title, not the commit subject: PRs are squash-merged here, so the
      # title is what release-plz and the Conventional Commit check read. Passed
      # through the environment rather than interpolated into the script, so a
      # PR title cannot inject shell.
      #
      # The title `!` is the only accepted declaration. A `BREAKING CHANGE:`
      # footer in the PR *body* is deliberately not accepted: this repo's
      # squash_merge_commit_message is COMMIT_MESSAGES, so the body never reaches
      # the squashed commit, and release-plz reads commits. Honouring a body-only
      # footer would pass the gate on a `fix(pg-core):` PR that release-plz then
      # publishes as a patch release of a breaking change.
      - name: Read the breaking-change declaration off the PR title
        id: declared
        if: steps.changes.outputs.api == 'true'
        shell: bash
        env:
          PR_TITLE: ${{ github.event.pull_request.title }}
        run: |
          if [[ "$PR_TITLE" =~ ^[a-zA-Z]+(\([^\)]*\))?!: ]]; then
            echo "Breaking change declared in the PR title; a major bump is allowed."
            echo "release_type=major" >> "$GITHUB_OUTPUT"
          else
            echo "No '!' in the PR title; any breaking change will fail the gate."
          fi

      - name: Check the public API
        if: steps.changes.outputs.api == 'true'
        shell: bash
        env:
          SEMVER_RELEASE_TYPE: ${{ steps.declared.outputs.release_type }}
        run: ./scripts/semver-checks.sh
  # The Node half of the same gate (#261): the containers wire-compat-rust
  # sealed must also open with the published npm readers in COMPATIBILITY.md's
  # support window. It downloads rather than re-seals, so both halves are held
  # to the same bytes and a non-deterministic sealer cannot hide between them.
  #
  # `if: !cancelled()` rather than the default: a red wire-compat-rust is
  # exactly when it is worth knowing whether the JS readers broke the same way,
  # and the artifact is uploaded before that job's read step for this reason.
  # Without it, `needs` would skip this job on the run where it has the most to
  # say.
  # ---------------------------------------------------------------------------
  wire-compat-js:
    name: Wire compat (published pg-wasm/pg-js)
    runs-on: ubuntu-latest
    needs: wire-compat-rust
    if: ${{ !cancelled() }}
    permissions:
      contents: read
    steps:
      - uses: actions/checkout@v4

      # Every step below is behind the seal outcome, not just the download.
      # `outcome` is 'skipped' when the path filter said no (nothing to open,
      # report success) and 'failure' when sealing itself broke (wire-compat-rust
      # owns that failure; do not report it twice).
      - if: needs.wire-compat-rust.outputs.sealed == 'success'
        uses: actions/setup-node@v6
        with:
          node-version: '24'
          cache: npm
          cache-dependency-path: pg-compat-js/package-lock.json

      - name: Download the sample set
        if: needs.wire-compat-rust.outputs.sealed == 'success'
        uses: actions/download-artifact@v4
        with:
          name: wire-compat-artifacts-${{ github.event.pull_request.head.sha || github.sha }}
          path: ${{ runner.temp }}/wire-compat-artifacts

      # ci, not install: the readers are pinned by pg-compat-js/package-lock.json,
      # and a gate that quietly resolved a different reader would be measuring
      # something other than the support window.
      - name: Install the published readers
        if: needs.wire-compat-rust.outputs.sealed == 'success'
        shell: bash
        working-directory: pg-compat-js
        run: npm ci

      - name: Open the set with published pg-wasm/pg-js
        if: needs.wire-compat-rust.outputs.sealed == 'success'
        shell: bash
        working-directory: pg-compat-js
        env:
          PG_COMPAT_ARTIFACTS: ${{ runner.temp }}/wire-compat-artifacts
        run: npm test

  # ---------------------------------------------------------------------------
  # Single required check consolidating the two halves above (#262). The
  # repoint happened in #296: "Wire compat" is the one required context, and the
  # two per-language names are no longer required individually. Since #299 it is
  # also the sole context of the `main: required checks` ruleset, whose
  # `bypass_actors` is empty -- so unlike classic protection (`enforce_admins`
  # is still false) this name cannot be merged past with `--admin`. Renaming
  # this job silently disarms that ruleset; rename the ruleset's context in the
  # same change.
  #
  # `if: !cancelled()` for the same reason as wire-compat-js above: this job
  # must still run and report when an upstream half failed, or the required
  # check sits pending forever instead of turning red.
  # ---------------------------------------------------------------------------
  wire-compat:
    name: Wire compat
    runs-on: ubuntu-latest
    needs: [wire-compat-rust, wire-compat-js]
    if: ${{ !cancelled() }}
    permissions:
      contents: read
    steps:
      - name: Require both halves to have passed
        shell: bash
        run: |
          echo "wire-compat-rust: ${{ needs.wire-compat-rust.result }}"
          echo "wire-compat-js: ${{ needs.wire-compat-js.result }}"
          if [[ "${{ needs.wire-compat-rust.result }}" != "success" || "${{ needs.wire-compat-js.result }}" != "success" ]]; then
            exit 1
          fi

  # ---------------------------------------------------------------------------
  # The registry half of the wiring guards (#318).
  #
  # `pg-core/tests/ci_wiring.rs` asserts that the job above is still *named*
  # `Wire compat`. This asserts the other end of that link: that the `main`
  # ruleset still *requires* that context. Neither end is sufficient. A context
  # required but not produced blocks nothing (#299 measured that an absent check
  # is as dangerous as a red one); a context produced but not required enforces
  # nothing while every test stays green.
  #
  # Both #272 and postguard-js#222 declared this end unreachable from CI, on the
  # assumption it needs an admin credential. Measured in #318: it does not. On a
  # public repo the effective-rules endpoint answers 200 to the built-in token
  # at any permission level, so `contents: read` below is already more than this
  # needs, and no secret is involved.
  #
  # Deliberately NOT part of the `Wire compat` aggregator and deliberately not a
  # required context: this reads a live third-party API, and a GitHub API outage
  # must not become an unmergeable repo. Exit 2 (undetermined) is therefore a
  # distinct outcome from exit 1 (drift) -- see scripts/ruleset-drift.sh.
  #
  # And deliberately no `schedule:`. A nightly would detect drift during quiet
  # periods, but a disarmed gate can only do harm when something merges, and
  # every merge here passes through a `pull_request` and a `push` run of this
  # workflow. Running at exactly the moment the gate is relied upon also makes
  # the result unmissable, which a scheduled run is not -- the ticket's own
  # warning was that a drift detector nobody reads is itself a silent gate.
  # ---------------------------------------------------------------------------
  ruleset-drift:
    name: Ruleset still requires the gate
    runs-on: ubuntu-latest
    permissions:
      contents: read
      # For the reporter's `gh issue create` below (#422). Only the
      # required_status_checks half of the ruleset is asserted anywhere in
      # this job: the review-requirement half lives in classic protection
      # (`required_approving_review_count`), and `/branches/main/protection`
      # 403s for the Actions token, so it cannot be read from CI at all.
      # `postguard-js` asserts its review rule only because it keeps that
      # rule in a ruleset instead.
      issues: write
    steps:
      - uses: actions/checkout@v4
        with:
          # The changelog-coverage checker's self-test below (#412) runs
          # against fixtures that are real tags in this repo's history, so the
          # job needs full history and tags -- the default shallow checkout
          # makes the coverage script report exit 2 (undetermined) on every
          # one of them instead of exercising the case.
          fetch-depth: 0

      - name: Compare the live ruleset against the guard's constant
        shell: bash
        env:
          # Not required for the read -- unauthenticated works on a public repo.
          # It is passed because the runner's anonymous budget is 60/hour per
          # shared IP, and exhausting it would show up as exit 2 noise.
          GH_TOKEN: ${{ github.token }}
          GH_REPO: ${{ github.repository }}
        run: scripts/ruleset-drift-report.sh

      - name: Exit codes still mean what the gate reports
        shell: bash
        run: scripts/ruleset-drift-test.sh

      - name: Test the changelog-coverage checker
        shell: bash
        run: scripts/changelog-coverage-test.sh

      - name: Test the wasm-package checker
        shell: bash
        run: scripts/wasm-package-check-test.sh

      - name: Test the changelog-coverage reporter
        shell: bash
        run: scripts/changelog-coverage-report-test.sh

      - name: Test the ruleset-drift reporter
        shell: bash
        run: scripts/ruleset-drift-report-test.sh

      - name: Test the map-budget checker
        shell: bash
        run: scripts/map-budget-test.sh

Applying it is one paste (computes the current sha itself, so it works whether or not this exact command has already been tried):

gh api -X PUT repos/encryption4all/postguard/contents/.github/workflows/build.yml -f branch=dobby/map-budget-445 -f message="ci: apply the workflow half of the map-budget patch (#445)" -f sha="$(gh api "repos/encryption4all/postguard/contents/.github/workflows/build.yml?ref=dobby/map-budget-445" --jq .sha)" -f content=b246CiAgcHVzaDoKICAgIGJyYW5jaGVzOgogICAgICAtIG1haW4KICBwdWxsX3JlcXVlc3Q6CiAgICAjIGBlZGl0ZWRgIGluY2x1ZGVkIGZvciBiYXNlIHJldGFyZ2V0cyAoZS5nLiBhIHN0YWNrZWQgUFIncyBiYXNlIG1lcmdpbmcpOgogICAgIyBwYXRocy1maWx0ZXIncyB2ZXJkaWN0IGRlcGVuZHMgb24gdGhlIGJhc2UsIGFuZCB3aXRob3V0IGBlZGl0ZWRgIGEgc3RhbGUKICAgICMgdmVyZGljdCBzdGF5cyBhdHRhY2hlZCB0byB0aGUgdW5jaGFuZ2VkIGhlYWQgc2hhLgogICAgdHlwZXM6IFtvcGVuZWQsIHN5bmNocm9uaXplLCByZW9wZW5lZCwgZWRpdGVkXQogICAgYnJhbmNoZXM6CiAgICAgIC0gbWFpbgogIHdvcmtmbG93X2Rpc3BhdGNoOgoKbmFtZTogQ29udGludW91cyBpbnRlZ3JhdGlvbgoKam9iczoKICAjIHNvbmFycXViZToKICAjICAgbmFtZTogU29uYXJRdWJlCiAgIyAgIHJ1bnMtb246IHVidW50dS1sYXRlc3QKICAjICAgY29udGludWUtb24tZXJyb3I6IHRydWUKICAjICAgc3RlcHM6CiAgIyAgICAgLSB1c2VzOiBhY3Rpb25zL2NoZWNrb3V0QHY0CiAgIyAgICAgICB3aXRoOgogICMgICAgICAgICBmZXRjaC1kZXB0aDogMCAgIyBTaGFsbG93IGNsb25lcyBzaG91bGQgYmUgZGlzYWJsZWQgZm9yIGEgYmV0dGVyIHJlbGV2YW5jeSBvZiBhbmFseXNpcwogICMgICAgIC0gbmFtZTogU29uYXJRdWJlIFNjYW4KICAjICAgICAgIHVzZXM6IFNvbmFyU291cmNlL3NvbmFycXViZS1zY2FuLWFjdGlvbkB2NgogICMgICAgICAgZW52OgogICMgICAgICAgICBTT05BUl9UT0tFTjogJHt7IHNlY3JldHMuU09OQVJfVE9LRU4gfX0KICAjIFRoZSBtYXRyaXgga2V5cyBvbiB0aGUgY3JhdGUgZGlyZWN0b3J5IHJhdGhlciB0aGFuIGEgYHBnLWAgc3VmZml4LCBiZWNhdXNlCiAgIyBgY3J5cHRpZnlgIGRvZXMgbm90IGNhcnJ5IHRoYXQgcHJlZml4LiBUaGlzIHJlbmFtZXMgdGhlIGNoZWNrIGNvbnRleHRzIGZyb20KICAjIGBUZXN0IHdvcmtzcGFjZSAoY29yZSlgIHRvIGBUZXN0IHdvcmtzcGFjZSAocGctY29yZSlgOyBub3RoaW5nIHJlcXVpcmVkCiAgIyBwb2ludHMgYXQgdGhlbSB0b2RheSAob25seSB0aGUgdHdvIGBXaXJlIGNvbXBhdGAgY29udGV4dHMgYXJlIHJlcXVpcmVkKSwgYnV0CiAgIyBhIHJ1bGVzZXQgYWRkZWQgbGF0ZXIgbXVzdCB1c2UgdGhlIG5ldyBuYW1lcy4KICB0ZXN0OgogICAgbmFtZTogVGVzdCB3b3Jrc3BhY2UKICAgIHN0cmF0ZWd5OgogICAgICBtYXRyaXg6CiAgICAgICAgY3JhdGU6IFtwZy1jb3JlLCBwZy1wa2csIHBnLWNsaSwgcGctZmZpLCBjcnlwdGlmeV0KICAgIHJ1bnMtb246IHVidW50dS1sYXRlc3QKICAgIHN0ZXBzOgogICAgICAtIHVzZXM6IGFjdGlvbnMvY2hlY2tvdXRAdjQKICAgICAgLSB1c2VzOiBkdG9sbmF5L3J1c3QtdG9vbGNoYWluQHN0YWJsZQogICAgICAtIGlmOiAke3sgbWF0cml4LmNyYXRlID09ICdwZy1jb3JlJyB9fQogICAgICAgIHJ1bjogY2FyZ28gdGVzdCAtLW1hbmlmZXN0LXBhdGggcGctY29yZS9DYXJnby50b21sIC0tZmVhdHVyZXMgdGVzdCxydXN0LHN0cmVhbQogICAgICAjIGNyeXB0aWZ5IGRlY2xhcmVzIG5vIGZlYXR1cmVzIG9mIGl0cyBvd24sIHNvIGAtLWFsbC10YXJnZXRzYCAod2hhdCBpdHMKICAgICAgIyBvd24gQ0kgcmFuKSBpcyB0aGUgZXF1aXZhbGVudCBpbnZvY2F0aW9uLgogICAgICAtIGlmOiAke3sgbWF0cml4LmNyYXRlID09ICdjcnlwdGlmeScgfX0KICAgICAgICBydW46IGNhcmdvIHRlc3QgLS1tYW5pZmVzdC1wYXRoIGNyeXB0aWZ5L0NhcmdvLnRvbWwgLS1hbGwtdGFyZ2V0cwogICAgICAtIGlmOiAke3sgbWF0cml4LmNyYXRlICE9ICdwZy1jb3JlJyAmJiBtYXRyaXguY3JhdGUgIT0gJ2NyeXB0aWZ5JyB9fQogICAgICAgIHJ1bjogY2FyZ28gdGVzdCAtLW1hbmlmZXN0LXBhdGggJHt7IG1hdHJpeC5jcmF0ZSB9fS9DYXJnby50b21sIC0tYWxsLWZlYXR1cmVzCgogIGZvcm1hdDoKICAgIG5hbWU6IEZvcm1hdCB3b3Jrc3BhY2UKICAgIHN0cmF0ZWd5OgogICAgICBtYXRyaXg6CiAgICAgICAgY3JhdGU6IFtwZy1jb3JlLCBwZy1wa2csIHBnLWNsaSwgcGctZmZpLCBjcnlwdGlmeV0KICAgIHJ1bnMtb246IHVidW50dS1sYXRlc3QKICAgIHN0ZXBzOgogICAgICAtIHVzZXM6IGFjdGlvbnMvY2hlY2tvdXRAdjQKICAgICAgLSB1c2VzOiBkdG9sbmF5L3J1c3QtdG9vbGNoYWluQHN0YWJsZQogICAgICAtIHJ1bjogY2FyZ28gZm10IC0tbWFuaWZlc3QtcGF0aCAke3sgbWF0cml4LmNyYXRlIH19L0NhcmdvLnRvbWwgLS1hbGwgLS0gLS1jaGVjawoKICBjbGlwcHk6CiAgICBuYW1lOiBDbGlwcHkgd29ya3NwYWNlCiAgICBzdHJhdGVneToKICAgICAgbWF0cml4OgogICAgICAgIGNyYXRlOiBbcGctY29yZSwgcGctcGtnLCBwZy1jbGksIHBnLWZmaSwgY3J5cHRpZnldCiAgICBydW5zLW9uOiB1YnVudHUtbGF0ZXN0CiAgICBzdGVwczoKICAgICAgLSB1c2VzOiBhY3Rpb25zL2NoZWNrb3V0QHY0CiAgICAgIC0gdXNlczogZHRvbG5heS9ydXN0LXRvb2xjaGFpbkBzdGFibGUKICAgICAgICB3aXRoOgogICAgICAgICAgY29tcG9uZW50czogY2xpcHB5CiAgICAgIC0gaWY6ICR7eyBtYXRyaXguY3JhdGUgPT0gJ3BnLWNvcmUnIH19CiAgICAgICAgcnVuOiBjYXJnbyBjbGlwcHkgLS1tYW5pZmVzdC1wYXRoIHBnLWNvcmUvQ2FyZ28udG9tbCAtLWFsbC10YXJnZXRzIC0tZmVhdHVyZXMgdGVzdCxydXN0LHN0cmVhbSAtLSAtRCB3YXJuaW5ncwogICAgICAtIGlmOiAke3sgbWF0cml4LmNyYXRlID09ICdjcnlwdGlmeScgfX0KICAgICAgICBydW46IGNhcmdvIGNsaXBweSAtLW1hbmlmZXN0LXBhdGggY3J5cHRpZnkvQ2FyZ28udG9tbCAtLWFsbC10YXJnZXRzIC0tIC1EIHdhcm5pbmdzCiAgICAgIC0gaWY6ICR7eyBtYXRyaXguY3JhdGUgIT0gJ3BnLWNvcmUnICYmIG1hdHJpeC5jcmF0ZSAhPSAnY3J5cHRpZnknIH19CiAgICAgICAgcnVuOiBjYXJnbyBjbGlwcHkgLS1tYW5pZmVzdC1wYXRoICR7eyBtYXRyaXguY3JhdGUgfX0vQ2FyZ28udG9tbCAtLWFsbC10YXJnZXRzIC0tYWxsLWZlYXR1cmVzIC0tIC1EIHdhcm5pbmdzCgogIHRlc3Qtd2FzbS1icm93c2VyczoKICAgIG5hbWU6IFJ1biB3YXNtIHRlc3RzIGluIGJyb3dzZXJzCiAgICBzdHJhdGVneToKICAgICAgbWF0cml4OgogICAgICAgIGluY2x1ZGU6CiAgICAgICAgICAtIGJyb3dzZXI6IGNocm9tZQogICAgICAgICAgICBvczogdWJ1bnR1LWxhdGVzdAogICAgICAgICAgLSBicm93c2VyOiBmaXJlZm94CiAgICAgICAgICAgIG9zOiB1YnVudHUtbGF0ZXN0CiAgICAgICAgICAtIGJyb3dzZXI6IHNhZmFyaQogICAgICAgICAgICBvczogbWFjb3MtbGF0ZXN0CiAgICBydW5zLW9uOiAke3sgbWF0cml4Lm9zIH19CiAgICAjIFRoZSBzdWl0ZSBpcyAxNiB0ZXN0cyBhbmQgZmluaXNoZXMgaW4gfjI1czsgYSBwYXNzaW5nIGpvYiBpcyAyLTYgbWludXRlcywKICAgICMgYWxtb3N0IGFsbCBvZiBpdCBgY2FyZ28gaW5zdGFsbCB3YXNtLXBhY2tgIGFuZCB0aGUgcmVsZWFzZSBidWlsZC4gMTIKICAgICMgbWludXRlcyBpcyB+MnggdGhlIHNsb3dlc3Qgb2JzZXJ2ZWQgc3VjY2Vzcy4gVGhlIHByZXZpb3VzIGJ1ZGdldCB3YXMgMjAKICAgICMgbWludXRlcyBpbnNpZGUgYSBgbmljay1maWVsZHMvcmV0cnlgIHdyYXBwZXIsIHdoaWNoIG9uIDIwMjYtMDktMDMgdHVybmVkCiAgICAjIHRocmVlIHdlZGdlZCBzYWZhcmkgc2Vzc2lvbnMgaW50byB+MjItbWludXRlIGpvYnMgd2l0aCB+MTcgbWludXRlcyBvZgogICAgIyBzaWxlbmNlIGVhY2ggKCM0MTYpLgogICAgdGltZW91dC1taW51dGVzOiAxMgogICAgZW52OgogICAgICAjIH42eCB0aGUgc2xvd2VzdCBzaW5nbGUgdGVzdCAofjVzKS4gVGhpcyBpcyBhIHBlci10ZXN0IHRpbWVyIHJ1bm5pbmcKICAgICAgIyBpbnNpZGUgdGhlIGJyb3dzZXIgcGFnZSwgc28gaXQgZG9lcyBOT1QgYm91bmQgYSB3ZWRnZWQgc2FmYXJpZHJpdmVyCiAgICAgICMgc2Vzc2lvbiAtLSB0aGF0IGlzIHdoYXQgYHRpbWVvdXQtbWludXRlc2AgYWJvdmUgaXMgZm9yLiBJdCB3YXMgMTIwLAogICAgICAjIHdoaWNoIGlzIGJvdGggMjR4IHRoZSBzbG93ZXN0IHRlc3QgYW5kLCBhdCAxNiB0ZXN0cywgYSAzMi1taW51dGUKICAgICAgIyB3b3JzdCBjYXNlIHRoYXQgdGhlIGpvYiBidWRnZXQgY291bGQgbmV2ZXIgcmVhY2guCiAgICAgIFdBU01fQklOREdFTl9URVNUX1RJTUVPVVQ6IDMwCiAgICBzdGVwczoKICAgICAgLSB1c2VzOiBhY3Rpb25zL2NoZWNrb3V0QHY0CiAgICAgIC0gbmFtZTogSW5zdGFsbAogICAgICAgIHJ1bjogY2FyZ28gaW5zdGFsbCB3YXNtLXBhY2sKICAgICAgLSBpZjogJHt7IG1hdHJpeC5icm93c2VyID09ICdmaXJlZm94JyB9fQogICAgICAgIHJ1bjogc3VkbyBhcHQgdXBkYXRlICYmIHN1ZG8gYXB0IGluc3RhbGwgZmlyZWZveAogICAgICAjIEEgcGxhaW4gYHJ1bjpgLCBub3QgYG5pY2stZmllbGRzL3JldHJ5YC4gVGhlIHdyYXBwZXIgcmV0cmllZCBnZW51aW5lCiAgICAgICMgbm9uLXplcm8gZXhpdHMgKGhpZGluZyBhIHJlYWwgZmFpbHVyZSBiZWhpbmQgYSBzZWNvbmQgcm9sbCkgYW5kIGRpZAogICAgICAjIE5PVCByZXRyeSBhIHRpbWVvdXQsIHdoaWNoIGlzIHRoZSBvbmx5IHNoYXBlIHRoaXMgam9iIGhhcyBldmVyIGhpdAogICAgICAjIC0tIGByZXRyeV9vbjogZXJyb3JgIGV4Y2x1ZGVzIHRpbWVvdXRzLCBzbyBgbWF4X2F0dGVtcHRzOiAyYCBuZXZlcgogICAgICAjIGVuZ2FnZWQuIERyb3BwaW5nIGl0IGFsc28gcmVtb3ZlcyB0aGUgbGl0ZXJhbCBiYWNrc2xhc2ggdGhlIGFjdGlvbgogICAgICAjIGlucHV0IHJlcXVpcmVkIChgLS1cJHt7IG1hdHJpeC5icm93c2VyIH19YCksIHdoaWNoIHN1cnZpdmVkIGludG8gdGhlCiAgICAgICMgcmVzb2x2ZWQgY29tbWFuZCBhbmQgb25seSB3b3JrZWQgYmVjYXVzZSBhIHNoZWxsIHN0cmlwcGVkIGl0LgogICAgICAtIG5hbWU6IFJ1biB3YXNtLXBhY2sgYnJvd3NlciB0ZXN0cwogICAgICAgIHJ1bjogd2FzbS1wYWNrIHRlc3QgLS1yZWxlYXNlIC0taGVhZGxlc3MgLS0ke3sgbWF0cml4LmJyb3dzZXIgfX0gLi9wZy13YXNtCgogICMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiAgIyBXaXJlIGNvbXBhdGliaWxpdHk6IEhFQUQtc2VhbGVkIGNvbnRhaW5lcnMgbXVzdCBvcGVuIHdpdGggcHVibGlzaGVkIHJlYWRlcnMKICAjICgjMjUxIC8gIzI2MCkuICMyNjEgYWRkcyBhIHdpcmUtY29tcGF0LWpzIGpvYiB0aGF0IGRvd25sb2FkcyB0aGUgYXJ0aWZhY3QKICAjIHRoaXMgam9iIHVwbG9hZHMuCiAgIwogICMgVGhlIHBhdGggZmlsdGVyIGlzIGEgc3RlcCwgbm90IGFuIGBvbjogcGF0aHM6YCBrZXksIG9uIHB1cnBvc2U6IGEgam9iIHRoYXQKICAjIGlzIHNraXBwZWQgYnkgYSBwYXRoIGZpbHRlciBuZXZlciByZXBvcnRzLCBzbyBhIHJlcXVpcmVkIGNoZWNrIHdvdWxkIHNpdAogICMgcGVuZGluZyBmb3JldmVyIG9uIFBScyB0aGF0IGRvIG5vdCB0b3VjaCB0aGUgd2lyZSBzdXJmYWNlLiBUaGlzIHNoYXBlIGFsd2F5cwogICMgcmVwb3J0cyBhbmQgb25seSBkb2VzIHRoZSBleHBlbnNpdmUgd29yayB3aGVuIGl0IG5lZWRzIHRvLgogICMKICAjIE9uIGBwdXNoYCB0aGUgZmlsdGVyIGlzIHRoZSB3cm9uZyBpbnN0cnVtZW50IGFuZCBpcyBieXBhc3NlZCAoIzI5OSkuIEl0CiAgIyBkaWZmcyBvbmx5IHRoZSBwdXNoIHRoYXQgdHJpZ2dlcmVkIGl0LCBzbyBhIGNvbW1pdCB0aGF0IHJlYWNoZWQgYG1haW5gCiAgIyB3aXRob3V0IHRoaXMgZ2F0ZSBldmVyIHJ1bm5pbmcgaXMgbmV2ZXIgcmUtY2hlY2tlZCBhZnRlcndhcmRzIC0tIGFuZCB0aGUgam9iCiAgIyBzdGlsbCByZXBvcnRzIGdyZWVuLCBiZWNhdXNlICJmaWx0ZXIgc2FpZCBubyIgYW5kICJnYXRlIHBhc3NlZCIgYXJlIHRoZSBzYW1lCiAgIyBzdWNjZXNzLiBUaGF0IGlzIG5vdCBoeXBvdGhldGljYWw6ICMyOTcgY2hhbmdlZCBzZXZlbiBmaWxlcyB1bmRlcgogICMgYHBnLWNvbXBhdC8qKmAgYW5kIG1lcmdlZCBkdXJpbmcgdGhlIDIwMjYtMDgtMDYgQWN0aW9ucyBvdXRhZ2Ugd2l0aCBubyBydW4KICAjIGF0IGFsbCwgYW5kIHRoZSB0aHJlZSBgbWFpbmAgcnVucyBhZnRlciBpdCAoNTA2MTQ0YzIsIGMxNjgyYmE3LCAxMjM4Mzg5YikKICAjIGVhY2ggcmVwb3J0ZWQgYFdpcmUgY29tcGF0OiBzdWNjZXNzYCB3aXRoIGBTZWFsYC9gT3BlbmAgYm90aCBza2lwcGVkLiBTbyBvbgogICMgYSBwdXNoIHRoZSBnYXRlIGFsd2F5cyBkb2VzIHRoZSByZWFsIHdvcms6IHdoYXRldmVyIGlzIG9uIGBtYWluYCBpcyB3aGF0CiAgIyBnZXRzIHNlYWxlZCBhbmQgb3BlbmVkLCBob3dldmVyIGl0IGdvdCB0aGVyZS4KICAjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQogIHdpcmUtY29tcGF0LXJ1c3Q6CiAgICBuYW1lOiBXaXJlIGNvbXBhdCAocHVibGlzaGVkIHBnLWNvcmUpCiAgICBydW5zLW9uOiB1YnVudHUtbGF0ZXN0CiAgICBwZXJtaXNzaW9uczoKICAgICAgY29udGVudHM6IHJlYWQKICAgICAgcHVsbC1yZXF1ZXN0czogcmVhZCAjIHBhdGhzLWZpbHRlciByZWFkcyB0aGUgUFIncyBjaGFuZ2VkIGZpbGVzCiAgICAjIHdpcmUtY29tcGF0LWpzICgjMjYxKSBvcGVucyB0aGUgc2FtZSBieXRlcyB3aXRoIHRoZSBwdWJsaXNoZWQgbnBtIHJlYWRlcnMuCiAgICAjIEl0IGRvd25sb2FkcyB0aGUgYXJ0aWZhY3QgdGhlIHNlYWwgc3RlcCBiZWxvdyBwcm9kdWNlcywgc28gaXQgZ2F0ZXMgb24gdGhlCiAgICAjIHNhbWUgb3V0Y29tZSB0aGUgdXBsb2FkIGdhdGVzIG9uIHJhdGhlciB0aGFuIHJlLXJ1bm5pbmcgdGhlIHBhdGggZmlsdGVyIGFuZAogICAgIyBkcmlmdGluZyBvdXQgb2Ygc3RlcCB3aXRoIGl0LgogICAgb3V0cHV0czoKICAgICAgc2VhbGVkOiAke3sgc3RlcHMuc2VhbC5vdXRjb21lIH19CiAgICBzdGVwczoKICAgICAgLSB1c2VzOiBhY3Rpb25zL2NoZWNrb3V0QHY0CiAgICAgICMgU0hBLXBpbm5lZCBhcyB0aGUgb25lIGFjdGlvbiBuZXcgdG8gdGhpcyByZXBvOyB0aGUgcmVzdCBmb2xsb3cgdGhlCiAgICAgICMgZmlsZSdzIGV4aXN0aW5nIGZsb2F0aW5nLXJlZiBjb252ZW50aW9uLgogICAgICAtIHVzZXM6IGRvcm55L3BhdGhzLWZpbHRlckA3YjQ1MGZmZjIxNDczYmNhNDYxZDRiOTJjZTQxNGI5ZDA0MjBkNzA2ICMgdjQuMC4yCiAgICAgICAgaWQ6IGNoYW5nZXMKICAgICAgICB3aXRoOgogICAgICAgICAgZmlsdGVyczogfAogICAgICAgICAgICB3aXJlOgogICAgICAgICAgICAgIC0gJ3BnLWNvcmUvKionCiAgICAgICAgICAgICAgLSAncGctd2FzbS8qKicKICAgICAgICAgICAgICAtICdwZy1jb21wYXQvKionCiAgICAgICAgICAgICAgLSAncGctY29tcGF0LWpzLyoqJwogICAgICAgICAgICAgICMgcGctY29yZSBoYXMgbm8gbG9ja2ZpbGUgb2YgaXRzIG93bjogdGhlIGJ5dGVzIEhFQUQgc2VhbHMgYXJlIGEKICAgICAgICAgICAgICAjIGZ1bmN0aW9uIG9mIHRoZSBST09UIGxvY2tmaWxlIChhIGJpbmNvZGUtbmV4dC9pYmUvc2VyZGUgYnVtcAogICAgICAgICAgICAgICMgdG91Y2hlcyBvbmx5IHRoZXNlIHR3byBmaWxlcykuIFJvb3QgQ2FyZ28udG9tbCBhbHNvIGhvbGRzIHRoZQogICAgICAgICAgICAgICMgZXhjbHVkZSBsaXN0IHRoYXQga2VlcHMgcGctY29tcGF0IG9uIGNyYXRlcy5pbyBwZy1jb3JlLgogICAgICAgICAgICAgIC0gJ0NhcmdvLmxvY2snCiAgICAgICAgICAgICAgLSAnQ2FyZ28udG9tbCcKICAgICAgICAgICAgICAtICcuZ2l0aHViL3dvcmtmbG93cy9idWlsZC55bWwnCgogICAgICAjIE9uZSBkZWNpc2lvbiwgcmVhZCBieSBldmVyeSBzdGVwIGJlbG93LCBzbyB0aGUgcHVzaCBvdmVycmlkZSBjYW5ub3QgYmUKICAgICAgIyBhcHBsaWVkIHRvIHRoZSBzZWFsIGJ1dCBmb3Jnb3R0ZW4gb24gdGhlIG9wZW4uCiAgICAgIC0gbmFtZTogRGVjaWRlIHdoZXRoZXIgdG8gZG8gdGhlIHJlYWwgd29yawogICAgICAgIGlkOiBnYXRlCiAgICAgICAgc2hlbGw6IGJhc2gKICAgICAgICBlbnY6CiAgICAgICAgICBXSVJFX0NIQU5HRUQ6ICR7eyBzdGVwcy5jaGFuZ2VzLm91dHB1dHMud2lyZSB9fQogICAgICAgIHJ1bjogfAogICAgICAgICAgaWYgW1sgIiRXSVJFX0NIQU5HRUQiID09ICJ0cnVlIiB8fCAiJEdJVEhVQl9FVkVOVF9OQU1FIiA9PSAicHVzaCIgXV07IHRoZW4KICAgICAgICAgICAgZWNobyAicnVuPXRydWUiID4+ICIkR0lUSFVCX09VVFBVVCIKICAgICAgICAgIGVsc2UKICAgICAgICAgICAgZWNobyAicnVuPWZhbHNlIiA+PiAiJEdJVEhVQl9PVVRQVVQiCiAgICAgICAgICBmaQoKICAgICAgLSBpZjogc3RlcHMuZ2F0ZS5vdXRwdXRzLnJ1biA9PSAndHJ1ZScKICAgICAgICB1c2VzOiBkdG9sbmF5L3J1c3QtdG9vbGNoYWluQHN0YWJsZQogICAgICAgIHdpdGg6CiAgICAgICAgICBjb21wb25lbnRzOiBjbGlwcHksIHJ1c3RmbXQKCiAgICAgIC0gbmFtZTogU2VhbCB0aGUgc2FtcGxlIHNldCB3aXRoIEhFQUQKICAgICAgICBpZDogc2VhbAogICAgICAgIGlmOiBzdGVwcy5nYXRlLm91dHB1dHMucnVuID09ICd0cnVlJwogICAgICAgIHNoZWxsOiBiYXNoCiAgICAgICAgcnVuOiB8CiAgICAgICAgICBjYXJnbyBydW4gLS1sb2NrZWQgLXAgcGctY29yZSAtLWZlYXR1cmVzIHN0cmVhbSAtLWV4YW1wbGUgc2VhbC1zYW1wbGVzIFwKICAgICAgICAgICAgLS0gIiRSVU5ORVJfVEVNUC93aXJlLWNvbXBhdC1hcnRpZmFjdHMiCgogICAgICAjIEhhbmQtb2ZmIHRvIHdpcmUtY29tcGF0LWpzICgjMjYxKS4gVXBsb2FkZWQgYmVmb3JlIHRoZSByZWFkZXIgcnVucyBzbyBhCiAgICAgICMgcmVkIGdhdGUgc3RpbGwgbGVhdmVzIHRoZSBieXRlcyB0aGF0IGJyb2tlIGl0LgogICAgICAtIG5hbWU6IFVwbG9hZCB0aGUgc2FtcGxlIHNldAogICAgICAgIGlmOiBzdGVwcy5zZWFsLm91dGNvbWUgPT0gJ3N1Y2Nlc3MnCiAgICAgICAgdXNlczogYWN0aW9ucy91cGxvYWQtYXJ0aWZhY3RAdjQKICAgICAgICB3aXRoOgogICAgICAgICAgbmFtZTogd2lyZS1jb21wYXQtYXJ0aWZhY3RzLSR7eyBnaXRodWIuZXZlbnQucHVsbF9yZXF1ZXN0LmhlYWQuc2hhIHx8IGdpdGh1Yi5zaGEgfX0KICAgICAgICAgIHBhdGg6ICR7eyBydW5uZXIudGVtcCB9fS93aXJlLWNvbXBhdC1hcnRpZmFjdHMKICAgICAgICAgIHJldGVudGlvbi1kYXlzOiA3CgogICAgICAtIG5hbWU6IE9wZW4gaXQgd2l0aCBwdWJsaXNoZWQgcGctY29yZQogICAgICAgIGlmOiBzdGVwcy5nYXRlLm91dHB1dHMucnVuID09ICd0cnVlJwogICAgICAgIHNoZWxsOiBiYXNoCiAgICAgICAgZW52OgogICAgICAgICAgUEdfQ09NUEFUX0FSVElGQUNUUzogJHt7IHJ1bm5lci50ZW1wIH19L3dpcmUtY29tcGF0LWFydGlmYWN0cwogICAgICAgIHJ1bjogY2FyZ28gdGVzdCAtLW1hbmlmZXN0LXBhdGggcGctY29tcGF0L0NhcmdvLnRvbWwgLS1sb2NrZWQKCiAgIyBwZy1jb21wYXQgaXMgb3V0c2lkZSB0aGUgd29ya3NwYWNlLCBzbyB0aGUgcGVyLWNyYXRlIGZtdC9jbGlwcHkgbWF0cmljZXMgZG8KICAjIG5vdCBjb3ZlciBpdC4gS2VwdCBzZXBhcmF0ZSBmcm9tIHdpcmUtY29tcGF0LXJ1c3Qgb24gcHVycG9zZTogYSBuZXcgc3RhYmxlCiAgIyBydXN0YyBsaW50IG11c3Qgbm90IHJlZCBhIHJlcXVpcmVkIGNoZWNrIG5hbWVkIGFmdGVyIHdpcmUgY29tcGF0aWJpbGl0eSwKICAjIG5vciBwcmUtZW1wdCB0aGUgc2VhbC9yZWFkIHN0ZXBzIHRoYXQgYW5zd2VyIGl0LgogIHBnLWNvbXBhdC1saW50OgogICAgbmFtZTogTGludCBwZy1jb21wYXQKICAgIHJ1bnMtb246IHVidW50dS1sYXRlc3QKICAgIHBlcm1pc3Npb25zOgogICAgICBjb250ZW50czogcmVhZAogICAgICBwdWxsLXJlcXVlc3RzOiByZWFkCiAgICBzdGVwczoKICAgICAgLSB1c2VzOiBhY3Rpb25zL2NoZWNrb3V0QHY0CiAgICAgIC0gdXNlczogZG9ybnkvcGF0aHMtZmlsdGVyQDdiNDUwZmZmMjE0NzNiY2E0NjFkNGI5MmNlNDE0YjlkMDQyMGQ3MDYgIyB2NC4wLjIKICAgICAgICBpZDogY2hhbmdlcwogICAgICAgIHdpdGg6CiAgICAgICAgICBmaWx0ZXJzOiB8CiAgICAgICAgICAgIHdpcmU6CiAgICAgICAgICAgICAgLSAncGctY29tcGF0LyoqJwogICAgICAgICAgICAgIC0gJ0NhcmdvLmxvY2snCiAgICAgICAgICAgICAgLSAnQ2FyZ28udG9tbCcKICAgICAgICAgICAgICAtICcuZ2l0aHViL3dvcmtmbG93cy9idWlsZC55bWwnCiAgICAgIC0gaWY6IHN0ZXBzLmNoYW5nZXMub3V0cHV0cy53aXJlID09ICd0cnVlJwogICAgICAgIHVzZXM6IGR0b2xuYXkvcnVzdC10b29sY2hhaW5Ac3RhYmxlCiAgICAgICAgd2l0aDoKICAgICAgICAgIGNvbXBvbmVudHM6IGNsaXBweSwgcnVzdGZtdAogICAgICAtIG5hbWU6IEZvcm1hdCBwZy1jb21wYXQKICAgICAgICBpZjogc3RlcHMuY2hhbmdlcy5vdXRwdXRzLndpcmUgPT0gJ3RydWUnCiAgICAgICAgcnVuOiBjYXJnbyBmbXQgLS1tYW5pZmVzdC1wYXRoIHBnLWNvbXBhdC9DYXJnby50b21sIC0tYWxsIC0tIC0tY2hlY2sKICAgICAgLSBuYW1lOiBDbGlwcHkgcGctY29tcGF0CiAgICAgICAgaWY6IHN0ZXBzLmNoYW5nZXMub3V0cHV0cy53aXJlID09ICd0cnVlJwogICAgICAgIHJ1bjogY2FyZ28gY2xpcHB5IC0tbWFuaWZlc3QtcGF0aCBwZy1jb21wYXQvQ2FyZ28udG9tbCAtLWFsbC10YXJnZXRzIC0tbG9ja2VkIC0tIC1EIHdhcm5pbmdzCgogICMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiAgIyBQdWJsaWMtQVBJIHNlbXZlciBnYXRlICgjMjUzKS4gRmFpbHMgYSBQUiB0aGF0IGJyZWFrcyBwZy1jb3JlJ3Mgb3IgcGctd2FzbSdzCiAgIyBwdWJsaWMgQVBJIHdpdGhvdXQgZGVjbGFyaW5nIHRoZSBicmVhay4KICAjCiAgIyBWZXJzaW9ucyBoZXJlIGFyZSBidW1wZWQgYnkgcmVsZWFzZS1wbHosIG5vdCBieSB0aGUgUFIgdGhhdCBtYWtlcyB0aGUKICAjIGNoYW5nZSwgc28gImRlY2xhcmVkIiBtZWFucyB0aGUgY29udmVudGlvbmFsLWNvbW1pdCBgIWAgbWFya2VyIHRoYXQKICAjIHJlbGVhc2UtcGx6IHR1cm5zIGludG8gYSBtYWpvciBidW1wLiBUaGUgbWFya2VyIGlzIHJlYWQgb2ZmIHRoZSBQUiB0aXRsZQogICMgaW50byBTRU1WRVJfUkVMRUFTRV9UWVBFOyBgZWRpdGVkYCBpcyBhbHJlYWR5IGluIHRoaXMgZmlsZSdzIHRyaWdnZXIgbGlzdCwKICAjIHNvIGFkZGluZyB0aGUgYCFgIHRvIHRoZSB0aXRsZSByZS1ydW5zIHRoZSBnYXRlLgogICMKICAjIFNhbWUgYWx3YXlzLXJlcG9ydHMgc2hhcGUgYXMgd2lyZS1jb21wYXQtcnVzdDogdGhlIHBhdGggZmlsdGVyIGlzIGEgc3RlcCwKICAjIG5vdCBhbiBgb246IHBhdGhzOmAga2V5LCBzbyBhIHJlcXVpcmVkIGNoZWNrIG5ldmVyIHNpdHMgcGVuZGluZy4KICAjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQogIHNlbXZlci1jaGVja3M6CiAgICBuYW1lOiBQdWJsaWMgQVBJIHNlbXZlcgogICAgaWY6IGdpdGh1Yi5ldmVudF9uYW1lID09ICdwdWxsX3JlcXVlc3QnCiAgICBydW5zLW9uOiB1YnVudHUtbGF0ZXN0CiAgICBwZXJtaXNzaW9uczoKICAgICAgY29udGVudHM6IHJlYWQKICAgICAgcHVsbC1yZXF1ZXN0czogcmVhZCAjIHBhdGhzLWZpbHRlciByZWFkcyB0aGUgUFIncyBjaGFuZ2VkIGZpbGVzCiAgICBzdGVwczoKICAgICAgIyBwZy13YXNtIGlzIG5vdCBwdWJsaXNoZWQgdG8gY3JhdGVzLmlvLCBzbyBpdHMgYmFzZWxpbmUgaXMgb3JpZ2luL21haW4KICAgICAgIyByYXRoZXIgdGhhbiBhIHJlZ2lzdHJ5IHZlcnNpb24sIGFuZCB0aGUgZnVsbCBoaXN0b3J5IGhhcyB0byBiZSBoZXJlLgogICAgICAtIHVzZXM6IGFjdGlvbnMvY2hlY2tvdXRAdjQKICAgICAgICB3aXRoOgogICAgICAgICAgZmV0Y2gtZGVwdGg6IDAKCiAgICAgIC0gdXNlczogZG9ybnkvcGF0aHMtZmlsdGVyQDdiNDUwZmZmMjE0NzNiY2E0NjFkNGI5MmNlNDE0YjlkMDQyMGQ3MDYgIyB2NC4wLjIKICAgICAgICBpZDogY2hhbmdlcwogICAgICAgIHdpdGg6CiAgICAgICAgICBmaWx0ZXJzOiB8CiAgICAgICAgICAgIGFwaToKICAgICAgICAgICAgICAtICdwZy1jb3JlLyoqJwogICAgICAgICAgICAgIC0gJ3BnLXdhc20vKionCiAgICAgICAgICAgICAgLSAnQ2FyZ28udG9tbCcKICAgICAgICAgICAgICAtICdDYXJnby5sb2NrJwogICAgICAgICAgICAgIC0gJ3NjcmlwdHMvc2VtdmVyLWNoZWNrcy5zaCcKICAgICAgICAgICAgICAtICdzY3JpcHRzL3NlbXZlci1jaGVja3MtdGVzdC5zaCcKICAgICAgICAgICAgICAtICcuZ2l0aHViL3dvcmtmbG93cy9idWlsZC55bWwnCgogICAgICAjIFN0dWJzIGNhcmdvLCBzbyBpdCBuZWVkcyBubyB0b29sY2hhaW4gYW5kIHJ1bnMgaW4gdW5kZXIgYSBzZWNvbmQuIEl0CiAgICAgICMgY292ZXJzIHRoZSAxMDAtdnMtMTAxIGV4aXQtY29kZSBtYXBwaW5nIHRoZSBnYXRlIGRlcGVuZHMgb246IDEwMCBpcyBhCiAgICAgICMgc2VtdmVyIHZpb2xhdGlvbiwgMTAxIGlzIHRoZSB0b29sIG9yIHRoZSBidWlsZCBmYWlsaW5nLgogICAgICAtIG5hbWU6IFRlc3QgdGhlIGdhdGUgc2NyaXB0CiAgICAgICAgaWY6IHN0ZXBzLmNoYW5nZXMub3V0cHV0cy5hcGkgPT0gJ3RydWUnCiAgICAgICAgc2hlbGw6IGJhc2gKICAgICAgICBydW46IC4vc2NyaXB0cy9zZW12ZXItY2hlY2tzLXRlc3Quc2gKCiAgICAgIC0gaWY6IHN0ZXBzLmNoYW5nZXMub3V0cHV0cy5hcGkgPT0gJ3RydWUnCiAgICAgICAgdXNlczogZHRvbG5heS9ydXN0LXRvb2xjaGFpbkBzdGFibGUKICAgICAgICB3aXRoOgogICAgICAgICAgdGFyZ2V0czogd2FzbTMyLXVua25vd24tdW5rbm93bgoKICAgICAgIyBQaW5uZWQgcmVsZWFzZSBiaW5hcnkgcmF0aGVyIHRoYW4gYGNhcmdvIGluc3RhbGxgIChhIGZpdmUtbWludXRlIGJ1aWxkKQogICAgICAjIG9yIGEgZm91cnRoIHRoaXJkLXBhcnR5IGFjdGlvbi4gQnVtcCB0aGUgdmVyc2lvbiBhbmQgdGhlIGRpZ2VzdCB0b2dldGhlci4KICAgICAgLSBuYW1lOiBJbnN0YWxsIGNhcmdvLXNlbXZlci1jaGVja3MKICAgICAgICBpZjogc3RlcHMuY2hhbmdlcy5vdXRwdXRzLmFwaSA9PSAndHJ1ZScKICAgICAgICBzaGVsbDogYmFzaAogICAgICAgIGVudjoKICAgICAgICAgIFZFUlNJT046IDAuNDkuMAogICAgICAgICAgU0hBMjU2OiA3MmY2ODM0ZDc1ZDI4YTY2ZTAyYzlmZDZhMjMwY2U5MDFiYjMwZWVlNjA2N2I4NTg2N2E5NzQ0NWRmMDQwZTRhCiAgICAgICAgcnVuOiB8CiAgICAgICAgICBjdXJsIC1zU2ZMIC1vICIkUlVOTkVSX1RFTVAvY2FyZ28tc2VtdmVyLWNoZWNrcy50YXIuZ3oiIFwKICAgICAgICAgICAgImh0dHBzOi8vZ2l0aHViLmNvbS9vYmkxa2Vub2JpL2NhcmdvLXNlbXZlci1jaGVja3MvcmVsZWFzZXMvZG93bmxvYWQvdiR7VkVSU0lPTn0vY2FyZ28tc2VtdmVyLWNoZWNrcy14ODZfNjQtdW5rbm93bi1saW51eC1nbnUudGFyLmd6IgogICAgICAgICAgZWNobyAiJHtTSEEyNTZ9ICAkUlVOTkVSX1RFTVAvY2FyZ28tc2VtdmVyLWNoZWNrcy50YXIuZ3oiIHwgc2hhMjU2c3VtIC1jIC0KICAgICAgICAgIHRhciAteHpmICIkUlVOTkVSX1RFTVAvY2FyZ28tc2VtdmVyLWNoZWNrcy50YXIuZ3oiIC1DICIkSE9NRS8uY2FyZ28vYmluIiBjYXJnby1zZW12ZXItY2hlY2tzCgogICAgICAjIFRoZSBQUiB0aXRsZSwgbm90IHRoZSBjb21taXQgc3ViamVjdDogUFJzIGFyZSBzcXVhc2gtbWVyZ2VkIGhlcmUsIHNvIHRoZQogICAgICAjIHRpdGxlIGlzIHdoYXQgcmVsZWFzZS1wbHogYW5kIHRoZSBDb252ZW50aW9uYWwgQ29tbWl0IGNoZWNrIHJlYWQuIFBhc3NlZAogICAgICAjIHRocm91Z2ggdGhlIGVudmlyb25tZW50IHJhdGhlciB0aGFuIGludGVycG9sYXRlZCBpbnRvIHRoZSBzY3JpcHQsIHNvIGEKICAgICAgIyBQUiB0aXRsZSBjYW5ub3QgaW5qZWN0IHNoZWxsLgogICAgICAjCiAgICAgICMgVGhlIHRpdGxlIGAhYCBpcyB0aGUgb25seSBhY2NlcHRlZCBkZWNsYXJhdGlvbi4gQSBgQlJFQUtJTkcgQ0hBTkdFOmAKICAgICAgIyBmb290ZXIgaW4gdGhlIFBSICpib2R5KiBpcyBkZWxpYmVyYXRlbHkgbm90IGFjY2VwdGVkOiB0aGlzIHJlcG8ncwogICAgICAjIHNxdWFzaF9tZXJnZV9jb21taXRfbWVzc2FnZSBpcyBDT01NSVRfTUVTU0FHRVMsIHNvIHRoZSBib2R5IG5ldmVyIHJlYWNoZXMKICAgICAgIyB0aGUgc3F1YXNoZWQgY29tbWl0LCBhbmQgcmVsZWFzZS1wbHogcmVhZHMgY29tbWl0cy4gSG9ub3VyaW5nIGEgYm9keS1vbmx5CiAgICAgICMgZm9vdGVyIHdvdWxkIHBhc3MgdGhlIGdhdGUgb24gYSBgZml4KHBnLWNvcmUpOmAgUFIgdGhhdCByZWxlYXNlLXBseiB0aGVuCiAgICAgICMgcHVibGlzaGVzIGFzIGEgcGF0Y2ggcmVsZWFzZSBvZiBhIGJyZWFraW5nIGNoYW5nZS4KICAgICAgLSBuYW1lOiBSZWFkIHRoZSBicmVha2luZy1jaGFuZ2UgZGVjbGFyYXRpb24gb2ZmIHRoZSBQUiB0aXRsZQogICAgICAgIGlkOiBkZWNsYXJlZAogICAgICAgIGlmOiBzdGVwcy5jaGFuZ2VzLm91dHB1dHMuYXBpID09ICd0cnVlJwogICAgICAgIHNoZWxsOiBiYXNoCiAgICAgICAgZW52OgogICAgICAgICAgUFJfVElUTEU6ICR7eyBnaXRodWIuZXZlbnQucHVsbF9yZXF1ZXN0LnRpdGxlIH19CiAgICAgICAgcnVuOiB8CiAgICAgICAgICBpZiBbWyAiJFBSX1RJVExFIiA9fiBeW2EtekEtWl0rKFwoW15cKV0qXCkpPyE6IF1dOyB0aGVuCiAgICAgICAgICAgIGVjaG8gIkJyZWFraW5nIGNoYW5nZSBkZWNsYXJlZCBpbiB0aGUgUFIgdGl0bGU7IGEgbWFqb3IgYnVtcCBpcyBhbGxvd2VkLiIKICAgICAgICAgICAgZWNobyAicmVsZWFzZV90eXBlPW1ham9yIiA+PiAiJEdJVEhVQl9PVVRQVVQiCiAgICAgICAgICBlbHNlCiAgICAgICAgICAgIGVjaG8gIk5vICchJyBpbiB0aGUgUFIgdGl0bGU7IGFueSBicmVha2luZyBjaGFuZ2Ugd2lsbCBmYWlsIHRoZSBnYXRlLiIKICAgICAgICAgIGZpCgogICAgICAtIG5hbWU6IENoZWNrIHRoZSBwdWJsaWMgQVBJCiAgICAgICAgaWY6IHN0ZXBzLmNoYW5nZXMub3V0cHV0cy5hcGkgPT0gJ3RydWUnCiAgICAgICAgc2hlbGw6IGJhc2gKICAgICAgICBlbnY6CiAgICAgICAgICBTRU1WRVJfUkVMRUFTRV9UWVBFOiAke3sgc3RlcHMuZGVjbGFyZWQub3V0cHV0cy5yZWxlYXNlX3R5cGUgfX0KICAgICAgICBydW46IC4vc2NyaXB0cy9zZW12ZXItY2hlY2tzLnNoCiAgIyBUaGUgTm9kZSBoYWxmIG9mIHRoZSBzYW1lIGdhdGUgKCMyNjEpOiB0aGUgY29udGFpbmVycyB3aXJlLWNvbXBhdC1ydXN0CiAgIyBzZWFsZWQgbXVzdCBhbHNvIG9wZW4gd2l0aCB0aGUgcHVibGlzaGVkIG5wbSByZWFkZXJzIGluIENPTVBBVElCSUxJVFkubWQncwogICMgc3VwcG9ydCB3aW5kb3cuIEl0IGRvd25sb2FkcyByYXRoZXIgdGhhbiByZS1zZWFscywgc28gYm90aCBoYWx2ZXMgYXJlIGhlbGQKICAjIHRvIHRoZSBzYW1lIGJ5dGVzIGFuZCBhIG5vbi1kZXRlcm1pbmlzdGljIHNlYWxlciBjYW5ub3QgaGlkZSBiZXR3ZWVuIHRoZW0uCiAgIwogICMgYGlmOiAhY2FuY2VsbGVkKClgIHJhdGhlciB0aGFuIHRoZSBkZWZhdWx0OiBhIHJlZCB3aXJlLWNvbXBhdC1ydXN0IGlzCiAgIyBleGFjdGx5IHdoZW4gaXQgaXMgd29ydGgga25vd2luZyB3aGV0aGVyIHRoZSBKUyByZWFkZXJzIGJyb2tlIHRoZSBzYW1lIHdheSwKICAjIGFuZCB0aGUgYXJ0aWZhY3QgaXMgdXBsb2FkZWQgYmVmb3JlIHRoYXQgam9iJ3MgcmVhZCBzdGVwIGZvciB0aGlzIHJlYXNvbi4KICAjIFdpdGhvdXQgaXQsIGBuZWVkc2Agd291bGQgc2tpcCB0aGlzIGpvYiBvbiB0aGUgcnVuIHdoZXJlIGl0IGhhcyB0aGUgbW9zdCB0bwogICMgc2F5LgogICMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiAgd2lyZS1jb21wYXQtanM6CiAgICBuYW1lOiBXaXJlIGNvbXBhdCAocHVibGlzaGVkIHBnLXdhc20vcGctanMpCiAgICBydW5zLW9uOiB1YnVudHUtbGF0ZXN0CiAgICBuZWVkczogd2lyZS1jb21wYXQtcnVzdAogICAgaWY6ICR7eyAhY2FuY2VsbGVkKCkgfX0KICAgIHBlcm1pc3Npb25zOgogICAgICBjb250ZW50czogcmVhZAogICAgc3RlcHM6CiAgICAgIC0gdXNlczogYWN0aW9ucy9jaGVja291dEB2NAoKICAgICAgIyBFdmVyeSBzdGVwIGJlbG93IGlzIGJlaGluZCB0aGUgc2VhbCBvdXRjb21lLCBub3QganVzdCB0aGUgZG93bmxvYWQuCiAgICAgICMgYG91dGNvbWVgIGlzICdza2lwcGVkJyB3aGVuIHRoZSBwYXRoIGZpbHRlciBzYWlkIG5vIChub3RoaW5nIHRvIG9wZW4sCiAgICAgICMgcmVwb3J0IHN1Y2Nlc3MpIGFuZCAnZmFpbHVyZScgd2hlbiBzZWFsaW5nIGl0c2VsZiBicm9rZSAod2lyZS1jb21wYXQtcnVzdAogICAgICAjIG93bnMgdGhhdCBmYWlsdXJlOyBkbyBub3QgcmVwb3J0IGl0IHR3aWNlKS4KICAgICAgLSBpZjogbmVlZHMud2lyZS1jb21wYXQtcnVzdC5vdXRwdXRzLnNlYWxlZCA9PSAnc3VjY2VzcycKICAgICAgICB1c2VzOiBhY3Rpb25zL3NldHVwLW5vZGVAdjYKICAgICAgICB3aXRoOgogICAgICAgICAgbm9kZS12ZXJzaW9uOiAnMjQnCiAgICAgICAgICBjYWNoZTogbnBtCiAgICAgICAgICBjYWNoZS1kZXBlbmRlbmN5LXBhdGg6IHBnLWNvbXBhdC1qcy9wYWNrYWdlLWxvY2suanNvbgoKICAgICAgLSBuYW1lOiBEb3dubG9hZCB0aGUgc2FtcGxlIHNldAogICAgICAgIGlmOiBuZWVkcy53aXJlLWNvbXBhdC1ydXN0Lm91dHB1dHMuc2VhbGVkID09ICdzdWNjZXNzJwogICAgICAgIHVzZXM6IGFjdGlvbnMvZG93bmxvYWQtYXJ0aWZhY3RAdjQKICAgICAgICB3aXRoOgogICAgICAgICAgbmFtZTogd2lyZS1jb21wYXQtYXJ0aWZhY3RzLSR7eyBnaXRodWIuZXZlbnQucHVsbF9yZXF1ZXN0LmhlYWQuc2hhIHx8IGdpdGh1Yi5zaGEgfX0KICAgICAgICAgIHBhdGg6ICR7eyBydW5uZXIudGVtcCB9fS93aXJlLWNvbXBhdC1hcnRpZmFjdHMKCiAgICAgICMgY2ksIG5vdCBpbnN0YWxsOiB0aGUgcmVhZGVycyBhcmUgcGlubmVkIGJ5IHBnLWNvbXBhdC1qcy9wYWNrYWdlLWxvY2suanNvbiwKICAgICAgIyBhbmQgYSBnYXRlIHRoYXQgcXVpZXRseSByZXNvbHZlZCBhIGRpZmZlcmVudCByZWFkZXIgd291bGQgYmUgbWVhc3VyaW5nCiAgICAgICMgc29tZXRoaW5nIG90aGVyIHRoYW4gdGhlIHN1cHBvcnQgd2luZG93LgogICAgICAtIG5hbWU6IEluc3RhbGwgdGhlIHB1Ymxpc2hlZCByZWFkZXJzCiAgICAgICAgaWY6IG5lZWRzLndpcmUtY29tcGF0LXJ1c3Qub3V0cHV0cy5zZWFsZWQgPT0gJ3N1Y2Nlc3MnCiAgICAgICAgc2hlbGw6IGJhc2gKICAgICAgICB3b3JraW5nLWRpcmVjdG9yeTogcGctY29tcGF0LWpzCiAgICAgICAgcnVuOiBucG0gY2kKCiAgICAgIC0gbmFtZTogT3BlbiB0aGUgc2V0IHdpdGggcHVibGlzaGVkIHBnLXdhc20vcGctanMKICAgICAgICBpZjogbmVlZHMud2lyZS1jb21wYXQtcnVzdC5vdXRwdXRzLnNlYWxlZCA9PSAnc3VjY2VzcycKICAgICAgICBzaGVsbDogYmFzaAogICAgICAgIHdvcmtpbmctZGlyZWN0b3J5OiBwZy1jb21wYXQtanMKICAgICAgICBlbnY6CiAgICAgICAgICBQR19DT01QQVRfQVJUSUZBQ1RTOiAke3sgcnVubmVyLnRlbXAgfX0vd2lyZS1jb21wYXQtYXJ0aWZhY3RzCiAgICAgICAgcnVuOiBucG0gdGVzdAoKICAjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQogICMgU2luZ2xlIHJlcXVpcmVkIGNoZWNrIGNvbnNvbGlkYXRpbmcgdGhlIHR3byBoYWx2ZXMgYWJvdmUgKCMyNjIpLiBUaGUKICAjIHJlcG9pbnQgaGFwcGVuZWQgaW4gIzI5NjogIldpcmUgY29tcGF0IiBpcyB0aGUgb25lIHJlcXVpcmVkIGNvbnRleHQsIGFuZCB0aGUKICAjIHR3byBwZXItbGFuZ3VhZ2UgbmFtZXMgYXJlIG5vIGxvbmdlciByZXF1aXJlZCBpbmRpdmlkdWFsbHkuIFNpbmNlICMyOTkgaXQgaXMKICAjIGFsc28gdGhlIHNvbGUgY29udGV4dCBvZiB0aGUgYG1haW46IHJlcXVpcmVkIGNoZWNrc2AgcnVsZXNldCwgd2hvc2UKICAjIGBieXBhc3NfYWN0b3JzYCBpcyBlbXB0eSAtLSBzbyB1bmxpa2UgY2xhc3NpYyBwcm90ZWN0aW9uIChgZW5mb3JjZV9hZG1pbnNgCiAgIyBpcyBzdGlsbCBmYWxzZSkgdGhpcyBuYW1lIGNhbm5vdCBiZSBtZXJnZWQgcGFzdCB3aXRoIGAtLWFkbWluYC4gUmVuYW1pbmcKICAjIHRoaXMgam9iIHNpbGVudGx5IGRpc2FybXMgdGhhdCBydWxlc2V0OyByZW5hbWUgdGhlIHJ1bGVzZXQncyBjb250ZXh0IGluIHRoZQogICMgc2FtZSBjaGFuZ2UuCiAgIwogICMgYGlmOiAhY2FuY2VsbGVkKClgIGZvciB0aGUgc2FtZSByZWFzb24gYXMgd2lyZS1jb21wYXQtanMgYWJvdmU6IHRoaXMgam9iCiAgIyBtdXN0IHN0aWxsIHJ1biBhbmQgcmVwb3J0IHdoZW4gYW4gdXBzdHJlYW0gaGFsZiBmYWlsZWQsIG9yIHRoZSByZXF1aXJlZAogICMgY2hlY2sgc2l0cyBwZW5kaW5nIGZvcmV2ZXIgaW5zdGVhZCBvZiB0dXJuaW5nIHJlZC4KICAjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQogIHdpcmUtY29tcGF0OgogICAgbmFtZTogV2lyZSBjb21wYXQKICAgIHJ1bnMtb246IHVidW50dS1sYXRlc3QKICAgIG5lZWRzOiBbd2lyZS1jb21wYXQtcnVzdCwgd2lyZS1jb21wYXQtanNdCiAgICBpZjogJHt7ICFjYW5jZWxsZWQoKSB9fQogICAgcGVybWlzc2lvbnM6CiAgICAgIGNvbnRlbnRzOiByZWFkCiAgICBzdGVwczoKICAgICAgLSBuYW1lOiBSZXF1aXJlIGJvdGggaGFsdmVzIHRvIGhhdmUgcGFzc2VkCiAgICAgICAgc2hlbGw6IGJhc2gKICAgICAgICBydW46IHwKICAgICAgICAgIGVjaG8gIndpcmUtY29tcGF0LXJ1c3Q6ICR7eyBuZWVkcy53aXJlLWNvbXBhdC1ydXN0LnJlc3VsdCB9fSIKICAgICAgICAgIGVjaG8gIndpcmUtY29tcGF0LWpzOiAke3sgbmVlZHMud2lyZS1jb21wYXQtanMucmVzdWx0IH19IgogICAgICAgICAgaWYgW1sgIiR7eyBuZWVkcy53aXJlLWNvbXBhdC1ydXN0LnJlc3VsdCB9fSIgIT0gInN1Y2Nlc3MiIHx8ICIke3sgbmVlZHMud2lyZS1jb21wYXQtanMucmVzdWx0IH19IiAhPSAic3VjY2VzcyIgXV07IHRoZW4KICAgICAgICAgICAgZXhpdCAxCiAgICAgICAgICBmaQoKICAjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQogICMgVGhlIHJlZ2lzdHJ5IGhhbGYgb2YgdGhlIHdpcmluZyBndWFyZHMgKCMzMTgpLgogICMKICAjIGBwZy1jb3JlL3Rlc3RzL2NpX3dpcmluZy5yc2AgYXNzZXJ0cyB0aGF0IHRoZSBqb2IgYWJvdmUgaXMgc3RpbGwgKm5hbWVkKgogICMgYFdpcmUgY29tcGF0YC4gVGhpcyBhc3NlcnRzIHRoZSBvdGhlciBlbmQgb2YgdGhhdCBsaW5rOiB0aGF0IHRoZSBgbWFpbmAKICAjIHJ1bGVzZXQgc3RpbGwgKnJlcXVpcmVzKiB0aGF0IGNvbnRleHQuIE5laXRoZXIgZW5kIGlzIHN1ZmZpY2llbnQuIEEgY29udGV4dAogICMgcmVxdWlyZWQgYnV0IG5vdCBwcm9kdWNlZCBibG9ja3Mgbm90aGluZyAoIzI5OSBtZWFzdXJlZCB0aGF0IGFuIGFic2VudCBjaGVjawogICMgaXMgYXMgZGFuZ2Vyb3VzIGFzIGEgcmVkIG9uZSk7IGEgY29udGV4dCBwcm9kdWNlZCBidXQgbm90IHJlcXVpcmVkIGVuZm9yY2VzCiAgIyBub3RoaW5nIHdoaWxlIGV2ZXJ5IHRlc3Qgc3RheXMgZ3JlZW4uCiAgIwogICMgQm90aCAjMjcyIGFuZCBwb3N0Z3VhcmQtanMjMjIyIGRlY2xhcmVkIHRoaXMgZW5kIHVucmVhY2hhYmxlIGZyb20gQ0ksIG9uIHRoZQogICMgYXNzdW1wdGlvbiBpdCBuZWVkcyBhbiBhZG1pbiBjcmVkZW50aWFsLiBNZWFzdXJlZCBpbiAjMzE4OiBpdCBkb2VzIG5vdC4gT24gYQogICMgcHVibGljIHJlcG8gdGhlIGVmZmVjdGl2ZS1ydWxlcyBlbmRwb2ludCBhbnN3ZXJzIDIwMCB0byB0aGUgYnVpbHQtaW4gdG9rZW4KICAjIGF0IGFueSBwZXJtaXNzaW9uIGxldmVsLCBzbyBgY29udGVudHM6IHJlYWRgIGJlbG93IGlzIGFscmVhZHkgbW9yZSB0aGFuIHRoaXMKICAjIG5lZWRzLCBhbmQgbm8gc2VjcmV0IGlzIGludm9sdmVkLgogICMKICAjIERlbGliZXJhdGVseSBOT1QgcGFydCBvZiB0aGUgYFdpcmUgY29tcGF0YCBhZ2dyZWdhdG9yIGFuZCBkZWxpYmVyYXRlbHkgbm90IGEKICAjIHJlcXVpcmVkIGNvbnRleHQ6IHRoaXMgcmVhZHMgYSBsaXZlIHRoaXJkLXBhcnR5IEFQSSwgYW5kIGEgR2l0SHViIEFQSSBvdXRhZ2UKICAjIG11c3Qgbm90IGJlY29tZSBhbiB1bm1lcmdlYWJsZSByZXBvLiBFeGl0IDIgKHVuZGV0ZXJtaW5lZCkgaXMgdGhlcmVmb3JlIGEKICAjIGRpc3RpbmN0IG91dGNvbWUgZnJvbSBleGl0IDEgKGRyaWZ0KSAtLSBzZWUgc2NyaXB0cy9ydWxlc2V0LWRyaWZ0LnNoLgogICMKICAjIEFuZCBkZWxpYmVyYXRlbHkgbm8gYHNjaGVkdWxlOmAuIEEgbmlnaHRseSB3b3VsZCBkZXRlY3QgZHJpZnQgZHVyaW5nIHF1aWV0CiAgIyBwZXJpb2RzLCBidXQgYSBkaXNhcm1lZCBnYXRlIGNhbiBvbmx5IGRvIGhhcm0gd2hlbiBzb21ldGhpbmcgbWVyZ2VzLCBhbmQKICAjIGV2ZXJ5IG1lcmdlIGhlcmUgcGFzc2VzIHRocm91Z2ggYSBgcHVsbF9yZXF1ZXN0YCBhbmQgYSBgcHVzaGAgcnVuIG9mIHRoaXMKICAjIHdvcmtmbG93LiBSdW5uaW5nIGF0IGV4YWN0bHkgdGhlIG1vbWVudCB0aGUgZ2F0ZSBpcyByZWxpZWQgdXBvbiBhbHNvIG1ha2VzCiAgIyB0aGUgcmVzdWx0IHVubWlzc2FibGUsIHdoaWNoIGEgc2NoZWR1bGVkIHJ1biBpcyBub3QgLS0gdGhlIHRpY2tldCdzIG93bgogICMgd2FybmluZyB3YXMgdGhhdCBhIGRyaWZ0IGRldGVjdG9yIG5vYm9keSByZWFkcyBpcyBpdHNlbGYgYSBzaWxlbnQgZ2F0ZS4KICAjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQogIHJ1bGVzZXQtZHJpZnQ6CiAgICBuYW1lOiBSdWxlc2V0IHN0aWxsIHJlcXVpcmVzIHRoZSBnYXRlCiAgICBydW5zLW9uOiB1YnVudHUtbGF0ZXN0CiAgICBwZXJtaXNzaW9uczoKICAgICAgY29udGVudHM6IHJlYWQKICAgICAgIyBGb3IgdGhlIHJlcG9ydGVyJ3MgYGdoIGlzc3VlIGNyZWF0ZWAgYmVsb3cgKCM0MjIpLiBPbmx5IHRoZQogICAgICAjIHJlcXVpcmVkX3N0YXR1c19jaGVja3MgaGFsZiBvZiB0aGUgcnVsZXNldCBpcyBhc3NlcnRlZCBhbnl3aGVyZSBpbgogICAgICAjIHRoaXMgam9iOiB0aGUgcmV2aWV3LXJlcXVpcmVtZW50IGhhbGYgbGl2ZXMgaW4gY2xhc3NpYyBwcm90ZWN0aW9uCiAgICAgICMgKGByZXF1aXJlZF9hcHByb3ZpbmdfcmV2aWV3X2NvdW50YCksIGFuZCBgL2JyYW5jaGVzL21haW4vcHJvdGVjdGlvbmAKICAgICAgIyA0MDNzIGZvciB0aGUgQWN0aW9ucyB0b2tlbiwgc28gaXQgY2Fubm90IGJlIHJlYWQgZnJvbSBDSSBhdCBhbGwuCiAgICAgICMgYHBvc3RndWFyZC1qc2AgYXNzZXJ0cyBpdHMgcmV2aWV3IHJ1bGUgb25seSBiZWNhdXNlIGl0IGtlZXBzIHRoYXQKICAgICAgIyBydWxlIGluIGEgcnVsZXNldCBpbnN0ZWFkLgogICAgICBpc3N1ZXM6IHdyaXRlCiAgICBzdGVwczoKICAgICAgLSB1c2VzOiBhY3Rpb25zL2NoZWNrb3V0QHY0CiAgICAgICAgd2l0aDoKICAgICAgICAgICMgVGhlIGNoYW5nZWxvZy1jb3ZlcmFnZSBjaGVja2VyJ3Mgc2VsZi10ZXN0IGJlbG93ICgjNDEyKSBydW5zCiAgICAgICAgICAjIGFnYWluc3QgZml4dHVyZXMgdGhhdCBhcmUgcmVhbCB0YWdzIGluIHRoaXMgcmVwbydzIGhpc3RvcnksIHNvIHRoZQogICAgICAgICAgIyBqb2IgbmVlZHMgZnVsbCBoaXN0b3J5IGFuZCB0YWdzIC0tIHRoZSBkZWZhdWx0IHNoYWxsb3cgY2hlY2tvdXQKICAgICAgICAgICMgbWFrZXMgdGhlIGNvdmVyYWdlIHNjcmlwdCByZXBvcnQgZXhpdCAyICh1bmRldGVybWluZWQpIG9uIGV2ZXJ5CiAgICAgICAgICAjIG9uZSBvZiB0aGVtIGluc3RlYWQgb2YgZXhlcmNpc2luZyB0aGUgY2FzZS4KICAgICAgICAgIGZldGNoLWRlcHRoOiAwCgogICAgICAtIG5hbWU6IENvbXBhcmUgdGhlIGxpdmUgcnVsZXNldCBhZ2FpbnN0IHRoZSBndWFyZCdzIGNvbnN0YW50CiAgICAgICAgc2hlbGw6IGJhc2gKICAgICAgICBlbnY6CiAgICAgICAgICAjIE5vdCByZXF1aXJlZCBmb3IgdGhlIHJlYWQgLS0gdW5hdXRoZW50aWNhdGVkIHdvcmtzIG9uIGEgcHVibGljIHJlcG8uCiAgICAgICAgICAjIEl0IGlzIHBhc3NlZCBiZWNhdXNlIHRoZSBydW5uZXIncyBhbm9ueW1vdXMgYnVkZ2V0IGlzIDYwL2hvdXIgcGVyCiAgICAgICAgICAjIHNoYXJlZCBJUCwgYW5kIGV4aGF1c3RpbmcgaXQgd291bGQgc2hvdyB1cCBhcyBleGl0IDIgbm9pc2UuCiAgICAgICAgICBHSF9UT0tFTjogJHt7IGdpdGh1Yi50b2tlbiB9fQogICAgICAgICAgR0hfUkVQTzogJHt7IGdpdGh1Yi5yZXBvc2l0b3J5IH19CiAgICAgICAgcnVuOiBzY3JpcHRzL3J1bGVzZXQtZHJpZnQtcmVwb3J0LnNoCgogICAgICAtIG5hbWU6IEV4aXQgY29kZXMgc3RpbGwgbWVhbiB3aGF0IHRoZSBnYXRlIHJlcG9ydHMKICAgICAgICBzaGVsbDogYmFzaAogICAgICAgIHJ1bjogc2NyaXB0cy9ydWxlc2V0LWRyaWZ0LXRlc3Quc2gKCiAgICAgIC0gbmFtZTogVGVzdCB0aGUgY2hhbmdlbG9nLWNvdmVyYWdlIGNoZWNrZXIKICAgICAgICBzaGVsbDogYmFzaAogICAgICAgIHJ1bjogc2NyaXB0cy9jaGFuZ2Vsb2ctY292ZXJhZ2UtdGVzdC5zaAoKICAgICAgLSBuYW1lOiBUZXN0IHRoZSB3YXNtLXBhY2thZ2UgY2hlY2tlcgogICAgICAgIHNoZWxsOiBiYXNoCiAgICAgICAgcnVuOiBzY3JpcHRzL3dhc20tcGFja2FnZS1jaGVjay10ZXN0LnNoCgogICAgICAtIG5hbWU6IFRlc3QgdGhlIGNoYW5nZWxvZy1jb3ZlcmFnZSByZXBvcnRlcgogICAgICAgIHNoZWxsOiBiYXNoCiAgICAgICAgcnVuOiBzY3JpcHRzL2NoYW5nZWxvZy1jb3ZlcmFnZS1yZXBvcnQtdGVzdC5zaAoKICAgICAgLSBuYW1lOiBUZXN0IHRoZSBydWxlc2V0LWRyaWZ0IHJlcG9ydGVyCiAgICAgICAgc2hlbGw6IGJhc2gKICAgICAgICBydW46IHNjcmlwdHMvcnVsZXNldC1kcmlmdC1yZXBvcnQtdGVzdC5zaAoKICAgICAgLSBuYW1lOiBUZXN0IHRoZSBtYXAtYnVkZ2V0IGNoZWNrZXIKICAgICAgICBzaGVsbDogYmFzaAogICAgICAgIHJ1bjogc2NyaXB0cy9tYXAtYnVkZ2V0LXRlc3Quc2gK

@dobby-coder

dobby-coder Bot commented Sep 23, 2026

Copy link
Copy Markdown
Contributor Author

Workflow handover (2/2): new map-budget.yml

The live check (#445): a new workflow, gated on the wayfinder:map label, triggered on issues: edited plus workflow_dispatch, never push. Reads github.event.issue.body via env: only (never interpolated into run:), runs scripts/map-budget.sh, comments on the issue on a real finding (deduped, paginated, against the most recent comment), and is not wired into any required context. Same reasoning as posted on #445; full file below since this is a new workflow that will run with the repo's secrets, not a diff:

Full contents of new .github/workflows/map-budget.yml
name: Map budget
#
# Checks #247's two stated budgets on every edit to its body (#442, #445):
# the whole body <=153,600 bytes (150 KiB), every "Decisions so far" entry
# <=400 bytes. See scripts/map-budget.sh for the full rule, and why both
# are byte counts rather than character counts.
#
# `issues: edited`, not a schedule: the event being budgeted is the body
# changing, and that event exists. No `push` trigger either -- a push says
# nothing about an issue body.
#
# Deliberately NOT a required context. It watches an issue body, which no PR
# can fix, so making it required would block every merge on an edit nobody
# on that PR could act on.

on:
  issues:
    types: [edited]
  workflow_dispatch:

jobs:
  map-budget:
    name: Map body stays within budget
    runs-on: ubuntu-latest
    # The label, not the issue number, so a second wayfinder map is covered
    # for free and no number is hardcoded in two places. `workflow_dispatch`
    # carries no `issue`, so a manual run of this workflow as it stands
    # always evaluates false and skips -- that is #445's decided shape, not
    # an oversight; wiring a manual re-run to a specific issue would need an
    # `issue_number` input this ticket does not ask for.
    if: contains(github.event.issue.labels.*.name, 'wayfinder:map')
    permissions:
      contents: read
      issues: write
    steps:
      - uses: actions/checkout@v4

      - name: Write the issue body to a file
        env:
          # Never interpolated into a `run:` block: the body is a Markdown
          # document full of backticks and $(...) shell examples, and
          # interpolating it would execute them.
          ISSUE_BODY: ${{ github.event.issue.body }}
        run: printf '%s' "$ISSUE_BODY" >"$RUNNER_TEMP/map-body.md"

      - name: Check the map body's budgets
        env:
          GH_TOKEN: ${{ github.token }}
          GH_REPO: ${{ github.repository }}
          ISSUE_NUMBER: ${{ github.event.issue.number }}
        run: |
          # `if cmd; then ... else code=$?; fi` rather than
          # `out=$(cmd); code=$?`: this step has no `shell:` key, so it runs
          # as `bash -e {0}`, and a bare command substitution's own non-zero
          # exit would abort the step before a following `code=$?` line was
          # ever reached (#429, #422). `if`'s condition is exempt from `-e`,
          # so this survives the failure -- but `$?` must be captured as the
          # *first* thing in the `else` branch: bash resets `$?` to 0 for the
          # `if` statement itself once the `then` branch is skipped, so a
          # `code=$?` placed after `fi` instead would always read 0.
          if output=$(scripts/map-budget.sh "$RUNNER_TEMP/map-body.md" 2>&1); then
            echo "map-budget: OK -- within both stated budgets"
            exit 0
          else
            code=$?
          fi

          if [[ $code -eq 2 ]]; then
            echo "::error::scripts/map-budget.sh could not determine the map body's budgets (exit 2) -- the checker did not run to completion, this is not a finding"
            printf '%s\n' "$output"
            exit 2
          fi

          # Only 1 (a real finding) remains. Dedupe against the most recent
          # comment so an edit storm does not spam the issue with the same
          # finding repeatedly. `--paginate` walks every page -- without it,
          # a plain `.[-1]` reads only the API's first page (30 comments by
          # default), and once #247 crosses that count the dedup silently
          # stops seeing the true most recent comment, reintroducing the
          # edit-storm spam this step exists to prevent. The body
          # round-trips through base64 because `--paginate` applies the
          # `--jq` filter once per page and prints one result per page: a
          # multi-line comment body would otherwise span multiple output
          # lines, and `tail -n 1` would grab only its last line rather than
          # the whole last comment.
          last_comment=$(gh api --paginate "repos/$GH_REPO/issues/$ISSUE_NUMBER/comments" --jq '.[-1].body // "" | @base64' | tail -n 1 | base64 -d)
          if [[ $last_comment == "$output" ]]; then
            echo "map-budget: over budget, but the most recent comment already says so -- not reposting"
          else
            gh issue comment "$ISSUE_NUMBER" --body "$output"
          fi
          printf '%s\n' "$output"
          exit 1

Applying it is one paste (this is a new file, so the sha lookup resolves empty and is ignored):

gh api -X PUT repos/encryption4all/postguard/contents/.github/workflows/map-budget.yml -f branch=dobby/map-budget-445 -f message="ci: add the map-budget live check workflow (#445)" -f sha="$(gh api "repos/encryption4all/postguard/contents/.github/workflows/map-budget.yml?ref=dobby/map-budget-445" --jq .sha)" -f content=bmFtZTogTWFwIGJ1ZGdldAojCiMgQ2hlY2tzICMyNDcncyB0d28gc3RhdGVkIGJ1ZGdldHMgb24gZXZlcnkgZWRpdCB0byBpdHMgYm9keSAoIzQ0MiwgIzQ0NSk6CiMgdGhlIHdob2xlIGJvZHkgPD0xNTMsNjAwIGJ5dGVzICgxNTAgS2lCKSwgZXZlcnkgIkRlY2lzaW9ucyBzbyBmYXIiIGVudHJ5CiMgPD00MDAgYnl0ZXMuIFNlZSBzY3JpcHRzL21hcC1idWRnZXQuc2ggZm9yIHRoZSBmdWxsIHJ1bGUsIGFuZCB3aHkgYm90aAojIGFyZSBieXRlIGNvdW50cyByYXRoZXIgdGhhbiBjaGFyYWN0ZXIgY291bnRzLgojCiMgYGlzc3VlczogZWRpdGVkYCwgbm90IGEgc2NoZWR1bGU6IHRoZSBldmVudCBiZWluZyBidWRnZXRlZCBpcyB0aGUgYm9keQojIGNoYW5naW5nLCBhbmQgdGhhdCBldmVudCBleGlzdHMuIE5vIGBwdXNoYCB0cmlnZ2VyIGVpdGhlciAtLSBhIHB1c2ggc2F5cwojIG5vdGhpbmcgYWJvdXQgYW4gaXNzdWUgYm9keS4KIwojIERlbGliZXJhdGVseSBOT1QgYSByZXF1aXJlZCBjb250ZXh0LiBJdCB3YXRjaGVzIGFuIGlzc3VlIGJvZHksIHdoaWNoIG5vIFBSCiMgY2FuIGZpeCwgc28gbWFraW5nIGl0IHJlcXVpcmVkIHdvdWxkIGJsb2NrIGV2ZXJ5IG1lcmdlIG9uIGFuIGVkaXQgbm9ib2R5CiMgb24gdGhhdCBQUiBjb3VsZCBhY3Qgb24uCgpvbjoKICBpc3N1ZXM6CiAgICB0eXBlczogW2VkaXRlZF0KICB3b3JrZmxvd19kaXNwYXRjaDoKCmpvYnM6CiAgbWFwLWJ1ZGdldDoKICAgIG5hbWU6IE1hcCBib2R5IHN0YXlzIHdpdGhpbiBidWRnZXQKICAgIHJ1bnMtb246IHVidW50dS1sYXRlc3QKICAgICMgVGhlIGxhYmVsLCBub3QgdGhlIGlzc3VlIG51bWJlciwgc28gYSBzZWNvbmQgd2F5ZmluZGVyIG1hcCBpcyBjb3ZlcmVkCiAgICAjIGZvciBmcmVlIGFuZCBubyBudW1iZXIgaXMgaGFyZGNvZGVkIGluIHR3byBwbGFjZXMuIGB3b3JrZmxvd19kaXNwYXRjaGAKICAgICMgY2FycmllcyBubyBgaXNzdWVgLCBzbyBhIG1hbnVhbCBydW4gb2YgdGhpcyB3b3JrZmxvdyBhcyBpdCBzdGFuZHMKICAgICMgYWx3YXlzIGV2YWx1YXRlcyBmYWxzZSBhbmQgc2tpcHMgLS0gdGhhdCBpcyAjNDQ1J3MgZGVjaWRlZCBzaGFwZSwgbm90CiAgICAjIGFuIG92ZXJzaWdodDsgd2lyaW5nIGEgbWFudWFsIHJlLXJ1biB0byBhIHNwZWNpZmljIGlzc3VlIHdvdWxkIG5lZWQgYW4KICAgICMgYGlzc3VlX251bWJlcmAgaW5wdXQgdGhpcyB0aWNrZXQgZG9lcyBub3QgYXNrIGZvci4KICAgIGlmOiBjb250YWlucyhnaXRodWIuZXZlbnQuaXNzdWUubGFiZWxzLioubmFtZSwgJ3dheWZpbmRlcjptYXAnKQogICAgcGVybWlzc2lvbnM6CiAgICAgIGNvbnRlbnRzOiByZWFkCiAgICAgIGlzc3Vlczogd3JpdGUKICAgIHN0ZXBzOgogICAgICAtIHVzZXM6IGFjdGlvbnMvY2hlY2tvdXRAdjQKCiAgICAgIC0gbmFtZTogV3JpdGUgdGhlIGlzc3VlIGJvZHkgdG8gYSBmaWxlCiAgICAgICAgZW52OgogICAgICAgICAgIyBOZXZlciBpbnRlcnBvbGF0ZWQgaW50byBhIGBydW46YCBibG9jazogdGhlIGJvZHkgaXMgYSBNYXJrZG93bgogICAgICAgICAgIyBkb2N1bWVudCBmdWxsIG9mIGJhY2t0aWNrcyBhbmQgJCguLi4pIHNoZWxsIGV4YW1wbGVzLCBhbmQKICAgICAgICAgICMgaW50ZXJwb2xhdGluZyBpdCB3b3VsZCBleGVjdXRlIHRoZW0uCiAgICAgICAgICBJU1NVRV9CT0RZOiAke3sgZ2l0aHViLmV2ZW50Lmlzc3VlLmJvZHkgfX0KICAgICAgICBydW46IHByaW50ZiAnJXMnICIkSVNTVUVfQk9EWSIgPiIkUlVOTkVSX1RFTVAvbWFwLWJvZHkubWQiCgogICAgICAtIG5hbWU6IENoZWNrIHRoZSBtYXAgYm9keSdzIGJ1ZGdldHMKICAgICAgICBlbnY6CiAgICAgICAgICBHSF9UT0tFTjogJHt7IGdpdGh1Yi50b2tlbiB9fQogICAgICAgICAgR0hfUkVQTzogJHt7IGdpdGh1Yi5yZXBvc2l0b3J5IH19CiAgICAgICAgICBJU1NVRV9OVU1CRVI6ICR7eyBnaXRodWIuZXZlbnQuaXNzdWUubnVtYmVyIH19CiAgICAgICAgcnVuOiB8CiAgICAgICAgICAjIGBpZiBjbWQ7IHRoZW4gLi4uIGVsc2UgY29kZT0kPzsgZmlgIHJhdGhlciB0aGFuCiAgICAgICAgICAjIGBvdXQ9JChjbWQpOyBjb2RlPSQ/YDogdGhpcyBzdGVwIGhhcyBubyBgc2hlbGw6YCBrZXksIHNvIGl0IHJ1bnMKICAgICAgICAgICMgYXMgYGJhc2ggLWUgezB9YCwgYW5kIGEgYmFyZSBjb21tYW5kIHN1YnN0aXR1dGlvbidzIG93biBub24temVybwogICAgICAgICAgIyBleGl0IHdvdWxkIGFib3J0IHRoZSBzdGVwIGJlZm9yZSBhIGZvbGxvd2luZyBgY29kZT0kP2AgbGluZSB3YXMKICAgICAgICAgICMgZXZlciByZWFjaGVkICgjNDI5LCAjNDIyKS4gYGlmYCdzIGNvbmRpdGlvbiBpcyBleGVtcHQgZnJvbSBgLWVgLAogICAgICAgICAgIyBzbyB0aGlzIHN1cnZpdmVzIHRoZSBmYWlsdXJlIC0tIGJ1dCBgJD9gIG11c3QgYmUgY2FwdHVyZWQgYXMgdGhlCiAgICAgICAgICAjICpmaXJzdCogdGhpbmcgaW4gdGhlIGBlbHNlYCBicmFuY2g6IGJhc2ggcmVzZXRzIGAkP2AgdG8gMCBmb3IgdGhlCiAgICAgICAgICAjIGBpZmAgc3RhdGVtZW50IGl0c2VsZiBvbmNlIHRoZSBgdGhlbmAgYnJhbmNoIGlzIHNraXBwZWQsIHNvIGEKICAgICAgICAgICMgYGNvZGU9JD9gIHBsYWNlZCBhZnRlciBgZmlgIGluc3RlYWQgd291bGQgYWx3YXlzIHJlYWQgMC4KICAgICAgICAgIGlmIG91dHB1dD0kKHNjcmlwdHMvbWFwLWJ1ZGdldC5zaCAiJFJVTk5FUl9URU1QL21hcC1ib2R5Lm1kIiAyPiYxKTsgdGhlbgogICAgICAgICAgICBlY2hvICJtYXAtYnVkZ2V0OiBPSyAtLSB3aXRoaW4gYm90aCBzdGF0ZWQgYnVkZ2V0cyIKICAgICAgICAgICAgZXhpdCAwCiAgICAgICAgICBlbHNlCiAgICAgICAgICAgIGNvZGU9JD8KICAgICAgICAgIGZpCgogICAgICAgICAgaWYgW1sgJGNvZGUgLWVxIDIgXV07IHRoZW4KICAgICAgICAgICAgZWNobyAiOjplcnJvcjo6c2NyaXB0cy9tYXAtYnVkZ2V0LnNoIGNvdWxkIG5vdCBkZXRlcm1pbmUgdGhlIG1hcCBib2R5J3MgYnVkZ2V0cyAoZXhpdCAyKSAtLSB0aGUgY2hlY2tlciBkaWQgbm90IHJ1biB0byBjb21wbGV0aW9uLCB0aGlzIGlzIG5vdCBhIGZpbmRpbmciCiAgICAgICAgICAgIHByaW50ZiAnJXNcbicgIiRvdXRwdXQiCiAgICAgICAgICAgIGV4aXQgMgogICAgICAgICAgZmkKCiAgICAgICAgICAjIE9ubHkgMSAoYSByZWFsIGZpbmRpbmcpIHJlbWFpbnMuIERlZHVwZSBhZ2FpbnN0IHRoZSBtb3N0IHJlY2VudAogICAgICAgICAgIyBjb21tZW50IHNvIGFuIGVkaXQgc3Rvcm0gZG9lcyBub3Qgc3BhbSB0aGUgaXNzdWUgd2l0aCB0aGUgc2FtZQogICAgICAgICAgIyBmaW5kaW5nIHJlcGVhdGVkbHkuIGAtLXBhZ2luYXRlYCB3YWxrcyBldmVyeSBwYWdlIC0tIHdpdGhvdXQgaXQsCiAgICAgICAgICAjIGEgcGxhaW4gYC5bLTFdYCByZWFkcyBvbmx5IHRoZSBBUEkncyBmaXJzdCBwYWdlICgzMCBjb21tZW50cyBieQogICAgICAgICAgIyBkZWZhdWx0KSwgYW5kIG9uY2UgIzI0NyBjcm9zc2VzIHRoYXQgY291bnQgdGhlIGRlZHVwIHNpbGVudGx5CiAgICAgICAgICAjIHN0b3BzIHNlZWluZyB0aGUgdHJ1ZSBtb3N0IHJlY2VudCBjb21tZW50LCByZWludHJvZHVjaW5nIHRoZQogICAgICAgICAgIyBlZGl0LXN0b3JtIHNwYW0gdGhpcyBzdGVwIGV4aXN0cyB0byBwcmV2ZW50LiBUaGUgYm9keQogICAgICAgICAgIyByb3VuZC10cmlwcyB0aHJvdWdoIGJhc2U2NCBiZWNhdXNlIGAtLXBhZ2luYXRlYCBhcHBsaWVzIHRoZQogICAgICAgICAgIyBgLS1qcWAgZmlsdGVyIG9uY2UgcGVyIHBhZ2UgYW5kIHByaW50cyBvbmUgcmVzdWx0IHBlciBwYWdlOiBhCiAgICAgICAgICAjIG11bHRpLWxpbmUgY29tbWVudCBib2R5IHdvdWxkIG90aGVyd2lzZSBzcGFuIG11bHRpcGxlIG91dHB1dAogICAgICAgICAgIyBsaW5lcywgYW5kIGB0YWlsIC1uIDFgIHdvdWxkIGdyYWIgb25seSBpdHMgbGFzdCBsaW5lIHJhdGhlciB0aGFuCiAgICAgICAgICAjIHRoZSB3aG9sZSBsYXN0IGNvbW1lbnQuCiAgICAgICAgICBsYXN0X2NvbW1lbnQ9JChnaCBhcGkgLS1wYWdpbmF0ZSAicmVwb3MvJEdIX1JFUE8vaXNzdWVzLyRJU1NVRV9OVU1CRVIvY29tbWVudHMiIC0tanEgJy5bLTFdLmJvZHkgLy8gIiIgfCBAYmFzZTY0JyB8IHRhaWwgLW4gMSB8IGJhc2U2NCAtZCkKICAgICAgICAgIGlmIFtbICRsYXN0X2NvbW1lbnQgPT0gIiRvdXRwdXQiIF1dOyB0aGVuCiAgICAgICAgICAgIGVjaG8gIm1hcC1idWRnZXQ6IG92ZXIgYnVkZ2V0LCBidXQgdGhlIG1vc3QgcmVjZW50IGNvbW1lbnQgYWxyZWFkeSBzYXlzIHNvIC0tIG5vdCByZXBvc3RpbmciCiAgICAgICAgICBlbHNlCiAgICAgICAgICAgIGdoIGlzc3VlIGNvbW1lbnQgIiRJU1NVRV9OVU1CRVIiIC0tYm9keSAiJG91dHB1dCIKICAgICAgICAgIGZpCiAgICAgICAgICBwcmludGYgJyVzXG4nICIkb3V0cHV0IgogICAgICAgICAgZXhpdCAxCg==

@dobby-coder dobby-coder Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rule-compliance check against ~/dobby-rules.md (5 binding rules: atomic-commits, code-comments, draft-pull-requests, no-summary-issues, test-suite-before-submit) found no violations — the PR is a draft, its two commits are atomic and well-described (the second is a documented fix-up found in review), the full pg-core test suite plus fmt/clippy/offline self-tests were run and reported, no summary/report issues were created, and comment density in the new scripts matches its siblings (ruleset-drift.sh, changelog-coverage.sh) and ci_wiring.rs's existing assertions.

One bug survives from review: scripts/map-budget.sh's heading-existence check and its section-extraction disagree on what counts as the ## Decisions so far heading, which lets a heading-level typo silently defeat the entry budget check — exactly the case the script's own header disclaims ("Never reported as 0 -- a body that cannot be parsed is not a body within budget"). See inline comment for a reproduction and a verified one-line fix that keeps the existing 12/12 map-budget-test.sh suite green.

Verdict: changes requested (posted as a COMMENT review, not REQUEST_CHANGES — GitHub rejects that event on a PR authored by this same bot identity). That one fix is needed before this is ready to apply the posted workflow patches; leaving in draft.

Comment thread scripts/map-budget.sh Outdated
exit 2
fi

if ! grep -qF '## Decisions so far' "$path"; then

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This existence check and the section-extractor just below it (line 84, /^## Decisions so far/) disagree on what counts as the heading. grep -qF here is unanchored, so it matches ## Decisions so far as a substring anywhere in a line — including inside a heading-level typo like ### Decisions so far (three #s: drop the leading # and the rest is an exact substring match). But the awk extractor's ^## Decisions so far is anchored, and a three-# line never matches it, so found stays 0 and section comes back empty. Every entry inside that (mistyped) section is then silently skipped by the byte-cap loop below, and the script falls through to reporting exit 0 "OK" even when an entry in the section is well over the 400-byte cap — the exact case the module comment above disclaims ("Never reported as 0 -- a body that cannot be parsed is not a body within budget").

Reproduced: a body with a ### Decisions so far heading and a 450-byte entry inside it exits 0 today. Anchoring the existence check the same way as the awk extraction fixes it — the same input then correctly exits 2 (heading not found), and the full map-budget-test.sh suite (12/12) still passes unmodified.

Suggested change
if ! grep -qF '## Decisions so far' "$path"; then
if ! grep -qE '^## Decisions so far' "$path"; then

grep -qF '## Decisions so far' matched the string anywhere in a line,
including inside a heading-level typo like '### Decisions so far'. The
awk section-extractor right below it is anchored (^## Decisions so
far), so that typo left `found` unset there: the section came back
empty, every entry inside it was silently skipped, and the script fell
through to exit 0 "OK" -- the exact "reported as 0 when it can't
parse" case the module comment disclaims.

Anchor the existence check the same way as the extractor. Adds a
committed regression case to map-budget-test.sh: a '### Decisions so
far' typo with a 450-byte entry inside it now exits 2, not 0; the
existing 12 cases are unaffected.

Found in review of PR #452.

@dobby-coder dobby-coder Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: APPROVE (posted as COMMENT — GitHub blocks self-approval; this PR's author is app/dobby-coder, the same identity as this review)

Rule check (5 binding rules) and review findings both clean.

Rule compliance (~/dobby-rules.md):

  • atomic-commits — three focused commits: add the checker + tests, fix the KB-budget arithmetic, fix the heading-anchor bug with a regression test.
  • code-comments — comment density in ci_wiring.rs and map-budget.sh matches their established siblings; no restated-obvious comments found.
  • draft-pull-requests — PR is open as a draft.
  • no-summary-issues — no issues created by dobby-coder; only comments on the pre-existing tracking issue (#445) and this PR carrying the workflow-patch handover.
  • test-suite-before-submit — scripts/map-budget-test.sh (13/13) and cargo test/fmt/clippy results are documented in the PR description, with the one expected-red assertion (the_map_budget_checkers_self_test_runs_in_ci) explained and pre-existing.

Review findings (from the prior review cycle, verified at head e1710db): the grep -qF → grep -qE '^## Decisions so far' fix correctly aligns the heading-existence check with the awk section extractor's anchor, closing the heading-typo false-pass. The new regression test is well-built and the full suite passes. CI red is confirmed pre-existing and out of this container's reach (no workflows: write), documented both in-code and in the PR body; the other three Test workspace failures are matrix cancellations cascading from that one red job, not independent findings.

No blocking issues. Staying in draft per the two workflow patches awaiting a maintainer to apply them (posted on #445 and on this PR).

)

Applies the two workflow patches posted on #445, which dobby-coder cannot
push without workflows: write:

- build.yml: a 'Test the map-budget checker' step in the ruleset-drift job,
  which turns the_map_budget_checkers_self_test_runs_in_ci green.
- map-budget.yml: checks the wayfinder:map issue body on every edit; comments
  on exit 1 (deduped against the latest comment), ::error:: on exit 2.
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