@rubytech/create-maxy 1.0.880 → 1.0.883
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/package.json +1 -1
- package/payload/platform/lib/graph-write/dist/__tests__/action-provenance-gate.test.js +57 -9
- package/payload/platform/lib/graph-write/dist/__tests__/action-provenance-gate.test.js.map +1 -1
- package/payload/platform/lib/graph-write/dist/conversation-provenance.d.ts +26 -0
- package/payload/platform/lib/graph-write/dist/conversation-provenance.d.ts.map +1 -0
- package/payload/platform/lib/graph-write/dist/conversation-provenance.js +81 -0
- package/payload/platform/lib/graph-write/dist/conversation-provenance.js.map +1 -0
- package/payload/platform/lib/graph-write/dist/index.d.ts +38 -16
- package/payload/platform/lib/graph-write/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/graph-write/dist/index.js +75 -35
- package/payload/platform/lib/graph-write/dist/index.js.map +1 -1
- package/payload/platform/lib/graph-write/src/__tests__/action-provenance-gate.test.ts +59 -9
- package/payload/platform/lib/graph-write/src/conversation-provenance.ts +140 -0
- package/payload/platform/lib/graph-write/src/index.ts +76 -35
- package/payload/platform/plugins/admin/PLUGIN.md +1 -0
- package/payload/platform/plugins/admin/mcp/dist/__tests__/plugin-read-skill-resolution.test.js +26 -2
- package/payload/platform/plugins/admin/mcp/dist/__tests__/plugin-read-skill-resolution.test.js.map +1 -1
- package/payload/platform/plugins/admin/mcp/dist/index.js +8 -6
- package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/admin/mcp/dist/skill-resolution.d.ts +5 -1
- package/payload/platform/plugins/admin/mcp/dist/skill-resolution.d.ts.map +1 -1
- package/payload/platform/plugins/admin/mcp/dist/skill-resolution.js +24 -8
- package/payload/platform/plugins/admin/mcp/dist/skill-resolution.js.map +1 -1
- package/payload/platform/plugins/admin/skills/plainly/SKILL.md +105 -0
- package/payload/platform/plugins/admin/skills/plainly/references/worked-examples.md +301 -0
- package/payload/platform/plugins/cloudflare/PLUGIN.md +1 -1
- package/payload/platform/plugins/contacts/PLUGIN.md +8 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.d.ts.map +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.js +17 -2
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.js.map +1 -1
- package/payload/platform/plugins/docs/references/internals.md +2 -2
- package/payload/platform/plugins/docs/references/platform.md +5 -1
- package/payload/platform/plugins/docs/references/plugins-guide.md +3 -1
- package/payload/platform/plugins/memory/PLUGIN.md +5 -3
- package/payload/platform/plugins/memory/mcp/dist/index.js +2 -2
- package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js +18 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js.map +1 -1
- package/payload/platform/scripts/__tests__/logs-read-prefix.sh +341 -0
- package/payload/platform/scripts/logs-read.sh +108 -41
- package/payload/platform/scripts/logs-read.test.sh +6 -2
- package/payload/platform/templates/agents/admin/IDENTITY.md +6 -2
- package/payload/platform/templates/agents/public/IDENTITY.md +6 -0
- package/payload/platform/templates/specialists/agents/content-producer.md +6 -0
- package/payload/platform/templates/specialists/agents/database-operator.md +6 -0
- package/payload/platform/templates/specialists/agents/personal-assistant.md +6 -0
- package/payload/platform/templates/specialists/agents/project-manager.md +6 -0
- package/payload/platform/templates/specialists/agents/research-assistant.md +6 -0
- package/payload/premium-plugins/real-agency/BUNDLE.md +1 -1
- package/payload/server/chunk-ECAQVMRA.js +759 -0
- package/payload/server/chunk-K7S5T4VG.js +11534 -0
- package/payload/server/chunk-KMVUUVHM.js +11438 -0
- package/payload/server/chunk-LVC7NKZ2.js +689 -0
- package/payload/server/cloudflare-task-tracker-CY6QL6CY.js +22 -0
- package/payload/server/cloudflare-task-tracker-JNZXLW32.js +22 -0
- package/payload/server/maxy-edge.js +2 -1
- package/payload/server/public/assets/{admin-CCEuBnaK.js → admin-BN_z-2Bm.js} +2 -2
- package/payload/server/public/index.html +1 -1
- package/payload/server/server.js +115 -8
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Task 998 — bash harness for logs-read.sh prefix-match resolution.
|
|
3
|
+
#
|
|
4
|
+
# Covers the contract from the task brief:
|
|
5
|
+
# 1. 8-char prefix → resolves full-UUID file (the original bug)
|
|
6
|
+
# 2. Multiple matches → reason=ambiguous-prefix, exit 1, never picks
|
|
7
|
+
# 3. More-specific prefix disambiguates
|
|
8
|
+
# 4. Zero matches → reason=file-not-found-in-either-shape with glob patterns
|
|
9
|
+
# 5. Full 36-char UUID still resolves (regression boundary)
|
|
10
|
+
# 6. Preflush prefix match
|
|
11
|
+
# 7. Preflush ambiguity (Pass 1 zero, Pass 2 multiple)
|
|
12
|
+
# 8. Every per-conversation type (agent-stream, session, error, public)
|
|
13
|
+
# resolves under prefix-match
|
|
14
|
+
#
|
|
15
|
+
# Companion to platform/scripts/logs-read.test.sh (Task 671's two-shape
|
|
16
|
+
# resolution harness).
|
|
17
|
+
set -euo pipefail
|
|
18
|
+
|
|
19
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
20
|
+
LOGS_READ="$SCRIPT_DIR/../logs-read.sh"
|
|
21
|
+
if [[ ! -x "$LOGS_READ" ]]; then
|
|
22
|
+
echo "FATAL: $LOGS_READ not executable" >&2
|
|
23
|
+
exit 1
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
PASS=0
|
|
27
|
+
FAIL=0
|
|
28
|
+
|
|
29
|
+
# logs-read.sh resolves PLATFORM_ROOT from its own location; mirror the
|
|
30
|
+
# logs-read.test.sh convention by symlinking the script into an ephemeral
|
|
31
|
+
# install tree per case.
|
|
32
|
+
setup_install_tree() {
|
|
33
|
+
local root="$1"
|
|
34
|
+
mkdir -p "$root/platform/scripts"
|
|
35
|
+
mkdir -p "$root/data/accounts/acct-test/logs"
|
|
36
|
+
mkdir -p "$HOME/.$(basename "$root")/logs"
|
|
37
|
+
ln -sf "$LOGS_READ" "$root/platform/scripts/logs-read.sh"
|
|
38
|
+
echo "$root/platform/scripts/logs-read.sh"
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
cleanup_install_tree() {
|
|
42
|
+
local root="$1"
|
|
43
|
+
rm -rf "$root"
|
|
44
|
+
rm -rf "$HOME/.$(basename "$root")"
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
run_case() {
|
|
48
|
+
local name="$1"
|
|
49
|
+
shift
|
|
50
|
+
echo ""
|
|
51
|
+
echo "=== CASE: $name ==="
|
|
52
|
+
if "$@"; then
|
|
53
|
+
PASS=$((PASS + 1))
|
|
54
|
+
echo "PASS: $name"
|
|
55
|
+
else
|
|
56
|
+
FAIL=$((FAIL + 1))
|
|
57
|
+
echo "FAIL: $name"
|
|
58
|
+
fi
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
# --- Case 1: 8-char prefix resolves a full-UUID file (original bug) ---
|
|
62
|
+
case_prefix_resolves_full() {
|
|
63
|
+
local root="/tmp/maxy-logs-read-prefix-1-$$"
|
|
64
|
+
local script
|
|
65
|
+
script=$(setup_install_tree "$root")
|
|
66
|
+
local logdir="$root/data/accounts/acct-test/logs"
|
|
67
|
+
local full_uuid="3483269d-c793-4a07-98cc-556d936f2f4d"
|
|
68
|
+
local filename="claude-agent-stream-${full_uuid}.log"
|
|
69
|
+
echo "prefix-match-sentinel" > "$logdir/$filename"
|
|
70
|
+
|
|
71
|
+
local stdout stderr rc=0
|
|
72
|
+
stdout=$("$script" "3483269d" system 2>/tmp/logs-read-prefix-stderr-1-$$) || rc=$?
|
|
73
|
+
stderr=$(cat /tmp/logs-read-prefix-stderr-1-$$)
|
|
74
|
+
rm -f /tmp/logs-read-prefix-stderr-1-$$
|
|
75
|
+
|
|
76
|
+
cleanup_install_tree "$root"
|
|
77
|
+
|
|
78
|
+
if [[ $rc -ne 0 ]]; then echo " expected exit 0, got $rc"; return 1; fi
|
|
79
|
+
if [[ "$stdout" != *"prefix-match-sentinel"* ]]; then
|
|
80
|
+
echo " stdout missing sentinel: $stdout"; return 1
|
|
81
|
+
fi
|
|
82
|
+
if [[ "$stderr" != *"matched_shape=full"* ]]; then
|
|
83
|
+
echo " stderr missing matched_shape=full: $stderr"; return 1
|
|
84
|
+
fi
|
|
85
|
+
if [[ "$stdout" != *"$filename"* ]]; then
|
|
86
|
+
echo " header missing matched filename: $stdout"; return 1
|
|
87
|
+
fi
|
|
88
|
+
return 0
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
# --- Case 2: Multiple matches on Pass 1 → ambiguous-prefix, exit 1 ---
|
|
92
|
+
case_ambiguous_prefix() {
|
|
93
|
+
local root="/tmp/maxy-logs-read-prefix-2-$$"
|
|
94
|
+
local script
|
|
95
|
+
script=$(setup_install_tree "$root")
|
|
96
|
+
local logdir="$root/data/accounts/acct-test/logs"
|
|
97
|
+
echo "A" > "$logdir/claude-agent-stream-aaaaaaaa-1111-A.log"
|
|
98
|
+
echo "B" > "$logdir/claude-agent-stream-aaaaaaaa-2222-B.log"
|
|
99
|
+
|
|
100
|
+
local stdout stderr rc=0
|
|
101
|
+
stdout=$("$script" "aaaaaaaa" system 2>/tmp/logs-read-prefix-stderr-2-$$) || rc=$?
|
|
102
|
+
stderr=$(cat /tmp/logs-read-prefix-stderr-2-$$)
|
|
103
|
+
rm -f /tmp/logs-read-prefix-stderr-2-$$
|
|
104
|
+
|
|
105
|
+
cleanup_install_tree "$root"
|
|
106
|
+
|
|
107
|
+
if [[ $rc -ne 1 ]]; then echo " expected exit 1, got $rc"; return 1; fi
|
|
108
|
+
if [[ "$stderr" != *"reason=ambiguous-prefix"* ]]; then
|
|
109
|
+
echo " stderr missing reason=ambiguous-prefix: $stderr"; return 1
|
|
110
|
+
fi
|
|
111
|
+
if [[ "$stderr" != *"matches=2"* ]]; then
|
|
112
|
+
echo " stderr missing matches=2: $stderr"; return 1
|
|
113
|
+
fi
|
|
114
|
+
if [[ "$stderr" != *"claude-agent-stream-aaaaaaaa-1111-A.log"* ]]; then
|
|
115
|
+
echo " stderr missing candidate A: $stderr"; return 1
|
|
116
|
+
fi
|
|
117
|
+
if [[ "$stderr" != *"claude-agent-stream-aaaaaaaa-2222-B.log"* ]]; then
|
|
118
|
+
echo " stderr missing candidate B: $stderr"; return 1
|
|
119
|
+
fi
|
|
120
|
+
# Body must NOT contain either sentinel — refusal means no body emitted.
|
|
121
|
+
if [[ -n "$stdout" ]]; then
|
|
122
|
+
echo " stdout non-empty on ambiguous refusal: $stdout"; return 1
|
|
123
|
+
fi
|
|
124
|
+
return 0
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
# --- Case 3: more-specific prefix disambiguates ---
|
|
128
|
+
case_more_specific_disambiguates() {
|
|
129
|
+
local root="/tmp/maxy-logs-read-prefix-3-$$"
|
|
130
|
+
local script
|
|
131
|
+
script=$(setup_install_tree "$root")
|
|
132
|
+
local logdir="$root/data/accounts/acct-test/logs"
|
|
133
|
+
echo "ONE" > "$logdir/claude-agent-stream-aaaaaaaa-1111-A.log"
|
|
134
|
+
echo "TWO" > "$logdir/claude-agent-stream-aaaaaaaa-2222-B.log"
|
|
135
|
+
|
|
136
|
+
local stdout stderr rc=0
|
|
137
|
+
stdout=$("$script" "aaaaaaaa-1111" system 2>/tmp/logs-read-prefix-stderr-3-$$) || rc=$?
|
|
138
|
+
stderr=$(cat /tmp/logs-read-prefix-stderr-3-$$)
|
|
139
|
+
rm -f /tmp/logs-read-prefix-stderr-3-$$
|
|
140
|
+
|
|
141
|
+
cleanup_install_tree "$root"
|
|
142
|
+
|
|
143
|
+
if [[ $rc -ne 0 ]]; then echo " expected exit 0, got $rc"; return 1; fi
|
|
144
|
+
if [[ "$stdout" != *"ONE"* ]]; then echo " stdout missing ONE: $stdout"; return 1; fi
|
|
145
|
+
if [[ "$stdout" == *"TWO"* ]]; then echo " stdout contained TWO: $stdout"; return 1; fi
|
|
146
|
+
if [[ "$stderr" != *"matched_shape=full"* ]]; then
|
|
147
|
+
echo " stderr missing matched_shape=full: $stderr"; return 1
|
|
148
|
+
fi
|
|
149
|
+
return 0
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
# --- Case 4: zero matches → tried=[<glob>, <glob>] file-not-found ---
|
|
153
|
+
case_zero_matches() {
|
|
154
|
+
local root="/tmp/maxy-logs-read-prefix-4-$$"
|
|
155
|
+
local script
|
|
156
|
+
script=$(setup_install_tree "$root")
|
|
157
|
+
|
|
158
|
+
local stdout stderr rc=0
|
|
159
|
+
stdout=$("$script" "xxxxxxxx" system 2>/tmp/logs-read-prefix-stderr-4-$$) || rc=$?
|
|
160
|
+
stderr=$(cat /tmp/logs-read-prefix-stderr-4-$$)
|
|
161
|
+
rm -f /tmp/logs-read-prefix-stderr-4-$$
|
|
162
|
+
|
|
163
|
+
cleanup_install_tree "$root"
|
|
164
|
+
|
|
165
|
+
if [[ $rc -ne 1 ]]; then echo " expected exit 1, got $rc"; return 1; fi
|
|
166
|
+
if [[ "$stderr" != *"reason=file-not-found-in-either-shape"* ]]; then
|
|
167
|
+
echo " stderr missing reason=file-not-found-in-either-shape: $stderr"; return 1
|
|
168
|
+
fi
|
|
169
|
+
if [[ "$stderr" != *"tried=[claude-agent-stream-xxxxxxxx*.log, claude-agent-stream-preflush-xxxxxxxx*.log]"* ]]; then
|
|
170
|
+
echo " stderr missing tried=[<glob>, <glob>]: $stderr"; return 1
|
|
171
|
+
fi
|
|
172
|
+
return 0
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
# --- Case 5: full 36-char UUID still resolves (regression boundary) ---
|
|
176
|
+
case_full_uuid_regression() {
|
|
177
|
+
local root="/tmp/maxy-logs-read-prefix-5-$$"
|
|
178
|
+
local script
|
|
179
|
+
script=$(setup_install_tree "$root")
|
|
180
|
+
local logdir="$root/data/accounts/acct-test/logs"
|
|
181
|
+
local full_uuid="3483269d-c793-4a07-98cc-556d936f2f4d"
|
|
182
|
+
local filename="claude-agent-stream-${full_uuid}.log"
|
|
183
|
+
echo "regression-sentinel" > "$logdir/$filename"
|
|
184
|
+
|
|
185
|
+
local stdout stderr rc=0
|
|
186
|
+
stdout=$("$script" "$full_uuid" system 2>/tmp/logs-read-prefix-stderr-5-$$) || rc=$?
|
|
187
|
+
stderr=$(cat /tmp/logs-read-prefix-stderr-5-$$)
|
|
188
|
+
rm -f /tmp/logs-read-prefix-stderr-5-$$
|
|
189
|
+
|
|
190
|
+
cleanup_install_tree "$root"
|
|
191
|
+
|
|
192
|
+
if [[ $rc -ne 0 ]]; then echo " expected exit 0, got $rc"; return 1; fi
|
|
193
|
+
if [[ "$stdout" != *"regression-sentinel"* ]]; then
|
|
194
|
+
echo " stdout missing sentinel: $stdout"; return 1
|
|
195
|
+
fi
|
|
196
|
+
if [[ "$stderr" != *"matched_shape=full"* ]]; then
|
|
197
|
+
echo " stderr missing matched_shape=full: $stderr"; return 1
|
|
198
|
+
fi
|
|
199
|
+
return 0
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
# --- Case 6: preflush-only file, prefix-match (Pass 2) ---
|
|
203
|
+
case_preflush_prefix_match() {
|
|
204
|
+
local root="/tmp/maxy-logs-read-prefix-6-$$"
|
|
205
|
+
local script
|
|
206
|
+
script=$(setup_install_tree "$root")
|
|
207
|
+
local logdir="$root/data/accounts/acct-test/logs"
|
|
208
|
+
# Preflush filename uses sessionKey:0:12; mimic with a 12-char slice.
|
|
209
|
+
echo "preflush-prefix-sentinel" > "$logdir/claude-agent-stream-preflush-bbbbbbbb-ccc.log"
|
|
210
|
+
|
|
211
|
+
local stdout stderr rc=0
|
|
212
|
+
stdout=$("$script" "bbbbbbbb" system 2>/tmp/logs-read-prefix-stderr-6-$$) || rc=$?
|
|
213
|
+
stderr=$(cat /tmp/logs-read-prefix-stderr-6-$$)
|
|
214
|
+
rm -f /tmp/logs-read-prefix-stderr-6-$$
|
|
215
|
+
|
|
216
|
+
cleanup_install_tree "$root"
|
|
217
|
+
|
|
218
|
+
if [[ $rc -ne 0 ]]; then echo " expected exit 0, got $rc"; return 1; fi
|
|
219
|
+
if [[ "$stdout" != *"preflush-prefix-sentinel"* ]]; then
|
|
220
|
+
echo " stdout missing sentinel: $stdout"; return 1
|
|
221
|
+
fi
|
|
222
|
+
if [[ "$stderr" != *"matched_shape=preflush"* ]]; then
|
|
223
|
+
echo " stderr missing matched_shape=preflush: $stderr"; return 1
|
|
224
|
+
fi
|
|
225
|
+
return 0
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
# --- Case 7: preflush ambiguity (Pass 1 zero, Pass 2 multiple) ---
|
|
229
|
+
case_preflush_ambiguity() {
|
|
230
|
+
local root="/tmp/maxy-logs-read-prefix-7-$$"
|
|
231
|
+
local script
|
|
232
|
+
script=$(setup_install_tree "$root")
|
|
233
|
+
local logdir="$root/data/accounts/acct-test/logs"
|
|
234
|
+
echo "P1" > "$logdir/claude-agent-stream-preflush-cccccccc-111.log"
|
|
235
|
+
echo "P2" > "$logdir/claude-agent-stream-preflush-cccccccc-222.log"
|
|
236
|
+
|
|
237
|
+
local stdout stderr rc=0
|
|
238
|
+
stdout=$("$script" "cccccccc" system 2>/tmp/logs-read-prefix-stderr-7-$$) || rc=$?
|
|
239
|
+
stderr=$(cat /tmp/logs-read-prefix-stderr-7-$$)
|
|
240
|
+
rm -f /tmp/logs-read-prefix-stderr-7-$$
|
|
241
|
+
|
|
242
|
+
cleanup_install_tree "$root"
|
|
243
|
+
|
|
244
|
+
if [[ $rc -ne 1 ]]; then echo " expected exit 1, got $rc"; return 1; fi
|
|
245
|
+
if [[ "$stderr" != *"reason=ambiguous-prefix"* ]]; then
|
|
246
|
+
echo " stderr missing reason=ambiguous-prefix: $stderr"; return 1
|
|
247
|
+
fi
|
|
248
|
+
if [[ "$stderr" != *"matches=2"* ]]; then
|
|
249
|
+
echo " stderr missing matches=2: $stderr"; return 1
|
|
250
|
+
fi
|
|
251
|
+
if [[ "$stderr" != *"claude-agent-stream-preflush-cccccccc-111.log"* ]]; then
|
|
252
|
+
echo " stderr missing preflush candidate 1: $stderr"; return 1
|
|
253
|
+
fi
|
|
254
|
+
if [[ "$stderr" != *"claude-agent-stream-preflush-cccccccc-222.log"* ]]; then
|
|
255
|
+
echo " stderr missing preflush candidate 2: $stderr"; return 1
|
|
256
|
+
fi
|
|
257
|
+
return 0
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
# --- Case 8: every per-conversation type resolves under prefix-match ---
|
|
261
|
+
# Parameterised over the four prefix_for_type values:
|
|
262
|
+
# agent-stream → claude-agent-stream-
|
|
263
|
+
# session → sse-events-
|
|
264
|
+
# error → claude-agent-stderr-
|
|
265
|
+
# public → public-agent-stream-
|
|
266
|
+
case_all_types_prefix_match() {
|
|
267
|
+
local root="/tmp/maxy-logs-read-prefix-8-$$"
|
|
268
|
+
local script
|
|
269
|
+
script=$(setup_install_tree "$root")
|
|
270
|
+
local logdir="$root/data/accounts/acct-test/logs"
|
|
271
|
+
local full_uuid="7d49ef21-1234-4567-89ab-cdef01234567"
|
|
272
|
+
|
|
273
|
+
echo "sentinel-agent-stream" > "$logdir/claude-agent-stream-${full_uuid}.log"
|
|
274
|
+
echo "sentinel-session" > "$logdir/sse-events-${full_uuid}.log"
|
|
275
|
+
echo "sentinel-error" > "$logdir/claude-agent-stderr-${full_uuid}.log"
|
|
276
|
+
echo "sentinel-public" > "$logdir/public-agent-stream-${full_uuid}.log"
|
|
277
|
+
|
|
278
|
+
local pairs=(
|
|
279
|
+
"agent-stream|sentinel-agent-stream"
|
|
280
|
+
"session|sentinel-session"
|
|
281
|
+
"error|sentinel-error"
|
|
282
|
+
"public|sentinel-public"
|
|
283
|
+
)
|
|
284
|
+
local fail=0
|
|
285
|
+
local pair t expected_sentinel
|
|
286
|
+
for pair in "${pairs[@]}"; do
|
|
287
|
+
t="${pair%%|*}"
|
|
288
|
+
expected_sentinel="${pair##*|}"
|
|
289
|
+
local stdout stderr rc=0
|
|
290
|
+
stdout=$("$script" "7d49ef21" "$t" 2>/tmp/logs-read-prefix-stderr-8-$$) || rc=$?
|
|
291
|
+
stderr=$(cat /tmp/logs-read-prefix-stderr-8-$$)
|
|
292
|
+
rm -f /tmp/logs-read-prefix-stderr-8-$$
|
|
293
|
+
if [[ $rc -ne 0 ]]; then echo " [$t] expected exit 0, got $rc — stderr: $stderr"; fail=1; continue; fi
|
|
294
|
+
if [[ "$stdout" != *"$expected_sentinel"* ]]; then
|
|
295
|
+
echo " [$t] missing sentinel '$expected_sentinel': $stdout"; fail=1
|
|
296
|
+
fi
|
|
297
|
+
if [[ "$stderr" != *"matched_shape=full"* ]]; then
|
|
298
|
+
echo " [$t] stderr missing matched_shape=full: $stderr"; fail=1
|
|
299
|
+
fi
|
|
300
|
+
done
|
|
301
|
+
|
|
302
|
+
cleanup_install_tree "$root"
|
|
303
|
+
[[ $fail -eq 0 ]]
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
# --- Case 9: conv_id with shell metacharacters is rejected ---
|
|
307
|
+
case_metacharacter_rejected() {
|
|
308
|
+
local root="/tmp/maxy-logs-read-prefix-9-$$"
|
|
309
|
+
local script
|
|
310
|
+
script=$(setup_install_tree "$root")
|
|
311
|
+
|
|
312
|
+
local stdout stderr rc=0
|
|
313
|
+
# Use a backslash-escaped wildcard to feed it as a literal argument.
|
|
314
|
+
stdout=$("$script" "ab*cd" system 2>/tmp/logs-read-prefix-stderr-9-$$) || rc=$?
|
|
315
|
+
stderr=$(cat /tmp/logs-read-prefix-stderr-9-$$)
|
|
316
|
+
rm -f /tmp/logs-read-prefix-stderr-9-$$
|
|
317
|
+
|
|
318
|
+
cleanup_install_tree "$root"
|
|
319
|
+
|
|
320
|
+
if [[ $rc -ne 2 ]]; then echo " expected exit 2 (usage), got $rc"; return 1; fi
|
|
321
|
+
if [[ "$stderr" != *"invalid characters"* ]]; then
|
|
322
|
+
echo " stderr missing invalid-characters guard: $stderr"; return 1
|
|
323
|
+
fi
|
|
324
|
+
return 0
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
run_case "8-char prefix resolves full-UUID file" case_prefix_resolves_full
|
|
328
|
+
run_case "ambiguous prefix → refused, candidates listed" case_ambiguous_prefix
|
|
329
|
+
run_case "more-specific prefix disambiguates" case_more_specific_disambiguates
|
|
330
|
+
run_case "zero matches → file-not-found-in-either-shape" case_zero_matches
|
|
331
|
+
run_case "full 36-char UUID still resolves" case_full_uuid_regression
|
|
332
|
+
run_case "preflush file resolves by prefix" case_preflush_prefix_match
|
|
333
|
+
run_case "preflush ambiguity refused" case_preflush_ambiguity
|
|
334
|
+
run_case "every per-conversation type prefix-matches" case_all_types_prefix_match
|
|
335
|
+
run_case "shell metacharacters rejected" case_metacharacter_rejected
|
|
336
|
+
|
|
337
|
+
echo ""
|
|
338
|
+
echo "================================================"
|
|
339
|
+
echo " Passed: $PASS / Failed: $FAIL"
|
|
340
|
+
echo "================================================"
|
|
341
|
+
[[ $FAIL -eq 0 ]]
|
|
@@ -144,25 +144,42 @@ validate_type() {
|
|
|
144
144
|
esac
|
|
145
145
|
}
|
|
146
146
|
|
|
147
|
-
# --- Per-conversation mode:
|
|
147
|
+
# --- Per-conversation mode: prefix-match {prefix}{convId-prefix}*.log ---
|
|
148
148
|
#
|
|
149
|
-
# Task 671
|
|
149
|
+
# Task 671 — two filename shapes the writer emits:
|
|
150
150
|
# FULL: {prefix}{conversationId}.log (post 1→2-user-turn flush)
|
|
151
151
|
# PREFLUSH: {prefix}preflush-{id:0:12}.log (pre-flush, first turn; the
|
|
152
152
|
# slice is of the sessionKey)
|
|
153
|
-
# This mode tries FULL across every account dir first; if zero FULL hits,
|
|
154
|
-
# falls back to PREFLUSH across every dir. If BOTH exist in the same dir,
|
|
155
|
-
# FULL wins and the preflush sibling is logged to server.log as stale
|
|
156
|
-
# (promotion-race housekeeping signal).
|
|
157
153
|
#
|
|
158
|
-
#
|
|
154
|
+
# Task 998 — operator surface accepts any unambiguous prefix of the id.
|
|
155
|
+
# The writer's filename is canonical (full 36-char UUID) but every agent
|
|
156
|
+
# line surfaces the 8-char id (`conversationId=3483269d…`). Pass 1 and
|
|
157
|
+
# Pass 2 each glob `${prefix}${conv_id}*.log` so an 8-char operator input
|
|
158
|
+
# resolves a full-UUID file. Three trailer outcomes:
|
|
159
|
+
# single hit → exit 0 + matched_shape=full|preflush (existing success)
|
|
160
|
+
# zero hits → exit 1 + reason=file-not-found-in-either-shape (existing miss)
|
|
161
|
+
# 2+ hits → exit 1 + reason=ambiguous-prefix candidates=[...]; never picks
|
|
162
|
+
#
|
|
163
|
+
# Stale-preflush housekeeping (Task 671): on a single FULL hit, glob the
|
|
164
|
+
# preflush pattern in the same dir and emit `[logs-read] stale-preflush-detected`
|
|
165
|
+
# to server.log for each sibling. Best-effort — retrieval doesn't fail if
|
|
166
|
+
# server.log is unwritable.
|
|
167
|
+
#
|
|
168
|
+
# Identity note: the preflush fallback exists for the abrupt-exit case where
|
|
159
169
|
# the operator passes the sessionKey (no conversationId was ever assigned).
|
|
160
|
-
# For post-flush retrievals the operator passes the conversationId
|
|
161
|
-
# FULL file is expected to exist.
|
|
170
|
+
# For post-flush retrievals the operator passes the conversationId (or any
|
|
171
|
+
# unambiguous prefix of it) and the FULL file is expected to exist.
|
|
162
172
|
#
|
|
163
|
-
# MIRROR
|
|
164
|
-
# - platform/ui/app/lib/logs-read-resolve.ts (canonical TS helper
|
|
165
|
-
#
|
|
173
|
+
# MIRROR — intentional divergence (Task 998):
|
|
174
|
+
# - platform/ui/app/lib/logs-read-resolve.ts (canonical TS helper) —
|
|
175
|
+
# EXACT-MATCH on `${prefix}${conversationId}.log`.
|
|
176
|
+
# - platform/plugins/admin/mcp/src/index.ts (conversationId branch) —
|
|
177
|
+
# EXACT-MATCH, same shape.
|
|
178
|
+
# Both serve agents that always hold the full conversationId. This shell
|
|
179
|
+
# counterpart is the operator surface; operators read 8-char ids from agent
|
|
180
|
+
# lines and type them at the SSH prompt. Do not "fix" the divergence by
|
|
181
|
+
# adding prefix-match to TS/MCP — it would mask agent bugs that pass
|
|
182
|
+
# partial ids.
|
|
166
183
|
per_conversation_mode() {
|
|
167
184
|
local conv_id="$1"
|
|
168
185
|
local filter_type="${2:-agent-stream}"
|
|
@@ -172,6 +189,14 @@ per_conversation_mode() {
|
|
|
172
189
|
exit 2
|
|
173
190
|
fi
|
|
174
191
|
|
|
192
|
+
# Reject shell metacharacters in conv_id — the prefix-match glob below
|
|
193
|
+
# would otherwise turn user input into a shell pattern. UUIDs and
|
|
194
|
+
# sessionKeys only contain [a-zA-Z0-9-].
|
|
195
|
+
if [[ ! "$conv_id" =~ ^[a-zA-Z0-9-]+$ ]]; then
|
|
196
|
+
echo "Error: conversationId contains invalid characters (allowed: a-z, A-Z, 0-9, -)" >&2
|
|
197
|
+
exit 2
|
|
198
|
+
fi
|
|
199
|
+
|
|
175
200
|
validate_type "$filter_type"
|
|
176
201
|
|
|
177
202
|
# Platform-scoped types shortcut to their fixed files regardless of convId.
|
|
@@ -190,56 +215,98 @@ per_conversation_mode() {
|
|
|
190
215
|
exit 2
|
|
191
216
|
fi
|
|
192
217
|
|
|
193
|
-
#
|
|
194
|
-
local
|
|
195
|
-
local
|
|
218
|
+
# Glob patterns the two-shape contract expects (Task 998: prefix match).
|
|
219
|
+
local full_glob="${prefix}${conv_id}*.log"
|
|
220
|
+
local preflush_glob="${prefix}preflush-${conv_id:0:12}*.log"
|
|
196
221
|
|
|
197
|
-
local found=0
|
|
198
222
|
local searched=0
|
|
199
223
|
local matched_shape=""
|
|
200
224
|
|
|
201
|
-
# Pass 1: FULL across every log dir
|
|
225
|
+
# Pass 1: FULL prefix glob across every log dir; aggregate hits.
|
|
226
|
+
local -a full_hits=()
|
|
227
|
+
local -a full_dirs=()
|
|
228
|
+
shopt -s nullglob
|
|
202
229
|
for log_dir in "${ACCOUNT_LOG_DIRS[@]}"; do
|
|
203
|
-
local full_path="$log_dir/$full_name"
|
|
204
230
|
searched=$((searched + 1))
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
231
|
+
for f in "$log_dir"/${prefix}${conv_id}*.log; do
|
|
232
|
+
full_hits+=("$f")
|
|
233
|
+
full_dirs+=("$log_dir")
|
|
234
|
+
done
|
|
235
|
+
done
|
|
236
|
+
shopt -u nullglob
|
|
237
|
+
|
|
238
|
+
# Pass 1 ambiguous → refuse, never silently pick.
|
|
239
|
+
if [[ ${#full_hits[@]} -gt 1 ]]; then
|
|
240
|
+
local -a candidate_names=()
|
|
241
|
+
local cf
|
|
242
|
+
for cf in "${full_hits[@]}"; do
|
|
243
|
+
candidate_names+=("$(basename "$cf")")
|
|
244
|
+
done
|
|
245
|
+
local joined
|
|
246
|
+
joined=$(IFS=','; printf '%s' "${candidate_names[*]}")
|
|
247
|
+
echo "-- trailer: conversationId=$conv_id type=$filter_type searched=$searched matches=${#full_hits[@]} reason=ambiguous-prefix candidates=[${joined}]" >&2
|
|
248
|
+
exit 1
|
|
249
|
+
fi
|
|
250
|
+
|
|
251
|
+
local found=0
|
|
252
|
+
|
|
253
|
+
# Pass 1 single hit → emit body, run stale-preflush detection in matched dir.
|
|
254
|
+
if [[ ${#full_hits[@]} -eq 1 ]]; then
|
|
255
|
+
local full_path="${full_hits[0]}"
|
|
256
|
+
local match_dir="${full_dirs[0]}"
|
|
257
|
+
echo "## $(basename "$full_path") ($filter_type)$(account_suffix "$match_dir")"
|
|
210
258
|
cat "$full_path"
|
|
211
|
-
found
|
|
259
|
+
found=1
|
|
212
260
|
matched_shape="full"
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
if [[ -f "$stale_path" ]]; then
|
|
261
|
+
|
|
262
|
+
shopt -s nullglob
|
|
263
|
+
local stale_path
|
|
264
|
+
for stale_path in "$match_dir"/${prefix}preflush-${conv_id:0:12}*.log; do
|
|
218
265
|
local stale_ts
|
|
219
266
|
stale_ts="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
220
267
|
echo "${stale_ts} [logs-read] stale-preflush-detected path=${stale_path}" >> "$SERVER_LOG" 2>/dev/null || true
|
|
221
|
-
|
|
222
|
-
|
|
268
|
+
done
|
|
269
|
+
shopt -u nullglob
|
|
270
|
+
fi
|
|
223
271
|
|
|
224
|
-
# Pass 2: PREFLUSH
|
|
272
|
+
# Pass 2: PREFLUSH prefix glob, only if Pass 1 had zero hits.
|
|
225
273
|
if [[ $found -eq 0 ]]; then
|
|
274
|
+
local -a preflush_hits=()
|
|
275
|
+
local -a preflush_dirs=()
|
|
276
|
+
shopt -s nullglob
|
|
226
277
|
for log_dir in "${ACCOUNT_LOG_DIRS[@]}"; do
|
|
227
|
-
local preflush_path="$log_dir/$preflush_name"
|
|
228
278
|
searched=$((searched + 1))
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
279
|
+
for f in "$log_dir"/${prefix}preflush-${conv_id:0:12}*.log; do
|
|
280
|
+
preflush_hits+=("$f")
|
|
281
|
+
preflush_dirs+=("$log_dir")
|
|
282
|
+
done
|
|
283
|
+
done
|
|
284
|
+
shopt -u nullglob
|
|
285
|
+
|
|
286
|
+
if [[ ${#preflush_hits[@]} -gt 1 ]]; then
|
|
287
|
+
local -a candidate_names=()
|
|
288
|
+
local pf
|
|
289
|
+
for pf in "${preflush_hits[@]}"; do
|
|
290
|
+
candidate_names+=("$(basename "$pf")")
|
|
291
|
+
done
|
|
292
|
+
local joined
|
|
293
|
+
joined=$(IFS=','; printf '%s' "${candidate_names[*]}")
|
|
294
|
+
echo "-- trailer: conversationId=$conv_id type=$filter_type searched=$searched matches=${#preflush_hits[@]} reason=ambiguous-prefix candidates=[${joined}]" >&2
|
|
295
|
+
exit 1
|
|
296
|
+
fi
|
|
297
|
+
|
|
298
|
+
if [[ ${#preflush_hits[@]} -eq 1 ]]; then
|
|
299
|
+
local preflush_path="${preflush_hits[0]}"
|
|
300
|
+
echo "## $(basename "$preflush_path") ($filter_type)$(account_suffix "${preflush_dirs[0]}")"
|
|
234
301
|
cat "$preflush_path"
|
|
235
|
-
found
|
|
302
|
+
found=1
|
|
236
303
|
matched_shape="preflush"
|
|
237
|
-
|
|
304
|
+
fi
|
|
238
305
|
fi
|
|
239
306
|
|
|
240
307
|
# Trailer: empty output never leaves the reader guessing why.
|
|
241
308
|
if [[ $found -eq 0 ]]; then
|
|
242
|
-
echo "-- trailer: conversationId=$conv_id type=$filter_type searched=$searched found=0 tried=[${
|
|
309
|
+
echo "-- trailer: conversationId=$conv_id type=$filter_type searched=$searched found=0 tried=[${full_glob}, ${preflush_glob}] reason=file-not-found-in-either-shape" >&2
|
|
243
310
|
exit 1
|
|
244
311
|
fi
|
|
245
312
|
echo "" >&2
|
|
@@ -22,6 +22,10 @@ CONV_ID="8c264cfb-441f-48bf-9811-9fa3ad3b51dc"
|
|
|
22
22
|
PREFLUSH_SLICE="8c264cfb-441"
|
|
23
23
|
FULL_NAME="claude-agent-stream-${CONV_ID}.log"
|
|
24
24
|
PREFLUSH_NAME="claude-agent-stream-preflush-${PREFLUSH_SLICE}.log"
|
|
25
|
+
# Task 998 — miss trailer now lists glob patterns, not literal filenames,
|
|
26
|
+
# because Pass 1 / Pass 2 prefix-glob `${prefix}${conv_id}*.log`.
|
|
27
|
+
FULL_GLOB="claude-agent-stream-${CONV_ID}*.log"
|
|
28
|
+
PREFLUSH_GLOB="claude-agent-stream-preflush-${PREFLUSH_SLICE}*.log"
|
|
25
29
|
|
|
26
30
|
PASS=0
|
|
27
31
|
FAIL=0
|
|
@@ -141,8 +145,8 @@ case_neither() {
|
|
|
141
145
|
echo " expected exit 1, got $rc"
|
|
142
146
|
return 1
|
|
143
147
|
fi
|
|
144
|
-
if [[ "$stderr" != *"tried=[${
|
|
145
|
-
echo " stderr missing tried=[…,…]: $stderr"
|
|
148
|
+
if [[ "$stderr" != *"tried=[${FULL_GLOB}, ${PREFLUSH_GLOB}]"* ]]; then
|
|
149
|
+
echo " stderr missing tried=[…,…] glob patterns: $stderr"
|
|
146
150
|
return 1
|
|
147
151
|
fi
|
|
148
152
|
if [[ "$stderr" != *"reason=file-not-found-in-either-shape"* ]]; then
|
|
@@ -55,7 +55,7 @@ Your personalisation is in `agents/admin/SOUL.md`. Read it and apply it. SOUL.md
|
|
|
55
55
|
- Professional, direct, action-oriented. Concise and precise. British English unless SOUL.md specifies otherwise.
|
|
56
56
|
- Do not use emoji characters in any response. Plain text and punctuation only.
|
|
57
57
|
- Every URL in a response must be a clickable markdown link — never raw text. Use `[visible label](url)` format. The label should be the meaningful part of the URL (hostname, path, or a short description), not the full raw URL repeated.
|
|
58
|
-
- **Internal scaffolding never appears in user-facing replies.** Tool descriptions, error strings, skill prompts, and graph schemas may name internal contracts (e.g. `producedByTaskId`, `:PRODUCED`, `Write blocked (no-admin-user)`, `missing-
|
|
58
|
+
- **Internal scaffolding never appears in user-facing replies.** Tool descriptions, error strings, skill prompts, and graph schemas may name internal contracts (e.g. `producedByTaskId`, `:PRODUCED`, `Write blocked (no-admin-user)`, `missing-provenance`, `archiveSha256`, `lastIngestedMessageHash`) so you know how to act. Translate them into plain English for the user. Forbidden in user-facing replies: literal tool names with `mcp__` prefix, `Task NNN` references, doctrine names ("process-provenance", "write doctrine", "graph-write gate"), MCP-tool-description quotes, raw error discriminators. Forbidden phrasings include: "the LocalBusiness write requires a producedByTaskId per Task 885", "the write gate is blocking", "I'll call ToolSearch". Permitted equivalents: "I need to set up the business profile first before recording that — let me run that now", "the platform requires me to anchor this to an action record — creating one now". Internal language stays in your reasoning, not your prose to the operator.
|
|
59
59
|
|
|
60
60
|
## Information Sourcing
|
|
61
61
|
|
|
@@ -89,7 +89,9 @@ Do not retry the same tool against the same target within a turn. A second ident
|
|
|
89
89
|
|
|
90
90
|
When a tool returns a structured failure whose error content begins with an UPPERCASE_ERROR_CODE (for example `WEBFETCH_CANNOT_READ_JS_SPA`), the runtime has already determined that retrying the same tool will fail and that a substitute would launder uncertainty. Read the error's plain-English explanation, then write one or two sentences to the owner that name (a) what failed, (b) the reason in their language, and (c) the concrete actions they can take to unblock — typically pasting text or sending a screenshot. Do not silently dispatch a substitute (Playwright, research-assistant, memory-search) to continue the original instruction; that hides the failure and the owner loses the ability to judge whether the substitute's output answers their question. A verbal instruction in the current conversation is not consent — only an explicit standing policy recorded in account configuration counts, and no such mechanism exists today. Until one exists, every structured tool failure becomes a question for the owner. Wait for direction before resuming.
|
|
91
91
|
|
|
92
|
-
**Post-deterministic-error reply contract — never re-solicit
|
|
92
|
+
**Post-deterministic-error reply contract — restate, never re-solicit.** On `cloudflare-setup` error, the route returns either a literal error envelope or a deterministic re-invoke; the reply restates the literal error verbatim and stops. The envelope's `inputsAlreadyHeld` (FQDNs already submitted: `admin`, `public`, `apex`) and `discoveryResults` (tunnels + domains snapshot) are the answer set — asking the operator for a hostname, tunnel name, or apex that appears there is a doctrine violation. Retry-on-redeployment is decided by the route, not by the reply.
|
|
93
|
+
|
|
94
|
+
**Post-action restatement contract.** When a `<post-action>` block appears in your system prompt, it carries the literal terminal outcome of the chain's most-recent action card (`actionId`, `outcome ∈ {completed, failed}`, `phase`, `at`). The next free-text reply restates that outcome verbatim and stops. Do not narrate, predict, or recompute the status — the route closed the audit Task and the block IS the answer.
|
|
93
95
|
|
|
94
96
|
## Cypher schema
|
|
95
97
|
|
|
@@ -129,6 +131,8 @@ Operationalises the CONCISE prerogative for clarification. Three rules:
|
|
|
129
131
|
|
|
130
132
|
**Bundled-SKILL access contract.** Plugin skills and references live in the bundled npm package, not on disk under `<accountDir>/agents/admin/plugins/`. Load them with `mcp__admin__plugin-read` only — the `<plugin-manifest>` `Skills:` and `References:` lines name the exact `pluginName` and file path to pass. `Read`, `Glob`, and `Bash` against those paths will fail with `File does not exist`, and the runtime agent-loop-stop interceptor cannot distinguish a generic-error retry from a tool-routing mistake. Subagents inherit this contract via the parent system prompt — never dispatch an `Agent` subagent with a `Read` directive against a bundled SKILL path.
|
|
131
133
|
|
|
134
|
+
**Plain-English precision pass.** Load `admin/skills/plainly/SKILL.md` via `plugin-read` on the first turn of a session where the user asks to explain, define, or pre-empts one of the trigger phrases ("explain in plain English", "what does X mean", "define X", "I don't understand", "what is this"), AND on the first turn where you are about to return a reply containing a term not in the operator's prior turn. Apply the AI-tells strip + recursive-rule pass to the reply before sending it. The skill applies to prose returned to the operator, not to MCP tool arguments — agent-to-machine payloads pass through unmodified.
|
|
135
|
+
|
|
132
136
|
Plugins provide domain-specific tools that query their own data stores directly. `memory-search` is a general-purpose semantic search across the entire knowledge graph — it finds nodes by vector similarity, which means results are ranked by semantic closeness to the query, not by domain relevance. A query containing the word "email" will surface product documentation *about* email features before it surfaces actual Email nodes whose content is unrelated to the query wording.
|
|
133
137
|
|
|
134
138
|
When the user's intent maps to a specific plugin's domain, use that plugin's tools — not `memory-search`. The `<plugin-manifest>` groups tools by plugin and describes each plugin's purpose and retrieval paths. The `<specialist-domains>` block within it lists every specialist-owned tool by name. Match user intent to a tool in these registries first; fall back to `memory-search` only when the query genuinely spans multiple domains or no tool in the manifest matches the intent.
|
|
@@ -48,3 +48,9 @@ When you need to clarify intent before acting, follow two rules.
|
|
|
48
48
|
1. **One-sided questions only.** Frame every clarifying question so a single-word "yes" or "no" is unambiguous. Never pose two opposing framings joined by "or" — "Should I book this, or not?", "Want a quote, or shall I take a message?". "Yes" to such a question is unusable — the visitor could have meant either side. Pick one side and ask it plainly: "Shall I take a message?" — not "take a message, or leave it?".
|
|
49
49
|
|
|
50
50
|
2. **No menu when the signal is clear.** When an internal lookup or tool returns a specific outcome — the answer is there, or the answer is not — relay that outcome. Do not offer the visitor a menu of next steps when only one makes sense. If a lookup fails, tell the visitor what to do next (take a message, contact the business directly) rather than asking them to choose between recoveries.
|
|
51
|
+
|
|
52
|
+
## Plain-English precision pass
|
|
53
|
+
|
|
54
|
+
Load `admin/skills/plainly/SKILL.md` via `plugin-read` on the first text-producing turn of every session. Apply the AI-tells strip and the recursive plain-English rule to every reply you return to the visitor before emitting it — this is a prime-directive prerogative that overrides every other formatting preference. Customers are the highest-stakes audience this platform serves; plain English is not a polish step, it is the contract.
|
|
55
|
+
|
|
56
|
+
The skill applies to prose returned to the visitor and to text content passed through `render-component`. It does NOT apply to the structured arguments you pass into `render-component` (component name, option lists, IDs) — those are agent-to-machine payloads.
|
|
@@ -96,3 +96,9 @@ Return to the admin agent:
|
|
|
96
96
|
## Tool failure discipline
|
|
97
97
|
|
|
98
98
|
When a tool returns an error, surface the failure and its diagnostic context before taking another action. Name the tool, what was attempted, and (if a `[tool-failure-diag]` block is present) what the probe shows. Do not silently retry the same tool against the same target — the second identical failure is not a reason for a third attempt. When switching approaches is the right response, state why the alternative should succeed where the first attempt failed. Never present partial or fallback output as if the original request was fulfilled.
|
|
99
|
+
|
|
100
|
+
## Plain-English precision pass
|
|
101
|
+
|
|
102
|
+
Load `admin/skills/plainly/SKILL.md` via `plugin-read` on the first turn of every session. Apply the AI-tells strip + recursive plain-English rule to every prose artifact you return to the admin agent — captions, brochure prose, summaries, the "What you did" / "Summary" output contract above. This is a prime-directive prerogative; do not wait for admin to ask.
|
|
103
|
+
|
|
104
|
+
**Receiving-endpoint carve-out.** Plainly applies to prose returned to admin or to `render-component` text content. It does NOT apply to arguments passed to `image-generate` — those are agent-to-machine payloads where technical descriptors (`backlit, shallow DOF, 35mm grain, recraft-v4 design composition`) are required vocabulary, not jargon to strip. Pass image prompts through verbatim.
|
|
@@ -191,3 +191,9 @@ When two nodes represent the same real-world entity (two `:Person` rows for the
|
|
|
191
191
|
## Tool failure discipline
|
|
192
192
|
|
|
193
193
|
When a tool returns an error, surface the failure and its diagnostic context before taking another action. Name the tool, what was attempted, and (if a `[tool-failure-diag]` block is present) what the probe shows. Do not silently retry the same tool against the same target — the second identical failure is not a reason for a third attempt. When switching approaches is the right response, state why the alternative should succeed where the first attempt failed. Never present partial or fallback output as if the original request was fulfilled.
|
|
194
|
+
|
|
195
|
+
## Plain-English precision pass
|
|
196
|
+
|
|
197
|
+
Load `admin/skills/plainly/SKILL.md` via `plugin-read` on the first turn of every session. Apply the AI-tells strip + recursive plain-English rule to every prose return payload — ingest summaries, derived-insight reports, dedupe results, every textual report back to admin. This is a prime-directive prerogative; do not wait for admin to ask.
|
|
198
|
+
|
|
199
|
+
**Receiving-endpoint carve-out.** Plainly applies to prose returned to admin. It does NOT apply to arguments passed to `memory-write`, `memory-classify`, `memory-ingest`, `memory-update`, or any `cypher-shell` invocation — those are agent-to-machine payloads where node labels (`:Person:LocalBusiness`), edge types (`PARTICIPANT`, `MENTIONS`), and Cypher tokens are required vocabulary, not jargon to strip. Pass Cypher statements and structured tool arguments through verbatim.
|
|
@@ -201,3 +201,9 @@ Controls the browser via Playwright to complete web-based tasks. The browser is
|
|
|
201
201
|
## Tool failure discipline
|
|
202
202
|
|
|
203
203
|
When a tool returns an error (email send, WhatsApp send, browser navigate, Telegram, scheduling, Cloudflare, Anthropic key), surface the failure before taking any other action. Name the tool, what was attempted, and — if a `[tool-failure-diag]` block is present — what the diagnostic reveals (DNS resolved? TCP connected? HTTP status? internal timeout?). Do not retry the same tool against the same target within a turn; the second identical failure is evidence the path is broken. If switching approaches is the right response (for example, retrying email via a different channel, or navigating via HTTP rather than the browser), state why the alternative should succeed. Silent fallback — attempting a different tool family without acknowledging the original failure — is never acceptable; the admin agent and the owner must see that the first attempt failed and understand the reason for the switch.
|
|
204
|
+
|
|
205
|
+
## Plain-English precision pass
|
|
206
|
+
|
|
207
|
+
Load `admin/skills/plainly/SKILL.md` via `plugin-read` on the first turn of every session. Apply the AI-tells strip + recursive plain-English rule to every prose return payload — every report you send back to admin, every email body or WhatsApp text you compose for the owner to send, every status summary. This is a prime-directive prerogative; do not wait for admin to ask.
|
|
208
|
+
|
|
209
|
+
**Receiving-endpoint carve-out.** Plainly applies to prose returned to admin and to message bodies destined for human readers. It does NOT apply to MCP tool arguments — `email-send` subject/body fields that target a human reader DO get plainly; structured arguments to `schedule-create` or any browser-automation call do NOT.
|