@miller-tech/uap 1.183.2 → 1.184.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.
Files changed (70) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/bin/cli.js +14 -0
  3. package/dist/bin/cli.js.map +1 -1
  4. package/dist/cli/hooks.d.ts.map +1 -1
  5. package/dist/cli/hooks.js +20 -0
  6. package/dist/cli/hooks.js.map +1 -1
  7. package/dist/cli/memory.d.ts +14 -0
  8. package/dist/cli/memory.d.ts.map +1 -1
  9. package/dist/cli/memory.js +35 -0
  10. package/dist/cli/memory.js.map +1 -1
  11. package/dist/cli/principles.d.ts +10 -0
  12. package/dist/cli/principles.d.ts.map +1 -0
  13. package/dist/cli/principles.js +138 -0
  14. package/dist/cli/principles.js.map +1 -0
  15. package/dist/config/policy-recommendations.d.ts.map +1 -1
  16. package/dist/config/policy-recommendations.js +17 -0
  17. package/dist/config/policy-recommendations.js.map +1 -1
  18. package/dist/config/settings-registry.d.ts +1 -1
  19. package/dist/config/settings-registry.d.ts.map +1 -1
  20. package/dist/config/settings-registry.js +24 -0
  21. package/dist/config/settings-registry.js.map +1 -1
  22. package/dist/coordination/reactor.d.ts.map +1 -1
  23. package/dist/coordination/reactor.js +15 -0
  24. package/dist/coordination/reactor.js.map +1 -1
  25. package/dist/delivery/convergence-loop.d.ts +7 -1
  26. package/dist/delivery/convergence-loop.d.ts.map +1 -1
  27. package/dist/delivery/convergence-loop.js +32 -4
  28. package/dist/delivery/convergence-loop.js.map +1 -1
  29. package/dist/delivery/judge.d.ts.map +1 -1
  30. package/dist/delivery/judge.js +6 -0
  31. package/dist/delivery/judge.js.map +1 -1
  32. package/dist/delivery/task-orchestrator.d.ts.map +1 -1
  33. package/dist/delivery/task-orchestrator.js +8 -1
  34. package/dist/delivery/task-orchestrator.js.map +1 -1
  35. package/dist/principles/config.d.ts +10 -0
  36. package/dist/principles/config.d.ts.map +1 -0
  37. package/dist/principles/config.js +32 -0
  38. package/dist/principles/config.js.map +1 -0
  39. package/dist/principles/index.d.ts +16 -0
  40. package/dist/principles/index.d.ts.map +1 -0
  41. package/dist/principles/index.js +34 -0
  42. package/dist/principles/index.js.map +1 -0
  43. package/dist/principles/reactor-inject.d.ts +12 -0
  44. package/dist/principles/reactor-inject.d.ts.map +1 -0
  45. package/dist/principles/reactor-inject.js +69 -0
  46. package/dist/principles/reactor-inject.js.map +1 -0
  47. package/dist/principles/render.d.ts +14 -0
  48. package/dist/principles/render.d.ts.map +1 -0
  49. package/dist/principles/render.js +64 -0
  50. package/dist/principles/render.js.map +1 -0
  51. package/dist/principles/rules.d.ts +53 -0
  52. package/dist/principles/rules.d.ts.map +1 -0
  53. package/dist/principles/rules.js +101 -0
  54. package/dist/principles/rules.js.map +1 -0
  55. package/dist/principles/stance.d.ts +54 -0
  56. package/dist/principles/stance.d.ts.map +1 -0
  57. package/dist/principles/stance.js +154 -0
  58. package/dist/principles/stance.js.map +1 -0
  59. package/dist/types/config.d.ts +46 -0
  60. package/dist/types/config.d.ts.map +1 -1
  61. package/dist/types/config.js +17 -0
  62. package/dist/types/config.js.map +1 -1
  63. package/docs/reference/CONFIGURATION_REFERENCE.md +221 -0
  64. package/docs/reference/PLATFORMS.md +1 -0
  65. package/package.json +1 -1
  66. package/src/policies/enforcers/__pycache__/_common.cpython-312.pyc +0 -0
  67. package/src/policies/schemas/policies/engineering-principles.md +93 -0
  68. package/templates/hooks/__pycache__/deliver_autoroute.cpython-312.pyc +0 -0
  69. package/templates/hooks/post-tool-use-read.sh +72 -0
  70. package/tools/agents/scripts/__pycache__/toolcall_path_normalizer.cpython-312.pyc +0 -0
@@ -26,6 +26,7 @@ uap config wizard # interactive expert configurator (also: uap set
26
26
  - [Orchestrator & hands-free](#orchestration) — Long-task autonomy: decompose, resume, and loop-to-100%.
27
27
  - [Reactor (auto-apply)](#reactor) — Per-prompt injection of the matching experts, skills, and patterns.
28
28
  - [Design system](#design) — DESIGN.md interrogation and the hard token gate for UI work.
29
+ - [Engineering principles](#principles) — How code should be written, and the backward-compatibility stance.
29
30
  - [Worktree workflow](#worktree) — Branch-per-feature isolation and auto-cleanup.
30
31
  - [Inference proxy tuning](#proxy) — Guardrails and context limits for a local model behind the proxy.
31
32
  - [Dashboard](#dashboard) — The live analytics server and its mutation token.
@@ -60,6 +61,18 @@ How local-model sessions are routed through delivery. `deliver` runs builds thro
60
61
 
61
62
  **Recommendation:** `deliver` when a local model does the writing (routes it through the verified loop); `advisory` for exploratory work.
62
63
 
64
+ ### `deliver.escalateModel`
65
+
66
+ | | |
67
+ |---|---|
68
+ | **Where** | `.uap.json` |
69
+ | **Type** | string |
70
+ | **Default** | `null` |
71
+
72
+ Stronger model id for deliver escalation ladders: repair passes, the phase-5 escalation tier, and the evaluator fallback. Same role as $UAP_ESCALATE_MODEL but persisted in .uap.json (reproducible).
73
+
74
+ **Recommendation:** Point at your strongest available preset/model (e.g. an Opus cloud id) when the executor is a local model — stuck epics then escalate instead of re-splitting into the same wall.
75
+
63
76
  ### `UAP_ENFORCE_DELIVERY`
64
77
 
65
78
  | | |
@@ -100,6 +113,30 @@ Installs the runtime-verify Stop-hook: at end of turn it actually runs the chang
100
113
 
101
114
  **Recommendation:** Enable for any project with a runnable artifact — it catches "declared done but never ran". Safe on empty projects (it skips when nothing is runnable).
102
115
 
116
+ ### `delivery.userValidation`
117
+
118
+ | | |
119
+ |---|---|
120
+ | **Where** | `.uap.json` |
121
+ | **Type** | string |
122
+ | **Default** | `block` |
123
+
124
+ User-path validation gate: deliver runs the .uap/user-paths.json critical journeys through the real client (headless browser / HTTP / built CLI) as the terminal gate rung. block = DELIVERED requires them green; advisory = report only; off = disabled.
125
+
126
+ **Recommendation:** Leave on block — it is the only gate that proves the artifact works for a real user, not just that tests pass.
127
+
128
+ ### `UAP_USER_VALIDATION`
129
+
130
+ | | |
131
+ |---|---|
132
+ | **Where** | shell env |
133
+ | **Type** | boolean |
134
+ | **Default** | `true` |
135
+
136
+ Runtime downgrade for the user-validation gate: `0` demotes block to advisory for this run only. Persisting `0` is blocked by the self-protect enforcer.
137
+
138
+ **Recommendation:** Leave unset. Use inline `UAP_USER_VALIDATION=0 uap deliver ...` only to unblock a run where the gate itself misfires.
139
+
103
140
  ### `UAP_VERIFY_ON_STOP`
104
141
 
105
142
  | | |
@@ -112,6 +149,78 @@ Master switch for the runtime execution gate in the Stop hook. `0` bypasses it.
112
149
 
113
150
  **Recommendation:** Leave on (default). Set `0` only to unblock a session where the runtime gate misfires.
114
151
 
152
+ ### `fidelity.mode`
153
+
154
+ | | |
155
+ |---|---|
156
+ | **Where** | `.uap.json` |
157
+ | **Type** | enum (standard \| max) |
158
+ | **Default** | `standard` |
159
+
160
+ Maximum-fidelity mode. `max` flips every verification default to its strongest: raised verifier floor (runtime+integration), acceptance judge required, blocking vision review, and a fail-CLOSED visual gate — a delivery is accepted only when it builds, runs, looks right, and matches the spec. (`UAP_FIDELITY` overrides at runtime.)
161
+
162
+ **Recommendation:** `max` when correctness matters more than speed (UI work, releases, hands-free autonomy). `standard` for fast exploratory iteration.
163
+
164
+ ### `UAP_FIDELITY`
165
+
166
+ | | |
167
+ |---|---|
168
+ | **Where** | shell env |
169
+ | **Type** | enum (standard \| max) |
170
+ | **Default** | `standard` |
171
+
172
+ Runtime override of `fidelity.mode`, read from the shell env by verify/deliver and the Python enforcers. Takes precedence over the config value.
173
+
174
+ **Recommendation:** Set inline (`UAP_FIDELITY=max <cmd>`) to force max fidelity for one command without editing config.
175
+
176
+ ### `fidelity.visionMinScore`
177
+
178
+ | | |
179
+ |---|---|
180
+ | **Where** | `.uap.json` |
181
+ | **Type** | number |
182
+ | **Default** | `6` |
183
+
184
+ Minimum aesthetic score (0–10) the vision judge must give a rendered UI before it passes under `max` fidelity.
185
+
186
+ **Recommendation:** 6 is a reasonable "looks like a real, polished app" bar. Raise toward 8 for design-critical surfaces; lower to 4 to only catch broken/blank UIs.
187
+
188
+ ### `fidelity.visualBaselines`
189
+
190
+ | | |
191
+ |---|---|
192
+ | **Where** | `.uap.json` |
193
+ | **Type** | boolean |
194
+ | **Default** | `true` |
195
+
196
+ Keep approved UI screenshots as regression baselines under `.uap/visual/baseline/` and block on visual drift beyond threshold on later runs.
197
+
198
+ **Recommendation:** Leave on so accepted UIs are pinned against regressions. Disable for throwaway prototypes where every render legitimately differs.
199
+
200
+ ### `UAP_VISION_ENDPOINT`
201
+
202
+ | | |
203
+ |---|---|
204
+ | **Where** | `.uap/proxy.env` |
205
+ | **Type** | string |
206
+ | **Default** | `` |
207
+
208
+ Base URL of an OpenAI-compatible, image_url-capable endpoint used for aesthetic screenshot review (e.g. http://127.0.0.1:8080/v1). Defaults to the local model when set by setup.
209
+
210
+ **Recommendation:** Point at your local vision-capable model so aesthetic review runs offline with no per-image cost.
211
+
212
+ ### `UAP_VISION_MODEL`
213
+
214
+ | | |
215
+ |---|---|
216
+ | **Where** | `.uap/proxy.env` |
217
+ | **Type** | string |
218
+ | **Default** | `` |
219
+
220
+ Model id sent to the vision endpoint for aesthetic review (e.g. qwen36-35b-a3b-iq4xs).
221
+
222
+ **Recommendation:** Set by `uap setup` to your local vision model. Required for blocking vision review under `max` fidelity.
223
+
115
224
  ## Model routing
116
225
 
117
226
  <a id="routing"></a>Which model handles planning, execution, and review.
@@ -482,6 +591,58 @@ Hard-blocks UI edits that hardcode off-token colors or off-scale spacing.
482
591
 
483
592
  **Recommendation:** Enable once your DESIGN.md tokens are stable — it keeps the UI on-system automatically.
484
593
 
594
+ ## Engineering principles
595
+
596
+ <a id="principles"></a>How code should be written, and the backward-compatibility stance.
597
+
598
+ ### `principles.enabled`
599
+
600
+ | | |
601
+ |---|---|
602
+ | **Where** | `.uap.json` |
603
+ | **Type** | boolean |
604
+ | **Default** | `true` |
605
+
606
+ Applies the engineering principles (simplest sufficient implementation, reuse over reinvention, no stopgaps, prior art first) to generated code.
607
+
608
+ **Recommendation:** Leave on. Turn it off only if your project has its own conflicting house style.
609
+
610
+ ### `principles.compat`
611
+
612
+ | | |
613
+ |---|---|
614
+ | **Where** | `.uap.json` |
615
+ | **Type** | enum (ask \| preserve \| remove) |
616
+ | **Default** | `ask` |
617
+
618
+ Backward-compatibility stance. `remove` deletes obsolete paths outright; `preserve` keeps them working and migrates callers; `ask` (default) prompts once per session instead of guessing.
619
+
620
+ **Recommendation:** Leave on `ask` unless the project's answer is settled. `remove` is for side projects — on anything published it tells the agent to delete migration paths.
621
+
622
+ ### `principles.maturity`
623
+
624
+ | | |
625
+ |---|---|
626
+ | **Where** | `.uap.json` |
627
+ | **Type** | enum (ask \| greenfield \| production) |
628
+ | **Default** | `ask` |
629
+
630
+ What breaking a caller costs. `production` adds caveats about existing callers and dependency cost; `greenfield` states the rules absolutely.
631
+
632
+ **Recommendation:** Set `production` for anything with real users; `greenfield` for a fresh side project.
633
+
634
+ ### `principles.injectDeliver`
635
+
636
+ | | |
637
+ |---|---|
638
+ | **Where** | `.uap.json` |
639
+ | **Type** | boolean |
640
+ | **Default** | `true` |
641
+
642
+ Injects the compact principles block into deliver prompts, so generated code follows them rather than only the agent preamble.
643
+
644
+ **Recommendation:** Leave on. Disable only if you are tight on prompt budget with a small-context model.
645
+
485
646
  ## Worktree workflow
486
647
 
487
648
  <a id="worktree"></a>Branch-per-feature isolation and auto-cleanup.
@@ -651,6 +812,66 @@ Dashboard data refresh interval in milliseconds (floor 250).
651
812
 
652
813
  **Recommendation:** 2000 is fine; lower for a more live feel at higher CPU cost.
653
814
 
815
+ ### `proxy.dashboard`
816
+
817
+ | | |
818
+ |---|---|
819
+ | **Where** | `.uap.json` |
820
+ | **Type** | boolean |
821
+ | **Default** | `true` |
822
+
823
+ Ride-along dashboard: `uap proxy ensure|start` also starts (or adopts) `uap dashboard serve`, and release/stop tears it down under the same ownership rules — so a session gets monitoring without running a second command. Only takes effect where the proxy itself runs (hooks gate on proxy.autostart).
824
+
825
+ **Recommendation:** Leave on. Turn off if you prefer to run `uap dash serve` yourself, or the port is spoken for.
826
+
827
+ ### `UAP_PROXY_DASHBOARD`
828
+
829
+ | | |
830
+ |---|---|
831
+ | **Where** | shell env |
832
+ | **Type** | boolean |
833
+ | **Default** | `true` |
834
+
835
+ Force the ride-along dashboard on (1/on/true) or off (0/off/false). Wins over .uap.json proxy.dashboard.
836
+
837
+ **Recommendation:** Use for one-off overrides; prefer `uap proxy dashboard on|off` for a durable project setting.
838
+
839
+ ### `UAP_DASH_PORT`
840
+
841
+ | | |
842
+ |---|---|
843
+ | **Where** | shell env |
844
+ | **Type** | number |
845
+ | **Default** | `3847` |
846
+
847
+ Port the ride-along dashboard binds and is probed on (1-65535). Does not change `uap dash serve --port`.
848
+
849
+ **Recommendation:** Change only on a port clash; keep it consistent across sessions so adoption works.
850
+
851
+ ### `UAP_DASH_HOST`
852
+
853
+ | | |
854
+ |---|---|
855
+ | **Where** | shell env |
856
+ | **Type** | string |
857
+ | **Default** | `localhost` |
858
+
859
+ Interface the ride-along dashboard binds. `0.0.0.0` exposes it beyond this machine.
860
+
861
+ **Recommendation:** Keep localhost. Bind wider only behind a trusted network, and set UAP_DASHBOARD_TOKEN when you do.
862
+
863
+ ### `UAP_DASH_HEALTH_WAIT_MS`
864
+
865
+ | | |
866
+ |---|---|
867
+ | **Where** | shell env |
868
+ | **Type** | number |
869
+ | **Default** | `10000` |
870
+
871
+ How long `uap proxy ensure` waits for the ride-along dashboard to serve before giving up.
872
+
873
+ **Recommendation:** Lower it if session start feels slow on a cold dashboard; the proxy is unaffected either way.
874
+
654
875
  ## Token & time optimization
655
876
 
656
877
  <a id="optimization"></a>Context budgets, caching, batching, and parallelism.
@@ -66,6 +66,7 @@ platform's lifecycle events:
66
66
  | `pre-tool-use-bash.sh` | PreToolUse (Bash) | Dangerous-command guard — **blocks** force push, `terraform apply`, etc. |
67
67
  | `uap-policy-gate.sh` | PreToolUse | DB-driven policy gate (`policies.db` + `.policy-tools/*.py`). |
68
68
  | `post-tool-use-edit-write.sh` | PostToolUse (Edit/Write) | Build gate + backup reminder after edits. |
69
+ | `post-tool-use-read.sh` | PostToolUse (Read/Grep/Glob) | Writes `.uap/read_log.state` — the evidence `codebase-read-before-plan` gates on. |
69
70
  | `pre-compact.sh` | PreCompact | Flushes a compaction marker to memory. |
70
71
  | `post-compact.sh` | PostCompact | Re-injects policy awareness after compaction. |
71
72
  | `stop.sh` | Stop | Completion-gate checklist + session cleanup. |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@miller-tech/uap",
3
- "version": "1.183.2",
3
+ "version": "1.184.0",
4
4
  "description": "Autonomous AI agent memory system with CLAUDE.md protocol enforcement",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -0,0 +1,93 @@
1
+ # engineering-principles
2
+
3
+ **Category**: quality
4
+ **Level**: RECOMMENDED
5
+ **Enforcement Stage**: pre-exec
6
+ **Tags**: quality, simplicity, architecture, dependencies, prior-art
7
+
8
+ ## Rule
9
+
10
+ Write code to these principles. Rules 2-8 always apply. Rule 1 depends on a
11
+ stance resolved per project per session — run `uap principles show` to see the
12
+ form in force, and `uap principles ask` to answer it.
13
+
14
+ 1. **Backward compatibility — stance-dependent.**
15
+ - `remove`: delete obsolete paths instead of adding compatibility layers,
16
+ fallbacks, or migrations.
17
+ - `preserve`: keep existing paths working and migrate callers before
18
+ removing anything.
19
+
20
+ Under `remove`, these surfaces are still preserved and migrated, never
21
+ deleted, because their callers are not yours to update:
22
+ - the public CLI surface — command names, flags, and their output contracts
23
+ - MCP tool names and input schemas
24
+ - database, config, and on-disk state schemas, including their migrations
25
+ - exported types and public module entry points
26
+
27
+ 2. **Choose the simplest implementation that fully meets the current
28
+ requirements.** Avoid speculative abstractions, configuration, and
29
+ indirection.
30
+
31
+ 3. **Grow the system in layers.** Start from the smallest version that works end
32
+ to end, and add each new capability on top of a product that already works.
33
+ Never trade a working product for unfinished complexity.
34
+
35
+ 4. **Keep components modular and concerns clearly separated.**
36
+
37
+ 5. **Prefer established, well-maintained libraries** when they reduce overall
38
+ complexity or improve reliability. Do not reimplement common functionality
39
+ without a clear reason.
40
+
41
+ 6. **Lean on the dependencies already in the project** before writing your own
42
+ implementation or adding packages. Do not assume a library lacks a capability
43
+ without checking its documentation and types.
44
+
45
+ 7. **Make architectural decisions for the long term.** Do not accept a stopgap
46
+ that only works for now and is meant to be replaced later.
47
+
48
+ 8. **Study how established products solve the problem before designing a
49
+ solution.** Adopt their proven patterns and conventions rather than inventing
50
+ an approach from scratch.
51
+
52
+ ## Why
53
+
54
+ Adapted from an AGENTS.md distilled from roughly 60B tokens of agent-driven
55
+ coding (x.com/MarcosHernanz/status/2083954734487212511). Its author scopes it to
56
+ side projects — "don't use it in production if you don't want to destroy your
57
+ codebase" — and rule 1 is why: deleting obsolete paths is right when you own
58
+ every caller and destructive when you do not. Rather than adopt or drop rule 1
59
+ wholesale, UAP resolves it per project per session and carves out the surfaces
60
+ other people are bound to.
61
+
62
+ The remaining rules are direction-of-travel guidance, not gates. They are
63
+ RECOMMENDED because a machine cannot tell a speculative abstraction from a
64
+ necessary one, and a blocking check that guesses would cost more than it saves.
65
+
66
+ ## Enforcement
67
+
68
+ Advisory. There is no Python enforcer: these are judgment calls, and blocking
69
+ gates in this repo have repeatedly cost more in false positives than they
70
+ prevented. The principles reach the model three other ways:
71
+
72
+ - **Deliver prompts** — the compact form is injected into every convergence-loop
73
+ and orchestrated task prompt, so generated code is held to them rather than
74
+ only the agent's preamble.
75
+ - **Reactor** — while the rule-1 stance is unresolved, the agent is told to ask
76
+ the user once; after that it stays silent.
77
+ - **Judge** — competing candidates are scored on reuse-over-reimplementation and
78
+ absence of stopgaps.
79
+
80
+ Related: pattern P38 (Prior Art First) covers rules 5, 6 and 8 at plan time; the
81
+ `/simplify` and `distill` skills cover rule 2 on existing code.
82
+
83
+ ```rules
84
+ - title: "Simplest implementation that meets the requirement"
85
+ keywords: [implement, refactor, design, abstraction, config, indirection]
86
+ antiPatterns: [speculative-abstraction, premature-config, needless-indirection]
87
+ - title: "Reuse before reimplementation"
88
+ keywords: [library, dependency, package, util, helper, from scratch]
89
+ antiPatterns: [reimplemented-common-functionality, unchecked-library-capability]
90
+ - title: "No stopgaps"
91
+ keywords: [temporary, for now, placeholder, replace later, quick fix]
92
+ antiPatterns: [stopgap-architecture, deferred-rewrite]
93
+ ```
@@ -0,0 +1,72 @@
1
+ #!/usr/bin/env bash
2
+ # UAP Read Logger — INFORMATIONAL hook
3
+ # Event: PostToolUse (matcher: Read|Grep|Glob)
4
+ # Appends "<epoch>\t<path>" to .uap/read_log.state, which is the evidence the
5
+ # codebase-read-before-plan enforcer checks before allowing a plan to be emitted.
6
+ #
7
+ # Without this hook that enforcer has no writer: the log goes stale, every entry
8
+ # ages past its 30-minute window, and ExitPlanMode is blocked permanently with a
9
+ # remedy ("read the codebase first") that can never be satisfied.
10
+ #
11
+ # Runs on EVERY Read/Grep/Glob — thousands of times a session, in the latency
12
+ # path of each one — so it does its work in bash builtins and forks exactly once
13
+ # (jq, which earns it: the payload shape differs per tool).
14
+ #
15
+ # Always exits 0 (never blocks).
16
+ set -uo pipefail
17
+
18
+ # Drain stdin without forking `cat`. `read -d ''` returns non-zero at EOF while
19
+ # still populating INPUT, so the failure is expected, not an error.
20
+ IFS= read -r -d '' INPUT || true
21
+ [ -n "${INPUT:-}" ] || exit 0
22
+
23
+ # Read passes file_path; Grep/Glob pass path (search root) and/or pattern.
24
+ TARGET=$(jq -r '
25
+ .tool_input.file_path // .tool_input.filePath // .tool_input.path // .tool_input.pattern // empty
26
+ ' 2>/dev/null <<< "$INPUT" || true)
27
+ [ -n "$TARGET" ] || exit 0
28
+
29
+ PROJECT_DIR="${CLAUDE_PROJECT_DIR:-.}"
30
+ STATE_DIR="${UAP_STATE_DIR:-${PROJECT_DIR}/.uap}"
31
+ LOG="${STATE_DIR}/read_log.state"
32
+
33
+ [ -d "$STATE_DIR" ] || mkdir -p "$STATE_DIR" 2>/dev/null || exit 0
34
+
35
+ # SECURITY: never append through a symlink. `>>` follows links, so a symlink
36
+ # planted at read_log.state would turn every file read in the session into an
37
+ # append to whatever it points at (~/.bashrc), with partly model-supplied
38
+ # content — an arbitrary-file-write primitive that escapes workdir-scope
39
+ # entirely. Writing the evidence is optional; this is not.
40
+ [ -L "$LOG" ] && exit 0
41
+
42
+ # Store project-relative where possible so entries stay readable across worktrees.
43
+ REL="${TARGET#"$PROJECT_DIR"/}"
44
+ # SECURITY: a record is "<epoch>\t<path>\n" and TARGET is model-supplied — a Grep
45
+ # pattern may legally contain tabs and newlines. Strip the two characters that
46
+ # frame a record so a crafted pattern cannot forge additional log entries (and
47
+ # thereby forge the gate evidence this file exists to provide).
48
+ REL="${REL//$'\t'/ }"
49
+ REL="${REL//$'\n'/ }"
50
+ REL="${REL//$'\r'/ }"
51
+
52
+ printf -v NOW '%(%s)T' -1 # bash builtin; no `date` fork
53
+ printf '%s\t%s\n' "$NOW" "$REL" >> "$LOG" 2>/dev/null || exit 0
54
+
55
+ # Bound the file. The enforcer only ever looks at a 30-minute window, so old
56
+ # lines are dead weight. Sampled rather than checked every call: reading the log
57
+ # on every Read to decide whether to trim costs more than the occasional
58
+ # overshoot, and the consumer filters by timestamp anyway.
59
+ if (( RANDOM % 50 == 0 )); then
60
+ mapfile -t _lines < "$LOG" 2>/dev/null || exit 0
61
+ if (( ${#_lines[@]} > 400 )); then
62
+ # PID-unique temp: several agents share one project, and a shared temp name
63
+ # lets two concurrent trims interleave into one file and lose entries.
64
+ if printf '%s\n' "${_lines[@]: -200}" > "${LOG}.tmp.$$" 2>/dev/null; then
65
+ mv "${LOG}.tmp.$$" "$LOG" 2>/dev/null || rm -f "${LOG}.tmp.$$" 2>/dev/null
66
+ else
67
+ rm -f "${LOG}.tmp.$$" 2>/dev/null
68
+ fi
69
+ fi
70
+ fi
71
+
72
+ exit 0