@agentsfleet/orly 0.6.5 → 0.6.6

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.
package/audits/ufs.sh CHANGED
@@ -4,6 +4,12 @@
4
4
  # Dispatch façade: dispatch/write_any.md (UFS Gate)
5
5
  # Fires in: make lint, CONFORM.
6
6
  #
7
+ # Languages read: Zig, TypeScript, JavaScript, Rust, Go (see `is_source`, which
8
+ # carries the pack that owns each extension and the reasons `.py` / `.sh` are
9
+ # held out). The set is pinned against the dispatch facade by the engine
10
+ # dispatch-coverage audit — a facade that fires on a language its leaf cannot
11
+ # read is a gate that prints green over an unscanned file.
12
+ #
7
13
  # Generic detection — no manifest of known literals, so the audit scales
8
14
  # as the codebase grows. Three classes of violation:
9
15
  #
@@ -15,9 +21,11 @@
15
21
  #
16
22
  # Carve-out: any `// pin test: literal is the contract` comment on or
17
23
  # above the offending line excludes that line from numeric-suspect.
18
- # A `const` / `pub const` / `export const` declaration line is likewise
19
- # exempt for both per-file checks (is_const_decl + its string-dup mirror
20
- # below) — binding the literal to a name on a const line clears the hit.
24
+ # A `const` / `pub const` / `export const` / `static` declaration line is
25
+ # likewise exempt for both per-file checks (is_const_decl + its string-dup
26
+ # mirror below) — binding the literal to a name on that line clears the hit.
27
+ # Go states the keyword once for a whole `const ( ... )` group; the string-dup
28
+ # lane tracks the group so its members are read as the declarations they are.
21
29
  #
22
30
  # Staged-scope semantics: --staged narrows WHICH FILES are scanned, not
23
31
  # how much of each — a staged file is audited in FULL, so staging a file
@@ -69,14 +77,34 @@ ok() { printf "OK: %s\n" "$*"; }
69
77
 
70
78
  # ── File scope ──────────────────────────────────────────────────────────────
71
79
 
80
+ # The extension set is the CONTRACT the dispatch façade already advertises:
81
+ # dispatch/write_any.sh's `dispatch_init "ANY" ...` list is what fires the gate,
82
+ # and this list is what the gate then reads. When the two disagree the façade
83
+ # runs, prints green, and has scanned nothing — which is exactly how a Rust
84
+ # crate carried `const S_PING = "PING"` past a gate that claimed to cover it.
85
+ # The engine fails its own dispatch-coverage audit on that drift now, so a
86
+ # language added to the façade must land here, or be named out of scope with a
87
+ # reason, before that audit goes green again.
88
+ #
89
+ # .zig language.zig
90
+ # .ts .tsx language.typescript
91
+ # .js .jsx language.javascript
92
+ # .rs language.rust
93
+ # .go language.go
94
+ #
95
+ # Out of scope, deliberately — the engine keeps the machine-readable twin of
96
+ # this list: `.py` (single-quoted literals dominate and the
97
+ # double-quote matcher below would report partial coverage as full), `.sh`
98
+ # (a repeated `"$var"` is interpolation, not a magic string — measured at 59%
99
+ # of hits on a real tree), `.sql` (its own gate, dispatch/write_sql.sh).
72
100
  is_source() {
73
101
  local f="$1"
74
102
  case "$f" in
75
103
  vendor/*|third_party/*|.zig-cache/*|*/node_modules/*|evals/dispatch/fixtures/*|*.tsbuildinfo) return 1 ;;
76
- *_test.zig|*.test.ts|*.test.tsx|*.test.js|*.test.jsx|*.unit.test.js|*.spec.ts) ;; # tests in scope
104
+ *_test.zig|*.test.ts|*.test.tsx|*.test.js|*.test.jsx|*.unit.test.js|*.spec.ts|*_test.go) ;; # tests in scope
77
105
  esac
78
106
  case "$f" in
79
- *.zig|*.ts|*.tsx|*.js|*.jsx) return 0 ;;
107
+ *.zig|*.ts|*.tsx|*.js|*.jsx|*.rs|*.go) return 0 ;;
80
108
  *) return 1 ;;
81
109
  esac
82
110
  }
@@ -112,9 +140,21 @@ while IFS= read -r row; do
112
140
  done < <(awk '
113
141
  FNR == 1 {
114
142
  prev_file = FILENAME
143
+ # Language lane. Each lane below carves out what its syntax makes
144
+ # UNFIXABLE — a literal a named const cannot legally replace. Anything
145
+ # a const CAN replace stays in scope, in every language.
146
+ lang = "other"
147
+ if (FILENAME ~ /\.zig$/) lang = "zig"
148
+ else if (FILENAME ~ /\.rs$/) lang = "rust"
149
+ else if (FILENAME ~ /\.go$/) lang = "go"
115
150
  # Test files: repetition is fixture data, not magic strings.
116
151
  # Skip string-dup-file for tests; other checks still apply.
117
- is_test = (FILENAME ~ /(_test\.zig|\.test\.|\.spec\.|\.unit\.test|\.integration\.test|\/test\/|\/tests\/)/)
152
+ is_test = (FILENAME ~ /(_test\.zig|_test\.go|\.test\.|\.spec\.|\.unit\.test|\.integration\.test|\/test\/|\/tests\/)/)
153
+ # Directory-shaped test and benchmark trees. Rust puts both at the CRATE
154
+ # ROOT (`tests/`, `benches/`) with no leading slash, so the `\/tests\/`
155
+ # form above — which needs a parent directory — walked straight past them
156
+ # and audited 100 fixture hits as production literals.
157
+ if (FILENAME ~ "(^|/)(test|tests|benches|__tests__)/") is_test = 1
118
158
  }
119
159
  is_test { next }
120
160
  # ui/ files: extracting class-strings or short literals to file-local
@@ -126,7 +166,7 @@ done < <(awk '
126
166
  # ("nonterminated character class"); use a dynamic-regex STRING — identical
127
167
  # match on gawk, portable on BWK awk (the macOS default). Do not re-inline.
128
168
  FILENAME ~ "^ui/packages/[^/]+/(src|app|tests|components|lib|hooks)/" { next }
129
- FNR == 1 { in_test_block = 0; test_depth = 0; in_block_comment = 0 }
169
+ FNR == 1 { in_test_block = 0; test_depth = 0; in_block_comment = 0; in_rs_test = 0; rs_open = 0; rs_depth = 0; in_go_const = 0 }
130
170
  {
131
171
  line = $0
132
172
  # Block-comment exclusion (TS/JS/Zig non-applicable): skip lines inside /* ... */
@@ -139,9 +179,9 @@ done < <(awk '
139
179
  next
140
180
  }
141
181
  # Zig multi-line string literal — lines start with `\\` after whitespace.
142
- if (FILENAME ~ /\.zig$/ && line ~ /^[[:space:]]*\\\\/) next
182
+ if (lang == "zig" && line ~ /^[[:space:]]*\\\\/) next
143
183
  # Inline-test exclusion (Zig): track depth across `test "..." {` blocks.
144
- if (FILENAME ~ /\.zig$/) {
184
+ if (lang == "zig") {
145
185
  if (in_test_block) {
146
186
  test_depth += gsub(/\{/, "{", line) - gsub(/\}/, "}", line)
147
187
  if (test_depth <= 0) in_test_block = 0
@@ -156,6 +196,54 @@ done < <(awk '
156
196
  next
157
197
  }
158
198
  }
199
+ # Inline-test exclusion (Rust): Rust keeps its unit tests INSIDE the file
200
+ # they cover, under `#[cfg(test)] mod tests { ... }`, so without this the
201
+ # fixture keys of a well-tested module read as the worst literal debt in
202
+ # the production half of that same file — a real crate reported `"key1"`
203
+ # 15 times from one such block. Same brace-depth shape as the Zig branch.
204
+ if (lang == "rust") {
205
+ if (in_rs_test) {
206
+ tmp = line
207
+ rs_depth += gsub(/\{/, "{", tmp) - gsub(/\}/, "}", tmp)
208
+ if (!rs_open && index(line, "{") > 0) rs_open = 1
209
+ if (rs_open && rs_depth <= 0) in_rs_test = 0
210
+ else if (!rs_open && line ~ /;[[:space:]]*$/) in_rs_test = 0
211
+ next
212
+ }
213
+ # Three ways Rust marks a block as fixture data, not shipped behaviour:
214
+ # `#[cfg(test)]`, a `#[test]`/`#[tokio::test]` function, and a
215
+ # FEATURE-GATED test seam — `#[cfg(feature = "test-util")]`, the
216
+ # convention for a helper that must compile into the crate so an
217
+ # integration test in a sibling file can call it (a `one_of_each_kind`
218
+ # error-surface enumerator is the usual shape). The feature name is
219
+ # matched narrowly, `test` as a whole word or a `-`/`_` segment, so a
220
+ # production gate like `#[cfg(feature = "redis")]` still counts and
221
+ # `"latest"` is not mistaken for a test seam.
222
+ if (line ~ /^[[:space:]]*#\[cfg\(test\)\]/ ||
223
+ line ~ /^[[:space:]]*#\[[A-Za-z_:]*test\]/ ||
224
+ line ~ /^[[:space:]]*#\[cfg\(.*feature[[:space:]]*=[[:space:]]*"([A-Za-z0-9]+[-_])?test(ing)?([-_][A-Za-z0-9_-]+)?"/) {
225
+ in_rs_test = 1; rs_open = 0; rs_depth = 0
226
+ next
227
+ }
228
+ # Attribute literals are UNFIXABLE, not undisciplined. `#[serde(rename =
229
+ # "...")]`, `#[cfg(feature = "...")]` and `#[doc = "..."]` take a literal
230
+ # token by language rule — a const is not accepted there, so naming one
231
+ # cannot resolve the hit and reporting it only teaches people to ignore
232
+ # the gate. Ordered after the cfg(test) branch, which is also a `#[`.
233
+ if (line ~ /^[[:space:]]*#!?\[/) next
234
+ }
235
+ # Grouped-const exclusion (Go): `const ( ... )` states the binding keyword
236
+ # ONCE, on the opening line, so a member like `metaKeyQoS = "conf_..."` is
237
+ # a definition carrying no `const` for the binding carve-out below to see.
238
+ # `var (` groups are deliberately NOT tracked — the carve-out is const-only
239
+ # in every other language and widening it here would be a silent divergence.
240
+ if (lang == "go") {
241
+ if (in_go_const) {
242
+ if (line ~ /^[[:space:]]*\)/) { in_go_const = 0; next }
243
+ if (line ~ /^[[:space:]]*[A-Za-z_][A-Za-z0-9_]*([[:space:]]+[A-Za-z0-9_.\[\]*]+)?[[:space:]]*=/) next
244
+ }
245
+ else if (line ~ /^[[:space:]]*const[[:space:]]*\([[:space:]]*$/) { in_go_const = 1; next }
246
+ }
159
247
  sub(/\/\/.*$/, "", line)
160
248
  # Strip single-line /* ... */ inline block comments (jsdoc/etc) so
161
249
  # literals inside them are not counted.
@@ -171,11 +259,23 @@ done < <(awk '
171
259
  # like any other use. (A prior line-level skip exempted those too and silently
172
260
  # gutted the check for most Zig code; the ufs_dup_string eval fixture pins the
173
261
  # binding-level semantic.)
174
- if (line ~ /(^|[[:space:]])(pub[[:space:]]+const|export[[:space:]]+const|const)[[:space:]]+[A-Za-z_$][A-Za-z0-9_$]*([[:space:]]*:[^=]*)?[[:space:]]*=[[:space:]]*"((\\.)|[^\\"])+"[[:space:]]*(as[[:space:]]+const)?[[:space:]]*[;,]?[[:space:]]*$/) next
262
+ # `static` joins the keyword set for Rust: `static NAME: &str = "..."` binds
263
+ # a literal to a name exactly as `const` does, and the leading
264
+ # `(^|[[:space:]])` anchor already carries the `pub`/`pub(crate)` prefixes.
265
+ # The second type slot belongs to Go, which spells the type with no colon
266
+ # (`const name string = "..."`), which the colon-only slot could not see.
267
+ if (line ~ /(^|[[:space:]])(pub[[:space:]]+const|export[[:space:]]+const|const|static)[[:space:]]+[A-Za-z_$][A-Za-z0-9_$]*([[:space:]]*:[^=]*|[[:space:]]+[A-Za-z_][A-Za-z0-9_.\[\]*]*)?[[:space:]]*=[[:space:]]*"((\\.)|[^\\"])+"[[:space:]]*(as[[:space:]]+const)?[[:space:]]*[;,]?[[:space:]]*$/) next
175
268
  rest = line
176
269
  # Strip Zig identifier-escape syntax @"name" — body is an identifier,
177
270
  # not a string literal, but the regex below would otherwise match it.
178
271
  gsub(/@"[^"]*"/, "", rest)
272
+ # Strip Go backtick runs. They are overwhelmingly struct tags
273
+ # (`json:"id" yaml:"id"`), whose quoted halves are tag SYNTAX addressed by
274
+ # reflection — a const cannot appear inside one, so the repeat is unfixable.
275
+ # This also drops single-line Go raw strings, which is the accepted cost:
276
+ # the alternative reports every `json:"id"` in a wire struct as a violation.
277
+ # A backtick run spanning lines is not tracked (best-effort, as documented).
278
+ if (lang == "go") gsub(/`[^`]*`/, "", rest)
179
279
  # Strip empty string literals so the regex below cannot fuse
180
280
  # two adjacent Zig literals through their inner gap.
181
281
  gsub(/""/, "", rest)
@@ -229,7 +329,7 @@ done < <(awk -v re="$NUMERIC_RE" '
229
329
  line = $0
230
330
  is_pin_now = (line ~ /pin test: literal is the contract/)
231
331
  is_pin_above = (prev ~ /pin test: literal is the contract/)
232
- is_const_decl = (line ~ /(^|[[:space:]])(pub[[:space:]]+const|export[[:space:]]+const|const)[[:space:]]/)
332
+ is_const_decl = (line ~ /(^|[[:space:]])(pub[[:space:]]+const|export[[:space:]]+const|const|static)[[:space:]]/)
233
333
  stripped = line
234
334
  sub(/\/\/.*$/, "", stripped)
235
335
  sub(/#.*$/, "", stripped)
@@ -343,7 +343,13 @@ ERROR REGISTRY GATE: <file>
343
343
  **Family:** Constant discipline. **Source:** `AGENTS.md` conformance enforcement
344
344
  + the selected language façade's Uniform Free Strings (UFS) clauses.
345
345
 
346
- **Triggers** — every `Edit`/`Write` to source files under `src/`, `ui/packages/*/src/`, `ui/packages/*/app/`, `ui/packages/*/lib/`, `ui/packages/*/components/`, `ui/packages/*/pages/`, `ui/packages/*/tests/`, `agentsfleet/src/`, `agentsfleet/test/` matching `*.zig`, `*.ts`, `*.tsx`, `*.js`, `*.jsx`. Excluded: `vendor/`, `third_party/`, `.zig-cache/`, `node_modules/`, `*.tsbuildinfo`.
346
+ **Triggers** — every `Edit`/`Write` to a source file under the repository's own source surfaces — the `surfaces.user` prefixes it declares in `.oracle/orly.json`, or its source roots where it declares none — matching `*.zig`, `*.ts`, `*.tsx`, `*.js`, `*.jsx`, `*.rs`, `*.go`. Excluded: `vendor/`, `third_party/`, `.zig-cache/`, `node_modules/`, `*.tsbuildinfo`. `*.py` and `*.sh` fire this façade but are held out of the UFS leaf — see Scope. Each repository binds its own prefixes; naming one repository's layout here is what sends every other repository a trigger list it cannot match.
347
+
348
+ <!-- oracle-packs:start product.agentsfleet -->
349
+ In this repository those surfaces are `src/` (Zig), `rustd/crates/*/src/` (Rust), `cli/src/` and `ui/packages/*/{src,app,lib,components,pages,tests}/` (TypeScript).
350
+
351
+ `agentsfleet/src/` and `agentsfleet/test/` were listed here until they stopped existing. A trigger path that resolves to nothing is the same silent green this card's gate exists to prevent — and the identical stale prefix in `ufs.sh`'s cross-runtime parity scan means its JavaScript half reads zero files today.
352
+ <!-- oracle-packs:end -->
347
353
 
348
354
  **Override:** `UFS GATE: SKIPPED per user override (reason: ...)` immediately preceding the edit. **User-only**; auto-mode does not cover.
349
355
 
@@ -376,12 +382,16 @@ Before saving an edit that introduces or relocates a literal:
376
382
 
377
383
  1. **Same-file repeat?** Grep within the file for the literal value. If it appears ≥1 time already, define a const at the top of the file (or in the module's `constants.ts`/`constants.zig`) and replace all occurrences.
378
384
 
379
- 2. **Same-module repeat?** Grep within the module/package (`ui/packages/app/`, `agentsfleet/src/`, `src/`). If repeated, define in the module's canonical types/constants file (`lib/types.ts`, `src/state/<topic>.zig`, `agentsfleet/src/constants/<topic>.js`).
385
+ 2. **Same-module repeat?** Grep within the module or package that owns the file. If repeated, define it in that module's canonical constants file and replace every occurrence.
380
386
 
381
- 3. **Cross-runtime sibling?** When the literal is a wire-format string or unit-conversion numeric, grep all three runtimes for an existing matching name. If found, reuse the name. If not, define in every runtime that uses the same value, same commit.
387
+ 3. **Cross-runtime sibling?** When the literal is a wire-format string or unit-conversion numeric, grep **every runtime this repository ships** for an existing matching name. If found, reuse it verbatim. If not, define it in each runtime that carries the value, same commit. A single-runtime repository answers this in one grep — the check is about the runtimes that exist, not a fixed count.
382
388
 
383
389
  4. **Carve-out check?** If the literal IS the contract a pin test verifies, write the `// pin test: literal is the contract` comment on or above the line. Otherwise, name it.
384
390
 
391
+ <!-- oracle-packs:start product.agentsfleet -->
392
+ Concretely here: modules are `src/` (Zig), `rustd/crates/*/` (Rust), `cli/src/` and `ui/packages/*/` (TypeScript), and their canonical constants files are `src/state/<topic>.zig`, the crate's own `constants.rs`, and `lib/types.ts`. The runtimes to grep in step 3 are Zig, TypeScript and Rust.
393
+ <!-- oracle-packs:end -->
394
+
385
395
  #### Required output (per Edit/Write — one line by default)
386
396
 
387
397
  > [DETERMINISTIC → UFS]
@@ -411,6 +421,19 @@ The previous `--diff` (`BASE...HEAD`) default was retired with M70. The forcing
411
421
 
412
422
  `--all` is accepted as a back-compat alias for the default. `--diff` is rejected with exit 2 + a pointer to this section.
413
423
 
424
+ **Languages the leaf reads.** Zig, TypeScript, JavaScript, Rust and Go. The rule is one rule, but each language hides its literals differently, so each gets a lane in `is_source` + the string-dup pass:
425
+
426
+ | Language | Held out of the count, and why |
427
+ |---|---|
428
+ | Rust | `#[cfg(test)]` / `#[test]` blocks (unit tests live *inside* the file they cover), feature-gated test seams (`#[cfg(feature = "test-util")]` — a helper that must compile into the crate so a sibling integration test can call it), `#[…]` attribute literals (`#[serde(rename = "…")]` takes a literal token by language rule — no const is legal there), `static` bindings alongside `const` |
429
+ | Go | `const ( … )` group members (the keyword is stated once, on the opening line), backtick struct tags (`json:"id"` is read by reflection; a const cannot appear inside one) |
430
+
431
+ The test-seam match is deliberately narrow — `test` as a whole word or a `-`/`_` segment — so a production gate like `#[cfg(feature = "redis")]` still counts and `"latest"` is not mistaken for a seam. `ufs_feature_gate.rs` pins that: widening it turns the fixture green.
432
+
433
+ `*.py` and `*.sh` are **out of scope on purpose**, and the engine pins the reason in its own dispatch-coverage audit: Python writes ~14% of its literals in single quotes the matcher does not read, so it would report partial coverage as full; and a repeated `"$var"` in shell is interpolation, not a magic string — 59% of hits on a real tree, with no `const` to bind to. Adding either means building its lane first, not widening the glob.
434
+
435
+ **A façade that fires on a language its leaf cannot read is a silent green.** `write_any.sh` has declared `*.rs` and `*.go` since those packs existed while `ufs.sh` read neither, so the gate printed a green UFS row over a file it never opened — and a Rust crate shipped a bare, twice-spelled wire verb under it. The engine now fails its dispatch-coverage audit on that divergence: every extension the façade claims is read by the leaf, or carries a written reason it is not.
436
+
414
437
  #### Self-audit (end-of-turn / CONFORM)
415
438
 
416
439
  > [DETERMINISTIC → UFS]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentsfleet/orly",
3
- "version": "0.6.5",
3
+ "version": "0.6.6",
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",
@@ -20,5 +20,13 @@ a working value.
20
20
  Table-driven tests are the default shape. Concurrency work needs a `-race` run,
21
21
  and a deterministic contention test rather than a sleep.
22
22
 
23
+ A string literal repeated in one file becomes a named constant — `audits/ufs.sh`
24
+ reads `*.go` and enforces it. Two Go shapes are held out, because no rename can
25
+ fix them: members of a `const ( … )` group, which state the binding keyword once
26
+ on the opening line rather than per member, and backtick struct tags, whose
27
+ quoted halves (`json:"id"`) are addressed by reflection and cannot hold a const.
28
+ Both are carve-outs for what the *language* makes unfixable, not permission —
29
+ a flag name or a label spelled inline twice is still the rule.
30
+
23
31
  The repository owns formatting, vet, lint, build, and test commands — it
24
32
  declares them in `.oracle/orly.json`.
@@ -31,6 +31,35 @@ Local convention wins on conflict, but the divergence is named in the review
31
31
  output. Refresh the copy when the user asks or a cited rule looks stale
32
32
  (re-run the fetch; upstream updates continuously).
33
33
 
34
+ ## Constant discipline (RULE UFS)
35
+
36
+ `audits/ufs.sh` reads `*.rs`. It did not until this pack said so, and the cost
37
+ was concrete: a crate spelled a wire verb inline twice — `pong == "PONG" ||
38
+ pong.contains("PONG")` — while `write_any.sh` printed a green UFS row over the
39
+ file, because the façade had always declared `*.rs` and the leaf had never read
40
+ it. Bind the repeated literal to a `const` or `static` and use the name.
41
+
42
+ Four Rust shapes are held out of the count, because no rename can fix them:
43
+
44
+ - **`#[cfg(test)]` and `#[test]` blocks.** Rust keeps unit tests inside the file
45
+ they cover, so fixture keys would otherwise read as that file's worst debt.
46
+ - **Feature-gated test seams** — `#[cfg(feature = "test-util")]`. A helper that
47
+ hands a sibling integration test one of each error kind has to compile into
48
+ the crate, which is the only reason it is not behind `cfg(test)`; its contents
49
+ are fixture data all the same. The match is narrow on purpose: `test` as a
50
+ whole word or a `-`/`_` segment, so `#[cfg(feature = "redis")]` still counts.
51
+ - **Attribute literals** — `#[serde(rename = "…")]`, `#[cfg(feature = "…")]`,
52
+ `#[doc = "…"]`. An attribute takes a literal token by language rule; a const
53
+ is not accepted in that position.
54
+ - **`tests/` and `benches/`** at the crate root, which are fixture trees.
55
+
56
+ Everything else counts, including `examples/` — not because it compiles, but
57
+ because what repeats there is coupling, not demonstration. A cache namespace
58
+ declared at the top of a walkthrough and matched ninety lines below it, or a
59
+ fixture id that is both the fake store's match arm and the key five call sites
60
+ pass, breaks silently when one end moves. That is the drift RULE UFS exists to
61
+ catch, and example code is where a reader copies it from.
62
+
34
63
  ## Evolution
35
64
 
36
65
  This pack is young next to the Zig façade and grows the same way: a Rust