@rubytech/create-sitedesk-code 0.1.494 → 0.1.496
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/plugins/admin/skills/insight/SKILL.md +1 -1
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +17 -6
- package/payload/platform/plugins/docs/references/admin-ui.md +16 -5
- package/payload/platform/plugins/email/Archive.zip +0 -0
- package/payload/platform/plugins/memory/references/schema-estate-agent.md +1 -1
- package/payload/platform/scripts/__tests__/account-schema-owned-dirs.test.sh +51 -7
- package/payload/platform/scripts/__tests__/provision-role-stamp.test.sh +4 -1
- package/payload/platform/scripts/lib/account-schema-owned-dirs.py +13 -1
- package/payload/platform/scripts/lib/account-schema-owned-dirs.sh +23 -1
- package/payload/platform/scripts/lib/provision-account-dir.sh +5 -0
- package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/dist/lib/gates.d.ts +15 -0
- package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/dist/lib/gates.d.ts.map +1 -1
- package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/dist/lib/gates.js +19 -0
- package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/dist/lib/gates.js.map +1 -1
- package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/dist/lib/render-run.d.ts +1 -1
- package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/dist/lib/render-run.d.ts.map +1 -1
- package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/dist/lib/render-run.js +16 -3
- package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/dist/lib/render-run.js.map +1 -1
- package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/src/lib/__tests__/gates.test.ts +25 -0
- package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/src/lib/__tests__/render-run.test.ts +52 -0
- package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/src/lib/gates.ts +19 -0
- package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/src/lib/render-run.ts +16 -3
- package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/skills/quotation/references/quote-generation.md +14 -5
- package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/skills/quotation/references/verification.md +9 -4
- package/payload/server/public/activity.html +5 -5
- package/payload/server/public/assets/{AdminLoginScreens-DgVFsHtd.js → AdminLoginScreens-CFF28MGy.js} +1 -1
- package/payload/server/public/assets/AdminShell-ClIT8c0K.js +2 -0
- package/payload/server/public/assets/{activity-2DL2DwLr.js → activity-CElZcdjR.js} +1 -1
- package/payload/server/public/assets/{admin-COe0dT2U.js → admin-BHiA9_Ks.js} +1 -1
- package/payload/server/public/assets/{bot-CpBuDQlF.js → bot-Q-JmeVT3.js} +1 -1
- package/payload/server/public/assets/{browser-CSApuZrv.js → browser-BrfPJMYF.js} +1 -1
- package/payload/server/public/assets/{calendar-DlvwZnS7.js → calendar-D45S_aal.js} +1 -1
- package/payload/server/public/assets/chat-gy8scQov.js +1 -0
- package/payload/server/public/assets/chevron-down-CdxvexgY.js +1 -0
- package/payload/server/public/assets/chevron-left-D5LLz6ke.js +1 -0
- package/payload/server/public/assets/chevron-right-DzPmwDew.js +1 -0
- package/payload/server/public/assets/data-CUsNHUpu.js +1 -0
- package/payload/server/public/assets/{graph-kaxS9a_7.js → graph-BLO4nVi3.js} +1 -1
- package/payload/server/public/assets/{graph-labels-GgejRtgV.js → graph-labels-ibEi7ggo.js} +1 -1
- package/payload/server/public/assets/{maximize-2-CPq3nwZA.js → maximize-2-CezchDEF.js} +1 -1
- package/payload/server/public/assets/{operator-Cxq9TxaH.js → operator-B-3vkwO8.js} +1 -1
- package/payload/server/public/assets/{page-DBHzC59_.js → page-BL1ko2_-.js} +1 -1
- package/payload/server/public/assets/{page-BE6DkJP9.js → page-BbxoY4QI.js} +1 -1
- package/payload/server/public/assets/{public-jIzvdjt_.js → public-DxfSLhNp.js} +1 -1
- package/payload/server/public/assets/{rotate-ccw-DieZzl10.js → rotate-ccw-CWvGAut4.js} +1 -1
- package/payload/server/public/assets/{tasks-DYyCGz8W.js → tasks-DlXIN3v3.js} +1 -1
- package/payload/server/public/assets/{time-entry-format-CC_9jFuR.js → time-entry-format-VzHwljTv.js} +1 -1
- package/payload/server/public/assets/{triangle-alert-7mQfCQWP.js → triangle-alert-C5BpoPlq.js} +1 -1
- package/payload/server/public/assets/{useCopyFeedback-os7N0yCx.js → useCopyFeedback-h1qT8mpZ.js} +1 -1
- package/payload/server/public/assets/{useSubAccountSwitcher-Bgfqig1V.css → useSubAccountSwitcher-DnfX9Uxe.css} +1 -1
- package/payload/server/public/assets/{useVoiceRecorder-ZFwt5i6d.js → useVoiceRecorder-kqMitFlk.js} +1 -1
- package/payload/server/public/browser.html +4 -4
- package/payload/server/public/calendar.html +6 -6
- package/payload/server/public/chat.html +13 -13
- package/payload/server/public/data.html +10 -10
- package/payload/server/public/graph.html +10 -10
- package/payload/server/public/index.html +15 -15
- package/payload/server/public/operator.html +14 -14
- package/payload/server/public/public.html +13 -13
- package/payload/server/public/tasks.html +6 -6
- package/payload/server/public/assets/AdminShell-BwNqwb4k.js +0 -2
- package/payload/server/public/assets/chat-1SSmFsjd.js +0 -1
- package/payload/server/public/assets/chevron-down-Dnuuw6Q2.js +0 -1
- package/payload/server/public/assets/chevron-left-CC4zmWOr.js +0 -1
- package/payload/server/public/assets/chevron-right-CG_lHTx_.js +0 -1
- package/payload/server/public/assets/data-CJbC_cVy.js +0 -1
- /package/payload/server/public/assets/{useSubAccountSwitcher-Q941cZYV.js → useSubAccountSwitcher-DDStL7Nt.js} +0 -0
package/package.json
CHANGED
|
@@ -15,7 +15,7 @@ Run the four passes now over the session so far, then keep going in this same se
|
|
|
15
15
|
|
|
16
16
|
Pass 1 — Technical learnings. Walk this session for operator corrections and self-detected mistakes. For each one, delegate one graph write to `database-operator` via the Task tool, persisting a Learning or Correction node per the schema. Count what you write.
|
|
17
17
|
|
|
18
|
-
Pass 2 — Operator-relationship updates. Walk this session for tonal signals, rejected phrasings, and working-style preferences. For each one worth carrying forward, Write the observation into `SOUL.md
|
|
18
|
+
Pass 2 — Operator-relationship updates. Walk this session for tonal signals, rejected phrasings, and working-style preferences. For each one worth carrying forward, Write the observation into `agents/admin/SOUL.md`, the account-scoped identity path the spawn composer reads. That path is the only identity file; never write a bare `SOUL.md` at the account root, which the composer never reads and the schema guard blocks. Count what you write.
|
|
19
19
|
|
|
20
20
|
Pass 3 — Graph completeness sweep. Walk this session for any node, edge, or commitment that was discussed but not written to the graph in-flight. For each missing write, delegate to `database-operator` via the Task tool. Count what you write.
|
|
21
21
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: platform-architecture
|
|
3
3
|
description: Use when grounding any documented-surface claim about what SiteDesk ships — plugins, skills, specialists, install/deploy flows, internals. This is the install catalogue, not evidence of what is enabled on the current account. For install state on this account, call `capabilities-here`; for documented surface, cite the `Source:` URL inline.
|
|
4
|
-
content-hash: sha256:
|
|
4
|
+
content-hash: sha256:e3da93a9c86c4a41c7ea79d4cdc353a85ac8c69826cc5238c9d2c00d53811895
|
|
5
5
|
brand: sitedesk-code
|
|
6
6
|
product-name: SiteDesk
|
|
7
7
|
---
|
|
@@ -3300,9 +3300,11 @@ authoritative. This section names the surfaces and what backs each.
|
|
|
3300
3300
|
>
|
|
3301
3301
|
> - **Nav rows** are Dashboard, Chat, Tasks, Calendar, Data, the brand container
|
|
3302
3302
|
> row (Projects / Properties / Jobs, opening the graph filtered to the brand's
|
|
3303
|
-
> primary container),
|
|
3304
|
-
>
|
|
3305
|
-
>
|
|
3303
|
+
> primary container), Graph (the unfiltered whole graph at `/graph`, waypoints
|
|
3304
|
+
> icon — active on plain `/graph`; the container row is active only when a
|
|
3305
|
+
> `label=` filter is present), Activity, and Browser. The active row carries an
|
|
3306
|
+
> accent left indicator. The older People / Agents / Artefacts entries in the
|
|
3307
|
+
> table below are pre-redesign.
|
|
3306
3308
|
> - **Dashboard home** renders on `/` (a greeting, stat cards, a Live-now column,
|
|
3307
3309
|
> a Recent artefacts card, and a Channels column), consuming existing endpoints
|
|
3308
3310
|
> only.
|
|
@@ -3317,14 +3319,23 @@ authoritative. This section names the surfaces and what backs each.
|
|
|
3317
3319
|
> - **Mobile (< 720px).** The sidebar opens as a drawer from a 44x44 header menu
|
|
3318
3320
|
> button, and a fixed bottom tab bar (Home, Chat, Tasks, Data, Calendar) sits
|
|
3319
3321
|
> under the content; Graph, Activity, and Browser stay reachable through the
|
|
3320
|
-
> drawer. Between 721 and 1080px the sidebar defaults to the rail.
|
|
3322
|
+
> drawer. Between 721 and 1080px the sidebar defaults to the rail. The
|
|
3323
|
+
> drawer's SESSIONS head collapses its New-session pill to an icon-only `+`
|
|
3324
|
+
> (34px square); the label returns above 720px. The **operator** variant gets
|
|
3325
|
+
> its own fixed bottom tab bar — Chat, Conversations (rendered only when the
|
|
3326
|
+
> pane handler is wired), Data — while its burger keeps the AccountMenu tail.
|
|
3327
|
+
> In the webchat composer the icon control bar moves **above** the input box
|
|
3328
|
+
> with 44px touch squares (desktop keeps the below-input row), and the
|
|
3329
|
+
> composer placeholder never wraps (single line, ellipsized).
|
|
3321
3330
|
|
|
3322
3331
|
> **Admin variant only.** Every surface below belongs to the `admin` shell
|
|
3323
3332
|
> variant. The **operator** variant (`operator.<host>`) mounts no sidebar at
|
|
3324
3333
|
> all: its dashboard is the persistent admin webchat at `/` and the data
|
|
3325
3334
|
> browser at `/data`, plus a **Conversations** item reached from a dynamic
|
|
3326
3335
|
> burger item — header order Chat → Conversations → Data → Log out, in a
|
|
3327
|
-
> single-column `.platform.platform-operator` grid.
|
|
3336
|
+
> single-column `.platform.platform-operator` grid. Below 720px the same three
|
|
3337
|
+
> nav items also render as the operator bottom tab bar (see Mobile above);
|
|
3338
|
+
> the Chat tab targets the operator root `/`, the canonical-session route. On the operator **chat**
|
|
3328
3339
|
> surface the Conversations item opens the same in-chat session
|
|
3329
3340
|
> switcher the admin `/chat` surface uses (`ChatSurface`'s `SessionList` flyout):
|
|
3330
3341
|
> the operator's own admin webchat conversations, newest-first, each resuming
|
|
@@ -399,9 +399,11 @@ authoritative. This section names the surfaces and what backs each.
|
|
|
399
399
|
>
|
|
400
400
|
> - **Nav rows** are Dashboard, Chat, Tasks, Calendar, Data, the brand container
|
|
401
401
|
> row (Projects / Properties / Jobs, opening the graph filtered to the brand's
|
|
402
|
-
> primary container),
|
|
403
|
-
>
|
|
404
|
-
>
|
|
402
|
+
> primary container), Graph (the unfiltered whole graph at `/graph`, waypoints
|
|
403
|
+
> icon — active on plain `/graph`; the container row is active only when a
|
|
404
|
+
> `label=` filter is present), Activity, and Browser. The active row carries an
|
|
405
|
+
> accent left indicator. The older People / Agents / Artefacts entries in the
|
|
406
|
+
> table below are pre-redesign.
|
|
405
407
|
> - **Dashboard home** renders on `/` (a greeting, stat cards, a Live-now column,
|
|
406
408
|
> a Recent artefacts card, and a Channels column), consuming existing endpoints
|
|
407
409
|
> only.
|
|
@@ -416,14 +418,23 @@ authoritative. This section names the surfaces and what backs each.
|
|
|
416
418
|
> - **Mobile (< 720px).** The sidebar opens as a drawer from a 44x44 header menu
|
|
417
419
|
> button, and a fixed bottom tab bar (Home, Chat, Tasks, Data, Calendar) sits
|
|
418
420
|
> under the content; Graph, Activity, and Browser stay reachable through the
|
|
419
|
-
> drawer. Between 721 and 1080px the sidebar defaults to the rail.
|
|
421
|
+
> drawer. Between 721 and 1080px the sidebar defaults to the rail. The
|
|
422
|
+
> drawer's SESSIONS head collapses its New-session pill to an icon-only `+`
|
|
423
|
+
> (34px square); the label returns above 720px. The **operator** variant gets
|
|
424
|
+
> its own fixed bottom tab bar — Chat, Conversations (rendered only when the
|
|
425
|
+
> pane handler is wired), Data — while its burger keeps the AccountMenu tail.
|
|
426
|
+
> In the webchat composer the icon control bar moves **above** the input box
|
|
427
|
+
> with 44px touch squares (desktop keeps the below-input row), and the
|
|
428
|
+
> composer placeholder never wraps (single line, ellipsized).
|
|
420
429
|
|
|
421
430
|
> **Admin variant only.** Every surface below belongs to the `admin` shell
|
|
422
431
|
> variant. The **operator** variant (`operator.<host>`) mounts no sidebar at
|
|
423
432
|
> all: its dashboard is the persistent admin webchat at `/` and the data
|
|
424
433
|
> browser at `/data`, plus a **Conversations** item reached from a dynamic
|
|
425
434
|
> burger item — header order Chat → Conversations → Data → Log out, in a
|
|
426
|
-
> single-column `.platform.platform-operator` grid.
|
|
435
|
+
> single-column `.platform.platform-operator` grid. Below 720px the same three
|
|
436
|
+
> nav items also render as the operator bottom tab bar (see Mobile above);
|
|
437
|
+
> the Chat tab targets the operator root `/`, the canonical-session route. On the operator **chat**
|
|
427
438
|
> surface the Conversations item opens the same in-chat session
|
|
428
439
|
> switcher the admin `/chat` surface uses (`ChatSurface`'s `SessionList` flyout):
|
|
429
440
|
> the operator's own admin webchat conversations, newest-first, each resuming
|
|
Binary file
|
|
@@ -13,7 +13,7 @@ When loading this reference, confirm: "Using schema-base + schema-estate-agent".
|
|
|
13
13
|
| Entity | Neo4j Label | Owner | Schema.org Type | Required Properties |
|
|
14
14
|
|--------|-------------|-------|-----------------|---------------------|
|
|
15
15
|
| Property | `Property` | | `schema:Accommodation` | `accountId`, `slug` (id, address-derived), `address` |
|
|
16
|
-
| Listing | `Listing` |
|
|
16
|
+
| Listing | `Listing` | `Property` | `schema:RealEstateListing` | `accountId`, `slug`, `displayName`, `listingType` (sale/let), `status`, `sourceSystem`, `sourceId`, `scope`, `heroImageUrl`*, `pageUrl`*, `blurb` |
|
|
17
17
|
| Viewing | `Viewing` | `Listing` | `schema:Event` (subtype) | `accountId`, `listing` (id), `applicant` (id), `date`, `feedback`, `outcome` |
|
|
18
18
|
| Offer | `Offer` | `Listing` | `schema:Offer` | `accountId`, `listing` (id), `applicant` (id), `price`, `conditions`, `status`, `date` |
|
|
19
19
|
|
|
@@ -13,6 +13,8 @@ PASS=0; FAIL=0; FAILED=()
|
|
|
13
13
|
assert_grep() { if printf '%s' "$2" | grep -qF "$1"; then PASS=$((PASS+1)); else FAIL=$((FAIL+1)); FAILED+=("$3: expected to find [$1]"); fi; }
|
|
14
14
|
assert_nogrep() { if printf '%s' "$2" | grep -qF "$1"; then FAIL=$((FAIL+1)); FAILED+=("$3: expected NOT to find [$1]"); else PASS=$((PASS+1)); fi; }
|
|
15
15
|
assert_eq() { if [ "$1" = "$2" ]; then PASS=$((PASS+1)); else FAIL=$((FAIL+1)); FAILED+=("$3: expected [$2] got [$1]"); fi; }
|
|
16
|
+
assert_dir() { if [ -d "$1" ]; then PASS=$((PASS+1)); else FAIL=$((FAIL+1)); FAILED+=("$2: expected directory [$1]"); fi; }
|
|
17
|
+
assert_nodir() { if [ -d "$1" ]; then FAIL=$((FAIL+1)); FAILED+=("$2: expected NO directory [$1]"); else PASS=$((PASS+1)); fi; }
|
|
16
18
|
|
|
17
19
|
. "$PLAT_SCRIPTS/lib/account-schema-owned-dirs.sh"
|
|
18
20
|
|
|
@@ -156,21 +158,63 @@ assert_eq "$(shasum "$A_C/SCHEMA.md" | awk '{print $1}')" "$B_C" "con-idempotent
|
|
|
156
158
|
RCS="$(PROJECT_DIR="$PROJECT_DIR" python3 "$PLAT_SCRIPTS/lib/account-schema-owned-dirs.py" reconcile "$A_C")"
|
|
157
159
|
assert_grep "ontologyEntity=Supplier bucket=suppliers present=true" "$RCS" "con-supplier-root-present"
|
|
158
160
|
|
|
159
|
-
# --- estate-agent vertical:
|
|
161
|
+
# --- estate-agent vertical: Property is the sole root; Listing (and its own
|
|
162
|
+
# children Viewing/Offer) nest, mirroring Quote<-Job in construction ---
|
|
160
163
|
T_E="$(make_vertical_tree '["real-agent"]' 'schema-estate-agent')"; A_E="$(make_account "$T_E")"
|
|
161
164
|
export PROJECT_DIR="$T_E/platform"
|
|
162
|
-
merge_owned_dirs_into_schema "$A_E"
|
|
165
|
+
OUT_E="$(merge_owned_dirs_into_schema "$A_E")"
|
|
166
|
+
assert_grep "domainBuckets=properties" "$OUT_E" "ea-domain-log"
|
|
167
|
+
assert_grep "roots=properties" "$OUT_E" "ea-roots-log"
|
|
168
|
+
assert_grep "nested=Listing<-Property,Viewing<-Listing,Offer<-Listing" "$OUT_E" "ea-nested-log"
|
|
163
169
|
ALLOWED_E="$(awk '/^```allowed-top-level$/{f=1;next} /^```$/{f=0} f' "$A_E/SCHEMA.md")"
|
|
164
|
-
#
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
for d in viewings offers; do
|
|
170
|
+
# Property (blank Owner) is the only root bucket.
|
|
171
|
+
assert_grep "properties" "$ALLOWED_E" "ea-allow-properties"
|
|
172
|
+
# Listing and its children nest; none is a root bucket.
|
|
173
|
+
for d in listings viewings offers; do
|
|
169
174
|
assert_nogrep "$d" "$ALLOWED_E" "ea-noroot-$d"
|
|
170
175
|
done
|
|
171
176
|
assert_nogrep "jobs" "$ALLOWED_E" "ea-no-sitedesk-jobs"
|
|
172
177
|
# The suppliers/ bucket is construction-only; it must not leak into another vertical.
|
|
173
178
|
assert_nogrep "suppliers" "$ALLOWED_E" "ea-no-suppliers"
|
|
179
|
+
# The descriptive region tells the agent Listing records live under properties/.
|
|
180
|
+
assert_grep '`Listing` records live under `properties/` (owned by `Property`), not a root bucket.' "$(cat "$A_E/SCHEMA.md")" "ea-nested-line"
|
|
181
|
+
|
|
182
|
+
# --- roots subcommand + seed_domain_root_dirs: buckets materialise on disk ---
|
|
183
|
+
# estate-agent (PROJECT_DIR still $T_E/platform): properties/ is created.
|
|
184
|
+
ROOTS_E="$(PROJECT_DIR="$T_E/platform" python3 "$PLAT_SCRIPTS/lib/account-schema-owned-dirs.py" roots)"
|
|
185
|
+
assert_grep "properties" "$ROOTS_E" "ea-roots-cmd"
|
|
186
|
+
assert_nogrep "listings" "$ROOTS_E" "ea-roots-cmd-no-listings"
|
|
187
|
+
SEED_E="$(seed_domain_root_dirs "$A_E")"
|
|
188
|
+
assert_grep "seeded-roots=properties" "$SEED_E" "ea-seed-log"
|
|
189
|
+
assert_dir "$A_E/properties" "ea-seed-dir"
|
|
190
|
+
# Idempotent: a second run creates nothing.
|
|
191
|
+
SEED_E2="$(seed_domain_root_dirs "$A_E")"
|
|
192
|
+
assert_grep "seeded-roots=none" "$SEED_E2" "ea-seed-idempotent"
|
|
193
|
+
|
|
194
|
+
# construction: every root bucket is created; nested entities are not.
|
|
195
|
+
export PROJECT_DIR="$T_C/platform"
|
|
196
|
+
A_SEED_C="$(make_account "$T_C")"
|
|
197
|
+
ROOTS_C="$(PROJECT_DIR="$T_C/platform" python3 "$PLAT_SCRIPTS/lib/account-schema-owned-dirs.py" roots)"
|
|
198
|
+
for d in jobs customers customer-sites assets parts suppliers ppm-contracts; do
|
|
199
|
+
assert_grep "$d" "$ROOTS_C" "con-roots-cmd-$d"
|
|
200
|
+
done
|
|
201
|
+
seed_domain_root_dirs "$A_SEED_C" >/dev/null
|
|
202
|
+
for d in jobs customers customer-sites assets parts suppliers ppm-contracts; do
|
|
203
|
+
assert_dir "$A_SEED_C/$d" "con-seed-dir-$d"
|
|
204
|
+
done
|
|
205
|
+
for d in quotes visits purchase-orders; do
|
|
206
|
+
assert_nodir "$A_SEED_C/$d" "con-seed-nonested-$d"
|
|
207
|
+
done
|
|
208
|
+
|
|
209
|
+
# The all-accounts sweep seeds an existing account's root buckets too, so a
|
|
210
|
+
# sub-account (provisioned once at creation) gains properties/ on upgrade.
|
|
211
|
+
export PROJECT_DIR="$T_E/platform"
|
|
212
|
+
SWEEP_ACCTS="$(mktemp -d)"; mkdir -p "$SWEEP_ACCTS/ea1"
|
|
213
|
+
cp "$T_E/platform/templates/account-schema/SCHEMA.md" "$SWEEP_ACCTS/ea1/SCHEMA.md"
|
|
214
|
+
printf '{"accountId":"ea1","role":"client"}\n' > "$SWEEP_ACCTS/ea1/account.json"
|
|
215
|
+
SWEEP_E="$(reconcile_all_accounts_owned_dirs "$SWEEP_ACCTS" 2>&1)"
|
|
216
|
+
assert_grep "seeded-roots=properties" "$SWEEP_E" "sweep-seed-log"
|
|
217
|
+
assert_dir "$SWEEP_ACCTS/ea1/properties" "sweep-seed-properties-dir"
|
|
174
218
|
|
|
175
219
|
# --- no vertical declared: generic base only ---
|
|
176
220
|
T_N="$(make_tree '[]')"; A_N="$(make_account "$T_N")"
|
|
@@ -20,8 +20,11 @@ export USERS_FILE="/nonexistent/users.json"
|
|
|
20
20
|
|
|
21
21
|
# Fresh account -> role=house stamped
|
|
22
22
|
T1=$(mktemp -d); A1="$T1/acct-house"
|
|
23
|
-
provision_account_dir "$A1" "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" "house" >/dev/null
|
|
23
|
+
OUT1="$(provision_account_dir "$A1" "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" "house" 2>/dev/null)"
|
|
24
24
|
assert_eq "$(role_of "$A1/account.json")" "house" "fresh-house-role"
|
|
25
|
+
# Task 1938 — provision seeds the vertical's root domain buckets; the log line
|
|
26
|
+
# proves seed_domain_root_dirs ran (bucket set depends on the brand's vertical).
|
|
27
|
+
assert_eq "$(printf '%s' "$OUT1" | grep -c 'seeded-roots=')" "1" "fresh-seed-roots-logged"
|
|
25
28
|
assert_eq "$(ms_of "$A1/account.json")" "False" "fresh-managedService-default"
|
|
26
29
|
# Task 1483 — the WhatsApp passive requirement-intake identity is seeded, non-empty.
|
|
27
30
|
assert_nonempty "$A1/agents/passive/IDENTITY.md" "passive-identity-seeded"
|
|
@@ -410,9 +410,19 @@ def reconcile(project_dir, account_dir):
|
|
|
410
410
|
f"bucket={b['dir']} expectedAtRoot=false stray={is_stray}")
|
|
411
411
|
|
|
412
412
|
|
|
413
|
+
def roots(project_dir):
|
|
414
|
+
"""Print each root domain bucket dir (blank/absent Owner) of the brand's
|
|
415
|
+
vertical, one per line, in table order. Empty when no vertical is declared.
|
|
416
|
+
Provision consumes this to mkdir the operator-data buckets so they exist as
|
|
417
|
+
directories, not just allowed names."""
|
|
418
|
+
for b in resolve_domain_buckets(project_dir):
|
|
419
|
+
if _is_root(b["owner"]):
|
|
420
|
+
print(b["dir"])
|
|
421
|
+
|
|
422
|
+
|
|
413
423
|
def main():
|
|
414
424
|
if len(sys.argv) < 2:
|
|
415
|
-
sys.exit("usage: account-schema-owned-dirs.py {resolve|merge|reconcile} [account_dir]")
|
|
425
|
+
sys.exit("usage: account-schema-owned-dirs.py {resolve|merge|reconcile|roots} [account_dir]")
|
|
416
426
|
cmd = sys.argv[1]
|
|
417
427
|
pd = _project_dir()
|
|
418
428
|
if cmd == "resolve":
|
|
@@ -421,6 +431,8 @@ def main():
|
|
|
421
431
|
merge(pd, sys.argv[2])
|
|
422
432
|
elif cmd == "reconcile":
|
|
423
433
|
reconcile(pd, sys.argv[2])
|
|
434
|
+
elif cmd == "roots":
|
|
435
|
+
roots(pd)
|
|
424
436
|
else:
|
|
425
437
|
sys.exit(f"unknown command: {cmd}")
|
|
426
438
|
|
|
@@ -17,9 +17,30 @@ merge_owned_dirs_into_schema() {
|
|
|
17
17
|
echo " [acct-schema] op=seed-owned-dirs ${_out}"
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
# seed_domain_root_dirs <account_dir>
|
|
21
|
+
# mkdir each root domain bucket the brand's vertical projects (Property ->
|
|
22
|
+
# properties/ for estate-agent, Job/Customer/... for construction), so the
|
|
23
|
+
# operator-data buckets exist as directories, not just allowed names. Idempotent.
|
|
24
|
+
# Logs the buckets created so a provision can be audited from its log.
|
|
25
|
+
seed_domain_root_dirs() {
|
|
26
|
+
local _acct="$1"
|
|
27
|
+
local _created=""
|
|
28
|
+
local _d
|
|
29
|
+
while IFS= read -r _d; do
|
|
30
|
+
[ -n "$_d" ] || continue
|
|
31
|
+
if [ ! -d "$_acct/$_d" ]; then
|
|
32
|
+
mkdir -p "$_acct/$_d"
|
|
33
|
+
_created="${_created:+$_created,}$_d"
|
|
34
|
+
fi
|
|
35
|
+
done < <(PROJECT_DIR="$PROJECT_DIR" python3 "$_ACCT_SCHEMA_PY" roots 2>/dev/null)
|
|
36
|
+
echo " [acct-schema] seeded-roots=${_created:-none}"
|
|
37
|
+
}
|
|
38
|
+
|
|
20
39
|
# reconcile_all_accounts_owned_dirs <accounts_dir>
|
|
21
40
|
# Standing check + backfill: for every account dir with an account.json, log the
|
|
22
|
-
# pre-merge present state of each declared owned dir,
|
|
41
|
+
# pre-merge present state of each declared owned dir, merge (backfill), then seed
|
|
42
|
+
# the vertical's root buckets so an existing account (sub-accounts are only
|
|
43
|
+
# provisioned once, at creation) gains the same directories a fresh one gets.
|
|
23
44
|
reconcile_all_accounts_owned_dirs() {
|
|
24
45
|
local _accts="$1"
|
|
25
46
|
[ -d "$_accts" ] || return 0
|
|
@@ -29,5 +50,6 @@ reconcile_all_accounts_owned_dirs() {
|
|
|
29
50
|
PROJECT_DIR="$PROJECT_DIR" python3 "$_ACCT_SCHEMA_PY" reconcile "$_acct" 2>/dev/null \
|
|
30
51
|
| while IFS= read -r _line; do echo " [acct-schema] op=reconcile ${_line}"; done
|
|
31
52
|
merge_owned_dirs_into_schema "$_acct"
|
|
53
|
+
seed_domain_root_dirs "$_acct"
|
|
32
54
|
done
|
|
33
55
|
}
|
|
@@ -45,6 +45,11 @@ provision_account_dir() {
|
|
|
45
45
|
# seeded SCHEMA.md so the guard admits them (e.g. quoting/ on SiteDesk).
|
|
46
46
|
merge_owned_dirs_into_schema "$ACCOUNT_DIR"
|
|
47
47
|
|
|
48
|
+
# Create the vertical's root domain buckets on disk (Property -> properties/
|
|
49
|
+
# for estate-agent, Job/Customer/... for construction) so they are present
|
|
50
|
+
# directories, not just allowed names the operator never sees.
|
|
51
|
+
seed_domain_root_dirs "$ACCOUNT_DIR"
|
|
52
|
+
|
|
48
53
|
# Claude Code discovers project-level .claude/ relative to the nearest .git
|
|
49
54
|
# root. Without a .git in the account dir it traverses up to ~/maxy/.git and
|
|
50
55
|
# misses accountDir/.claude/. Initialise a repo so the account dir IS the root.
|
|
@@ -27,6 +27,11 @@ export interface RefVerdict {
|
|
|
27
27
|
reason?: "inconsistent";
|
|
28
28
|
refs?: string[];
|
|
29
29
|
}
|
|
30
|
+
export interface SignatureVerdict {
|
|
31
|
+
pass: boolean;
|
|
32
|
+
skipped?: boolean;
|
|
33
|
+
reason?: "not-order" | "no-asset" | "absent";
|
|
34
|
+
}
|
|
30
35
|
/** The document's displayed grand total (data-quote-total) must equal the
|
|
31
36
|
* engine total at penny precision — money is penny-precise, so a sub-penny
|
|
32
37
|
* engine total (e.g. 3395.235) reconciles against the penny total the client
|
|
@@ -64,4 +69,14 @@ export declare function gateTermsWeight(html: string): TermsVerdict;
|
|
|
64
69
|
* `1848/1` class, where the header says `1848` and the acceptance block says
|
|
65
70
|
* `1848/1` — fails and reports the distinct values in first-seen order. */
|
|
66
71
|
export declare function gateReferenceConsistency(html: string): RefVerdict;
|
|
72
|
+
/** An order-class document (marked `data-doc-order`) must embed the account's
|
|
73
|
+
* declared signature asset (`quoting/templates/signature.html`) verbatim — the
|
|
74
|
+
* operator's own signature block, its structure and embedded image data, not a
|
|
75
|
+
* rebuilt approximation. The order marker is the class signal, checked first: a
|
|
76
|
+
* document without it is not an order and is not checked (a plain quote carries
|
|
77
|
+
* no signature). An order with no declared asset skips (the same opt-in as the
|
|
78
|
+
* header template: no declared asset → no enforcement). An order with an asset
|
|
79
|
+
* declared must contain that asset verbatim (whitespace tolerant, the header
|
|
80
|
+
* mechanism); one that omits it fails closed — an unsigned order is not shipped. */
|
|
81
|
+
export declare function gateSignatureAsset(html: string, signatureTemplate: string | null): SignatureVerdict;
|
|
67
82
|
//# sourceMappingURL=gates.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gates.d.ts","sourceRoot":"","sources":["../../src/lib/gates.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"gates.d.ts","sourceRoot":"","sources":["../../src/lib/gates.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,gBAAgB;IAAG,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE;AACnE,MAAM,WAAW,WAAW;IAAG,IAAI,EAAE,OAAO,CAAA;CAAE;AAC9C,MAAM,WAAW,eAAe;IAAG,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,GAAG,UAAU,GAAG,WAAW,GAAG,UAAU,CAAA;CAAE;AACzI,MAAM,WAAW,aAAa;IAAG,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,kBAAkB,GAAG,UAAU,CAAA;CAAE;AAC7G,MAAM,WAAW,YAAY;IAAG,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,eAAe,CAAA;CAAE;AAC5F,MAAM,WAAW,UAAU;IAAG,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,cAAc,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE;AACxH,MAAM,WAAW,gBAAgB;IAAG,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,GAAG,UAAU,GAAG,QAAQ,CAAA;CAAE;AAEpH;;;;;;;sEAOsE;AACtE,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,gBAAgB,CAMjF;AAED;8EAC8E;AAC9E,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAExD;AAED;+EAC+E;AAC/E,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,CAS1D;AAUD;;;;;;;+DAO+D;AAC/D,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,aAAa,CAM1F;AA4BD;;;;iEAIiE;AACjE,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,CAO1D;AAED;;;;;4EAK4E;AAC5E,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAMjE;AAED;;;;;;;;qFAQqF;AACrF,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,GAAG,IAAI,GAAG,gBAAgB,CAMnG"}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
// data-doc-header the header block (must be the declared canonical template verbatim)
|
|
8
8
|
// data-doc-terms the standing-terms section (must carry no above-normal weight)
|
|
9
9
|
// data-doc-ref every place the document reference appears (must all agree)
|
|
10
|
+
// data-doc-order the document is issued as an order (must embed the declared signature asset)
|
|
10
11
|
/** The document's displayed grand total (data-quote-total) must equal the
|
|
11
12
|
* engine total at penny precision — money is penny-precise, so a sub-penny
|
|
12
13
|
* engine total (e.g. 3395.235) reconciles against the penny total the client
|
|
@@ -127,4 +128,22 @@ export function gateReferenceConsistency(html) {
|
|
|
127
128
|
return { pass: true, ref: distinct[0] };
|
|
128
129
|
return { pass: false, reason: "inconsistent", refs: distinct };
|
|
129
130
|
}
|
|
131
|
+
/** An order-class document (marked `data-doc-order`) must embed the account's
|
|
132
|
+
* declared signature asset (`quoting/templates/signature.html`) verbatim — the
|
|
133
|
+
* operator's own signature block, its structure and embedded image data, not a
|
|
134
|
+
* rebuilt approximation. The order marker is the class signal, checked first: a
|
|
135
|
+
* document without it is not an order and is not checked (a plain quote carries
|
|
136
|
+
* no signature). An order with no declared asset skips (the same opt-in as the
|
|
137
|
+
* header template: no declared asset → no enforcement). An order with an asset
|
|
138
|
+
* declared must contain that asset verbatim (whitespace tolerant, the header
|
|
139
|
+
* mechanism); one that omits it fails closed — an unsigned order is not shipped. */
|
|
140
|
+
export function gateSignatureAsset(html, signatureTemplate) {
|
|
141
|
+
if (!/\bdata-doc-order\b/i.test(html))
|
|
142
|
+
return { pass: true, skipped: true, reason: "not-order" };
|
|
143
|
+
// A null or empty/whitespace-only template is "no signature asset declared":
|
|
144
|
+
// skip, rather than let `includes("")` report a pass that enforced nothing.
|
|
145
|
+
if (signatureTemplate === null || normaliseHtml(signatureTemplate) === "")
|
|
146
|
+
return { pass: true, skipped: true, reason: "no-asset" };
|
|
147
|
+
return normaliseHtml(html).includes(normaliseHtml(signatureTemplate)) ? { pass: true } : { pass: false, reason: "absent" };
|
|
148
|
+
}
|
|
130
149
|
//# sourceMappingURL=gates.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gates.js","sourceRoot":"","sources":["../../src/lib/gates.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,yEAAyE;AACzE,6DAA6D;AAC7D,gFAAgF;AAChF,qFAAqF;AACrF,mFAAmF;AACnF,2FAA2F;AAC3F,sFAAsF;AACtF,mFAAmF;
|
|
1
|
+
{"version":3,"file":"gates.js","sourceRoot":"","sources":["../../src/lib/gates.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type SignLinkVerdict } from "./gates.js";
|
|
2
|
-
export type RenderFault = "render-absent" | "render-error" | "reconcile-fail" | "margin-leak" | "sign-link" | "header-mismatch" | "terms-weight" | "ref-inconsistent";
|
|
2
|
+
export type RenderFault = "render-absent" | "render-error" | "reconcile-fail" | "margin-leak" | "sign-link" | "header-mismatch" | "terms-weight" | "ref-inconsistent" | "signature-absent";
|
|
3
3
|
export interface RunRenderArgs {
|
|
4
4
|
accountDir: string;
|
|
5
5
|
renderScriptPath: string;
|
package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/dist/lib/render-run.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render-run.d.ts","sourceRoot":"","sources":["../../src/lib/render-run.ts"],"names":[],"mappings":"AAYA,OAAO,
|
|
1
|
+
{"version":3,"file":"render-run.d.ts","sourceRoot":"","sources":["../../src/lib/render-run.ts"],"names":[],"mappings":"AAYA,OAAO,EAQL,KAAK,eAAe,EACrB,MAAM,YAAY,CAAC;AAGpB,MAAM,MAAM,WAAW,GACnB,eAAe,GACf,cAAc,GACd,gBAAgB,GAChB,aAAa,GACb,WAAW,GACX,iBAAiB,GACjB,cAAc,GACd,kBAAkB,GAClB,kBAAkB,CAAC;AAEvB,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,MAAM,YAAY,GACpB;IACE,EAAE,EAAE,IAAI,CAAC;IACT,IAAI,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7D,SAAS,EAAE;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,SAAS,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC;IAGrE,QAAQ,EAAE,eAAe,GAAG,IAAI,CAAC;CAClC,GACD;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,WAAW,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAIvD,wBAAsB,SAAS,CAAC,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,CAkJvE"}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import { spawn } from "node:child_process";
|
|
10
10
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
11
11
|
import { dirname, join } from "node:path";
|
|
12
|
-
import { gateReconcile, gateMarginLeak, gateSignLink, gateHeaderConformance, gateTermsWeight, gateReferenceConsistency, } from "./gates.js";
|
|
12
|
+
import { gateReconcile, gateMarginLeak, gateSignLink, gateHeaderConformance, gateTermsWeight, gateReferenceConsistency, gateSignatureAsset, } from "./gates.js";
|
|
13
13
|
import { writeRenderReceipt } from "./render-receipt.js";
|
|
14
14
|
export async function runRender(a) {
|
|
15
15
|
if (!existsSync(a.renderScriptPath)) {
|
|
@@ -95,9 +95,18 @@ export async function runRender(a) {
|
|
|
95
95
|
const header = gateHeaderConformance(html.quote, headerTemplate);
|
|
96
96
|
const terms = gateTermsWeight(html.quote);
|
|
97
97
|
const ref = gateReferenceConsistency(html.quote);
|
|
98
|
+
// Order-class signature gate: when the render marks the client quote an order
|
|
99
|
+
// (data-doc-order), it must embed the account's declared signature asset
|
|
100
|
+
// (quoting/templates/signature.html, parallel to the header template). A
|
|
101
|
+
// non-order quote is not checked; an order with no declared asset skips. A
|
|
102
|
+
// marked order missing the declared asset fails closed, so an unsigned order
|
|
103
|
+
// writes no receipt and the browser-pdf-save gate then refuses the print.
|
|
104
|
+
const signatureAssetPath = join(a.accountDir, "quoting", "templates", "signature.html");
|
|
105
|
+
const signatureAsset = existsSync(signatureAssetPath) ? readFileSync(signatureAssetPath, "utf8") : null;
|
|
106
|
+
const signature = gateSignatureAsset(html.quote, signatureAsset);
|
|
98
107
|
const internalOk = recInternal.pass;
|
|
99
108
|
const breakdownOk = recBreakdown.pass && leakBreakdown.pass;
|
|
100
|
-
const quoteOk = recQuote.pass && leakQuote.pass && signOk && header.pass && terms.pass && ref.pass;
|
|
109
|
+
const quoteOk = recQuote.pass && leakQuote.pass && signOk && header.pass && terms.pass && ref.pass && signature.pass;
|
|
101
110
|
if (!internalOk || !breakdownOk || !quoteOk) {
|
|
102
111
|
// Reason names the first failing gate, in document then gate order.
|
|
103
112
|
let reason;
|
|
@@ -134,10 +143,14 @@ export async function runRender(a) {
|
|
|
134
143
|
reason = "terms-weight";
|
|
135
144
|
detail = "quote terms section carries above-normal font weight (bold/strong/font-weight >= 500)";
|
|
136
145
|
}
|
|
137
|
-
else {
|
|
146
|
+
else if (!ref.pass) {
|
|
138
147
|
reason = "ref-inconsistent";
|
|
139
148
|
detail = `quote document references disagree: ${(ref.refs ?? []).join(", ")}`;
|
|
140
149
|
}
|
|
150
|
+
else {
|
|
151
|
+
reason = "signature-absent";
|
|
152
|
+
detail = "order-class quote (data-doc-order) does not embed the account's declared signature asset (quoting/templates/signature.html)";
|
|
153
|
+
}
|
|
141
154
|
return { ok: false, reason, detail };
|
|
142
155
|
}
|
|
143
156
|
const internalPath = join(a.accountDir, "memory/admin/customers", phone, `quote-${a.jobId}-internal.html`);
|
package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/dist/lib/render-run.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render-run.js","sourceRoot":"","sources":["../../src/lib/render-run.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,4EAA4E;AAC5E,6EAA6E;AAC7E,+EAA+E;AAC/E,uEAAuE;AACvE,EAAE;AACF,wEAAwE;AACxE,iFAAiF;AAEjF,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EACL,aAAa,EACb,cAAc,EACd,YAAY,EACZ,qBAAqB,EACrB,eAAe,EACf,wBAAwB,
|
|
1
|
+
{"version":3,"file":"render-run.js","sourceRoot":"","sources":["../../src/lib/render-run.ts"],"names":[],"mappings":"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"}
|
package/payload/premium-plugins/sitedesk/plugins/sitedesk-job/mcp/src/lib/__tests__/gates.test.ts
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
gateHeaderConformance,
|
|
7
7
|
gateTermsWeight,
|
|
8
8
|
gateReferenceConsistency,
|
|
9
|
+
gateSignatureAsset,
|
|
9
10
|
} from "../gates.js";
|
|
10
11
|
|
|
11
12
|
describe("gateReconcile — displayed total must equal the engine total", () => {
|
|
@@ -142,3 +143,27 @@ describe("gateReferenceConsistency — one document reference across every marke
|
|
|
142
143
|
expect(gateReferenceConsistency(html)).toEqual({ pass: false, reason: "inconsistent", refs: ["1848", "1848/1"] });
|
|
143
144
|
});
|
|
144
145
|
});
|
|
146
|
+
|
|
147
|
+
describe("gateSignatureAsset — an order must embed the account's declared signature asset", () => {
|
|
148
|
+
const SIGNATURE = '<div data-doc-signature><img src="data:image/png;base64,AAAAsigBBBB"><span>A. Glazier, Director</span></div>';
|
|
149
|
+
it("skips a document that is not an order (no data-doc-order marker), even with an asset declared", () => {
|
|
150
|
+
expect(gateSignatureAsset("<body><h1>Quote</h1></body>", SIGNATURE)).toEqual({ pass: true, skipped: true, reason: "not-order" });
|
|
151
|
+
});
|
|
152
|
+
it("skips an order when the account declares no signature asset (template null)", () => {
|
|
153
|
+
expect(gateSignatureAsset('<body data-doc-order><h1>Order</h1></body>', null)).toEqual({ pass: true, skipped: true, reason: "no-asset" });
|
|
154
|
+
});
|
|
155
|
+
it("skips an order on an empty or whitespace-only declared asset rather than passing via includes(\"\")", () => {
|
|
156
|
+
expect(gateSignatureAsset('<body data-doc-order><h1>Order</h1></body>', " \n ")).toEqual({ pass: true, skipped: true, reason: "no-asset" });
|
|
157
|
+
});
|
|
158
|
+
it("passes when the order embeds the declared signature asset verbatim (whitespace-tolerant)", () => {
|
|
159
|
+
const rendered = `<body data-doc-order>\n <div data-doc-signature>\n <img src="data:image/png;base64,AAAAsigBBBB">\n <span>A. Glazier, Director</span>\n </div>\n</body>`;
|
|
160
|
+
expect(gateSignatureAsset(rendered, SIGNATURE)).toEqual({ pass: true });
|
|
161
|
+
});
|
|
162
|
+
it("fails an order that carries the order marker but omits the declared signature asset", () => {
|
|
163
|
+
expect(gateSignatureAsset('<body data-doc-order><h1>Order</h1><p>terms</p></body>', SIGNATURE)).toEqual({ pass: false, reason: "absent" });
|
|
164
|
+
});
|
|
165
|
+
it("fails an order whose signature block is a rebuilt approximation (different signer data)", () => {
|
|
166
|
+
const rebuilt = '<body data-doc-order><div data-doc-signature><img src="data:image/png;base64,AAAAsigBBBB"><span>Someone Else</span></div></body>';
|
|
167
|
+
expect(gateSignatureAsset(rebuilt, SIGNATURE)).toEqual({ pass: false, reason: "absent" });
|
|
168
|
+
});
|
|
169
|
+
});
|
|
@@ -268,6 +268,58 @@ process.stdin.on("end", () => {
|
|
|
268
268
|
});
|
|
269
269
|
});
|
|
270
270
|
|
|
271
|
+
describe("runRender — order-class signature-asset gate against a declared asset", () => {
|
|
272
|
+
let sdir: string;
|
|
273
|
+
const SIG_TPL = '<div data-doc-signature><img src="data:image/png;base64,AAAAsigBBBB"><span>A. Glazier, Director</span></div>';
|
|
274
|
+
// Emit a quote body that is either an order (data-doc-order) or a plain quote,
|
|
275
|
+
// optionally embedding a signature block. Brand/layout is the operator's; the
|
|
276
|
+
// tool only gates. No acceptance anchor (default, no-signer path).
|
|
277
|
+
const renderEmitting = (quoteBody: string) => `
|
|
278
|
+
let input = "";
|
|
279
|
+
process.stdin.on("data", (d) => (input += d));
|
|
280
|
+
process.stdin.on("end", () => {
|
|
281
|
+
const { figures } = JSON.parse(input);
|
|
282
|
+
const t = figures.total;
|
|
283
|
+
const quote = ${JSON.stringify(quoteBody)}.replace(/__T__/g, t);
|
|
284
|
+
process.stdout.write(JSON.stringify({
|
|
285
|
+
internal: '<html><body><td data-internal-only>c</td><p data-quote-total="' + t + '">Total ' + t + '</p></body></html>',
|
|
286
|
+
breakdown: '<html><body><p data-quote-total="' + t + '">Total ' + t + '</p></body></html>',
|
|
287
|
+
quote,
|
|
288
|
+
}));
|
|
289
|
+
});
|
|
290
|
+
`;
|
|
291
|
+
const ORDER_SIGNED = '<html><body data-doc-order><p data-quote-total="__T__">Total __T__</p>' + SIG_TPL + '</body></html>';
|
|
292
|
+
const ORDER_UNSIGNED = '<html><body data-doc-order><p data-quote-total="__T__">Total __T__</p></body></html>';
|
|
293
|
+
const PLAIN_QUOTE = '<html><body><p data-quote-total="__T__">Total __T__</p></body></html>';
|
|
294
|
+
|
|
295
|
+
beforeAll(() => {
|
|
296
|
+
sdir = mkdtempSync(join(tmpdir(), "sd-render-sig-"));
|
|
297
|
+
mkdirSync(join(sdir, "quoting", "templates"), { recursive: true });
|
|
298
|
+
writeFileSync(join(sdir, "quoting", "templates", "signature.html"), SIG_TPL);
|
|
299
|
+
writeFileSync(join(sdir, "quoting", "render-order-signed.mjs"), renderEmitting(ORDER_SIGNED));
|
|
300
|
+
writeFileSync(join(sdir, "quoting", "render-order-unsigned.mjs"), renderEmitting(ORDER_UNSIGNED));
|
|
301
|
+
writeFileSync(join(sdir, "quoting", "render-plain.mjs"), renderEmitting(PLAIN_QUOTE));
|
|
302
|
+
});
|
|
303
|
+
afterAll(() => rmSync(sdir, { recursive: true, force: true }));
|
|
304
|
+
|
|
305
|
+
it("passes when the order embeds the declared signature asset verbatim", async () => {
|
|
306
|
+
const r = await runRender({ accountDir: sdir, renderScriptPath: join(sdir, "quoting", "render-order-signed.mjs"), figures, jobId: "job-sig-ok" });
|
|
307
|
+
expect(r.ok).toBe(true);
|
|
308
|
+
expect(existsSync(receiptPath(sdir, "job-sig-ok"))).toBe(true);
|
|
309
|
+
});
|
|
310
|
+
it("signature-absent when the order omits the declared signature asset (fail-closed, nothing filed)", async () => {
|
|
311
|
+
const r = await runRender({ accountDir: sdir, renderScriptPath: join(sdir, "quoting", "render-order-unsigned.mjs"), figures, jobId: "job-sig-bad" });
|
|
312
|
+
expect(r).toMatchObject({ ok: false, reason: "signature-absent" });
|
|
313
|
+
expect(existsSync(join(sdir, "memory/users/44700900000/documents/quote-job-sig-bad.html"))).toBe(false);
|
|
314
|
+
expect(existsSync(receiptPath(sdir, "job-sig-bad"))).toBe(false);
|
|
315
|
+
});
|
|
316
|
+
it("files a plain quote (no order marker) even though a signature asset is declared — non-orders are not checked", async () => {
|
|
317
|
+
const r = await runRender({ accountDir: sdir, renderScriptPath: join(sdir, "quoting", "render-plain.mjs"), figures, jobId: "job-sig-plain" });
|
|
318
|
+
expect(r.ok).toBe(true);
|
|
319
|
+
expect(existsSync(receiptPath(sdir, "job-sig-plain"))).toBe(true);
|
|
320
|
+
});
|
|
321
|
+
});
|
|
322
|
+
|
|
271
323
|
describe("runRender — script faults", () => {
|
|
272
324
|
it("render-absent when the render script is missing", async () => {
|
|
273
325
|
const r = await runRender({ accountDir: dir, renderScriptPath: rs("nope.mjs"), figures, signUrl, jobId });
|