@agentsfleet/orly 0.6.7 → 0.6.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -32,7 +32,11 @@ fi
32
32
  # that did or did not read the façade is still the session committing.
33
33
  SOURCE_PATTERN='\.(zig|ts|tsx|js|jsx|py|rs|go|sh|sql)$'
34
34
 
35
- if git diff --cached --name-only | grep -qE "$SOURCE_PATTERN"; then
35
+ # Governance paths join the source list here: dispatch/edit_rules.sh declares
36
+ # the corpus scope, so a staged rule edit now has a façade to have read. The
37
+ # check itself stays the arbiter — a path outside every leaf's globs still
38
+ # reports "nothing staged triggers a façade" and costs one grep.
39
+ if git diff --cached --name-only | grep -qE "$SOURCE_PATTERN|$GOVERNANCE_PATTERN"; then
36
40
  if ! bash audits/doc-read.sh check; then
37
41
  echo "pre-commit: a triggered façade went unread — read it, or record why" >&2
38
42
  exit 1
package/AGENTS.md CHANGED
@@ -144,6 +144,7 @@ Guards fire pre-hoc regardless of lifecycle stage. Override: `<GATE>: SKIPPED pe
144
144
  | write auth-flow / token-minting files | `write_auth` | `dispatch/write_auth.md` — auth invariants; reads the product repo's `docs/AUTH.md` before · 🟣 delegated (product repo). |
145
145
  | write published docs / OpenAPI prose | `write_documentation` | `dispatch/write_documentation.md` → `docs/DOCUMENTATION_RULES.md` before narrower guides; page, fragment, API, changelog scopes differ · 🟣 delegated to repository pre-commit checks. |
146
146
  | write a changelog entry | `write_changelog` | `dispatch/write_changelog.md` — changelog voice (one headline, no marketing words, `**Bold lead-noun**` bullets, history append-only); reads `docs/CHANGELOG_VOICE.md` · 🤔 judgment-only. |
147
+ | write/update a PR or MR body or its session notes | `write_pr_description` | `dispatch/write_pr_description.md` — append numbered `Session notes`, `Review`, and `Make`; measured fenced diagrams; review dispositions; commands with real output. |
147
148
  | claim "tests pass / ready / shipping" | `verify` | `dispatch/verify.md` — verification tiers (`make` canonical; package-scoped runners are **not** verification), done-message glyph format · 🤔 judgment-only, `VERIFY GATE: <target> skipped per environment constraint (reason: ...)` only when genuinely unrunnable. |
148
149
  | name a stream/channel/Redis namespace/queue/RPC/Postgres schema, or describe a flow | `name_architecture` | `dispatch/name_architecture.md` — architecture-consult discipline; grep relevant `docs/architecture/` (chat brainstorming counts) · **no override** — doc wins until reconciled. |
149
150
  | run a lifecycle stage (open→close a stream, worktree setup, milestone bootstrap) | `lifecycle` | `dispatch/lifecycle.md` — stage runbooks: CHORE(open/close) checklists, PLAN expansions, spec discipline, deferral format, pre-PR gates, LAND · 🤔 judgment-only. |
@@ -240,7 +241,7 @@ Required when spec involved — after last COMMIT, before PR. Also runs when par
240
241
 
241
242
  **PR budget — one per milestone.** One ready PR plus one follow-up is the ceiling. Fold new scope into the open PR (reopen `done/`→`active/` if needed), not a third.
242
243
 
243
- **Required outputs** — full checklist in `dispatch/lifecycle.md`: all Dimensions/Sections `DONE` (`IN_PROGRESS` if parked) · spec → `docs/v*/done/` iff complete · new changelog `<Update>` **and** the affected `~/Projects/docs/` pages revised · `docs/architecture/**` diff or Session-Notes why-not · PR `## Session notes` (decisions, deferrals, skill + babysit outcomes) · orphan sweep (RULE ORP) · ephemeral `HANDOFF*` docs deleted · `git status -uall` audit, **empty post-commit** before the PR · version sync (`make check-version` passes).
244
+ **Required outputs** — full checklist in `dispatch/lifecycle.md`: all Dimensions/Sections `DONE` (`IN_PROGRESS` if parked) · spec → `docs/v*/done/` iff complete · new changelog `<Update>` **and** the affected `~/Projects/docs/` pages revised · `docs/architecture/**` diff or Session-Notes why-not · PR body per `dispatch/write_pr_description.md` · orphan sweep (RULE ORP) · ephemeral `HANDOFF*` docs deleted · `git status -uall` audit, **empty post-commit** before the PR · version sync (`make check-version` passes).
244
245
 
245
246
  **Deferral discipline.** A "deferred to follow-up" claim requires an **user-acked verbatim quote** in PR Session Notes (or spec Discovery); agent-unilateral deferral = incomplete scope, not deferral — CHORE(close) blocks until the item lands or the quote is captured. Quote format + HANDOFF faithfulness: `dispatch/lifecycle.md`.
246
247
 
@@ -269,7 +269,7 @@ The questionnaire is organised by scenario. Each scenario corresponds to a momen
269
269
 
270
270
  This scenario exists because the most likely failure of the operating model is
271
271
  not a missing rule — it's the *agent misreading a rule that is present*.
272
- AGENTS.md is ~32 KB of table-dense, exception-laden prose (SOUL.md inlined); the conditions
272
+ AGENTS.md is byte-capped, table-dense prose (SOUL.md inlined); the conditions
273
273
  below are where an LLM reading it tends to drift, conflate, or confabulate.
274
274
  The questions force *proof of reading* over *recall*.
275
275
 
@@ -366,6 +366,18 @@ than only the one with a Read hook.
366
366
  | 28.3 | Does the DOC READ GATE require recording each triggered read with `bash audits/doc-read.sh log <path>` — runnable in every runtime, automatic in Claude Code via the `PostToolUse` Read hook — with `.githooks/pre-commit` comparing the record against the staged diff? | YES |
367
367
  | 28.4 | When no read record exists (a runtime without hook support, before the command is run), does `audits/doc-read.sh check` warn 🟠 and exit 0 rather than red — because a check that fires where it cannot work teaches people to ignore it? | YES |
368
368
 
369
+ ### Scenario 29 — Pull-request and merge-request descriptions
370
+
371
+ Pull Request (PR) and Merge Request (MR) bodies use the same shape on both
372
+ forges.
373
+
374
+ | # | Question | Expected |
375
+ |---|---|---|
376
+ | 29.1 | Does writing or updating a PR/MR body through either `gh` or `glab` route to `dispatch/write_pr_description.md`? | YES |
377
+ | 29.2 | Must each working session re-read the forge body, append the highest numbered `## Session notes <N>` plus one followed by `## Review` and `## Make`, and preserve every earlier unit? | YES |
378
+ | 29.3 | Does the façade require two or three outcome-first prose lines, useful fenced ASCII diagrams at most 78 columns using measured values, P0/P1/P2 review dispositions, and commands paired with actual output? | YES |
379
+ | 29.4 | Must clean review say `No findings.`, skipped review and verification name why, and unverified claims identify what would verify them? | YES |
380
+
369
381
  ## Comprehension layer
370
382
 
371
383
  The deterministic audit proves the rules are *present*; it cannot prove an
@@ -457,6 +469,8 @@ Scenario verdicts:
457
469
  | 25 | Allocator and concurrency discipline | <N/M YES> |
458
470
  | 26 | Rules propagation | <N/M YES> |
459
471
  | 27 | Rule-path residence and reachability | <N/M YES> |
472
+ | 28 | Rule-enforcement ledger and doc reads | <N/M YES> |
473
+ | 29 | PR and MR descriptions | <N/M YES> |
460
474
 
461
475
  OVERALL: PASS | FAIL — <reason if fail>
462
476
  ```
package/audits/data.sh CHANGED
@@ -45,7 +45,7 @@ REQUIRED_DISPATCH=(
45
45
  "write_spec" "write_http" "write_auth" "write_changelog"
46
46
  "write_documentation" "verify" "name_architecture" "edit_rules"
47
47
  "write_rust" "write_go" "write_python" "write_shell" "write_mdx"
48
- "lifecycle"
48
+ "write_pr_description" "lifecycle"
49
49
  )
50
50
 
51
51
  # Check 2 — every source/config language has at least one mention.
@@ -105,6 +105,7 @@ DOTFILES_RESIDENT=(
105
105
  "dispatch/write_auth.md"
106
106
  "dispatch/write_changelog.md"
107
107
  "dispatch/write_documentation.md"
108
+ "dispatch/write_pr_description.md"
108
109
  "dispatch/verify.md"
109
110
  "dispatch/name_architecture.md"
110
111
  "dispatch/edit_rules.md"
@@ -169,6 +170,7 @@ NAMED_SCENARIOS=(
169
170
  "Rules propagation" # Scenario 26
170
171
  "Rule-path" # Scenario 27
171
172
  "Rule-enforcement ledger" # Scenario 28
173
+ "Pull-request and merge-request descriptions" # Scenario 29
172
174
  )
173
175
 
174
176
  # Check 14 — the Rule-extension protocol must enumerate all four wiring steps.
@@ -94,19 +94,21 @@ content_hash() {
94
94
  git -C "$LEDGER_ROOT" hash-object "$LEDGER_ROOT/$1" 2>/dev/null || printf ''
95
95
  }
96
96
 
97
- # Façade pages the staged files trigger, one per line. Same glob map the
98
- # reachability report uses: a façade's scope is its dispatch_init line, so this
99
- # check and the ledger can never disagree about what fires.
97
+ # Façade pages the staged files trigger, one per line. Scope comes from
98
+ # ledger_facade_scope — the page's own oracle-scope tag, else its leaf's
99
+ # dispatch_init — which is the SAME resolver the ledger's reachability report
100
+ # uses, so this check and the ledger can never disagree about what fires.
101
+ # Iterating pages rather than leaves is the point: a façade without a
102
+ # deterministic leaf still has a trigger surface, and used to be unaskable.
100
103
  expected_facade_pages() {
101
- local staged="$1" script stem globs
102
- while IFS= read -r script; do
103
- [ -n "$script" ] || continue
104
- stem="$(basename "$script" .sh)"
105
- [ -f "$LEDGER_ROOT/dispatch/$stem.md" ] || continue
106
- globs="$(ledger_facade_globs "$script")"
107
- [ -n "$globs" ] || continue
108
- [ "$(ledger_match_count "$globs" < "$staged")" -gt 0 ] && printf 'dispatch/%s.md\n' "$stem"
109
- done < <(ledger_facade_scripts)
104
+ local staged="$1" page stem scope
105
+ while IFS= read -r page; do
106
+ [ -n "$page" ] || continue
107
+ scope="$(ledger_facade_scope "$page")"
108
+ [ -n "$scope" ] || continue
109
+ stem="$(basename "$page" .md)"
110
+ [ "$(ledger_match_count "$scope" < "$staged")" -gt 0 ] && printf 'dispatch/%s.md\n' "$stem"
111
+ done < <(ledger_facade_pages)
110
112
  }
111
113
 
112
114
  json_unescape() {
@@ -187,6 +187,40 @@ ledger_has_prefix() {
187
187
  DISPATCH_LIB_BASENAME="lib.sh"
188
188
 
189
189
  # The executable façades: dispatch/*.sh minus the shared library.
190
+ # A façade's trigger surface, declared where it belongs: in the façade. The tag
191
+ # is one HTML comment, invisible in rendered markdown, e.g.
192
+ # <!-- oracle-scope: *.rs -->
193
+ # Scope used to be readable ONLY from a leaf's dispatch_init line, so the five
194
+ # façades carrying deterministic checks had a voice and the other fourteen had
195
+ # none — a Rust author was never asked to read the Rust rules, because
196
+ # write_rust.md has no .sh. The page wins; the leaf remains the fallback so the
197
+ # mechanised façades keep the scope they already declare, in one place.
198
+ FACADE_SCOPE_TAG='^[[:space:]]*<!--[[:space:]]*oracle-scope:[^>]*-->[[:space:]]*$'
199
+
200
+ ledger_facade_pages() {
201
+ local file
202
+ for file in "$LEDGER_ROOT"/dispatch/*.md; do
203
+ [ -f "$file" ] || continue
204
+ printf '%s\n' "$file"
205
+ done
206
+ }
207
+
208
+ # Space-separated, trailing space — same shape ledger_facade_globs returns, so
209
+ # ledger_match_count reads either without knowing which declared it.
210
+ ledger_facade_scope() {
211
+ local page="$1" tag stem leaf
212
+ tag="$(grep -hE "$FACADE_SCOPE_TAG" "$page" 2>/dev/null | head -1)"
213
+ if [ -n "$tag" ]; then
214
+ printf '%s' "$tag" \
215
+ | sed -e 's/.*oracle-scope:[[:space:]]*//' -e 's/[[:space:]]*-->.*//' \
216
+ -e 's/[[:space:]][[:space:]]*/ /g' -e 's/[[:space:]]*$/ /'
217
+ return 0
218
+ fi
219
+ stem="$(basename "$page" .md)"
220
+ leaf="$(dirname "$page")/$stem.sh"
221
+ [ -f "$leaf" ] && ledger_facade_globs "$leaf"
222
+ }
223
+
190
224
  ledger_facade_scripts() {
191
225
  local file
192
226
  for file in "$LEDGER_ROOT"/dispatch/*.sh; do
@@ -208,7 +242,16 @@ ledger_facade_globs() {
208
242
  # is the match itself — the same expansion dispatch_resolve_files applies to
209
243
  # explicit file arguments, so a fire count means what the dispatcher means.
210
244
  ledger_match_count() {
211
- local globs="$1" path glob matches=0
245
+ local globs="$1" path glob matches=0 reglob=""
246
+ # `for glob in $globs` needs WORD SPLITTING and must not get PATHNAME
247
+ # EXPANSION: unguarded, bash expands each pattern against the real tree
248
+ # first, so a façade declaring 'src/*' iterates the 33 files that happen to
249
+ # exist there and then compares them as literals — src/foo.rs matches
250
+ # nothing and core/* "matches" only because it expanded to the one file in
251
+ # core/. Latent while every leaf declared extension globs ('*.zig') that
252
+ # match nothing at the root and so survive expansion unchanged; the first
253
+ # path-glob façade turns it into a scope that silently reads wrong.
254
+ case "$-" in *f*) ;; *) reglob="yes"; set -f ;; esac
212
255
  while IFS= read -r path; do
213
256
  [ -n "$path" ] || continue
214
257
  for glob in $globs; do
@@ -216,6 +259,7 @@ ledger_match_count() {
216
259
  case "$path" in $glob) matches=$((matches + 1)); break ;; esac
217
260
  done
218
261
  done
262
+ [ -n "$reglob" ] && set +f
219
263
  printf '%d' "$matches"
220
264
  }
221
265
 
@@ -101,12 +101,24 @@ check_registry_parity() {
101
101
  # is answerable from the tree — unlike "has this fired lately", which depends on
102
102
  # what happened to get committed recently and gates nothing.
103
103
  check_facade_scopes() {
104
- local script stem
104
+ local script stem page
105
+ # Iterates LEAVES, not pages: a leaf is a promise that something mechanical
106
+ # fires, so one declaring no scope is dead weight whether or not a page sits
107
+ # beside it. A façade with no leaf and no tag is situational — a lifecycle
108
+ # stage transition, a "tests pass" claim, writing a PR body — and has no
109
+ # staged-diff trigger to declare, so it is not a finding.
105
110
  while IFS= read -r script; do
106
111
  [ -n "$script" ] || continue
107
- [ -n "$(ledger_facade_globs "$script")" ] && continue
108
112
  stem="$(basename "$script" .sh)"
109
- fail "facade=$stem declares no dispatch_init scope — dispatch/$stem.sh (nothing it carries can ever fire)"
113
+ page="$LEDGER_ROOT/dispatch/$stem.md"
114
+ # Either surface may carry the scope; ledger_facade_scope prefers the page
115
+ # and falls back to this leaf, so both spellings satisfy the same check.
116
+ if [ -f "$page" ]; then
117
+ [ -n "$(ledger_facade_scope "$page")" ] && continue
118
+ else
119
+ [ -n "$(ledger_facade_globs "$script")" ] && continue
120
+ fi
121
+ fail "facade=$stem declares no scope — no oracle-scope tag in dispatch/$stem.md and no dispatch_init in dispatch/$stem.sh (nothing it carries can ever fire)"
110
122
  done < <(ledger_facade_scripts)
111
123
  }
112
124
 
@@ -148,6 +148,7 @@ Guards fire pre-hoc regardless of lifecycle stage. Override: `<GATE>: SKIPPED pe
148
148
  | write auth-flow / token-minting files | `write_auth` | `dispatch/write_auth.md` — auth invariants; reads the product repo's `docs/AUTH.md` before · 🟣 delegated (product repo). | <!-- oracle-packs:domain.auth -->
149
149
  | write published docs / OpenAPI prose | `write_documentation` | `dispatch/write_documentation.md` → `docs/DOCUMENTATION_RULES.md` before narrower guides; page, fragment, API, changelog scopes differ · 🟣 delegated to repository pre-commit checks. | <!-- oracle-packs:domain.documentation -->
150
150
  | write a changelog entry | `write_changelog` | `dispatch/write_changelog.md` — changelog voice (one headline, no marketing words, `**Bold lead-noun**` bullets, history append-only); reads `docs/CHANGELOG_VOICE.md` · 🤔 judgment-only. | <!-- oracle-packs:domain.changelog -->
151
+ | write/update a PR or MR body or its session notes | `write_pr_description` | `dispatch/write_pr_description.md` — append numbered `Session notes`, `Review`, and `Make`; measured fenced diagrams; review dispositions; commands with real output. | <!-- oracle-packs:universal.authoring -->
151
152
  | claim "tests pass / ready / shipping" | `verify` | `dispatch/verify.md` — verification tiers (`make` canonical; package-scoped runners are **not** verification), done-message glyph format · 🤔 judgment-only, `VERIFY GATE: <target> skipped per environment constraint (reason: ...)` only when genuinely unrunnable. | <!-- oracle-packs:product.agentsfleet -->
152
153
  | name a stream/channel/Redis namespace/queue/RPC/Postgres schema, or describe a flow | `name_architecture` | `dispatch/name_architecture.md` — architecture-consult discipline; grep relevant `docs/architecture/` (chat brainstorming counts) · **no override** — doc wins until reconciled. | <!-- oracle-packs:universal.authoring -->
153
154
  | run a lifecycle stage (open→close a stream, worktree setup, milestone bootstrap) | `lifecycle` | `dispatch/lifecycle.md` — stage runbooks: CHORE(open/close) checklists, PLAN expansions, spec discipline, deferral format, pre-PR gates, LAND · 🤔 judgment-only. |
@@ -255,7 +256,7 @@ Required when spec involved — after last COMMIT, before PR. Also runs when par
255
256
 
256
257
  **PR budget — one per milestone.** One ready PR plus one follow-up is the ceiling. Fold new scope into the open PR (reopen `done/`→`active/` if needed), not a third.
257
258
 
258
- **Required outputs** — full checklist in `dispatch/lifecycle.md`: all Dimensions/Sections `DONE` (`IN_PROGRESS` if parked) · spec → `docs/v*/done/` iff complete · new changelog `<Update>` **and** the affected `~/Projects/docs/` pages revised · `docs/architecture/**` diff or Session-Notes why-not · PR `## Session notes` (decisions, deferrals, skill + babysit outcomes) · orphan sweep (RULE ORP) · ephemeral `HANDOFF*` docs deleted · `git status -uall` audit, **empty post-commit** before the PR · version sync (`make check-version` passes).
259
+ **Required outputs** — full checklist in `dispatch/lifecycle.md`: all Dimensions/Sections `DONE` (`IN_PROGRESS` if parked) · spec → `docs/v*/done/` iff complete · new changelog `<Update>` **and** the affected `~/Projects/docs/` pages revised · `docs/architecture/**` diff or Session-Notes why-not · PR body per `dispatch/write_pr_description.md` · orphan sweep (RULE ORP) · ephemeral `HANDOFF*` docs deleted · `git status -uall` audit, **empty post-commit** before the PR · version sync (`make check-version` passes).
259
260
 
260
261
  **Deferral discipline.** A "deferred to follow-up" claim requires an **user-acked verbatim quote** in PR Session Notes (or spec Discovery); agent-unilateral deferral = incomplete scope, not deferral — CHORE(close) blocks until the item lands or the quote is captured. Quote format + HANDOFF faithfulness: `dispatch/lifecycle.md`.
261
262
 
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env bash
2
+ # dispatch/edit_rules.sh — the rule-corpus dispatch, DETERMINISTIC façade.
3
+ #
4
+ # Pairs with dispatch/edit_rules.md (the LATENT façade — prose the agent reads).
5
+ #
6
+ # This leaf exists for the reason RULE UFS exists in the Rust pack: a façade
7
+ # that declares its scope in PROSE declares it to nobody. edit_rules.md has
8
+ # always named its trigger surface — core/**, packs/**, schemas/**, src/**,
9
+ # registry.json, governance hooks (edit_rules.md:15, and the same row in
10
+ # docs/EXECUTE_DOC_READS.md) — but audits/doc-read.sh derives what fires from
11
+ # `dispatch_init` globs in a leaf, and this façade had no leaf. So every
12
+ # governance edit reported "nothing staged triggers a façade" and the DOC READ
13
+ # GATE could not fire on the one surface whose edits change the rules
14
+ # themselves. The globs below ARE that scope, now in a form a gate can read.
15
+ #
16
+ # dispatch/edit_rules.sh <file> [...] # explicit targets (EXECUTE)
17
+ # dispatch/edit_rules.sh --staged # staged corpus files (CONFORM, pre-commit)
18
+ #
19
+ # Layering: AGENTS.md → edit_rules.md (latent) → edit_rules.sh (this) → audits/*.sh
20
+ # Signals: 🟢 pass · 🔴 fail (blocks) · 🤔 DECIDE (judgment; blocks the TURN, not the script)
21
+ # Exit: 0 = mechanical gates pass · 1 = ≥1 failed · 2 = usage error.
22
+
23
+ source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
24
+
25
+ # Scope mirrors docs/EXECUTE_DOC_READS.md's governance row and edit_rules.md:15.
26
+ # Kept in one order with them: a glob here that the row does not name is a leaf
27
+ # reading more than the façade declares, which is the same drift inverted.
28
+ # ONE LINE, deliberately: ledger_facade_globs reads `head -1` of the
29
+ # dispatch_init line, so a backslash continuation declares an EMPTY scope and
30
+ # the façade silently stops firing — the exact failure this leaf exists to end.
31
+ dispatch_init "RULES" 'core/*' 'packs/*' 'schemas/*' 'src/*' 'registry.json' 'dispatch/*' 'audits/*' '.githooks/*' 'AGENTS.md' 'AGENTS.orly.md'
32
+ dispatch_resolve_files "$@"
33
+ dispatch_header
34
+
35
+ # ── deterministic gates ────────────────────────────────────────────
36
+ # Rule prose carries no milestone ids — the packs say so themselves ("never a
37
+ # memory file, never a milestone cite"), and until now nothing read the corpus
38
+ # for them. Same leaf write_any already wires, so no new code enters the gloss.
39
+ dispatch_run_helper "MSID" "msid-ui.sh" "--staged"
40
+
41
+ dispatch_verdict
@@ -736,6 +736,34 @@ Every triggered consult is logged in the active spec's **Discovery** section, or
736
736
 
737
737
  NLR is the cleanup-on-touch arm; NLG bans new legacy framing pre-v2.0.0; this guard covers the harder judgment calls ("should this whole subsystem exist") that need the user's input.
738
738
 
739
+ ## Porting a codebase between languages (RULE PORT)
740
+
741
+ > [JUDGMENT → NEW:PORT]
742
+
743
+ A port is a REWRITE with a conformance test, never a transliteration.
744
+
745
+ Before porting a construct, ask what it was working AROUND, not what it was.
746
+ A thread with a stop flag is usually the absence of async. A registration map
747
+ with generation counters is usually the absence of structured cancellation. A
748
+ defer chain is usually the absence of ownership. Port the GUARANTEE; delete the
749
+ workaround, and write down which guarantee replaced it.
750
+
751
+ Every construct carried over needs one sentence: "kept because <the target
752
+ language has no better mechanism>". If that sentence cannot be written
753
+ honestly, it is transliteration.
754
+
755
+ Conformance is on OBSERVABLE behaviour — wire bytes, rows, status codes, money,
756
+ exit codes. Never on internal shape. A test that pins internal shape freezes
757
+ the source language's debt into the target.
758
+
759
+ Read the reference implementations the spec names before designing. Take the
760
+ SHAPE and reject the specifics.
761
+
762
+ Name the tech debt you are NOT carrying over, and where it went.
763
+
764
+ A milestone titled "parity" means behavioural parity. It never licenses a
765
+ one-to-one file, type, or thread mapping.
766
+
739
767
  ## Comment Voice — New to the Codebase, Familiar with the Goal
740
768
 
741
769
  > [JUDGMENT → NEW:CMT]
@@ -1,5 +1,7 @@
1
1
  # Go authoring
2
2
 
3
+ <!-- oracle-scope: *.go -->
4
+
3
5
  Return errors, do not panic across a package boundary. Wrap with `%w` so the
4
6
  caller can still match the cause, and add context the caller does not already
5
7
  have — never re-wrap the same fact twice.
@@ -1,5 +1,7 @@
1
1
  # Markdown JSX authoring
2
2
 
3
+ <!-- oracle-scope: *.mdx -->
4
+
3
5
  Treat Markdown JSX (MDX) as a language, not generic prose. Validate front matter,
4
6
  component nesting, links, code fences, and accessible image descriptions.
5
7
 
@@ -0,0 +1,152 @@
1
+ # Pull-request and merge-request description dispatch
2
+
3
+ Read this file before writing or updating a Pull Request (PR) or Merge Request
4
+ (MR) body, including an update that only appends session notes. It governs
5
+ GitHub and GitLab equally.
6
+
7
+ ## Trigger
8
+
9
+ This dispatch fires before any forge command or API call that creates or changes
10
+ a PR or MR body. This includes `gh pr create`, `gh pr edit --body`,
11
+ `glab mr create`, and `glab mr update --description`. A helper, skill, or script
12
+ that writes the body fires the same rule.
13
+
14
+ ## Append unit and sequence
15
+
16
+ Append exactly one complete unit per working session that touches the PR or MR,
17
+ after review and verification have finished:
18
+
19
+ 1. `## Session notes <N>`
20
+ 2. `## Review`
21
+ 3. `## Make`
22
+
23
+ Read the complete existing body before composing the unit. Find every heading
24
+ that exactly matches `## Session notes <integer>`. Use 1 when none exists;
25
+ otherwise use the highest integer plus 1. Re-read the body immediately before
26
+ the write and recompute the number if another session appended meanwhile. The
27
+ forge body is the source of truth; chat, a local draft, and commit count are not.
28
+
29
+ Append the unit once, preserving every earlier numbered unit. Never renumber,
30
+ rewrite, reorder, or consolidate earlier units. Build the complete unit locally
31
+ and make one body update so Review and Make cannot be mistaken for missing.
32
+
33
+ For GitHub, read with `gh pr view --json body --jq .body` and write with
34
+ `gh pr edit --body` or `--body-file`. For GitLab, read with
35
+ `glab mr view --output json --jq .description` and write with
36
+ `glab mr update --description`. Preserve all body content that precedes the
37
+ first numbered unit.
38
+
39
+ ## Session notes <N>
40
+
41
+ Start with two or three lines of plain prose, never more. Lead with what was
42
+ fixed. Include decisions and open deferrals that the next agent needs. Do not
43
+ add a preamble or restate the ticket.
44
+
45
+ Follow with a pictorial explanation using American Standard Code for
46
+ Information Interchange (ASCII) diagrams in fenced code blocks. The picture is
47
+ the substance when it can show a before/after, topology, proportion, measured
48
+ scale, or broken flow more clearly than a sentence. Use the sentence when it is
49
+ clearer. Never draw a box that merely repeats its caption.
50
+
51
+ Every diagram must:
52
+
53
+ - stay at or below 78 columns, including indentation;
54
+ - be inside a fence so both forges render it monospaced;
55
+ - label axes and units where they exist;
56
+ - use measured values copied from real command output, never illustrative
57
+ values.
58
+
59
+ These are real examples from a test-hardening PR. Copy their register, never
60
+ their numbers:
61
+
62
+ ```text
63
+ BEFORE AFTER
64
+ ┌──────────────────────────┐ ┌──────────────────────────┐
65
+ │ 671 tests ........ PASS │ │ 671 tests ........ PASS │
66
+ │ incl. all 5 properties │ │ scope property ... FAIL ✓│
67
+ └──────────────────────────┘ └──────────────────────────┘
68
+ a live cross-tenant read the mutant is caught
69
+ ships green
70
+ ```
71
+
72
+ ```text
73
+ compiler accepts 16 keys generator emitted 8
74
+ ┌────────────────────────────────────────────────┐
75
+ │████████████████░░░░░░░░░░░░░░░░│ BEFORE 50% │
76
+ └────────────────────────────────────────────────┘
77
+ ```
78
+
79
+ ```text
80
+ 0.001ms 0.01 0.1 1 10 100
81
+ ├────────────┼─────────┼──────────┼─────────┼──────────┤
82
+ ▲ ▲ ▲ ▲
83
+ cache HIT NEW budget OLD budget cache MISS
84
+ 0.003-0.005ms 0.1ms 1.0ms 12-21ms
85
+ ```
86
+
87
+ ## Review
88
+
89
+ Lead with the review result. Record every finding with P0, P1, or P2 severity,
90
+ the file and line, the defect, and its disposition. Use `FIXED` or `DEFERRED`;
91
+ a deferral also needs the user acknowledgement required by the lifecycle rule.
92
+
93
+ ```text
94
+ P0 oql_property_tests.rs:86 tenant-scope property asserted absence only —
95
+ passed with the scope clause deleted. FIXED
96
+ P1 query_load_test.rs:183 only assertion was arithmetic, green at any
97
+ latency. FIXED
98
+ P2 grpc_auth_test.rs:33 constant re-typed instead of imported. DEFERRED
99
+ ```
100
+
101
+ Write `No findings.` when review ran and found none. If review did not run,
102
+ write `Review not run — <reason>.` A missing heading never means clean.
103
+
104
+ ## Make
105
+
106
+ Lead with the verification state. List each command actually run with its exit
107
+ status and decisive output line. Use measured counts and baselines from that
108
+ run; never replace output with “tests pass.”
109
+
110
+ ```text
111
+ make test exit 0 860 passed; 0 failed (baseline 846, +14)
112
+ cargo clippy --all-targets -- -D warnings exit 0
113
+ make harness-verify All checks passed!
114
+ gitleaks no leaks found, 54 commits scanned
115
+ ```
116
+
117
+ For every command that could not run, name the command and the reason. Label
118
+ the evidence source when it is not Continuous Integration (CI), such as
119
+ `local — real stack`. Include the review skill and `orly-babysit-prs` command
120
+ outcomes here; if either could not run, name why. Never present a local run as
121
+ a green pipeline.
122
+
123
+ ## Whole-body rules
124
+
125
+ - Every factual claim traces to an actual command output line in Make or to a
126
+ command-named measurement in the diagram. State unchecked facts as
127
+ `Unverified: <fact> — <what would verify it>`.
128
+ - Lead with the answer in each section. Put reasoning after it and detail last.
129
+ - Use no marketing adjectives, throat-clearing opener, or closing paragraph
130
+ that repeats the body.
131
+ - Write for the next agent first: branch state, decisions, and open work must
132
+ be findable without reconstructing the session.
133
+
134
+ ## Complete template
135
+
136
+ ````markdown
137
+ ## Session notes <N>
138
+
139
+ <Outcome in two or three lines, backed by the evidence below.>
140
+
141
+ ```text
142
+ <Useful measured diagram, at most 78 columns.>
143
+ ```
144
+
145
+ ## Review
146
+
147
+ <P0/P1/P2 entries with dispositions, No findings., or not-run reason.>
148
+
149
+ ## Make
150
+
151
+ <Commands paired with exit status and decisive real output lines.>
152
+ ````
@@ -1,5 +1,7 @@
1
1
  # Python authoring
2
2
 
3
+ <!-- oracle-scope: *.py -->
4
+
3
5
  Use standard-library parsers for structured input. Keep resource ownership in
4
6
  context managers. Validate data at parse boundaries and preserve specific
5
7
  exception classes until the caller can decide how to respond.
@@ -1,5 +1,7 @@
1
1
  # Rust authoring
2
2
 
3
+ <!-- oracle-scope: *.rs -->
4
+
3
5
  Use ownership and borrowing to make resource lifetime visible. Keep `unsafe`
4
6
  blocks small and state the invariant they rely on. Preserve error variants until
5
7
  the caller has enough information to decide whether to retry or stop.
@@ -1,5 +1,7 @@
1
1
  # Shell authoring
2
2
 
3
+ <!-- oracle-scope: *.sh -->
4
+
3
5
  Quote expansions, use arrays for argument lists, and clean temporary files on
4
6
  every exit path. Avoid `eval` for external input. Match the repository's required
5
7
  shell and macOS Bash compatibility level.
@@ -10,10 +10,13 @@ Every triggered document requires one `📖 DOC READ: <path>` proof-line **per t
10
10
  | `core/**`, `packs/**`, `schemas/**`, `src/**`, `registry.json`, agent-home instruction links, repository rules synchronization, or governance hooks | `dispatch/edit_rules.md` + `docs/ORLY_ARCHITECTURE.md` — canonical sources, profiles, snapshots, refusal boundaries, and evidence. | <!-- oracle-packs:workflow.governance -->
11
11
  | Lifecycle stage transition (CHORE(open)/PLAN/CHORE(close)/LAND, worktree setup, milestone bootstrap) | `dispatch/lifecycle.md` — the entered stage's runbook section only (section-scan; fires on the transition itself, not on an Edit/Write). |
12
12
  | Any source file (cross-cutting authoring) | `dispatch/write_any.md` — length, logging, milestone-id, error-registry, UFS, greptile read, legacy-workaround family. |
13
+ | Porting a codebase, module, or subsystem from another language | `dispatch/write_any.md` §Porting a codebase between languages — a port is a rewrite with a conformance test: port the guarantee, delete the workaround, conform on observable behaviour, name the debt not carried over. |
13
14
  | Spec's "Applicable Rules" | Each rule (canonical). Missing → the cross-cutting codes of `dispatch/write_any.md` are the floor; surface omission. |
14
15
  | `*.zig` | `dispatch/write_zig.md`. ZIG GATE per edit. |
15
16
  | `*.ts`/`*.tsx`/`*.js`/`*.jsx` | `dispatch/write_ts_adhere_bun.md` — TS FILE SHAPE DECISION (§1) at PLAN, const/import/Bun-primitive discipline, anti-patterns. |
16
17
  | `*.rs` | `dispatch/write_rust.md` — ownership, error variants, feature combinations, and contention tests. | <!-- oracle-packs:language.rust -->
18
+ | Rust error type, `Result` alias, or any fallible signature | `dispatch/write_rust.md` §Error discipline — one error per crate, one `Result` alias, `#[from]` composition, `source()` never returns self. | <!-- oracle-packs:language.rust -->
19
+ | Designing a Rust function, type, or module; writing `match` on a `Result`/`Option`; any production `unwrap()` | `dispatch/write_rust.md` §Functional design — Result pipelines over control-flow trees, illegal states unrepresentable, bind once, `unwrap` needs a proved invariant. | <!-- oracle-packs:language.rust -->
17
20
  | `*.py` | `dispatch/write_python.md` — parsing boundaries, resource ownership, and specific exceptions. |
18
21
  | `*.sh` | `dispatch/write_shell.md` — quoting, array arguments, cleanup, input safety, and shell compatibility. |
19
22
  | `*.mdx` | `dispatch/write_mdx.md` — structure, front matter, links, code fences, accessibility, and Mintlify isolation. | <!-- oracle-packs:language.mdx -->
@@ -25,5 +28,6 @@ Every triggered document requires one `📖 DOC READ: <path>` proof-line **per t
25
28
  | Auth-flow | `docs/AUTH.md`. |
26
29
  | Published `*.mdx`, reusable Markdown JSX (MDX) fragment, customer readme, or public OpenAPI prose | `dispatch/write_documentation.md` → `docs/DOCUMENTATION_RULES.md` — establish page, fragment, API, or changelog scope before narrower guides. |
27
30
  | Changelog `<Update>` / release note (`changelog.mdx`) | First `dispatch/write_documentation.md`, then `dispatch/write_changelog.md` → `docs/CHANGELOG_VOICE.md`; internal-only ⇒ no entry. |
31
+ | Write or update a PR/MR body, including session notes (`gh pr create/edit --body`, `glab mr create/update --description`, forge API, helper, or skill) | `dispatch/write_pr_description.md` — append-only numbered session unit, measured diagram, review findings, and command output. |
28
32
  | `schema/*.sql` / migration | `dispatch/write_sql.md` + `docs/SCHEMA_CONVENTIONS.md` — naming/type conventions, schema/migration rules + Schema Table Removal Guard. Re-print Schema Guard output. |
29
33
  | Any spec under `docs/v*/{pending,active,done}/` or `docs/TEMPLATE.md` | `docs/TEMPLATE.md` "Prohibited" section — no time/effort estimates, no complexity ratings, no percentage-complete, no owners/dates. SPEC TEMPLATE GATE per edit. |
@@ -13,8 +13,8 @@ regenerates this file and fails on any difference. To update it, run
13
13
  | `docs/LIFECYCLE_PATTERNS.md` | — | 0 | 0 | 0/22 | mechanical |
14
14
  | `docs/CHANGELOG_VOICE.md` | — | 0 | 0 | 0/3 | latent |
15
15
  | `docs/VERIFY_TIERS.md` | — | 0 | 0 | 0/4 | uncited |
16
- | `docs/greptile-learnings/RULES.md` | — | 0 | 0 | 0/113 | mechanical |
17
- | **corpus** | | | | **26/280** | |
16
+ | `docs/greptile-learnings/RULES.md` | — | 0 | 0 | 0/114 | mechanical |
17
+ | **corpus** | | | | **26/281** | |
18
18
 
19
19
  ## How to read this
20
20
 
@@ -179,6 +179,13 @@ every code a dispatch `.sh` emits resolves to exactly one row here.
179
179
  **Tags:** zig, testing, sql
180
180
  **Example:** tested 0 for a column with CHECK >= 512.
181
181
 
182
+ ## RULE TCF — A test that cannot fail is not a test
183
+
184
+ **Rule:** Before trusting a test, make it red — delete the clause, guard, or branch it covers and confirm it fails. Assert what the check FOUND (the count, the symbol, the line), never only its exit code. A test that survives removal of its own subject is repaired or deleted, never kept for coverage.
185
+ **Why:** A vacuous pass and a real pass look identical from the outside, so the suite reports green while the guarantee is already gone.
186
+ **Tags:** testing, review, all
187
+ **Example:** a tenant-scope property asserted absence only and passed with the scope clause deleted; a dispatch eval asserted an exit code while its glob pointed at a dead tree.
188
+
182
189
  ## RULE JCL — CLI JSON contract discipline
183
190
 
184
191
  **Rule:** Use only stable error codes; UNKNOWN_COMMAND must name the unrecognized token; dual jsonMode guards need a comment.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentsfleet/orly",
3
- "version": "0.6.7",
3
+ "version": "0.6.9",
4
4
  "description": "Repository-scoped engineering harness: renders agent rules, materialises the gates that enforce them, and proves the PR boundary \u2014 for any coding agent.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -1,5 +1,7 @@
1
1
  # Go authoring
2
2
 
3
+ <!-- oracle-scope: *.go -->
4
+
3
5
  Return errors, do not panic across a package boundary. Wrap with `%w` so the
4
6
  caller can still match the cause, and add context the caller does not already
5
7
  have — never re-wrap the same fact twice.
@@ -1,5 +1,7 @@
1
1
  # Markdown JSX authoring
2
2
 
3
+ <!-- oracle-scope: *.mdx -->
4
+
3
5
  Treat Markdown JSX (MDX) as a language, not generic prose. Validate front matter,
4
6
  component nesting, links, code fences, and accessible image descriptions.
5
7
 
@@ -1,5 +1,7 @@
1
1
  # Python authoring
2
2
 
3
+ <!-- oracle-scope: *.py -->
4
+
3
5
  Use standard-library parsers for structured input. Keep resource ownership in
4
6
  context managers. Validate data at parse boundaries and preserve specific
5
7
  exception classes until the caller can decide how to respond.
@@ -1,5 +1,7 @@
1
1
  # Rust authoring
2
2
 
3
+ <!-- oracle-scope: *.rs -->
4
+
3
5
  Use ownership and borrowing to make resource lifetime visible. Keep `unsafe`
4
6
  blocks small and state the invariant they rely on. Preserve error variants until
5
7
  the caller has enough information to decide whether to retry or stop.
@@ -31,6 +33,134 @@ Local convention wins on conflict, but the divergence is named in the review
31
33
  output. Refresh the copy when the user asks or a cited rule looks stale
32
34
  (re-run the fetch; upstream updates continuously).
33
35
 
36
+ ## Error discipline (RULE ERR-RS)
37
+
38
+ One error type per crate, and a `Result` alias beside it:
39
+
40
+ pub type Result<T, E = Error> = core::result::Result<T, E>;
41
+
42
+ Every fallible function in the crate returns `Result<T>`. The default type
43
+ parameter is what lets the few functions answering with a foreign error keep
44
+ the same spelling — `Result<T, VerifyError>` — instead of reaching for
45
+ `std::result::Result`. A reader must never have to check WHICH error a
46
+ signature returns to know it is this crate's.
47
+
48
+ **Compose with `From`; `?` does the lifting.** This is `M-FROM-ERROR` and is
49
+ read there, carve-out included; what the guideline leaves open is how the
50
+ variant is spelled, which locally is `#[from]`:
51
+
52
+ #[derive(Debug, thiserror::Error)]
53
+ pub enum BootFailure {
54
+ #[error(transparent)]
55
+ Environment(#[from] Refusal),
56
+ #[error("cannot boot: the database would not answer")]
57
+ Database(#[from] db::Error),
58
+ }
59
+
60
+ **`map_err` only to ADD context the call site alone knows** — a role tag, a
61
+ budget, an operation name; the same carve-out `M-FROM-ERROR` names. And never
62
+ this, which no guideline covers:
63
+
64
+ // WRONG: a lossy conversion wearing a conversion's clothes.
65
+ .map_err(|error| Mine::Database(error.to_string()))?
66
+
67
+ `to_string()` on the way INTO an error type destroys the `source()` chain. It
68
+ compiles, it reads fine, and it silently defeats every chain walker downstream.
69
+
70
+ **`source()` returns what caused you, never yourself.** An error whose
71
+ `Display` already renders its kind must not also return that kind as its
72
+ source, or a chain walker prints the same sentence twice before reaching
73
+ anything new:
74
+
75
+ // WRONG — Display is "[code] {kind}", so the kind is already printed.
76
+ fn source(&self) -> Option<&(dyn Error + 'static)> { Some(&self.kind) }
77
+
78
+ // RIGHT — skip ourselves, hand back what the kind wraps.
79
+ fn source(&self) -> Option<&(dyn Error + 'static)> {
80
+ std::error::Error::source(&self.kind)
81
+ }
82
+
83
+ **Not every error has a cause, and that is not a defect.** A variant holding
84
+ another error has one; a variant holding only data (`MissingUrl { knob }`) does
85
+ not — nothing *caused* an unset variable. A test asserting `source().is_some()`
86
+ for every variant is WRONG and forces authors to invent causes. Assert the real
87
+ invariant: where there IS a source, it is not a repeat of our own message.
88
+
89
+ **Divergence from the reference guideline, named as that section requires.**
90
+ `M-ERRORS-CANONICAL-STRUCTS` prescribes a situation-specific `struct` carrying
91
+ a `Backtrace` and lets a complex crate expose several error types;
92
+ `M-APP-ERROR` further lets an application crate skip its own type for
93
+ `anyhow`/`eyre`/`ohno::AppError`. This rule diverges on all three for one
94
+ reason: a `#[from]`-composed enum behind a single per-crate alias is what lets
95
+ `?` lift across a crate boundary with no call-site conversion, and an
96
+ application-level catch-all erases the variant the caller would have matched
97
+ on. A `Backtrace` inside a variant is welcome. A crate with two genuinely
98
+ unrelated fallible surfaces may carry a second type — with its own alias, under
99
+ these same rules. Cite this divergence in review rather than re-deriving it.
100
+
101
+ Prior art, not invention: `core_api` (multi-crate workspace, per-crate
102
+ `error.rs` with `pub type Result<T>` + flat `Error`, in production for years),
103
+ bun (`thiserror`, `#[from]`, `pub type Result<T, E = Error>`), habitat (one
104
+ payload-carrying `enum Error`, one alias).
105
+
106
+ ## Functional design (RULE FN-RS)
107
+
108
+ Design the fallible path as a Result PIPELINE, never as a control-flow tree.
109
+
110
+ **Fallibility lives in the type, never in a sentinel.** A function that can
111
+ fail returns `Result<T>`; a value that can be absent returns `Option<T>`.
112
+ Never a bool-plus-out-parameter, never a magic value (-1, empty string, null
113
+ UUID), never a logged-and-swallowed error. If the caller cannot see the
114
+ failure in the signature, the design is wrong before the body is written.
115
+
116
+ **Compose with combinators; spend `match` only where arms genuinely differ.**
117
+ `?`, `map`, `and_then`, `ok_or_else`, `unwrap_or_else` say "transform on
118
+ success, pass failure through" in one line, where a nested match spends five
119
+ and gives every arm a chance to swallow the `Err`:
120
+
121
+ // WRONG: the failure path is hand-plumbed, twice.
122
+ let config = match load(path) {
123
+ Ok(raw) => match parse(&raw) {
124
+ Ok(c) => c,
125
+ Err(e) => return Err(e.into()),
126
+ },
127
+ Err(e) => return Err(e.into()),
128
+ };
129
+
130
+ // RIGHT: one pipeline; `?` and `From` (RULE ERR-RS) do the lifting.
131
+ let config = parse(&load(path)?)?;
132
+
133
+ A `match` earns its place when the arms carry different LOGIC — not when one
134
+ arm is a hand-written `?`.
135
+
136
+ **Make illegal states unrepresentable; parse, don't validate.** Convert raw
137
+ input into a type that can only hold valid states ONCE, at the boundary, and
138
+ pass that type inward. Two `Option` fields where exactly one is ever set is an
139
+ enum. A `state: String` compared against literals is an enum. A checked-then-
140
+ used raw value is a newtype whose constructor returns `Result` — that
141
+ constructor is `M-STRONG-TYPES-GUARD`, and picking the type family is
142
+ `M-STRONG-TYPES`. What those do not say is the consequence: interior code that
143
+ trusts its types needs no defensive re-checks — delete them.
144
+
145
+ **Expression-orientation: bind once.** `let x = if ... { a } else { b };` and
146
+ `let x = match ...` — never a mutable default that later branches overwrite.
147
+ A `mut` that exists only to simulate an expression is a bug surface with no
148
+ compensating power.
149
+
150
+ **`unwrap()` / `expect()` are for tests and for invariants already proved.**
151
+ `M-PANIC-ON-BUG` settles when a panic is right and `M-PANIC-MESSAGE` that it
152
+ must be helpful; the addition here is WHAT the message carries — the proof
153
+ ("mutex never poisoned: no thread panics while holding it"). In production
154
+ code, an `unwrap()` without a written invariant is a `Result` that was never
155
+ designed.
156
+
157
+ Prior art, not invention: `core_api` — a multi-crate production workspace
158
+ where every crate with a fallible surface carries its own `error.rs` +
159
+ `Result` alias, and the fallible path is written as pipelines throughout
160
+ (573 `.map`, 463 `.and_then`, 164 `.ok_or_else` across the workspace). It has
161
+ run in production for years; its author attributes the absence of error-path
162
+ defects to exactly this design, and the maintenance record bears that out.
163
+
34
164
  ## Constant discipline (RULE UFS)
35
165
 
36
166
  `audits/ufs.sh` reads `*.rs`. It did not until this pack said so, and the cost
@@ -1,5 +1,7 @@
1
1
  # Shell authoring
2
2
 
3
+ <!-- oracle-scope: *.sh -->
4
+
3
5
  Quote expansions, use arrays for argument lists, and clean temporary files on
4
6
  every exit path. Avoid `eval` for external input. Match the repository's required
5
7
  shell and macOS Bash compatibility level.
package/registry.json CHANGED
@@ -32,6 +32,10 @@
32
32
  "source": "dispatch/name_architecture.md",
33
33
  "target": "dispatch/name_architecture.md"
34
34
  },
35
+ {
36
+ "source": "dispatch/write_pr_description.md",
37
+ "target": "dispatch/write_pr_description.md"
38
+ },
35
39
  {
36
40
  "source": "audits/ufs.sh",
37
41
  "target": "audits/ufs.sh"
@@ -259,6 +263,10 @@
259
263
  "source": "dispatch/edit_rules.md",
260
264
  "target": "dispatch/edit_rules.md"
261
265
  },
266
+ {
267
+ "source": "dispatch/edit_rules.sh",
268
+ "target": "dispatch/edit_rules.sh"
269
+ },
262
270
  {
263
271
  "source": "audits/agents-md.md",
264
272
  "target": "audits/agents-md.md"