From 477cc6e6a7708e1736cf9bf8cc4d880afdbca02a Mon Sep 17 00:00:00 2001 From: Sylpheed port agent Date: Sun, 30 Aug 2026 06:14:42 +0000 Subject: [PATCH] port: index DECISIONS.md -- it already answered last iteration's question Last iteration I filed title and title_jp's disagreement with sylpheed-cli as mechanism-unknown, to the Decoder as well as here. Both were already explained in this file, under headings that name the two screens. Checked rather than assumed. title: still ties on 0x8083, 0x80a0 and 0x8010, and the export declares paint_order_ties unresolved; the old entry's 904 px in the glow band matches my 790 px at the same place, same 4-6/255 magnitude. title_jp: the 'only non-integer scale' claim finds 26 keyframes export-wide, but exactly ONE element visible at rest -- ptlogo_eff2 at 125% -- which is the pose verify-screen uses. It survives narrowly. The failure is navigability: 6502 lines, 111 sections, no index, so 'has this been decided?' had no cheap answer and re-deriving it looked like diligence. index-decisions generates the contents; check-all runs --check. No line numbers (the first version was a fixpoint that failed its own check, and appends would invalidate them all), and checked, because a stale index answers 'already decided?' with a confident no. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF --- docs/port/DECISIONS.md | 174 +++++++++++++++++++++++++++++++++++++ tools/port/check-all | 3 + tools/port/index-decisions | 60 +++++++++++++ 3 files changed, 237 insertions(+) create mode 100755 tools/port/index-decisions diff --git a/docs/port/DECISIONS.md b/docs/port/DECISIONS.md index aecf4988..9853303d 100644 --- a/docs/port/DECISIONS.md +++ b/docs/port/DECISIONS.md @@ -6,6 +6,125 @@ dies, which is what this file is for. --- + + + +112 sections. Search this before re-deriving anything. + +* [P0 — the exporter, 2026-08-28](#p0--the-exporter-2026-08-28) +* [P1 — Godot draws the screen, 2026-08-28](#p1--godot-draws-the-screen-2026-08-28) +* [P1 gate — the diff, and what it found](#p1-gate--the-diff-and-what-it-found) +* [P2 — keyframe animation, 2026-08-28](#p2--keyframe-animation-2026-08-28) +* [`rest` misidentifies six elements, and the running game says so](#rest-misidentifies-six-elements-and-the-running-game-says-so) +* [The title is not settled, and P2 does not claim it](#the-title-is-not-settled-and-p2-does-not-claim-it) +* [P2, corrected — the pin moved, and the settle rule was wrong, 2026-08-28](#p2-corrected--the-pin-moved-and-the-settle-rule-was-wrong-2026-08-28) +* [The reference renderer was stale for three diff runs](#the-reference-renderer-was-stale-for-three-diff-runs) +* [The menu had no background, and P1 called that correct, 2026-08-29](#the-menu-had-no-background-and-p1-called-that-correct-2026-08-29) +* [P3 — splash → title, unattended, 2026-08-29](#p3--splash--title-unattended-2026-08-29) +* [P3 gate](#p3-gate) +* [Answers taken from the RE agent without re-deriving them](#answers-taken-from-the-re-agent-without-re-deriving-them) +* [P4 — the intro video, 2026-08-29](#p4--the-intro-video-2026-08-29) +* [P4 gate](#p4-gate) +* [RETRACTION — `sylpheed-cli` is not the oracle, 2026-08-29](#retraction--sylpheed-cli-is-not-the-oracle-2026-08-29) +* [P5 groundwork — the focus record, checked against a capture, 2026-08-29](#p5-groundwork--the-focus-record-checked-against-a-capture-2026-08-29) +* [P5 — navigation, 2026-08-29](#p5--navigation-2026-08-29) +* [`--headless` cannot draw, and the port hung instead of saying so, 2026-08-29](#--headless-cannot-draw-and-the-port-hung-instead-of-saying-so-2026-08-29) +* [Refutation — the focus ring IS drawn rotated, and it is not at 0° in either capture](#refutation--the-focus-ring-is-drawn-rotated-and-it-is-not-at-0-in-either-capture) +* [P5 end to end — and the title does not say `PRESS Ⓐ`, 2026-08-29](#p5-end-to-end--and-the-title-does-not-say-press--2026-08-29) +* [P6 — menu audio, 2026-08-29](#p6--menu-audio-2026-08-29) +* [P6 gate — the audio is in the mix, and a null control says which part](#p6-gate--the-audio-is-in-the-mix-and-a-null-control-says-which-part) +* [P3, reopened — the boot title was missing the `PRESS Ⓐ` plate, 2026-08-29](#p3-reopened--the-boot-title-was-missing-the-press--plate-2026-08-29) +* [P5 — the focus ring spins, 2026-08-29](#p5--the-focus-ring-spins-2026-08-29) +* [P3, corrected — the plate needs no authored delay at all, 2026-08-29](#p3-corrected--the-plate-needs-no-authored-delay-at-all-2026-08-29) +* [P7 — the new-game intro, 2026-08-29](#p7--the-new-game-intro-2026-08-29) +* [P7 gate](#p7-gate) +* [Modding — rule 4 was never implemented, 2026-08-29](#modding--rule-4-was-never-implemented-2026-08-29) +* [Refutation — the paint-order key, and the reach of its tie-break](#refutation--the-paint-order-key-and-the-reach-of-its-tie-break) +* [Correction — the runtime "clipping" I flagged 🔴 twice was overstated](#correction--the-runtime-clipping-i-flagged--twice-was-overstated) +* [The P1 regression harness had been broken since the monorepo merge, 2026-08-29](#the-p1-regression-harness-had-been-broken-since-the-monorepo-merge-2026-08-29) +* [Refutation — "builds 0/1 and 10/11 are the loading screen" is false in the index space this export uses](#refutation--builds-01-and-1011-are-the-loading-screen-is-false-in-the-index-space-this-export-uses) +* [The intro's missing dialogue was an export gap, not a transcode bug, 2026-08-29](#the-intros-missing-dialogue-was-an-export-gap-not-a-transcode-bug-2026-08-29) +* [Refutation, of my own exporter — MISSION §6 pins a downmix matrix, and the exporter ships a different one](#refutation-of-my-own-exporter--mission-6-pins-a-downmix-matrix-and-the-exporter-ships-a-different-one) +* [Refutation of my own two-stem reading — and it had already been adopted elsewhere](#refutation-of-my-own-two-stem-reading--and-it-had-already-been-adopted-elsewhere) +* [The mono fold I warned about, in the comment directly above the code that did it](#the-mono-fold-i-warned-about-in-the-comment-directly-above-the-code-that-did-it) +* [The leading chunk is the TAIL of the full one — measured, and it is why the region over-covers](#the-leading-chunk-is-the-tail-of-the-full-one--measured-and-it-is-why-the-region-over-covers) +* [Third reading of a voice region, and this one is decoded: three presentations of one take](#third-reading-of-a-voice-region-and-this-one-is-decoded-three-presentations-of-one-take) +* [The transcode cache had never hit, because the wipe ran first](#the-transcode-cache-had-never-hit-because-the-wipe-ran-first) +* [`settle_time()` — the answer arrived, and it refutes my own 🔴 more than it confirms it](#settle_time--the-answer-arrived-and-it-refutes-my-own--more-than-it-confirms-it) +* [The voice presentation is now unambiguously the port's choice, and the recommendation behind it was withdrawn](#the-voice-presentation-is-now-unambiguously-the-ports-choice-and-the-recommendation-behind-it-was-withdrawn) +* [Refutation of my dual-mono inference — the measurement stands, the generalisation does not](#refutation-of-my-dual-mono-inference--the-measurement-stands-the-generalisation-does-not) +* [Two rows of the P1 baseline were comparing blank frames and reporting OK](#two-rows-of-the-p1-baseline-were-comparing-blank-frames-and-reporting-ok) +* [Refutation attempt — the loading-screen variants, and it survived](#refutation-attempt--the-loading-screen-variants-and-it-survived) +* [🔴 The voice export is known incomplete — the game decodes all three streams at once](#the-voice-export-is-known-incomplete--the-game-decodes-all-three-streams-at-once) +* [🔴 The oracle capture does not contain the intro — a controlled negative](#the-oracle-capture-does-not-contain-the-intro--a-controlled-negative) +* [🔴 Take 2 is clean, my instrument was not, and the negative had to be re-earned](#take-2-is-clean-my-instrument-was-not-and-the-negative-had-to-be-re-earned) +* [Every music bank was summed at 1/3 when only two sub-waves are music — 3.52 dB, since P6](#every-music-bank-was-summed-at-13-when-only-two-sub-waves-are-music--352-db-since-p6) +* [Take 2 was starved, my correlator was fine, and `check-capture` was incomplete](#take-2-was-starved-my-correlator-was-fine-and-check-capture-was-incomplete) +* [The settle run carries an unmeasured real-time factor — and the numbers it touches were already unauthored](#the-settle-run-carries-an-unmeasured-real-time-factor--and-the-numbers-it-touches-were-already-unauthored) +* [`verify-dwell` — the comparison that refuted my own 🔴, made repeatable](#verify-dwell--the-comparison-that-refuted-my-own--made-repeatable) +* [The `PRESS Ⓐ` plate pulses — authored per element, because the census forbids a rule](#the-press--plate-pulses--authored-per-element-because-the-census-forbids-a-rule) +* [✅ The oracle finally speaks: the exported voice IS the game's centre channel](#the-oracle-finally-speaks-the-exported-voice-is-the-games-centre-channel) +* [The stripping control passes — `S00A` is obtainable, and the gate is cleared](#the-stripping-control-passes--s00a-is-obtainable-and-the-gate-is-cleared) +* [The correctness harness the docs promised for eight milestones did not exist](#the-correctness-harness-the-docs-promised-for-eight-milestones-did-not-exist) +* [Refutation attempt — the tone curve survives in its stated reach and not past it](#refutation-attempt--the-tone-curve-survives-in-its-stated-reach-and-not-past-it) +* [Identifying the capture's focused button — and my harness was posing the port wrong](#identifying-the-captures-focused-button--and-my-harness-was-posing-the-port-wrong) +* [`tools/port/which-focus` — the Decoder asked for a detector, and it carries its own control](#toolsportwhich-focus--the-decoder-asked-for-a-detector-and-it-carries-its-own-control) +* [The title's 1.82 % — three of my own explanations refuted, and the format has no blend mode](#the-titles-182---three-of-my-own-explanations-refuted-and-the-format-has-no-blend-mode) +* [🔴 The exporter dropped nested `.rat` leaf geometry on 45 elements — and it is the title's 1.82 %](#the-exporter-dropped-nested-rat-leaf-geometry-on-45-elements--and-it-is-the-titles-182) +* [The leaf composition is decoded and implemented — and it does **not** close the 1.82 %](#the-leaf-composition-is-decoded-and-implemented--and-it-does-not-close-the-182) +* [The −324 was the old keyframe association, and the corrected one is available **today**](#the-324-was-the-old-keyframe-association-and-the-corrected-one-is-available-today) +* [Re-running the P5/P6 gate after eight iterations of changes](#re-running-the-p5p6-gate-after-eight-iterations-of-changes) +* [Pinned `formats-pin-2026-08-29c` — and the knob I tested last iteration was retired](#pinned-formats-pin-2026-08-29c--and-the-knob-i-tested-last-iteration-was-retired) +* [Refuted — my own "the single non-whole-multiple scale in the export"](#refuted--my-own-the-single-non-whole-multiple-scale-in-the-export) +* [The 11.5 px was the fit's resolution, and the lesson inverts](#the-115-px-was-the-fits-resolution-and-the-lesson-inverts) +* [🔴 The focus ring had silently stopped, and BLOCKED had listed it](#the-focus-ring-had-silently-stopped-and-blocked-had-listed-it) +* [The plate's period is now the disc's 105, and it disagrees with the measurement](#the-plates-period-is-now-the-discs-105-and-it-disagrees-with-the-measurement) +* [The plate's period is 120, decoded — and it was falsified with my own ring number](#the-plates-period-is-120-decoded--and-it-was-falsified-with-my-own-ring-number) +* [✅ A settled screen is ONE instant, and it collapsed three residuals at once](#a-settled-screen-is-one-instant-and-it-collapsed-three-residuals-at-once) +* [Their census, and a framing of mine they sharpened](#their-census-and-a-framing-of-mine-they-sharpened) +* [Their "do not draw all five flashes" flag — checked, and it does not apply here](#their-do-not-draw-all-five-flashes-flag--checked-and-it-does-not-apply-here) +* [✅ The `publisher_logo` residual was a missing black hold, and we had both dismissed it](#the-publisher_logo-residual-was-a-missing-black-hold-and-we-had-both-dismissed-it) +* [`ptlogo_back2eff3` — recorded, deliberately not acted on](#ptlogo_back2eff3--recorded-deliberately-not-acted-on) +* [The narrow settle windows are harmless, and I can now say why](#the-narrow-settle-windows-are-harmless-and-i-can-now-say-why) +* [Refuted, mine — "the menu residual is localised on the `ptloop` sweeps"](#refuted-mine--the-menu-residual-is-localised-on-the-ptloop-sweeps) +* [Refuted — "the developer splash is one composited quad, the bounding box of the three logos"](#refuted--the-developer-splash-is-one-composited-quad-the-bounding-box-of-the-three-logos) +* [The black hold is 9 units, not 12 — measured in draws rather than luminance](#the-black-hold-is-9-units-not-12--measured-in-draws-rather-than-luminance) +* [The title's sweeps loop — measured, and the field could not have told us](#the-titles-sweeps-loop--measured-and-the-field-could-not-have-told-us) +* [The menus' residual is the tone floor, not structure — and `extras` is not really 3× worse](#the-menus-residual-is-the-tone-floor-not-structure--and-extras-is-not-really-3-worse) +* [Refutation attempt — their 239.8-unit figure, checked from my export](#refutation-attempt--their-2398-unit-figure-checked-from-my-export) +* [🔴 The loading screens are black at *every* instant — which proves the layer rule wrong for a layerless element](#the-loading-screens-are-black-at-every-instant--which-proves-the-layer-rule-wrong-for-a-layerless-element) +* [Their `eff3` retraction — my refusal was right, and my refutation found the same bug](#their-eff3-retraction--my-refusal-was-right-and-my-refutation-found-the-same-bug) +* [The forced backdrop: two of sixteen screens were black for their whole life](#the-forced-backdrop-two-of-sixteen-screens-were-black-for-their-whole-life) +* [Refutation attempt — the forced-backdrop rule's quantifier, and whether it misses a case](#refutation-attempt--the-forced-backdrop-rules-quantifier-and-whether-it-misses-a-case) +* [The 256/211 was never a disagreement — and my own census had already said so](#the-256211-was-never-a-disagreement--and-my-own-census-had-already-said-so) +* [The clock freezes at settle — the port's settle window, seen from the other side](#the-clock-freezes-at-settle--the-ports-settle-window-seen-from-the-other-side) +* [🔴 Withdrawn — "the boot is known too fast". The splash dwells are declared, and the port was already playing them](#withdrawn--the-boot-is-known-too-fast-the-splash-dwells-are-declared-and-the-port-was-already-playing-them) +* [Refutation attempt — their two splash boundaries are not anchored the same way](#refutation-attempt--their-two-splash-boundaries-are-not-anchored-the-same-way) +* [Their corrected boundaries check out against the file — all six, exactly](#their-corrected-boundaries-check-out-against-the-file--all-six-exactly) +* [The n=1 disclosure, and the one port constant that rests on a single run](#the-n1-disclosure-and-the-one-port-constant-that-rests-on-a-single-run) +* [P6 gate — sound on the P5 walk, verified, and the tool I nearly shipped instead](#p6-gate--sound-on-the-p5-walk-verified-and-the-tool-i-nearly-shipped-instead) +* [Their `.tbm` self-refutation does not reach this archive — and it fixes my guard anyway](#their-tbm-self-refutation-does-not-reach-this-archive--and-it-fixes-my-guard-anyway) +* [Coverage is now tested per instant, because scale animates](#coverage-is-now-tested-per-instant-because-scale-animates) +* [P7 gate — the new-game intro plays and returns, and a defect I nearly invented](#p7-gate--the-new-game-intro-plays-and-returns-and-a-defect-i-nearly-invented) +* [`ScreenView.skipped` was correct and unread since P1 — now it says so itself](#screenviewskipped-was-correct-and-unread-since-p1--now-it-says-so-itself) +* [Refutation attempt — "the element declared first paints first"](#refutation-attempt--the-element-declared-first-paints-first) +* [The menu bed plays under the cutscene, nobody decided that, and it stays](#the-menu-bed-plays-under-the-cutscene-nobody-decided-that-and-it-stays) +* [`wait:`, and the bed's loop seam is 3.4 seconds of silence](#waitseconds-and-the-beds-loop-seam-is-34-seconds-of-silence) +* [Two harness bugs, and the defect the second one was hiding](#two-harness-bugs-and-the-defect-the-second-one-was-hiding) +* [The `PRESS Ⓐ` plate: four bugs in a row, and a number I have been misquoting](#the-press--plate-four-bugs-in-a-row-and-a-number-i-have-been-misquoting) +* [The title's residual is the sweep phase, and the sweeps fit at ~400 units, not 357.7](#the-titles-residual-is-the-sweep-phase-and-the-sweeps-fit-at-400-units-not-3577) +* [A second capture closes the sweep-geometry question, and the plate matches at 0.00093 %](#a-second-capture-closes-the-sweep-geometry-question-and-the-plate-matches-at-000093) +* [`--focus=` did nothing on the menu path, and the corpus had an untested focus capture](#--focus-did-nothing-on-the-menu-path-and-the-corpus-had-an-untested-focus-capture) +* [The last unused capture, placed — and its residual is the oracle's, not the port's](#the-last-unused-capture-placed--and-its-residual-is-the-oracles-not-the-ports) +* [`MODDING.md` had five rules and no check. Now it has one, and all five pass](#moddingmd-had-five-rules-and-no-check-now-it-has-one-and-all-five-pass) +* [Five authored values had no reader — including the one I asked for measurements into](#five-authored-values-had-no-reader--including-the-one-i-asked-for-measurements-into) +* [`FORMAT.md` declared the port's own export invalid, and a failed export is not atomic](#formatmd-declared-the-ports-own-export-invalid-and-a-failed-export-is-not-atomic) +* [`check-all`, a verdict that ignored its own statistic, and a claim of mine that was wrong](#check-all-a-verdict-that-ignored-its-own-statistic-and-a-claim-of-mine-that-was-wrong) +* [The `title` disagreement, localised — and the question I filed for it was the wrong one](#the-title-disagreement-localised--and-the-question-i-filed-for-it-was-the-wrong-one) +* [Auditing `BLOCKED.md` found three stale rows, and the undated ones were all three](#auditing-blockedmd-found-three-stale-rows-and-the-undated-ones-were-all-three) +* [The record already answered last iteration's question, under headings that name it](#the-record-already-answered-last-iterations-question-under-headings-that-name-it) + + ## P0 — the exporter, 2026-08-28 ### The exporter reads one authored file, and stamps its provenance into the output @@ -6500,3 +6619,58 @@ audited against a running port. its own failure mode is not thereby protected from it.* This page has said "it goes stale, it has" since P1, in a header everyone reads and nobody acts on. The warning was doing the work of a check. + +## The record already answered last iteration's question, under headings that name it + +Last iteration I investigated `title`'s and `title_jp`'s disagreement with +`sylpheed-cli`, ruled out leaves and placement offsets, watched an edge test fail +its own control, and filed the mechanism as **unknown** — to the Decoder as well +as here. + +**Both were already explained in this file**, under +[`title`: a tie in the paint order](#title-a-tie-in-the-paint-order--neither-renderer-is-wrong) +and +[`title_jp`: nearest-neighbour sampling phase](#title_jp-nearest-neighbour-sampling-phase--the-cli-is-the-one-i-would-call-wrong). +Headings that name the two screens in question. + +### Both still hold, checked rather than assumed + +* **`title`** — the CLI uses a paint order *measured* off the running game; the + port derives one. Every disagreement is **inside a tie**. Verified against the + current export: `title` still ties on `0x8083` (the `back2` glow group, 5 + elements), `0x80a0` (7) and `0x8010` (2), and the export declares + `paint_order_ties` in `unresolved`. The old entry costs it at **904 px in the + glow band, all 4–6/255**; I measured **790 px at x≈938–1162, y≈172–310, max 6** + — same band, same magnitude, count moved with the export. +* **`title_jp`** — `ptlogo_eff2` at **125 %**, where the CLI samples the source at + the destination pixel's top-left and a GPU samples at its centre. The entry + claims it is the *only* drawn element in the export at a non-integer scale + ratio. 🟡 A whole-export census finds **26** such keyframes — until it is + restricted to elements **visible at `rest`**, which is the pose `verify-screen` + uses. Then there is exactly **one**: `title_jp/ptlogo_eff2`. The claim survives, + and it survives narrowly enough to be worth the check. + +### 🔴 The failure is navigability, not memory + +6 502 lines, 111 `##` sections, 243 `###`, and **no index**. So "has this been +decided already?" had no cheap answer, and the expensive answer — re-deriving it +— looked like diligence. It also cost the Decoder a message reporting an open +question that was closed. + +`tools/port/index-decisions` generates a contents block, and `check-all` runs +`--check`. ⚠️ Two things about the design, both learned the hard way in the same +half-hour: + +* it carries **no line numbers**. The first version did, and writing the index + shifted every line below it — a fixpoint that failed its own `--check` on the + first run. Worse, appending a section would silently invalidate all of them. +* it is **checked**, because a stale index is worse than none: it would answer + *"is this already decided?"* with a confident no. That is exactly the failure + it was built to prevent, and building an unchecked one would have reproduced it + one level up — which this port has now done often enough that I looked for it + deliberately. + +📌 The twelfth instance, and a new species. The first eleven were *"correct, +documented, unexercised."* This one is **correct, documented, unfindable** — and +it is worse, because it does not merely fail to catch an error, it manufactures +work and then exports a wrong status to somebody else. diff --git a/tools/port/check-all b/tools/port/check-all index 31f4eb89..0f9755c2 100755 --- a/tools/port/check-all +++ b/tools/port/check-all @@ -39,6 +39,9 @@ step format-validator must-pass "$BIN" check step modding-rules must-pass tools/port/check-modding step capture-controls must-pass tools/port/check-capture-controls step menu-audio must-pass env OUT="$OUT/audio" tools/port/verify-menu-audio +# A stale index is worse than none: it answers "is this already decided?" with a +# confident no. That is not hypothetical -- see the entry it was built after. +step decisions-index must-pass tools/port/index-decisions --check echo echo "reported, not asserted:" step oracle-captures report-only env OUT="$OUT/oracle" tools/port/verify-capture diff --git a/tools/port/index-decisions b/tools/port/index-decisions new file mode 100755 index 00000000..b8d2bdba --- /dev/null +++ b/tools/port/index-decisions @@ -0,0 +1,60 @@ +#!/usr/bin/env bash +# Regenerate the contents block at the top of `docs/port/DECISIONS.md`. +# +# tools/port/index-decisions # rewrite the index +# tools/port/index-decisions --check # fail if it is out of date +# +# 🔴 WHY THIS EXISTS. The record reached 6 500 lines and 111 sections with no +# index, and on 2026-08-30 I spent an iteration empirically re-deriving a result +# it already contained -- under two headings that name the screens in question -- +# then reported the question as unexplained to the Decoder. An unnavigable record +# is not a record that is hard to read; it is one that does not get read. +# +# ⚠️ `--check` exists because a stale index is worse than none: it would answer +# "is this already decided?" with a confident no. `check-all` runs it. +set -euo pipefail +cd "${PROJECT_DIR:-/work}" +DOC=docs/port/DECISIONS.md +BEG='' +END='' + +body=$(python3 - "$DOC" <<'PY' +import re, sys +lines = open(sys.argv[1]).read().split('\n') +out = [] +for l in lines: + if l.startswith('## '): + title = l[3:].strip() + # A GitHub anchor: lowercased, punctuation dropped, spaces to hyphens. + anchor = re.sub(r'[^\w\s-]', '', title.lower()).strip().replace(' ', '-') + # NO LINE NUMBERS. They would make the index a fixpoint problem -- writing + # it shifts every line below it -- and, worse, every appended section + # would silently invalidate all of them. An anchor survives both. + out.append(f"* [{title}](#{anchor})") +print('\n'.join(out)) +PY +) +new=$(printf '%s\n\n%d sections. Search this before re-deriving anything.\n\n%s\n\n%s\n' \ + "$BEG" "$(grep -c '^## ' "$DOC")" "$body" "$END") + +cur=$(awk -v b="$BEG" -v e="$END" 'index($0,b){f=1} f{print} index($0,e){f=0}' "$DOC") + +if [ "${1:-}" = --check ]; then + if [ "$cur" = "$new" ]; then echo " index-decisions ok"; exit 0 + else echo " index-decisions 🔴 the index is out of date -- run tools/port/index-decisions"; exit 1; fi +fi + +python3 - "$DOC" "$BEG" "$END" "$new" <<'PY' +import sys +doc, beg, end, new = sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4] +s = open(doc).read() +if beg in s: + i, j = s.index(beg), s.index(end) + len(end) + s = s[:i] + new.rstrip('\n') + s[j:] +else: + # First run: place it after the H1 and its opening paragraph. + k = s.index('\n## ') + s = s[:k] + '\n\n' + new.rstrip('\n') + s[k:] +open(doc, 'w').write(s) +print("index written") +PY