@agentsfleet/orly 0.5.0
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/.githooks/pre-commit +42 -0
- package/.githooks/pre-push +67 -0
- package/AGENTS.md +348 -0
- package/LICENSE +21 -0
- package/README.md +249 -0
- package/SOUL.md +90 -0
- package/audits/agents-md.md +497 -0
- package/audits/agents-md.sh +353 -0
- package/audits/cross-tier-rates.sh +105 -0
- package/audits/data.sh +185 -0
- package/audits/deinit-pairs.sh +224 -0
- package/audits/design-tokens.sh +143 -0
- package/audits/doc-read.sh +183 -0
- package/audits/error-codes.sh +221 -0
- package/audits/fixtures/clean.diff +15 -0
- package/audits/fixtures/dirty.diff +16 -0
- package/audits/lifecycle-anchors.sh +22 -0
- package/audits/logging.sh +217 -0
- package/audits/msid-ui.sh +144 -0
- package/audits/parity-dispatch.sh +51 -0
- package/audits/rule-ledger-lib.sh +264 -0
- package/audits/rule-ledger.sh +225 -0
- package/audits/rule-paths.sh +108 -0
- package/audits/spec-template.sh +258 -0
- package/audits/sql-mod.sh +102 -0
- package/audits/ufs.sh +303 -0
- package/bin/orly +26 -0
- package/core/operating-model.md +256 -0
- package/dispatch/edit_rules.md +69 -0
- package/dispatch/lib.sh +213 -0
- package/dispatch/lifecycle.md +187 -0
- package/dispatch/name_architecture.md +93 -0
- package/dispatch/verify.md +111 -0
- package/dispatch/write_any.md +740 -0
- package/dispatch/write_any.sh +49 -0
- package/dispatch/write_auth.md +36 -0
- package/dispatch/write_changelog.md +64 -0
- package/dispatch/write_documentation.md +45 -0
- package/dispatch/write_go.md +24 -0
- package/dispatch/write_http.md +45 -0
- package/dispatch/write_mdx.md +7 -0
- package/dispatch/write_python.md +8 -0
- package/dispatch/write_rust.md +38 -0
- package/dispatch/write_shell.md +8 -0
- package/dispatch/write_spec.md +105 -0
- package/dispatch/write_sql.md +112 -0
- package/dispatch/write_sql.sh +36 -0
- package/dispatch/write_ts_adhere_bun.md +493 -0
- package/dispatch/write_ts_adhere_bun.sh +37 -0
- package/dispatch/write_zig.md +852 -0
- package/dispatch/write_zig.sh +40 -0
- package/docs/CHANGELOG_VOICE.md +18 -0
- package/docs/DISPATCH_ARCHITECTURE.md +664 -0
- package/docs/DISPATCH_REVIEW_DISPOSITION.md +65 -0
- package/docs/DOCUMENTATION_RULES.md +209 -0
- package/docs/EXECUTE_DOC_READS.md +29 -0
- package/docs/HARNESS_VERIFY_OUTPUT.md +40 -0
- package/docs/LIFECYCLE_PATTERNS.md +316 -0
- package/docs/LOGGING_STANDARD.md +397 -0
- package/docs/ORLY_ARCHITECTURE.md +129 -0
- package/docs/REST_API_DESIGN_GUIDELINES.md +783 -0
- package/docs/RULE_ENFORCEMENT.md +41 -0
- package/docs/SCHEMA_CONVENTIONS.md +93 -0
- package/docs/TEMPLATE.md +411 -0
- package/docs/VERIFY_TIERS.md +57 -0
- package/docs/greptile-learnings/README.md +51 -0
- package/docs/greptile-learnings/RULES.md +1045 -0
- package/fixtures/registry-invalid.json +1 -0
- package/fixtures/registry-valid.json +18 -0
- package/fixtures/unclosed-pack-block.md +8 -0
- package/package.json +63 -0
- package/packs/language/go/rules.md +24 -0
- package/packs/language/mdx/rules.md +7 -0
- package/packs/language/python/rules.md +8 -0
- package/packs/language/rust/rules.md +38 -0
- package/packs/language/shell/rules.md +8 -0
- package/registry.json +405 -0
- package/schemas/evidence.schema.json +43 -0
- package/schemas/profile.schema.json +12 -0
- package/schemas/registry.schema.json +12 -0
- package/skills/orly-babysit-prs/SKILL.md +326 -0
- package/skills/orly-spec-new/SKILL.md +157 -0
- package/skills/orly-write-integration-test/SKILL.md +470 -0
- package/skills/orly-write-unit-test/SKILL.md +520 -0
- package/src/cli.test.ts +70 -0
- package/src/cli.ts +287 -0
- package/src/config.test.ts +226 -0
- package/src/config.ts +288 -0
- package/src/criteria.test.ts +137 -0
- package/src/criteria.ts +166 -0
- package/src/criteria_spec.ts +127 -0
- package/src/criteria_support.ts +53 -0
- package/src/gates.test.ts +183 -0
- package/src/gates.ts +169 -0
- package/src/gates_spec.test.ts +98 -0
- package/src/gates_test_support.ts +194 -0
- package/src/git_env.ts +20 -0
- package/src/install.test.ts +514 -0
- package/src/install.ts +447 -0
- package/src/install_nested.test.ts +32 -0
- package/src/model.test.ts +31 -0
- package/src/model.ts +245 -0
- package/src/references.test.ts +47 -0
- package/src/references.ts +147 -0
- package/src/render.test.ts +60 -0
- package/src/render.ts +94 -0
- package/src/surfaces.test.ts +43 -0
- package/src/surfaces.ts +89 -0
- package/src/validation.test.ts +11 -0
- package/src/validation.ts +87 -0
- package/src/verify.test.ts +13 -0
- package/src/verify.ts +59 -0
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Deterministic audit for AGENTS.md.
|
|
3
|
+
# Exit 0 = all checks pass. Non-zero = regression — see FAIL lines on stderr.
|
|
4
|
+
#
|
|
5
|
+
# Run before every AGENTS.md change (and from the pre-commit hook).
|
|
6
|
+
# Each check asserts an invariant that, if broken, would re-introduce a
|
|
7
|
+
# concrete failure mode the operating model already paid for.
|
|
8
|
+
|
|
9
|
+
set -uo pipefail
|
|
10
|
+
|
|
11
|
+
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
12
|
+
AGENTS="$ROOT/AGENTS.md"
|
|
13
|
+
FIXTURES="$ROOT/audits/fixtures"
|
|
14
|
+
FAIL=0
|
|
15
|
+
|
|
16
|
+
# Expectation tables (EXPECTED_LABELS, REQUIRED_GATES, EXTS, FORBIDDEN_KEYS,
|
|
17
|
+
# CONFORM_KEYS, DOTFILES_RESIDENT, LIFECYCLE_HEADERS, NAMED_SCENARIOS,
|
|
18
|
+
# RULE_EXTENSION_STEPS, AWK_PROG) live in the sibling data file — split out
|
|
19
|
+
# to keep this audit under the LENGTH GATE cap. Data drift is itself caught
|
|
20
|
+
# by the gate-parity + named-scenario-parity checks below.
|
|
21
|
+
DATA="$ROOT/audits/data.sh"
|
|
22
|
+
[[ -f "$DATA" ]] || { echo "FAIL: $DATA missing (audit data tables)" >&2; exit 2; }
|
|
23
|
+
# shellcheck source=audits/data.sh
|
|
24
|
+
. "$DATA"
|
|
25
|
+
SEEN_LABELS=()
|
|
26
|
+
|
|
27
|
+
# Colours — disable when stdout is not a TTY (e.g. piped, hook).
|
|
28
|
+
if [[ -t 1 ]]; then
|
|
29
|
+
C_GREEN=$'\033[32m'; C_RED=$'\033[31m'; C_YELLOW=$'\033[33m'
|
|
30
|
+
C_BLUE=$'\033[34m'; C_BOLD=$'\033[1m'; C_RESET=$'\033[0m'
|
|
31
|
+
else
|
|
32
|
+
C_GREEN=''; C_RED=''; C_YELLOW=''; C_BLUE=''; C_BOLD=''; C_RESET=''
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
fail() { printf '%s🔴 FAIL%s: %s\n' "$C_RED" "$C_RESET" "$*" >&2; FAIL=1; SEEN_LABELS+=("$*"); }
|
|
36
|
+
pass() { printf '%s🟢 PASS%s: %s\n' "$C_GREEN" "$C_RESET" "$*"; SEEN_LABELS+=("$*"); }
|
|
37
|
+
warn() { printf '%s🟡 WARN%s: %s\n' "$C_YELLOW" "$C_RESET" "$*" >&2; }
|
|
38
|
+
info() { printf '%s🔆 %s%s\n' "$C_BLUE" "$*" "$C_RESET"; }
|
|
39
|
+
|
|
40
|
+
info "Auditing $AGENTS"
|
|
41
|
+
|
|
42
|
+
[[ -f "$AGENTS" ]] || { echo "FAIL: $AGENTS missing" >&2; exit 2; }
|
|
43
|
+
|
|
44
|
+
# ---------------------------------------------------------------------------
|
|
45
|
+
# 1. Dispatch inventory — every dispatch entry (REQUIRED_DISPATCH in data) has
|
|
46
|
+
# a row in the AGENTS.md dispatch table, with the entry name as the table's
|
|
47
|
+
# SECOND column ("| <trigger> | `<entry>` | …"). This is the same column the
|
|
48
|
+
# parity check (check 9b) counts, so inventory and parity agree by
|
|
49
|
+
# construction — a renamed/dropped entry fails here AND there.
|
|
50
|
+
# ---------------------------------------------------------------------------
|
|
51
|
+
bt='`' # backtick held in a var — keeps it explicit inside the regex
|
|
52
|
+
missing_dispatch=0
|
|
53
|
+
for e in "${REQUIRED_DISPATCH[@]}"; do
|
|
54
|
+
grep -qE "^\|[^|]*\| *${bt}${e}${bt} *\|" "$AGENTS" \
|
|
55
|
+
|| { fail "dispatch entry missing from table: $e"; missing_dispatch=1; }
|
|
56
|
+
done
|
|
57
|
+
[[ $missing_dispatch -eq 0 ]] && pass "dispatch inventory (${#REQUIRED_DISPATCH[@]} entries present in table)"
|
|
58
|
+
|
|
59
|
+
# ---------------------------------------------------------------------------
|
|
60
|
+
# 2. Trigger surface — every source/config language has a mention (EXTS in data).
|
|
61
|
+
# ---------------------------------------------------------------------------
|
|
62
|
+
missing_exts=0
|
|
63
|
+
for ext in "${EXTS[@]}"; do
|
|
64
|
+
grep -qF "$ext" "$AGENTS" || { fail "trigger surface missing extension: $ext"; missing_exts=1; }
|
|
65
|
+
done
|
|
66
|
+
[[ $missing_exts -eq 0 ]] && pass "trigger surface (${#EXTS[@]} extensions named)"
|
|
67
|
+
|
|
68
|
+
# ---------------------------------------------------------------------------
|
|
69
|
+
# 3. Override syntax present file-wide (every gate either has an Override:
|
|
70
|
+
# line or an explicit "no override" marker).
|
|
71
|
+
# ---------------------------------------------------------------------------
|
|
72
|
+
override_hits=$(grep -cE 'Override|no override|SKIPPED per' "$AGENTS")
|
|
73
|
+
if [[ $override_hits -lt 7 ]]; then
|
|
74
|
+
fail "override-syntax mentions only $override_hits — expected ≥ 7"
|
|
75
|
+
else
|
|
76
|
+
pass "override syntax ($override_hits mentions)"
|
|
77
|
+
fi
|
|
78
|
+
|
|
79
|
+
# ---------------------------------------------------------------------------
|
|
80
|
+
# 4. Always-forbidden invariance — six hard bans (FORBIDDEN_KEYS in data).
|
|
81
|
+
# ---------------------------------------------------------------------------
|
|
82
|
+
missing_bans=0
|
|
83
|
+
for k in "${FORBIDDEN_KEYS[@]}"; do
|
|
84
|
+
grep -qF "$k" "$AGENTS" || { fail "always-forbidden item missing: $k"; missing_bans=1; }
|
|
85
|
+
done
|
|
86
|
+
[[ $missing_bans -eq 0 ]] && pass "always-forbidden list (${#FORBIDDEN_KEYS[@]} bans present)"
|
|
87
|
+
|
|
88
|
+
# ---------------------------------------------------------------------------
|
|
89
|
+
# 5. Skill-chain order — /orly-write-unit-test → runtime review → babysit.
|
|
90
|
+
# Anchored to within the CHORE(close) section so a stray earlier mention
|
|
91
|
+
# cannot satisfy the check.
|
|
92
|
+
# ---------------------------------------------------------------------------
|
|
93
|
+
awk '
|
|
94
|
+
/^### CHORE \(close\)/ { in_section=1; next }
|
|
95
|
+
/^### |^## / { if (in_section) in_section=0 }
|
|
96
|
+
in_section && /\/orly-write-unit-test/ && !a { a=NR }
|
|
97
|
+
in_section && /gstack/ && a && !b { b=NR }
|
|
98
|
+
in_section && /orly-babysit-prs/&& b && !c { c=NR }
|
|
99
|
+
END { exit (a && b && c && a<b && b<c) ? 0 : 1 }
|
|
100
|
+
' "$AGENTS" \
|
|
101
|
+
&& pass "skill-chain ordering (anchored to CHORE(close))" \
|
|
102
|
+
|| fail "skill chain not in order within CHORE(close): /orly-write-unit-test → gstack /review → orly-babysit-prs"
|
|
103
|
+
|
|
104
|
+
# One route for every runtime. A per-runtime split re-introduces the
|
|
105
|
+
# collapsing-two-near-named-steps failure SOUL.md records; assert the
|
|
106
|
+
# single route and that both pre-PR test skills are named.
|
|
107
|
+
review_routes_missing=0
|
|
108
|
+
grep -qF 'One route, every runtime: gstack `/review`.' "$AGENTS" \
|
|
109
|
+
|| { fail "single gstack review route missing"; review_routes_missing=1; }
|
|
110
|
+
grep -qF '/orly-write-integration-test' "$AGENTS" \
|
|
111
|
+
|| { fail "/orly-write-integration-test missing from the pre-PR skill chain"; review_routes_missing=1; }
|
|
112
|
+
[[ $review_routes_missing -eq 0 ]] && pass "unified review route + both test skills"
|
|
113
|
+
|
|
114
|
+
# ---------------------------------------------------------------------------
|
|
115
|
+
# 6. CONFORM rows — every gate keyword in the verdict block (CONFORM_KEYS).
|
|
116
|
+
# ---------------------------------------------------------------------------
|
|
117
|
+
missing_rows=0
|
|
118
|
+
for kw in "${CONFORM_KEYS[@]}"; do
|
|
119
|
+
grep -qF "$kw" "$AGENTS" || { fail "CONFORM row missing: $kw"; missing_rows=1; }
|
|
120
|
+
done
|
|
121
|
+
[[ $missing_rows -eq 0 ]] && pass "CONFORM coverage (${#CONFORM_KEYS[@]} rows present)"
|
|
122
|
+
|
|
123
|
+
# ---------------------------------------------------------------------------
|
|
124
|
+
# 7. Cross-references — dotfiles-resident docs must exist (DOTFILES_RESIDENT
|
|
125
|
+
# in data). Project-side docs (AUTH.md, architecture/, changelog.mdx) live
|
|
126
|
+
# in each project repo, not here, so they're excluded from this check.
|
|
127
|
+
# ---------------------------------------------------------------------------
|
|
128
|
+
broken_refs=0
|
|
129
|
+
for doc in "${DOTFILES_RESIDENT[@]}"; do
|
|
130
|
+
[[ -f "$ROOT/$doc" ]] || { fail "dotfiles-resident ref missing: $doc"; broken_refs=1; }
|
|
131
|
+
done
|
|
132
|
+
[[ $broken_refs -eq 0 ]] && pass "cross-references (dotfiles-resident docs exist)"
|
|
133
|
+
|
|
134
|
+
# ---------------------------------------------------------------------------
|
|
135
|
+
# 8. Combined-audit smoke — the awk pass (AWK_PROG in data) flags real
|
|
136
|
+
# violations and ignores clean diffs. Fixtures live in audits/fixtures/.
|
|
137
|
+
# ---------------------------------------------------------------------------
|
|
138
|
+
if [[ -f "$FIXTURES/dirty.diff" && -f "$FIXTURES/clean.diff" ]]; then
|
|
139
|
+
# Fixture content fingerprint — guards against well-meaning rewrites
|
|
140
|
+
# that "fix" dirty.diff to be clean (which would silently break the smoke).
|
|
141
|
+
grep -qF "M40_001" "$FIXTURES/dirty.diff" || fail "fixture dirty.diff missing M40_001 marker"
|
|
142
|
+
grep -qE '<button>' "$FIXTURES/dirty.diff" || fail "fixture dirty.diff missing raw <button>"
|
|
143
|
+
! grep -qF "M40_001" "$FIXTURES/clean.diff" || fail "fixture clean.diff has milestone-id leak"
|
|
144
|
+
! grep -qE '<button>' "$FIXTURES/clean.diff" || fail "fixture clean.diff has raw <button>"
|
|
145
|
+
|
|
146
|
+
dirty_hits=$(awk "$AWK_PROG" "$FIXTURES/dirty.diff" | wc -l | tr -d ' ')
|
|
147
|
+
clean_hits=$(awk "$AWK_PROG" "$FIXTURES/clean.diff" | wc -l | tr -d ' ')
|
|
148
|
+
|
|
149
|
+
# Pin exact expected counts — protects against false-positive "smoke ok"
|
|
150
|
+
# where a rigged dirty.diff produces ≥1 hit on an unrelated token.
|
|
151
|
+
if [[ $dirty_hits -eq 4 ]]; then
|
|
152
|
+
pass "audit fixture: dirty diff flagged exactly 4 hits"
|
|
153
|
+
else
|
|
154
|
+
fail "audit fixture: dirty diff flagged $dirty_hits hits (expected 4)"
|
|
155
|
+
fi
|
|
156
|
+
if [[ $clean_hits -eq 0 ]]; then
|
|
157
|
+
pass "audit fixture: clean diff produced 0 hits"
|
|
158
|
+
else
|
|
159
|
+
fail "audit fixture: clean diff falsely flagged ($clean_hits hits)"
|
|
160
|
+
fi
|
|
161
|
+
else
|
|
162
|
+
fail "audit fixture files missing under $FIXTURES/"
|
|
163
|
+
fi
|
|
164
|
+
|
|
165
|
+
# ---------------------------------------------------------------------------
|
|
166
|
+
# 9b. Dispatch parity — the three sources of truth must agree: dispatch/*.md
|
|
167
|
+
# bodies on disk, dispatch-table rows in AGENTS.md, and REQUIRED_DISPATCH
|
|
168
|
+
# (check #1). The check also asserts docs/gates/ is EMPTY — the dissolved-
|
|
169
|
+
# gate end state — so a half-done switchover (a leftover card) hard-fails.
|
|
170
|
+
# The logic lives in the sibling parity-dispatch.sh so the model-B sandbox
|
|
171
|
+
# test (evals/test-dispatch-parity.sh) can exercise it in isolation; this
|
|
172
|
+
# keeps the body completeness + existence guarantee that the old per-card
|
|
173
|
+
# gate-body check used to provide (each REQUIRED_DISPATCH entry must resolve
|
|
174
|
+
# to a real dispatch/<entry>.md).
|
|
175
|
+
# ---------------------------------------------------------------------------
|
|
176
|
+
. "$ROOT/audits/parity-dispatch.sh"
|
|
177
|
+
parity_out=$(check_dispatch_parity "$ROOT"); parity_rc=$?
|
|
178
|
+
if [[ $parity_rc -eq 0 ]]; then
|
|
179
|
+
pass "dispatch parity ${parity_out#PASS: dispatch parity }"
|
|
180
|
+
else
|
|
181
|
+
printf '%s\n' "$parity_out" >&2
|
|
182
|
+
fail "dispatch parity mismatch (see FAIL lines above)"
|
|
183
|
+
fi
|
|
184
|
+
|
|
185
|
+
# ---------------------------------------------------------------------------
|
|
186
|
+
# 10. audits/agents-md.md presence + basic shape (questionnaire layer).
|
|
187
|
+
# ---------------------------------------------------------------------------
|
|
188
|
+
INV="$ROOT/audits/agents-md.md"
|
|
189
|
+
if [[ -f "$INV" ]]; then
|
|
190
|
+
inv_scenarios=$(grep -cE '^### Scenario [0-9]+' "$INV")
|
|
191
|
+
if [[ $inv_scenarios -ge 8 ]]; then
|
|
192
|
+
pass "audits/agents-md.md present ($inv_scenarios scenarios)"
|
|
193
|
+
else
|
|
194
|
+
fail "audits/agents-md.md only $inv_scenarios scenarios — expected ≥ 8"
|
|
195
|
+
fi
|
|
196
|
+
else
|
|
197
|
+
fail "audits/agents-md.md missing"
|
|
198
|
+
fi
|
|
199
|
+
|
|
200
|
+
# ---------------------------------------------------------------------------
|
|
201
|
+
# 11. Lifecycle stage headers — all present in AGENTS.md (LIFECYCLE_HEADERS).
|
|
202
|
+
# ---------------------------------------------------------------------------
|
|
203
|
+
missing_stages=0
|
|
204
|
+
for h in "${LIFECYCLE_HEADERS[@]}"; do
|
|
205
|
+
grep -qF "$h" "$AGENTS" || { fail "lifecycle stage missing: $h"; missing_stages=1; }
|
|
206
|
+
done
|
|
207
|
+
[[ $missing_stages -eq 0 ]] && pass "lifecycle stages (${#LIFECYCLE_HEADERS[@]} headers present)"
|
|
208
|
+
|
|
209
|
+
# 11b. Lifecycle anchors — load-bearing strings in BOTH essence + runbook.
|
|
210
|
+
. "$ROOT/audits/lifecycle-anchors.sh"
|
|
211
|
+
anchors_out=$(check_lifecycle_anchors "$ROOT")
|
|
212
|
+
if [[ $? -eq 0 ]]; then pass "lifecycle anchors (${#LIFECYCLE_ANCHORS[@]} strings in essence + runbook)"; else
|
|
213
|
+
printf '%s\n' "$anchors_out" >&2; fail "lifecycle anchors (see FAIL lines above)"; fi
|
|
214
|
+
|
|
215
|
+
# 12. Named scenarios exist by title (NAMED_SCENARIOS in data) — catches a
|
|
216
|
+
# renamed/removed high-value scenario hiding behind a stable count.
|
|
217
|
+
INV="$ROOT/audits/agents-md.md"
|
|
218
|
+
missing_scenarios=0
|
|
219
|
+
if [[ -f "$INV" ]]; then
|
|
220
|
+
for s in "${NAMED_SCENARIOS[@]}"; do
|
|
221
|
+
grep -qF "$s" "$INV" || { fail "audits/agents-md.md missing scenario keyword: $s"; missing_scenarios=1; }
|
|
222
|
+
done
|
|
223
|
+
# Parity — the keyword array must keep pace with the actual scenario count,
|
|
224
|
+
# so a newly-added scenario can't sit unguarded (this is exactly how
|
|
225
|
+
# scenarios 20-22 slipped past the keyword list before this check existed).
|
|
226
|
+
actual_scenarios=$(grep -cE '^### Scenario [0-9]+' "$INV")
|
|
227
|
+
if [[ "${#NAMED_SCENARIOS[@]}" -ne "$actual_scenarios" ]]; then
|
|
228
|
+
fail "named-scenario parity: array has ${#NAMED_SCENARIOS[@]} keywords but audits/agents-md.md has $actual_scenarios scenarios (every scenario needs a keyword guard)"
|
|
229
|
+
missing_scenarios=1
|
|
230
|
+
fi
|
|
231
|
+
[[ $missing_scenarios -eq 0 ]] && pass "named scenarios (${#NAMED_SCENARIOS[@]} keywords ↔ $actual_scenarios scenarios, parity holds)"
|
|
232
|
+
else
|
|
233
|
+
fail "audits/agents-md.md missing"
|
|
234
|
+
fi
|
|
235
|
+
|
|
236
|
+
# ---------------------------------------------------------------------------
|
|
237
|
+
# 13. Hook trigger coverage — pre-commit AND pre-push must reference
|
|
238
|
+
# AGENTS.md AND dispatch/ so ruleset changes can never silently
|
|
239
|
+
# bypass either layer (dispatch/ replaces the dissolved docs/gates/).
|
|
240
|
+
# ---------------------------------------------------------------------------
|
|
241
|
+
PRE_COMMIT="$ROOT/.githooks/pre-commit"
|
|
242
|
+
PRE_PUSH="$ROOT/.githooks/pre-push"
|
|
243
|
+
hook_fail=0
|
|
244
|
+
for hook in "$PRE_COMMIT" "$PRE_PUSH"; do
|
|
245
|
+
name=$(basename "$hook")
|
|
246
|
+
if [[ ! -f "$hook" ]]; then
|
|
247
|
+
fail "hook missing: .githooks/$name"; hook_fail=1; continue
|
|
248
|
+
fi
|
|
249
|
+
grep -qE 'AGENTS(\\)?\.md' "$hook" || { fail "$name missing AGENTS.md trigger reference"; hook_fail=1; }
|
|
250
|
+
grep -qF "dispatch/" "$hook" || { fail "$name missing dispatch/ trigger reference"; hook_fail=1; }
|
|
251
|
+
for token in 'package\.json' 'bun\.lock' 'tsconfig\.json' 'fixtures/'; do
|
|
252
|
+
grep -qF "$token" "$hook" || { fail "$name missing $token trigger reference"; hook_fail=1; }
|
|
253
|
+
done
|
|
254
|
+
done
|
|
255
|
+
[[ $hook_fail -eq 0 ]] && pass "hook triggers (.githooks/pre-commit + pre-push both gate rules, package, toolchain, and fixtures)"
|
|
256
|
+
|
|
257
|
+
# ---------------------------------------------------------------------------
|
|
258
|
+
# 14. Rule extension protocol — AGENTS.md MUST document the 4-step recipe
|
|
259
|
+
# for landing a new rules file or gate body, AND that recipe must still
|
|
260
|
+
# enumerate all four wiring steps. Mere section-presence is not enough:
|
|
261
|
+
# the protocol is the only thing tying a new gate to the doc-reads table,
|
|
262
|
+
# the questionnaire, the DOTFILES_RESIDENT audit list, and `make audit`.
|
|
263
|
+
# If a step silently drops out of the recipe, gates can be added without
|
|
264
|
+
# that linkage — the same class of drift that hid UFS/DESIGN-TOKEN.
|
|
265
|
+
# ---------------------------------------------------------------------------
|
|
266
|
+
if grep -qF "Rule extension protocol" "$AGENTS"; then
|
|
267
|
+
protocol_line=$(grep -F "Rule extension protocol" "$AGENTS" | head -1)
|
|
268
|
+
rep_fail=0
|
|
269
|
+
for step in "${RULE_EXTENSION_STEPS[@]}"; do
|
|
270
|
+
grep -qF "$step" <<<"$protocol_line" || { fail "rule extension protocol missing step keyword: $step"; rep_fail=1; }
|
|
271
|
+
done
|
|
272
|
+
[[ $rep_fail -eq 0 ]] && pass "rule extension protocol section present (all 4 wiring steps enumerated)"
|
|
273
|
+
else
|
|
274
|
+
fail "AGENTS.md missing 'Rule extension protocol' section"
|
|
275
|
+
fi
|
|
276
|
+
|
|
277
|
+
# ---------------------------------------------------------------------------
|
|
278
|
+
# 15. Identity handles — AGENTS.md MUST identify the human (Kishore / Indy)
|
|
279
|
+
# and the agent (Oracle / Orly) so addressing resolves unambiguously.
|
|
280
|
+
# ---------------------------------------------------------------------------
|
|
281
|
+
if grep -qF "human is Kishore" "$AGENTS" && grep -qF "Orly" "$AGENTS"; then
|
|
282
|
+
pass "identity handles (Kishore/Indy ↔ Oracle/Orly present)"
|
|
283
|
+
else
|
|
284
|
+
fail "identity handles: AGENTS.md missing 'human is Kishore' or 'Orly'"
|
|
285
|
+
fi
|
|
286
|
+
|
|
287
|
+
# ---------------------------------------------------------------------------
|
|
288
|
+
# 15b. Memory discipline — auto-memory is retired in favour of dispatch + repo
|
|
289
|
+
# docs + HANDOFF. The section MUST keep three load-bearing facts so a
|
|
290
|
+
# future edit can't silently re-enable the per-session memory tax or drop
|
|
291
|
+
# the routing that replaces it: the disable flag, the never-write rule, and
|
|
292
|
+
# the dispatch-first routing target.
|
|
293
|
+
# ---------------------------------------------------------------------------
|
|
294
|
+
md_fail=0
|
|
295
|
+
grep -qF "## Memory Discipline" "$AGENTS" || { fail "memory discipline: section header missing"; md_fail=1; }
|
|
296
|
+
grep -qF "autoMemoryEnabled" "$AGENTS" || { fail "memory discipline: autoMemoryEnabled disable flag not documented"; md_fail=1; }
|
|
297
|
+
grep -qiE "NEVER write to .*memory" "$AGENTS" || { fail "memory discipline: never-write-memory rule missing"; md_fail=1; }
|
|
298
|
+
grep -qF 'dispatch/<entry>.md' "$AGENTS" || { fail "memory discipline: dispatch-first routing missing"; md_fail=1; }
|
|
299
|
+
[[ $md_fail -eq 0 ]] && pass "memory discipline (disable flag + never-write + dispatch routing present)"
|
|
300
|
+
|
|
301
|
+
# ---------------------------------------------------------------------------
|
|
302
|
+
# 15c. Rule-path residence — citations resolve inside the installing repo
|
|
303
|
+
# (audits/rule-paths.sh; incident: an M143 worktree agent resolved a
|
|
304
|
+
# dotfiles rule doc against the product repo and designed from memory).
|
|
305
|
+
# ---------------------------------------------------------------------------
|
|
306
|
+
. "$ROOT/audits/rule-paths.sh"
|
|
307
|
+
res_out=$(check_rule_residence "$ROOT")
|
|
308
|
+
if [[ $? -eq 0 ]]; then pass "rule-path residence (doctrine + anchors + zero relative hrefs)"; else
|
|
309
|
+
printf '%s\n' "$res_out" >&2; fail "rule-path residence (see FAIL lines above)"; fi
|
|
310
|
+
|
|
311
|
+
# ---------------------------------------------------------------------------
|
|
312
|
+
# 16. Size cap — soft guard against drift back to bloat.
|
|
313
|
+
# Generated metadata is included in the measured file.
|
|
314
|
+
#
|
|
315
|
+
# 40 KiB, raised from 32 KiB (Aug 22, 2026) on Indy's call: the render had
|
|
316
|
+
# reached the old cap exactly, so every new rule cost an existing one, and
|
|
317
|
+
# the next thing to cut would have been triggers rather than gloss. The cap
|
|
318
|
+
# exists for attention, not capacity — a rule the agent skims past enforces
|
|
319
|
+
# nothing — so it stays a real ceiling, just one with room to work in.
|
|
320
|
+
# ---------------------------------------------------------------------------
|
|
321
|
+
SIZE=$(wc -c < "$AGENTS" | tr -d ' ')
|
|
322
|
+
LIMIT=${AGENTS_MD_SIZE_LIMIT:-40960}
|
|
323
|
+
if [[ $SIZE -le $LIMIT ]]; then
|
|
324
|
+
pass "size $SIZE bytes (limit $LIMIT)"
|
|
325
|
+
else
|
|
326
|
+
fail "size $SIZE bytes exceeds limit $LIMIT (override via AGENTS_MD_SIZE_LIMIT=...)"
|
|
327
|
+
fi
|
|
328
|
+
|
|
329
|
+
# ---------------------------------------------------------------------------
|
|
330
|
+
# Self-fingerprint — every EXPECTED_LABELS entry must have been emitted by
|
|
331
|
+
# at least one pass/fail call. Catches the case where someone deletes a
|
|
332
|
+
# check from the script — the script then runs successfully but with
|
|
333
|
+
# fewer invariants enforced, which is exactly the kind of silent
|
|
334
|
+
# weakening this audit exists to prevent.
|
|
335
|
+
# ---------------------------------------------------------------------------
|
|
336
|
+
fp_fail=0
|
|
337
|
+
for label in "${EXPECTED_LABELS[@]}"; do
|
|
338
|
+
found=0
|
|
339
|
+
for seen in "${SEEN_LABELS[@]}"; do
|
|
340
|
+
[[ "$seen" == *"$label"* ]] && { found=1; break; }
|
|
341
|
+
done
|
|
342
|
+
[[ $found -eq 1 ]] || { fail "self-fingerprint: expected check '$label' did not run"; fp_fail=1; }
|
|
343
|
+
done
|
|
344
|
+
[[ $fp_fail -eq 0 ]] && pass "self-fingerprint (${#EXPECTED_LABELS[@]} expected checks ran)"
|
|
345
|
+
|
|
346
|
+
echo
|
|
347
|
+
if [[ $FAIL -eq 0 ]]; then
|
|
348
|
+
printf '%s✅ ALL CHECKS PASSED%s\n' "$C_GREEN$C_BOLD" "$C_RESET"
|
|
349
|
+
exit 0
|
|
350
|
+
else
|
|
351
|
+
printf '%s🔴 REGRESSION DETECTED%s — see FAIL lines above\n' "$C_RED$C_BOLD" "$C_RESET"
|
|
352
|
+
exit 1
|
|
353
|
+
fi
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# cross-tier-rates.sh — pin numeric parity of the rate constants
|
|
3
|
+
# across Zig + three TypeScript surfaces.
|
|
4
|
+
#
|
|
5
|
+
# RULE UFS extension. The shipped billing surface depends on these constants
|
|
6
|
+
# having identical numeric values everywhere they appear:
|
|
7
|
+
#
|
|
8
|
+
# RUN_NANOS_PER_SEC — per-second run rate (both postures) in nanos
|
|
9
|
+
#
|
|
10
|
+
# Two free-trial constants were pinned here and are both gone. FREE_TRIAL_END_MS
|
|
11
|
+
# went when the boundary moved onto the tenant row; FREE_TRIAL_STAGE_NANOS went
|
|
12
|
+
# when the trial mechanism was removed outright, because a rate that could
|
|
13
|
+
# silently price a metered slice to zero was worth deleting rather than
|
|
14
|
+
# guarding. The free allowance is wallet credit now — a balance, not a rate —
|
|
15
|
+
# so there is no cross-tier number to pin for it.
|
|
16
|
+
#
|
|
17
|
+
# Files (one definition site per constant per file):
|
|
18
|
+
#
|
|
19
|
+
# src/agentsfleetd/state/tenant_billing.zig — server enforces the charge
|
|
20
|
+
# ui/packages/website/src/lib/rates.ts — pricing page display
|
|
21
|
+
# ui/packages/app/lib/types.ts — dashboard display
|
|
22
|
+
# cli/src/constants/billing.ts — `agentsfleet doctor --json` billing block
|
|
23
|
+
#
|
|
24
|
+
# A drift between any two = a billing-display lie or a server-vs-CLI disagreement.
|
|
25
|
+
# Zig is the source of truth (server enforces); the three TS surfaces echo it.
|
|
26
|
+
#
|
|
27
|
+
# Fires in: make harness-verify, make harness-verify-all.
|
|
28
|
+
# Exits 0 clean, 1 on any drift.
|
|
29
|
+
|
|
30
|
+
set -euo pipefail
|
|
31
|
+
|
|
32
|
+
ROOT="$(git rev-parse --show-toplevel)"
|
|
33
|
+
cd "$ROOT"
|
|
34
|
+
|
|
35
|
+
readonly NAMES=(
|
|
36
|
+
RUN_NANOS_PER_SEC
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
readonly FILES=(
|
|
40
|
+
src/agentsfleetd/state/tenant_billing.zig
|
|
41
|
+
ui/packages/website/src/lib/rates.ts
|
|
42
|
+
ui/packages/app/lib/types.ts
|
|
43
|
+
cli/src/constants/billing.ts
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
# Extract the numeric value bound to `name` in `file`. Matches definition
|
|
47
|
+
# sites only — `^(pub |export )?const NAME` — not usages elsewhere in the
|
|
48
|
+
# file. Normalises: strips underscores (Zig `1_000_000`, TS `1_000_000`),
|
|
49
|
+
# strips the bigint `n` suffix (TS `1_000_000n`), trims whitespace.
|
|
50
|
+
extract_value() {
|
|
51
|
+
local file="$1" name="$2"
|
|
52
|
+
grep -E "^[[:space:]]*(pub |export )?const ${name}\\b" "$file" 2>/dev/null \
|
|
53
|
+
| head -1 \
|
|
54
|
+
| sed -E "s|.*=[[:space:]]*||; s|[;,].*||; s|//.*||; s|[[:space:]_n]||g"
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
FAIL=0
|
|
58
|
+
violations=()
|
|
59
|
+
|
|
60
|
+
printf "audit-cross-tier-rates: pinning %d constants across %d files\n" \
|
|
61
|
+
"${#NAMES[@]}" "${#FILES[@]}"
|
|
62
|
+
|
|
63
|
+
for name in "${NAMES[@]}"; do
|
|
64
|
+
expected=""
|
|
65
|
+
expected_file=""
|
|
66
|
+
for file in "${FILES[@]}"; do
|
|
67
|
+
if [[ ! -f "$file" ]]; then
|
|
68
|
+
violations+=("MISSING_FILE: $file")
|
|
69
|
+
FAIL=1
|
|
70
|
+
continue
|
|
71
|
+
fi
|
|
72
|
+
val="$(extract_value "$file" "$name")"
|
|
73
|
+
if [[ -z "$val" ]]; then
|
|
74
|
+
violations+=("MISSING_CONST: $name not defined in $file")
|
|
75
|
+
FAIL=1
|
|
76
|
+
continue
|
|
77
|
+
fi
|
|
78
|
+
if [[ -z "$expected" ]]; then
|
|
79
|
+
expected="$val"
|
|
80
|
+
expected_file="$file"
|
|
81
|
+
continue
|
|
82
|
+
fi
|
|
83
|
+
if [[ "$val" != "$expected" ]]; then
|
|
84
|
+
violations+=("DRIFT: $name = $val in $file vs $expected in $expected_file")
|
|
85
|
+
FAIL=1
|
|
86
|
+
fi
|
|
87
|
+
done
|
|
88
|
+
if [[ -n "$expected" ]]; then
|
|
89
|
+
printf " OK %-26s = %s\n" "$name" "$expected"
|
|
90
|
+
fi
|
|
91
|
+
done
|
|
92
|
+
|
|
93
|
+
if [[ $FAIL -ne 0 ]]; then
|
|
94
|
+
printf "\nFAIL: cross-tier rate drift detected:\n" >&2
|
|
95
|
+
for v in "${violations[@]}"; do
|
|
96
|
+
printf " %s\n" "$v" >&2
|
|
97
|
+
done
|
|
98
|
+
printf "\nFix: align all four files on the canonical Zig value\n" >&2
|
|
99
|
+
printf " (src/agentsfleetd/state/tenant_billing.zig is the server-side enforcer).\n" >&2
|
|
100
|
+
exit 1
|
|
101
|
+
fi
|
|
102
|
+
|
|
103
|
+
printf "OK: all %d rate constants pin across %d files\n" \
|
|
104
|
+
"${#NAMES[@]}" "${#FILES[@]}"
|
|
105
|
+
exit 0
|
package/audits/data.sh
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# shellcheck shell=bash
|
|
2
|
+
# Data tables for audits/agents-md.sh — the expectation lists every
|
|
3
|
+
# check asserts against, plus the combined-audit awk program. Split out of
|
|
4
|
+
# the main audit (LENGTH GATE: keeps agents-md.sh under the 350-line
|
|
5
|
+
# cap; data and logic separate cleanly). Sourced, never executed directly.
|
|
6
|
+
#
|
|
7
|
+
# Adding a dispatch entry/scenario/rule? Update the matching array HERE; the
|
|
8
|
+
# audit's dispatch-parity + named-scenario-parity checks fail loudly if a table
|
|
9
|
+
# drifts out of sync with AGENTS.md / dispatch/ / audits/agents-md.md.
|
|
10
|
+
|
|
11
|
+
# Self-fingerprint — every label here MUST be emitted by a pass/fail call in
|
|
12
|
+
# the audit. Deleting a check without updating this list trips the final
|
|
13
|
+
# self-fingerprint check. This is the script auditing itself.
|
|
14
|
+
EXPECTED_LABELS=(
|
|
15
|
+
"dispatch inventory"
|
|
16
|
+
"trigger surface"
|
|
17
|
+
"override syntax"
|
|
18
|
+
"always-forbidden list"
|
|
19
|
+
"skill-chain ordering"
|
|
20
|
+
"CONFORM coverage"
|
|
21
|
+
"cross-references"
|
|
22
|
+
"audit fixture: dirty diff"
|
|
23
|
+
"audit fixture: clean diff"
|
|
24
|
+
"dispatch parity"
|
|
25
|
+
"audits/agents-md.md present"
|
|
26
|
+
"lifecycle stages"
|
|
27
|
+
"named scenarios"
|
|
28
|
+
"hook triggers"
|
|
29
|
+
"rule extension protocol"
|
|
30
|
+
"identity handles"
|
|
31
|
+
"memory discipline"
|
|
32
|
+
"rule-path residence"
|
|
33
|
+
"lifecycle anchors"
|
|
34
|
+
"size"
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
# Check 1 (dispatch inventory) + check 9b (dispatch parity) — dispatch
|
|
38
|
+
# entries that replaced the 20 dissolved gate cards. Source of truth for both
|
|
39
|
+
# the inventory loop in agents-md.sh and check_dispatch_parity
|
|
40
|
+
# (audits/parity-dispatch.sh). Each name has a dispatch/<name>.md and a row in
|
|
41
|
+
# the AGENTS.md dispatch table (entry name in the 2nd column). Keep 1:1 with
|
|
42
|
+
# dispatch/*.md.
|
|
43
|
+
REQUIRED_DISPATCH=(
|
|
44
|
+
"write_zig" "write_ts_adhere_bun" "write_sql" "write_any"
|
|
45
|
+
"write_spec" "write_http" "write_auth" "write_changelog"
|
|
46
|
+
"write_documentation" "verify" "name_architecture" "edit_rules"
|
|
47
|
+
"write_rust" "write_go" "write_python" "write_shell" "write_mdx"
|
|
48
|
+
"lifecycle"
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
# Check 2 — every source/config language has at least one mention.
|
|
52
|
+
EXTS=( ".zig" ".ts" ".tsx" ".js" ".jsx" ".py" ".rs" ".go" ".sh" ".sql" )
|
|
53
|
+
|
|
54
|
+
# Check 4 — the six hard bans must remain.
|
|
55
|
+
FORBIDDEN_KEYS=(
|
|
56
|
+
"no-verify" # hooks/signing
|
|
57
|
+
"Plaintext secrets" # entity tables
|
|
58
|
+
"Static strings in SQL" # schema literals
|
|
59
|
+
"Resolving/printing credentials"
|
|
60
|
+
"Force-push default"
|
|
61
|
+
"core paths" # install-process launches in core paths
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
# Check 6 — every gate's keyword appears in the CONFORM verdict block.
|
|
65
|
+
CONFORM_KEYS=(
|
|
66
|
+
"FILE SHAPE" "PUB GATE" "LENGTH GATE" "MILESTONE-ID GATE"
|
|
67
|
+
"ZIG GATE" "UI GATE" "DESIGN TOKEN GATE"
|
|
68
|
+
"SCHEMA GUARD" "GREPTILE GATE"
|
|
69
|
+
"Architecture consult"
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
# Check 7 — dotfiles-resident docs + dispatch façades referenced by AGENTS.md
|
|
73
|
+
# must exist on disk. The per-language Zig/Bun standards docs were dropped —
|
|
74
|
+
# dissolved into dispatch/write_zig.md and dispatch/write_ts_adhere_bun.md
|
|
75
|
+
# respectively. The dispatch façades are listed so the AGENTS.md dispatch-table
|
|
76
|
+
# refs are checked. The docs/* entries double as the residence classifier for
|
|
77
|
+
# check 15c (audits/rule-paths.sh): these paths are cited through the
|
|
78
|
+
# ~/Projects/dotfiles/ anchor in every consumer-facing surface.
|
|
79
|
+
DOTFILES_RESIDENT=(
|
|
80
|
+
"docs/TEMPLATE.md"
|
|
81
|
+
"docs/REST_API_DESIGN_GUIDELINES.md"
|
|
82
|
+
"docs/LOGGING_STANDARD.md"
|
|
83
|
+
"docs/LIFECYCLE_PATTERNS.md"
|
|
84
|
+
"docs/DOCUMENTATION_RULES.md"
|
|
85
|
+
"docs/ORLY_ARCHITECTURE.md"
|
|
86
|
+
"docs/CHANGELOG_VOICE.md"
|
|
87
|
+
"docs/EXECUTE_DOC_READS.md"
|
|
88
|
+
"docs/VERIFY_TIERS.md"
|
|
89
|
+
"docs/HARNESS_VERIFY_OUTPUT.md"
|
|
90
|
+
"docs/SCHEMA_CONVENTIONS.md"
|
|
91
|
+
"docs/greptile-learnings/RULES.md"
|
|
92
|
+
"dispatch/write_zig.md"
|
|
93
|
+
"dispatch/write_ts_adhere_bun.md"
|
|
94
|
+
"dispatch/write_rust.md"
|
|
95
|
+
"dispatch/write_go.md"
|
|
96
|
+
"dispatch/write_python.md"
|
|
97
|
+
"dispatch/write_shell.md"
|
|
98
|
+
"dispatch/write_mdx.md"
|
|
99
|
+
"dispatch/write_sql.md"
|
|
100
|
+
"dispatch/write_any.md"
|
|
101
|
+
"dispatch/write_spec.md"
|
|
102
|
+
"dispatch/write_http.md"
|
|
103
|
+
"dispatch/write_auth.md"
|
|
104
|
+
"dispatch/write_changelog.md"
|
|
105
|
+
"dispatch/write_documentation.md"
|
|
106
|
+
"dispatch/verify.md"
|
|
107
|
+
"dispatch/name_architecture.md"
|
|
108
|
+
"dispatch/edit_rules.md"
|
|
109
|
+
"dispatch/lifecycle.md"
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
# Check 11 — every lifecycle stage header is present in AGENTS.md.
|
|
113
|
+
LIFECYCLE_HEADERS=(
|
|
114
|
+
"### CHORE (open)"
|
|
115
|
+
"### PLAN"
|
|
116
|
+
"### EXECUTE"
|
|
117
|
+
"### CONFORM"
|
|
118
|
+
"### VERIFY"
|
|
119
|
+
"### REVIEW"
|
|
120
|
+
"### DOCUMENT"
|
|
121
|
+
"### COMMIT"
|
|
122
|
+
"### CHORE (close)"
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
# Check 11b — load-bearing lifecycle strings that must exist in BOTH the
|
|
126
|
+
# rendered AGENTS.md (stage essence) and dispatch/lifecycle.md (the runbook).
|
|
127
|
+
# Consumed by audits/lifecycle-anchors.sh; a string dropped from either side
|
|
128
|
+
# is semantic drift the inventory parity check cannot see.
|
|
129
|
+
LIFECYCLE_ANCHORS=(
|
|
130
|
+
"Test Baseline:"
|
|
131
|
+
'`pwd` + `git worktree list`'
|
|
132
|
+
"user-acked verbatim quote"
|
|
133
|
+
"make check-version"
|
|
134
|
+
"RULE ORP"
|
|
135
|
+
"git status -uall"
|
|
136
|
+
"never force-push an open PR branch"
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
# Check 12 — each scenario must exist by keyword; array size must equal the
|
|
140
|
+
# actual scenario count (parity). Keep this list 1:1 with audits/agents-md.md.
|
|
141
|
+
NAMED_SCENARIOS=(
|
|
142
|
+
"New spec" # Scenario 1
|
|
143
|
+
"Brainstorming" # Scenario 2
|
|
144
|
+
"human spots and steers" # Scenario 3
|
|
145
|
+
"UI" # Scenario 4 (covers UI/Zig/TS/JS/shell/CI)
|
|
146
|
+
"Handover" # Scenario 5
|
|
147
|
+
"Conformance and verification lifecycle" # Scenario 6
|
|
148
|
+
"orly-babysit-prs" # Scenario 7
|
|
149
|
+
"/orly-write-unit-test" # Scenario 8
|
|
150
|
+
"Hot-fix" # Scenario 9
|
|
151
|
+
"Dotfiles" # Scenario 10
|
|
152
|
+
"Schema" # Scenario 11
|
|
153
|
+
"Auto-mode boundary" # Scenario 12
|
|
154
|
+
"Invariance Suite" # Scenario 13
|
|
155
|
+
"Communication" # Scenario 14
|
|
156
|
+
"Architecture-edit" # Scenario 15
|
|
157
|
+
"Credentials" # Scenario 16
|
|
158
|
+
"DB discipline" # Scenario 17
|
|
159
|
+
"worktree isolation" # Scenario 18
|
|
160
|
+
"combined audit" # Scenario 19
|
|
161
|
+
"Rule extension protocol" # Scenario 20
|
|
162
|
+
"Gate-flag triage" # Scenario 21
|
|
163
|
+
"Pre-commit audit scope" # Scenario 22
|
|
164
|
+
"Agent comprehension" # Scenario 23
|
|
165
|
+
"Memory routing" # Scenario 24
|
|
166
|
+
"Allocator & concurrency discipline" # Scenario 25
|
|
167
|
+
"Rules propagation" # Scenario 26
|
|
168
|
+
"Rule-path" # Scenario 27
|
|
169
|
+
"Rule-enforcement ledger" # Scenario 28
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
# Check 14 — the Rule-extension protocol must enumerate all four wiring steps.
|
|
173
|
+
RULE_EXTENSION_STEPS=( "doc-reads" "audits/agents-md.md" "DOTFILES_RESIDENT" "make audit" )
|
|
174
|
+
|
|
175
|
+
# Check 8 — combined-audit smoke: flags MS-ID / PUB / UI violations in a diff.
|
|
176
|
+
read -r -d '' AWK_PROG <<'AWKEOF' || true
|
|
177
|
+
/^\+\+\+ b\// { f=$2; sub("^b/","",f); next }
|
|
178
|
+
/^\+/ {
|
|
179
|
+
if (f ~ /\.(zig|sql|ts|tsx|js|jsx|py|rs|go|sh|toml|yaml|json)$/ && f !~ /^(docs|node_modules|vendor|third_party)\//) {
|
|
180
|
+
if (match($0, /M[0-9]+_[0-9]+|§[0-9]+(\.[0-9]+)+|\bT[0-9]+\b|\bdim [0-9]+\.[0-9]+\b/)) print "MS-ID:" f
|
|
181
|
+
}
|
|
182
|
+
if (f ~ /\.zig$/ && $0 ~ /^\+(pub | *pub fn | *[A-Z][a-zA-Z]+,$)/) print "PUB:" f
|
|
183
|
+
if (f ~ /^ui\/packages\/app\/.*\.(tsx|jsx)$/ && $0 ~ /<(section|button|input|dialog|article|nav|header|form)\b/) print "UI:" f
|
|
184
|
+
}
|
|
185
|
+
AWKEOF
|