@agentsfleet/orly 0.9.2 → 0.9.4
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/cross-tier-rates.sh +5 -5
- package/audits/deinit-pairs.sh +11 -5
- package/audits/error-codes.sh +140 -62
- package/audits/logging.sh +18 -8
- package/audits/spec-template.sh +7 -3
- package/audits/sql-mod.sh +5 -2
- package/audits/ufs.sh +33 -6
- package/dispatch/lib.sh +13 -3
- package/dispatch/write_any.sh +2 -2
- package/dispatch/write_zig.md +1 -1
- package/docs/CHANGELOG_VOICE.md +1 -1
- package/docs/EXECUTE_DOC_READS.md +1 -1
- package/docs/LOGGING_STANDARD.md +2 -2
- package/docs/ORLY_ARCHITECTURE.md +1 -1
- package/docs/REST_API_DESIGN_GUIDELINES.md +208 -210
- package/docs/RULE_ENFORCEMENT.md +2 -2
- package/docs/SCHEMA_CONVENTIONS.md +3 -3
- package/package.json +1 -1
- package/skills/orly-write-integration-test/SKILL.md +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# cross-tier-rates.sh — pin numeric parity of the rate constants
|
|
3
|
-
# across
|
|
3
|
+
# across the daemon + three TypeScript surfaces.
|
|
4
4
|
#
|
|
5
5
|
# RULE UFS extension. The shipped billing surface depends on these constants
|
|
6
6
|
# having identical numeric values everywhere they appear:
|
|
@@ -16,13 +16,13 @@
|
|
|
16
16
|
#
|
|
17
17
|
# Files (one definition site per constant per file):
|
|
18
18
|
#
|
|
19
|
-
#
|
|
19
|
+
# rustd/crates/afd_billing/src/nanos.rs — server enforces the charge
|
|
20
20
|
# ui/packages/website/src/lib/rates.ts — pricing page display
|
|
21
21
|
# ui/packages/app/lib/types.ts — dashboard display
|
|
22
22
|
# cli/src/constants/billing.ts — `agentsfleet doctor --json` billing block
|
|
23
23
|
#
|
|
24
24
|
# A drift between any two = a billing-display lie or a server-vs-CLI disagreement.
|
|
25
|
-
#
|
|
25
|
+
# The daemon is the source of truth (server enforces); the three TS surfaces echo it.
|
|
26
26
|
#
|
|
27
27
|
# Fires in: make harness-verify, make harness-verify-all.
|
|
28
28
|
# Exits 0 clean, 1 on any drift.
|
|
@@ -37,7 +37,7 @@ readonly NAMES=(
|
|
|
37
37
|
)
|
|
38
38
|
|
|
39
39
|
readonly FILES=(
|
|
40
|
-
|
|
40
|
+
rustd/crates/afd_billing/src/nanos.rs
|
|
41
41
|
ui/packages/website/src/lib/rates.ts
|
|
42
42
|
ui/packages/app/lib/types.ts
|
|
43
43
|
cli/src/constants/billing.ts
|
|
@@ -96,7 +96,7 @@ if [[ $FAIL -ne 0 ]]; then
|
|
|
96
96
|
printf " %s\n" "$v" >&2
|
|
97
97
|
done
|
|
98
98
|
printf "\nFix: align all four files on the canonical Zig value\n" >&2
|
|
99
|
-
printf " (
|
|
99
|
+
printf " (rustd/crates/afd_billing/src/nanos.rs is the server-side enforcer).\n" >&2
|
|
100
100
|
exit 1
|
|
101
101
|
fi
|
|
102
102
|
|
package/audits/deinit-pairs.sh
CHANGED
|
@@ -60,11 +60,14 @@ note() { printf "NOTE: %s\n" "$*"; }
|
|
|
60
60
|
# ---------------------------------------------------------------------------
|
|
61
61
|
case "$MODE" in
|
|
62
62
|
--staged|staged)
|
|
63
|
-
|
|
63
|
+
# while read, not mapfile — bash-3.2 portability (see scripts/run-playbook-tests.sh).
|
|
64
|
+
FILES=()
|
|
65
|
+
while IFS= read -r f; do FILES+=("$f"); done < <(git diff --cached --name-only --diff-filter=ACMRT \
|
|
64
66
|
| grep -E '^src/.*\.zig$' || true)
|
|
65
67
|
;;
|
|
66
68
|
--all|all)
|
|
67
|
-
|
|
69
|
+
FILES=()
|
|
70
|
+
while IFS= read -r f; do FILES+=("$f"); done < <(find src -type f -name '*.zig' 2>/dev/null || true)
|
|
68
71
|
;;
|
|
69
72
|
*)
|
|
70
73
|
printf "usage: %s [--staged|--all]\n" "$0" >&2
|
|
@@ -106,10 +109,13 @@ done
|
|
|
106
109
|
# M70 perf: pre-compute file sets in batched greps instead of one grep
|
|
107
110
|
# per file (~760 forks → 2 forks). Reads as "membership sets" for the
|
|
108
111
|
# per-init loop below.
|
|
109
|
-
declare -A
|
|
112
|
+
# A membership set as a newline-delimited string, not declare -A: associative
|
|
113
|
+
# arrays are bash 4+ and macOS ships 3.2. Lookup is a substring test on
|
|
114
|
+
# \n<file>\n, which filenames cannot fake.
|
|
115
|
+
files_with_cleanup=$'\n'
|
|
110
116
|
if [[ ${#present_files[@]} -gt 0 ]]; then
|
|
111
117
|
while IFS= read -r f; do
|
|
112
|
-
[[ -n "$f" ]] && files_with_cleanup
|
|
118
|
+
[[ -n "$f" ]] && files_with_cleanup="${files_with_cleanup}${f}"$'\n'
|
|
113
119
|
done < <(grep -lE "$CLEANUP_RE" "${present_files[@]}" 2>/dev/null || true)
|
|
114
120
|
fi
|
|
115
121
|
|
|
@@ -151,7 +157,7 @@ if [[ -n "$inits_list" ]]; then
|
|
|
151
157
|
fi
|
|
152
158
|
|
|
153
159
|
inits_requires_cleanup=$((inits_requires_cleanup + 1))
|
|
154
|
-
if [[
|
|
160
|
+
if [[ "$files_with_cleanup" != *$'\n'"$f"$'\n'* ]]; then
|
|
155
161
|
inits_unpaired=$((inits_unpaired + 1))
|
|
156
162
|
reason=""
|
|
157
163
|
[[ $heap -eq 1 ]] && reason="heap-return"
|
package/audits/error-codes.sh
CHANGED
|
@@ -1,60 +1,51 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
# error-codes.sh — orphan + dead code detection against
|
|
3
|
-
# error registry
|
|
4
|
-
#
|
|
5
|
-
#
|
|
2
|
+
# error-codes.sh — orphan + dead code detection against a repository's canonical
|
|
3
|
+
# error registry, plus a raw-literal check enforcing that <PREFIX>-<CAT>-<NNN>
|
|
4
|
+
# strings only appear in registry/allowlist files (every other call site must
|
|
5
|
+
# reference the registry symbol).
|
|
6
6
|
#
|
|
7
7
|
# Dispatch façade: dispatch/write_any.md (Error Registry Gate)
|
|
8
|
-
# Fires in: CONFORM (via
|
|
8
|
+
# Fires in: CONFORM (via the repository's declared `conform` command).
|
|
9
9
|
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
10
|
+
# WHAT IS CONFIGURED, AND WHY IT IS NOT A PATH IN THIS FILE.
|
|
11
|
+
# This script used to hard-code one repository's registry path, source globs and
|
|
12
|
+
# allowlist. That repository retired the runtime those paths named, and the gate
|
|
13
|
+
# then pointed at nothing — the same silent-green failure the UFS parity check
|
|
14
|
+
# was fixed for. Every one of them is now resolved at run time, and the file
|
|
15
|
+
# names no repository's layout:
|
|
16
|
+
#
|
|
17
|
+
# ORLY_ERROR_PREFIX code prefix (default: UZ)
|
|
18
|
+
# ORLY_ERROR_REGISTRY registry file(s), a glob (default: autodetected)
|
|
19
|
+
# ORLY_ERROR_SOURCES where codes may be USED (default: by runtime)
|
|
20
|
+
# ORLY_ERROR_ALLOWLIST extra raw-literal paths (regex, appended)
|
|
21
|
+
#
|
|
22
|
+
# AUTODETECTION picks the runtime that actually declares codes:
|
|
23
|
+
# Rust — a file declaring `ErrorCode::declare("<PREFIX>-…")`; sources are the
|
|
24
|
+
# crate tree's `*.rs`.
|
|
25
|
+
# Zig — a file named `error_registry.zig`; sources are `src/**/*.zig`.
|
|
26
|
+
# A repository with neither SKIPS with a reason and exits 0. It does not fail,
|
|
27
|
+
# and it does not pass by scanning nothing and calling that clean.
|
|
19
28
|
#
|
|
20
29
|
# Definitions:
|
|
21
|
-
# - DECLARED — every
|
|
22
|
-
# - USED — every
|
|
23
|
-
# the registry itself). agentsfleet/src/** is intentionally
|
|
24
|
-
# out of scope — the CLI consumes server-emitted UZ-* codes
|
|
25
|
-
# as opaque strings from API responses; the dispatcher's
|
|
26
|
-
# typed CliError variants are the CLI's named surface.
|
|
30
|
+
# - DECLARED — every <PREFIX>-<CAT>-<NNN> literal in the registry
|
|
31
|
+
# - USED — every such reference in the source set, minus the registry
|
|
27
32
|
# - ORPHAN — USED but not DECLARED (BLOCKING)
|
|
28
33
|
# - DEAD — DECLARED but not USED (INFORMATIONAL)
|
|
29
|
-
# - RAW LEAK —
|
|
30
|
-
#
|
|
31
|
-
# surface; non-allowlisted call sites must use a registry
|
|
32
|
-
# symbol, never duplicate the literal).
|
|
33
|
-
#
|
|
34
|
-
# Raw-literal allowlist (files where UZ-* literals are legitimate):
|
|
35
|
-
# - src/agentsfleetd/errors/** (canonical registry + entry tables)
|
|
36
|
-
# - src/runner/engine/client_errors.zig (the runner engine's own mirror file
|
|
37
|
-
# — see file header for build-module
|
|
38
|
-
# isolation rationale; deliberate
|
|
39
|
-
# duplication, all other executor
|
|
40
|
-
# sources import from there)
|
|
41
|
-
# - **/*_test.zig (tests)
|
|
42
|
-
# - src/zbench_fixtures.zig (benchmark fixture seeds)
|
|
43
|
-
# - src/agentsfleetd/http/test_harness.zig (HTTP test harness)
|
|
44
|
-
# - lines starting with `//` (comments)
|
|
45
|
-
# - lines annotated `// audit-error-codes: intentional-fake`
|
|
34
|
+
# - RAW LEAK — a code literal in a source file outside the registry and the
|
|
35
|
+
# allowlist (BLOCKING — RULE UFS at the error surface)
|
|
46
36
|
#
|
|
47
|
-
#
|
|
48
|
-
#
|
|
49
|
-
#
|
|
50
|
-
# `// audit-error-codes: intentional-fake` and the
|
|
37
|
+
# Raw-literal allowlist: the registry files themselves, anything test-shaped
|
|
38
|
+
# (`*_test.zig`, `*_test.rs`, a `tests/` directory, a test harness), plus
|
|
39
|
+
# whatever `ORLY_ERROR_ALLOWLIST` adds. Lines starting `//` are comments, and a
|
|
40
|
+
# line annotated `// audit-error-codes: intentional-fake` skips itself and the
|
|
41
|
+
# line below it — which is how a negative test names a code that must not exist.
|
|
51
42
|
#
|
|
52
43
|
# Modes:
|
|
53
44
|
# --staged diff-scope: only files in `git diff --cached` are checked
|
|
54
45
|
# for new orphan refs (registry checked unconditionally)
|
|
55
46
|
# --all (default) full repo scan
|
|
56
47
|
#
|
|
57
|
-
# Exits 0 clean, 1 on orphans or raw leaks (DEAD
|
|
48
|
+
# Exits 0 clean or skipped, 1 on orphans or raw leaks (DEAD never blocks).
|
|
58
49
|
|
|
59
50
|
set -euo pipefail
|
|
60
51
|
|
|
@@ -62,8 +53,54 @@ MODE="${1:-${SCOPE:-all}}"
|
|
|
62
53
|
ROOT="$(git rev-parse --show-toplevel)"
|
|
63
54
|
cd "$ROOT"
|
|
64
55
|
|
|
65
|
-
|
|
66
|
-
[[
|
|
56
|
+
PREFIX="${ORLY_ERROR_PREFIX:-UZ}"
|
|
57
|
+
CODE_RE="${PREFIX}-[A-Z][A-Z0-9]*-[0-9]{3,}"
|
|
58
|
+
|
|
59
|
+
# Registry files, then the runtime whose sources may reference them. Resolved
|
|
60
|
+
# together: the source set follows from what declares the codes, so a repository
|
|
61
|
+
# cannot end up scanning one runtime's files for another runtime's registry.
|
|
62
|
+
REGISTRY_PATHS=()
|
|
63
|
+
RUNTIME=""
|
|
64
|
+
if [[ -n "${ORLY_ERROR_REGISTRY:-}" ]]; then
|
|
65
|
+
while IFS= read -r f; do [[ -n "$f" ]] && REGISTRY_PATHS+=("$f"); done < <(
|
|
66
|
+
git ls-files -- $ORLY_ERROR_REGISTRY 2>/dev/null || true)
|
|
67
|
+
[[ ${#REGISTRY_PATHS[@]} -gt 0 ]] || {
|
|
68
|
+
printf "FAIL: ORLY_ERROR_REGISTRY matched no tracked file: %s\n" "$ORLY_ERROR_REGISTRY" >&2
|
|
69
|
+
exit 1
|
|
70
|
+
}
|
|
71
|
+
case "${REGISTRY_PATHS[0]}" in *.rs) RUNTIME=rust ;; *.zig) RUNTIME=zig ;; *) RUNTIME=other ;; esac
|
|
72
|
+
else
|
|
73
|
+
while IFS= read -r f; do [[ -n "$f" ]] && REGISTRY_PATHS+=("$f"); done < <(
|
|
74
|
+
git grep -l -E "ErrorCode::declare\(\"${PREFIX}-" -- '*.rs' 2>/dev/null \
|
|
75
|
+
| grep -vE "_tests?\.rs$|(^|/)tests?/|(^|/)tests?\.rs$" | sort || true)
|
|
76
|
+
if [[ ${#REGISTRY_PATHS[@]} -gt 0 ]]; then
|
|
77
|
+
RUNTIME=rust
|
|
78
|
+
else
|
|
79
|
+
while IFS= read -r f; do [[ -n "$f" ]] && REGISTRY_PATHS+=("$f"); done < <(
|
|
80
|
+
git ls-files -- '*error_registry.zig' 2>/dev/null | sort || true)
|
|
81
|
+
[[ ${#REGISTRY_PATHS[@]} -gt 0 ]] && RUNTIME=zig
|
|
82
|
+
fi
|
|
83
|
+
fi
|
|
84
|
+
|
|
85
|
+
if [[ ${#REGISTRY_PATHS[@]} -eq 0 ]]; then
|
|
86
|
+
printf "OK: audit-error-codes: skipped — no %s-* registry found, so this repository has no source of truth to compare against\n" "$PREFIX"
|
|
87
|
+
exit 0
|
|
88
|
+
fi
|
|
89
|
+
|
|
90
|
+
# Where a code may legitimately be REFERENCED.
|
|
91
|
+
if [[ -n "${ORLY_ERROR_SOURCES:-}" ]]; then
|
|
92
|
+
SOURCE_GLOB="$ORLY_ERROR_SOURCES"
|
|
93
|
+
elif [[ "$RUNTIME" = "rust" ]]; then
|
|
94
|
+
SOURCE_GLOB='*.rs'
|
|
95
|
+
else
|
|
96
|
+
SOURCE_GLOB='*.zig'
|
|
97
|
+
fi
|
|
98
|
+
|
|
99
|
+
# The registry files are their own allowlist, joined with the test-shaped paths
|
|
100
|
+
# every runtime spells differently, plus whatever the caller adds.
|
|
101
|
+
REGISTRY_RE="$(printf '%s\n' "${REGISTRY_PATHS[@]}" | sed 's|[.[\*^$/]|\\&|g' | paste -sd'|' -)"
|
|
102
|
+
ALLOWLIST_RE="^(${REGISTRY_RE})$|_tests?\\.(zig|rs)$|(^|/)tests?/|(^|/)tests?\\.rs$|/_?test_harness\\.[a-z]+$|_test_fixtures\\.[a-z]+$"
|
|
103
|
+
[[ -n "${ORLY_ERROR_ALLOWLIST:-}" ]] && ALLOWLIST_RE="${ALLOWLIST_RE}|${ORLY_ERROR_ALLOWLIST}"
|
|
67
104
|
|
|
68
105
|
FAIL=0
|
|
69
106
|
fail() { printf "FAIL: %s\n" "$*" >&2; FAIL=1; }
|
|
@@ -78,9 +115,9 @@ note() { printf "NOTE: %s\n" "$*"; }
|
|
|
78
115
|
# the entire UZ-UUIDV7-* family (its category ends in a digit), leaving those
|
|
79
116
|
# codes invisible to the orphan/dead/used passes below.
|
|
80
117
|
# ---------------------------------------------------------------------------
|
|
81
|
-
declared_codes=$(grep -
|
|
118
|
+
declared_codes=$(grep -hoE "${CODE_RE}\b" "${REGISTRY_PATHS[@]}" | sort -u)
|
|
82
119
|
if [[ -z "$declared_codes" ]]; then
|
|
83
|
-
fail "no codes declared in $
|
|
120
|
+
fail "no codes declared in ${REGISTRY_PATHS[*]} (registry empty?)"
|
|
84
121
|
exit 1
|
|
85
122
|
fi
|
|
86
123
|
declared_count=$(printf '%s\n' "$declared_codes" | wc -l | tr -d ' ')
|
|
@@ -93,13 +130,12 @@ declared_count=$(printf '%s\n' "$declared_codes" | wc -l | tr -d ' ')
|
|
|
93
130
|
gather_used_paths() {
|
|
94
131
|
case "$MODE" in
|
|
95
132
|
--staged|staged)
|
|
96
|
-
git diff --cached --name-only --diff-filter=ACMRT \
|
|
97
|
-
| grep -
|
|
98
|
-
| grep -vE '^src/agentsfleetd/errors/error_registry\.zig$' || true
|
|
133
|
+
git diff --cached --name-only --diff-filter=ACMRT -- $SOURCE_GLOB 2>/dev/null \
|
|
134
|
+
| grep -vE "^(${REGISTRY_RE})$" || true
|
|
99
135
|
;;
|
|
100
136
|
--all|all)
|
|
101
|
-
|
|
102
|
-
| grep -vE
|
|
137
|
+
git ls-files -- $SOURCE_GLOB 2>/dev/null \
|
|
138
|
+
| grep -vE "^(${REGISTRY_RE})$" || true
|
|
103
139
|
;;
|
|
104
140
|
*)
|
|
105
141
|
printf "usage: %s [--staged|--all]\n" "$0" >&2
|
|
@@ -108,24 +144,49 @@ gather_used_paths() {
|
|
|
108
144
|
esac
|
|
109
145
|
}
|
|
110
146
|
|
|
111
|
-
mapfile
|
|
147
|
+
# `while read` rather than mapfile: mapfile is bash 4+ and macOS ships 3.2 —
|
|
148
|
+
# the portability rule scripts/run-playbook-tests.sh already records.
|
|
149
|
+
USED_PATHS=()
|
|
150
|
+
while IFS= read -r p; do USED_PATHS+=("$p"); done < <(gather_used_paths)
|
|
112
151
|
if [[ ${#USED_PATHS[@]} -eq 0 ]]; then
|
|
113
152
|
ok "no source files in scope ($MODE)"
|
|
114
153
|
exit 0
|
|
115
154
|
fi
|
|
116
155
|
|
|
117
|
-
used_codes=$(awk '
|
|
156
|
+
used_codes=$(awk -v prefix="$PREFIX" '
|
|
118
157
|
# Reset per-file so a trailing intentional-fake marker in the previous
|
|
119
158
|
# file cannot carry skip_next=1 into the first line of the next file.
|
|
120
|
-
FNR == 1 { skip_next = 0 }
|
|
159
|
+
FNR == 1 { skip_next = 0; in_rs_test = 0; rs_open = 0; rs_depth = 0 }
|
|
160
|
+
# Rust keeps its unit tests INSIDE the file they cover, under
|
|
161
|
+
# `#[cfg(test)] mod tests { ... }`, so a negative test naming a code it
|
|
162
|
+
# expects to be refused reads as a raw literal in the production half of
|
|
163
|
+
# that same file. Same brace-depth shape as audits/ufs.sh.
|
|
164
|
+
FILENAME ~ /\.rs$/ {
|
|
165
|
+
if (in_rs_test) {
|
|
166
|
+
tmp = $0
|
|
167
|
+
rs_depth += gsub(/\{/, "{", tmp) - gsub(/\}/, "}", tmp)
|
|
168
|
+
if (!rs_open && index($0, "{") > 0) rs_open = 1
|
|
169
|
+
if (rs_open && rs_depth <= 0) in_rs_test = 0
|
|
170
|
+
next
|
|
171
|
+
}
|
|
172
|
+
if ($0 ~ /^[[:space:]]*#\[cfg\(test\)\]/ ||
|
|
173
|
+
$0 ~ /^[[:space:]]*#\[[A-Za-z_:]*test\]/) {
|
|
174
|
+
in_rs_test = 1; rs_open = 0; rs_depth = 0
|
|
175
|
+
next
|
|
176
|
+
}
|
|
177
|
+
}
|
|
121
178
|
# Skip the line carrying the marker AND the next non-blank line (the
|
|
122
179
|
# marker sits on the comment line above the code line in Zig style).
|
|
123
180
|
/audit-error-codes: intentional-fake/ { skip_next = 1; next }
|
|
181
|
+
# A code NAMED in prose is a reference to history, not a call site: Rust doc
|
|
182
|
+
# comments routinely say which code a path used to return. Skipping them here
|
|
183
|
+
# matches the raw-literal scan below, which has always skipped comments.
|
|
184
|
+
/^[[:space:]]*\/\// { next }
|
|
124
185
|
/^[[:space:]]*$/ { next }
|
|
125
186
|
skip_next { skip_next = 0; next }
|
|
126
187
|
{
|
|
127
188
|
line = $0;
|
|
128
|
-
while (match(line,
|
|
189
|
+
while (match(line, prefix "-[A-Z][A-Z0-9]*-[0-9][0-9][0-9][0-9]*")) {
|
|
129
190
|
m = substr(line, RSTART, RLENGTH);
|
|
130
191
|
# Reject codes whose digit run is followed by a word char (rejects
|
|
131
192
|
# placeholder forms like UZ-INTERNAL-00X that grep would truncate).
|
|
@@ -150,7 +211,7 @@ used_count=0
|
|
|
150
211
|
# ---------------------------------------------------------------------------
|
|
151
212
|
orphans=$(comm -23 <(printf '%s\n' "$used_codes") <(printf '%s\n' "$declared_codes") | grep -v '^$' || true)
|
|
152
213
|
if [[ -n "$orphans" ]]; then
|
|
153
|
-
fail "orphan codes (used but not declared in $
|
|
214
|
+
fail "orphan codes (used but not declared in ${REGISTRY_PATHS[*]}):"
|
|
154
215
|
while IFS= read -r code; do
|
|
155
216
|
[[ -z "$code" ]] && continue
|
|
156
217
|
printf " %s refs:\n" "$code" >&2
|
|
@@ -185,20 +246,37 @@ fi
|
|
|
185
246
|
# 2-segment pattern. We don't want a "UZ-X-Y-NNN" code reintroduced
|
|
186
247
|
# via a local const that the orphan pass silently ignores.
|
|
187
248
|
# ---------------------------------------------------------------------------
|
|
188
|
-
raw_leaks=$(awk '
|
|
189
|
-
FNR == 1 { skip_next = 0 }
|
|
249
|
+
raw_leaks=$(awk -v prefix="$PREFIX" '
|
|
250
|
+
FNR == 1 { skip_next = 0; in_rs_test = 0; rs_open = 0; rs_depth = 0 }
|
|
251
|
+
# Rust keeps its unit tests INSIDE the file they cover, under
|
|
252
|
+
# `#[cfg(test)] mod tests { ... }`, so a negative test naming a code it
|
|
253
|
+
# expects to be refused reads as a raw literal in the production half of
|
|
254
|
+
# that same file. Same brace-depth shape as audits/ufs.sh.
|
|
255
|
+
FILENAME ~ /\.rs$/ {
|
|
256
|
+
if (in_rs_test) {
|
|
257
|
+
tmp = $0
|
|
258
|
+
rs_depth += gsub(/\{/, "{", tmp) - gsub(/\}/, "}", tmp)
|
|
259
|
+
if (!rs_open && index($0, "{") > 0) rs_open = 1
|
|
260
|
+
if (rs_open && rs_depth <= 0) in_rs_test = 0
|
|
261
|
+
next
|
|
262
|
+
}
|
|
263
|
+
if ($0 ~ /^[[:space:]]*#\[cfg\(test\)\]/ ||
|
|
264
|
+
$0 ~ /^[[:space:]]*#\[[A-Za-z_:]*test\]/) {
|
|
265
|
+
in_rs_test = 1; rs_open = 0; rs_depth = 0
|
|
266
|
+
next
|
|
267
|
+
}
|
|
268
|
+
}
|
|
190
269
|
# Skip the line carrying the marker AND the next non-blank line.
|
|
191
270
|
/audit-error-codes: intentional-fake/ { skip_next = 1; next }
|
|
192
271
|
/^[[:space:]]*\/\// { next }
|
|
193
272
|
/^[[:space:]]*$/ { next }
|
|
194
273
|
skip_next { skip_next = 0; next }
|
|
195
274
|
# Match any "UZ-XXX(-YYY)*-NNN" string literal.
|
|
196
|
-
|
|
275
|
+
$0 ~ "\"" prefix "-[A-Z][A-Z0-9-]*-[0-9]+\"" {
|
|
197
276
|
print FILENAME ":" FNR ":" $0;
|
|
198
277
|
}
|
|
199
278
|
' $(printf '%s\n' "${USED_PATHS[@]}" | grep -vE \
|
|
200
|
-
|
|
201
|
-
|| true) 2>/dev/null || true)
|
|
279
|
+
"$ALLOWLIST_RE" || true) 2>/dev/null || true)
|
|
202
280
|
|
|
203
281
|
if [[ -n "$raw_leaks" ]]; then
|
|
204
282
|
fail "raw UZ-* literals found outside the registry allowlist (must reference a registry symbol):"
|
|
@@ -212,7 +290,7 @@ fi
|
|
|
212
290
|
# ---------------------------------------------------------------------------
|
|
213
291
|
# 5. Verdict.
|
|
214
292
|
# ---------------------------------------------------------------------------
|
|
215
|
-
ok "registry: $declared_count declared, $used_count used"
|
|
293
|
+
ok "registry (${RUNTIME:-unknown}): $declared_count declared, $used_count used"
|
|
216
294
|
if [[ $FAIL -ne 0 ]]; then
|
|
217
295
|
printf "\n🔴 ERROR REGISTRY GATE: violations found. Reference the registry symbol (e.g. \`error_codes.ERR_X\` or \`client_errors.ERR_X\` for the executor crate) instead of duplicating the literal.\n" >&2
|
|
218
296
|
exit 1
|
package/audits/logging.sh
CHANGED
|
@@ -110,7 +110,10 @@ gather_paths() {
|
|
|
110
110
|
esac
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
-
mapfile
|
|
113
|
+
# `while read` rather than mapfile: mapfile is bash 4+ and macOS ships 3.2 —
|
|
114
|
+
# the portability rule scripts/run-playbook-tests.sh already records.
|
|
115
|
+
FILES=()
|
|
116
|
+
while IFS= read -r p; do FILES+=("$p"); done < <(gather_paths)
|
|
114
117
|
if [[ ${#FILES[@]} -eq 0 ]]; then
|
|
115
118
|
ok "no source files in scope ($MODE)"
|
|
116
119
|
exit 0
|
|
@@ -139,7 +142,9 @@ for f in "${FILES[@]}"; do
|
|
|
139
142
|
done
|
|
140
143
|
|
|
141
144
|
if [[ ${#rust_nontest[@]} -gt 0 ]]; then
|
|
142
|
-
|
|
145
|
+
rust_candidates=()
|
|
146
|
+
while IFS= read -r p; do rust_candidates+=("$p"); done \
|
|
147
|
+
< <(grep -lE '(^|[^[:alnum:]_])(println|eprintln|dbg)!|tracing::(error|warn|info|debug|trace)!' "${rust_nontest[@]}" 2>/dev/null || true)
|
|
143
148
|
fi
|
|
144
149
|
|
|
145
150
|
# ---------------------------------------------------------------------------
|
|
@@ -388,12 +393,17 @@ fi
|
|
|
388
393
|
# ---------------------------------------------------------------------------
|
|
389
394
|
scoped_hits=0
|
|
390
395
|
scoped_eligible=()
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
396
|
+
# Guarded like every sibling loop: bash 3.2 under `set -u` treats "${empty[@]}"
|
|
397
|
+
# as unbound (fixed only in 4.4), so an unguarded expansion aborts the audit in
|
|
398
|
+
# any repository with no Zig sources. macOS ships 3.2.
|
|
399
|
+
if [[ ${#zig_nontest[@]} -gt 0 ]]; then
|
|
400
|
+
for f in "${zig_nontest[@]}"; do
|
|
401
|
+
case "$f" in
|
|
402
|
+
src/logging/*) continue ;;
|
|
403
|
+
esac
|
|
404
|
+
scoped_eligible+=("$f")
|
|
405
|
+
done
|
|
406
|
+
fi
|
|
397
407
|
if [[ ${#scoped_eligible[@]} -gt 0 ]]; then
|
|
398
408
|
while IFS= read -r line; do
|
|
399
409
|
[[ -z "$line" ]] && continue
|
package/audits/spec-template.sh
CHANGED
|
@@ -48,13 +48,17 @@ note() { printf "NOTE: %s\n" "$*"; }
|
|
|
48
48
|
# Discover spec files in scope.
|
|
49
49
|
case "$MODE" in
|
|
50
50
|
--staged|staged)
|
|
51
|
-
|
|
51
|
+
# while read, not mapfile — bash-3.2 portability (see scripts/run-playbook-tests.sh).
|
|
52
|
+
SPECS=()
|
|
53
|
+
while IFS= read -r s; do SPECS+=("$s"); done < <(git diff --cached --name-only --diff-filter=ACMRT | grep -E '^docs/v[0-9]+/(pending|active|done)/.*\.md$' || true)
|
|
52
54
|
;;
|
|
53
55
|
--all|all)
|
|
54
|
-
|
|
56
|
+
SPECS=()
|
|
57
|
+
while IFS= read -r s; do SPECS+=("$s"); done < <(find docs/v[0-9]* -type f -name '*.md' 2>/dev/null | grep -E '/(pending|active)/' || true)
|
|
55
58
|
;;
|
|
56
59
|
--include-done|include-done)
|
|
57
|
-
|
|
60
|
+
SPECS=()
|
|
61
|
+
while IFS= read -r s; do SPECS+=("$s"); done < <(find docs/v[0-9]* -type f -name '*.md' 2>/dev/null | grep -E '/(pending|active|done)/' || true)
|
|
58
62
|
;;
|
|
59
63
|
--file|file)
|
|
60
64
|
# Single named spec, both families — the lifecycle engine's spec.gate
|
package/audits/sql-mod.sh
CHANGED
|
@@ -46,10 +46,13 @@ added_hit() {
|
|
|
46
46
|
|
|
47
47
|
case "$MODE" in
|
|
48
48
|
--staged|staged)
|
|
49
|
-
|
|
49
|
+
# while read, not mapfile — bash-3.2 portability (see scripts/run-playbook-tests.sh).
|
|
50
|
+
FILES=()
|
|
51
|
+
while IFS= read -r f; do FILES+=("$f"); done < <(git diff --cached --name-only --diff-filter=ACMRT -- '*.zig' || true)
|
|
50
52
|
STAGED=1 ;;
|
|
51
53
|
--all|all)
|
|
52
|
-
|
|
54
|
+
FILES=()
|
|
55
|
+
while IFS= read -r f; do FILES+=("$f"); done < <(find src -type f -name '*.zig' 2>/dev/null || true)
|
|
53
56
|
STAGED=0 ;;
|
|
54
57
|
*)
|
|
55
58
|
printf "usage: %s [--staged|--all]\n" "$0" >&2
|
package/audits/ufs.sh
CHANGED
|
@@ -116,7 +116,12 @@ if [ "$MODE" = "--staged" ]; then
|
|
|
116
116
|
else
|
|
117
117
|
scope_files() { git ls-files; }
|
|
118
118
|
fi
|
|
119
|
-
mapfile
|
|
119
|
+
# `while read` rather than mapfile: mapfile is bash 4+ and macOS ships 3.2 —
|
|
120
|
+
# the portability rule scripts/run-playbook-tests.sh already records.
|
|
121
|
+
FILES=()
|
|
122
|
+
while IFS= read -r f; do
|
|
123
|
+
FILES+=("$f")
|
|
124
|
+
done < <(scope_files | while read -r f; do
|
|
120
125
|
is_source "$f" && echo "$f"
|
|
121
126
|
done)
|
|
122
127
|
|
|
@@ -344,9 +349,13 @@ done < <(awk -v re="$NUMERIC_RE" '
|
|
|
344
349
|
|
|
345
350
|
# ── 3. cross-runtime-orphan ─────────────────────────────────────────────────
|
|
346
351
|
# Full-tree ERR_* parity. Scoped to the ERR_* prefix because that is the
|
|
347
|
-
# cross-runtime contract surface — server error codes a client consumes.
|
|
348
|
-
# the source of truth
|
|
349
|
-
#
|
|
352
|
+
# cross-runtime contract surface — server error codes a client consumes. The
|
|
353
|
+
# daemon is the source of truth. A Rust daemon declares its codes through a
|
|
354
|
+
# registry — `ErrorCode::declare("…")` — and the contract a client holds is
|
|
355
|
+
# that STRING, so parity is checked on the string: every client
|
|
356
|
+
# `export const ERR_X = "…"` must name a code the registry declares. Where no
|
|
357
|
+
# registry exists and Zig does, the Zig `pub const ERR_*` NAMES are the
|
|
358
|
+
# oracle instead. Daemon-only codes are fine; a server-internal code needs no
|
|
350
359
|
# client mirror.
|
|
351
360
|
#
|
|
352
361
|
# Scoped BY RUNTIME, not by one repository's directory names. The previous form
|
|
@@ -381,11 +390,29 @@ parity_codes() {
|
|
|
381
390
|
| grep -oE 'ERR_[A-Z][A-Z0-9_]+' | sort -u || true
|
|
382
391
|
}
|
|
383
392
|
|
|
393
|
+
# The Rust registry outranks a Zig mirror: a runner that keeps a Zig subset of
|
|
394
|
+
# the daemon's codes for its own use is a client, not the oracle. `record`
|
|
395
|
+
# mutates globals, so the loop runs in this shell over a here-string — never on
|
|
396
|
+
# the right of a pipe, which would run it in a subshell and drop every finding.
|
|
397
|
+
rust_codes=$(git ls-files -z -- '*.rs' 2>/dev/null \
|
|
398
|
+
| { grep -zvE "$PARITY_EXCLUDE" || true; } \
|
|
399
|
+
| { xargs -0 grep -hoE 'ErrorCode::declare\("[^"]+"\)' 2>/dev/null || true; } \
|
|
400
|
+
| { grep -oE '"[^"]+"' || true; } | tr -d '"' | sort -u)
|
|
384
401
|
zig_err=$(parity_codes '^pub const ERR_[A-Z][A-Z0-9_]+[[:space:]]*=' '*.zig')
|
|
385
402
|
client_err=$(parity_codes '^export const ERR_[A-Z][A-Z0-9_]+[[:space:]]*=' '*.js' '*.jsx' '*.ts' '*.tsx')
|
|
386
403
|
|
|
387
|
-
if [ -
|
|
388
|
-
|
|
404
|
+
if [ -n "$rust_codes" ]; then
|
|
405
|
+
client_decls=$(git ls-files -z -- '*.js' '*.jsx' '*.ts' '*.tsx' 2>/dev/null \
|
|
406
|
+
| { grep -zvE "$PARITY_EXCLUDE" || true; } \
|
|
407
|
+
| { xargs -0 grep -hE '^export const ERR_[A-Z][A-Z0-9_]+[[:space:]]*=[[:space:]]*"[^"]+"' 2>/dev/null || true; })
|
|
408
|
+
while IFS= read -r line; do
|
|
409
|
+
[ -n "$line" ] || continue
|
|
410
|
+
name=$(printf '%s' "$line" | grep -oE 'ERR_[A-Z][A-Z0-9_]+' | head -1)
|
|
411
|
+
code=$(printf '%s' "$line" | sed -E 's/^[^=]*=[[:space:]]*"([^"]+)".*/\1/')
|
|
412
|
+
echo "$rust_codes" | grep -qxF -- "$code" || record "cross-runtime-orphan $name ($code) absent-in-daemon"
|
|
413
|
+
done <<<"$client_decls"
|
|
414
|
+
elif [ -z "$zig_err" ]; then
|
|
415
|
+
ok "audit-ufs: cross-runtime parity skipped — no Rust registry and no Zig ERR_* declared, so this repository has no source of truth to compare against"
|
|
389
416
|
else
|
|
390
417
|
# Every client-side ERR_* must exist in Zig.
|
|
391
418
|
for c in $client_err; do
|
package/dispatch/lib.sh
CHANGED
|
@@ -53,7 +53,10 @@ DISPATCH_RC=0
|
|
|
53
53
|
# Rule-code gloss map — canonical short expansions (full text in RULES.md legend).
|
|
54
54
|
# Keep in sync with RULES.md; dispatch-coverage.sh fails on a code with no
|
|
55
55
|
# gloss entry. snake-of-record: CODE → "Short Gloss".
|
|
56
|
-
declare -A
|
|
56
|
+
# A heredoc of `[CODE]="Gloss"` lines, not declare -A: associative arrays are
|
|
57
|
+
# bash 4+ and macOS ships 3.2. The line shape is load-bearing —
|
|
58
|
+
# evals/dispatch/coverage.sh greps lib.sh for it (checks f and g).
|
|
59
|
+
DISPATCH_GLOSS="$(cat <<'GLOSS'
|
|
57
60
|
[NDC]="No Dead Code"
|
|
58
61
|
[NLR]="No Legacy Retained (touch-it-fix-it)"
|
|
59
62
|
[NLG]="No Legacy compat shims (pre-v2.0.0)"
|
|
@@ -83,10 +86,17 @@ declare -A DISPATCH_GLOSS=(
|
|
|
83
86
|
[ERR-RS]="Rust ERror discipline (one type per crate, Result alias, no stringified cause)"
|
|
84
87
|
[GRP]="GREptile rule audit (diff vs greptile-learnings/RULES.md codes)"
|
|
85
88
|
[LDC]="Legacy-Design Consult (A remove / B patch / C keep)"
|
|
86
|
-
|
|
89
|
+
GLOSS
|
|
90
|
+
)"
|
|
87
91
|
|
|
88
92
|
# Look up a gloss; empty string if the code is unknown (audit will catch it).
|
|
89
|
-
|
|
93
|
+
# Pure parameter expansion — no fork per finding. `[` is escaped so the code
|
|
94
|
+
# is matched literally, not as a glob class.
|
|
95
|
+
dispatch_gloss() {
|
|
96
|
+
local rest; rest="${DISPATCH_GLOSS#*\[$1\]=\"}"
|
|
97
|
+
[[ "$rest" == "$DISPATCH_GLOSS" ]] && return 0
|
|
98
|
+
printf '%s' "${rest%%\"*}"
|
|
99
|
+
}
|
|
90
100
|
|
|
91
101
|
dispatch_init() {
|
|
92
102
|
DISPATCH_LANG="$1"; shift
|
package/dispatch/write_any.sh
CHANGED
|
@@ -33,12 +33,12 @@ dispatch_run_helper "LOG" "logging.sh" "--staged"
|
|
|
33
33
|
dispatch_run_helper "MSID" "msid-ui.sh" "--staged"
|
|
34
34
|
|
|
35
35
|
# ── delegations (run only in the product repo — need state absent from dotfiles) ──
|
|
36
|
-
# ERR's leaf hard-exits if
|
|
36
|
+
# ERR's leaf hard-exits if the product repo's error registry is absent (it
|
|
37
37
|
# cross-references emitted codes against the registry), unlike UFS/LOG/MSID which
|
|
38
38
|
# pass gracefully on empty input. So it can't run in dotfiles — delegate it; the
|
|
39
39
|
# product repo's `make harness-verify` runs error-codes.sh against the real
|
|
40
40
|
# registry. The .md keeps [DETERMINISTIC → ERR]: deterministic, just not run here.
|
|
41
|
-
dispatch_delegate "ERR" "make harness-verify (error-codes.sh — needs
|
|
41
|
+
dispatch_delegate "ERR" "make harness-verify (error-codes.sh — needs the product repo's error registry)"
|
|
42
42
|
|
|
43
43
|
# ── judgment gates (no script can decide; agent states verdict in chat) ──
|
|
44
44
|
dispatch_judgment "GRP" "per-iteration (diff langs change) + end-of-turn: audit the diff against docs/greptile-learnings/RULES.md — one row per applicable code (UFS/ORP/TST-NAM/PRI/EMS/…); \"it's just a label\" is not an exception"
|
package/dispatch/write_zig.md
CHANGED
|
@@ -480,7 +480,7 @@ Rules:
|
|
|
480
480
|
|
|
481
481
|
New production Zig modules must not define SQL statement text inline. Put query
|
|
482
482
|
text in a domain-local `sql.zig` and import it from the state/handler/service
|
|
483
|
-
module.
|
|
483
|
+
module. The shape is a domain-local `sql.zig` beside the module it serves: table names and
|
|
484
484
|
query statements stay grepable in one place, while the public module owns row
|
|
485
485
|
mapping, allocator ownership, and error translation.
|
|
486
486
|
|
package/docs/CHANGELOG_VOICE.md
CHANGED
|
@@ -14,5 +14,5 @@ Editing `~/Projects/docs/changelog.mdx` or any other Mintlify `<Update>` block.
|
|
|
14
14
|
- **Internal cleanup / refactor entries get the most aggressive trimming.** One lead paragraph + one bullet list. Skip "Test coverage" sections unless the test count is the headline. Indy's exact direction: *"Keep internal code cleanup, refactor to a minimal."*
|
|
15
15
|
- **Never drop load-bearing facts.** Error codes (`UZ-AUTH-003`), endpoint paths + method + body shape + status code, env var names + defaults, schema column / table names, CLI subcommand + flag names, migration steps, money amounts. Tighten prose, not meaning.
|
|
16
16
|
- **Historical entries are archives.** Brevity-pass them; never rewrite the past. A typo correction (e.g. `$0.001` → `$0.01` when it was never true) is allowed and must be called out in the commit message.
|
|
17
|
-
- **Rate constants flow through three pinned files only:** `
|
|
17
|
+
- **Rate constants flow through three pinned files only:** `rustd/crates/afd_billing/src/nanos.rs`, `ui/packages/website/src/lib/rates.ts`, `~/Projects/docs/snippets/rates.mdx`. Bumping a rate requires a paired Pull Request (PR) across the docs repo — there is no automated guard. In `*.mdx`, use `import { STARTER_CREDIT, EVENT_RATE, STAGE_RATE } from "/snippets/rates.mdx"` instead of hand-typing `$5`.
|
|
18
18
|
- **The Mintlify reference Indy pasted (May 1 / May 8 entries) is canonical voice.** Mirror its rhythm, not its product nouns.
|
|
@@ -22,7 +22,7 @@ Every triggered document requires one `📖 DOC READ: <path>` proof-line **per t
|
|
|
22
22
|
| `*.mdx` | `dispatch/write_mdx.md` — structure, front matter, links, code fences, accessibility, and Mintlify isolation. | <!-- oracle-packs:language.mdx -->
|
|
23
23
|
| Log emit (any language; see LOGGING GATE triggers) | `docs/LOGGING_STANDARD.md` — wire format (logfmt), severity ladder, error-code embedding, scope/event discipline, PII redaction, §10A tightenings. LOGGING GATE per edit. |
|
|
24
24
|
| Lifecycle method in `*.zig` (`init|deinit|close|release|destroy|shutdown|dispose|free`) | `docs/LIFECYCLE_PATTERNS.md` — init/deinit pairing, errdefer placement, allocator ownership, defer/errdefer mutual exclusion, §10A tightenings. LIFECYCLE GATE per edit. |
|
|
25
|
-
| `
|
|
25
|
+
| `rustd/crates/afd_api*/src/handler/**` or `public/openapi/**` | `docs/REST_API_DESIGN_GUIDELINES.md` — Quick Checklist; §1–§5 (URL/method/body/response/error), §6 (OpenAPI), §7 (6-place route registration), §8 (`Hx` handler interface), §10 (pre-PR gates). |
|
|
26
26
|
| `ui/packages/**/*.{tsx,jsx,css}`, `app/**/*.{tsx,jsx,css}`, `components/**/*.{tsx,jsx,css}`, repo-root `globals.css`, or any file changing visual tokens / motion / typography | `DESIGN.md` (repo root) or `docs/DESIGN_SYSTEM.md` — whichever the repo carries. Design system source of truth: typography stack, color tokens, the single accent and its currency rule, motion signature, spacing/density, component principles, CLI palette mapping. DOC READ proof-line per turn. |
|
|
27
27
|
| `*.tsx` / `*.jsx` under `ui/packages/{app,website}/` | `dispatch/write_ts_adhere_bun.md` (Design Tokens) — token-utility table (text/tracking/leading/max-w/min-w/spacing/motion/radius/color). DESIGN TOKEN GATE fires per edit; audit via project-side `audits/design-tokens.sh`. |
|
|
28
28
|
| Auth-flow | `docs/AUTH.md`. |
|
package/docs/LOGGING_STANDARD.md
CHANGED
|
@@ -35,7 +35,7 @@ Documented honestly, not aspirationally. Pre-M62 baseline (the fix-pass converge
|
|
|
35
35
|
|
|
36
36
|
- The named module `log` (source: `src/lib/logging/mod.zig`) exposes `scoped(.tag)` returning a logger struct with `.err` / `.warn` / `.info` / `.debug` methods. M62 removed the older free-function helpers (`logErr` / `logErrWithHint` / `logWarnErr`) — there is no compat shim.
|
|
37
37
|
- TypeScript (`cli/src/**`) calls `console.log`/`console.error` directly. No structure. No scope. No severity beyond err vs out.
|
|
38
|
-
- Error-code embedding (`UZ-XXX-NNN`) appears on some `err` lines but not others. The registry (`
|
|
38
|
+
- Error-code embedding (`UZ-XXX-NNN`) appears on some `err` lines but not others. The registry (`rustd/crates/afd_core/src/error_code.rs`) is the source of truth, but enforcement is voluntary.
|
|
39
39
|
- No collector-friendly format. Logs are a mix of free-form English and ad-hoc `key={value}` fragments.
|
|
40
40
|
|
|
41
41
|
The proposed standard below is what every new emit must conform to and what the fix-pass converges existing emits toward.
|
|
@@ -168,7 +168,7 @@ Log the source (`source=env:OPENAI_API_KEY`), never the value, at every level.
|
|
|
168
168
|
|
|
169
169
|
> [DETERMINISTIC → LOG]
|
|
170
170
|
|
|
171
|
-
Every `err` and `warn` record that maps to a domain error MUST carry `error_code=UZ-XXX-NNN` where `UZ-XXX-NNN` is declared in `
|
|
171
|
+
Every `err` and `warn` record that maps to a domain error MUST carry `error_code=UZ-XXX-NNN` where `UZ-XXX-NNN` is declared in `rustd/crates/afd_core/src/error_code.rs`.
|
|
172
172
|
|
|
173
173
|
- **Used-but-undeclared** (`UZ-FAKE-999` appearing in code, no entry in registry): **blocking** in `make lint`.
|
|
174
174
|
- **Declared-but-unreferenced** (registry has `UZ-LEGACY-007`, no code references it): **informational**. Deletion may be deferred to a sweep milestone.
|