@rubytech/create-realagent-code 0.1.257 → 0.1.259

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 (42) hide show
  1. package/dist/__tests__/installer-settings-permissions.test.js +39 -9
  2. package/dist/permissions-seed.js +18 -8
  3. package/package.json +1 -1
  4. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +2 -2
  5. package/payload/platform/plugins/business-assistant/PLUGIN.md +4 -1
  6. package/payload/platform/plugins/business-assistant/references/quote-engine.md +122 -0
  7. package/payload/platform/plugins/business-assistant/references/quote-generation.md +94 -0
  8. package/payload/platform/plugins/business-assistant/references/quoting.md +29 -0
  9. package/payload/platform/plugins/business-assistant/skills/pricing-method/SKILL.md +78 -0
  10. package/payload/platform/plugins/business-assistant/skills/pricing-method/references/learning-from-history.md +51 -0
  11. package/payload/platform/plugins/business-assistant/skills/pricing-method/references/maintenance.md +32 -0
  12. package/payload/platform/plugins/business-assistant/skills/pricing-method/references/manual-definition.md +42 -0
  13. package/payload/platform/plugins/business-assistant/skills/pricing-method/references/verification.md +37 -0
  14. package/payload/platform/plugins/docs/references/platform.md +1 -1
  15. package/payload/platform/plugins/email/PLUGIN.md +2 -2
  16. package/payload/platform/plugins/email/mcp/dist/index.js +16 -0
  17. package/payload/platform/plugins/email/mcp/dist/index.js.map +1 -1
  18. package/payload/platform/plugins/email/mcp/dist/lib/smtp.d.ts +8 -0
  19. package/payload/platform/plugins/email/mcp/dist/lib/smtp.d.ts.map +1 -1
  20. package/payload/platform/plugins/email/mcp/dist/lib/smtp.js +22 -2
  21. package/payload/platform/plugins/email/mcp/dist/lib/smtp.js.map +1 -1
  22. package/payload/platform/plugins/email/mcp/dist/tools/email-reply.d.ts +2 -0
  23. package/payload/platform/plugins/email/mcp/dist/tools/email-reply.d.ts.map +1 -1
  24. package/payload/platform/plugins/email/mcp/dist/tools/email-reply.js +4 -2
  25. package/payload/platform/plugins/email/mcp/dist/tools/email-reply.js.map +1 -1
  26. package/payload/platform/plugins/email/mcp/dist/tools/email-send.d.ts +2 -0
  27. package/payload/platform/plugins/email/mcp/dist/tools/email-send.d.ts.map +1 -1
  28. package/payload/platform/plugins/email/mcp/dist/tools/email-send.js +4 -2
  29. package/payload/platform/plugins/email/mcp/dist/tools/email-send.js.map +1 -1
  30. package/payload/platform/plugins/email/references/email-reference.md +5 -0
  31. package/payload/platform/scripts/backfill-bypass-permissions.sh +58 -0
  32. package/payload/platform/scripts/setup-account.sh +15 -10
  33. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
  34. package/payload/platform/services/claude-session-manager/dist/http-server.js +103 -31
  35. package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
  36. package/payload/platform/services/claude-session-manager/dist/jsonl-path.d.ts +26 -0
  37. package/payload/platform/services/claude-session-manager/dist/jsonl-path.d.ts.map +1 -1
  38. package/payload/platform/services/claude-session-manager/dist/jsonl-path.js +36 -0
  39. package/payload/platform/services/claude-session-manager/dist/jsonl-path.js.map +1 -1
  40. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
  41. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +27 -22
  42. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
@@ -1,8 +1,12 @@
1
- // Task 583 — Installer must seed permissions.defaultMode=bypassPermissions
2
- // and permissions.allow=["*"] into the brand-scoped settings.json so
3
- // claude.ai/code sessions never fall through to the Anthropic remote
4
- // auto-classifier. Existing top-level keys must be preserved; an
5
- // already-correct file must be a true no-op (mtime unchanged).
1
+ // Task 583 / Task 664 — Installer must seed permissions.defaultMode=
2
+ // bypassPermissions and permissions.allow=[] into the brand-scoped
3
+ // settings.json so claude.ai/code sessions never fall through to the
4
+ // Anthropic remote auto-classifier. bypassPermissions alone carries the
5
+ // Stage-1 short-circuit; the old wildcard allow=["*"] was redundant and is
6
+ // rejected by Claude Code >= 2.1.167 (blocking Settings Warning that halts
7
+ // the rc-spawn bind — Task 664). Existing top-level keys must be preserved;
8
+ // an already-correct file must be a true no-op (mtime unchanged); a
9
+ // "*"-bearing file must be rewritten on upgrade.
6
10
  import test from "node:test";
7
11
  import assert from "node:assert/strict";
8
12
  import { mkdtempSync, readFileSync, writeFileSync, statSync, rmSync } from "node:fs";
@@ -19,7 +23,7 @@ test("empty target: creates settings.json with the permissions block", () => {
19
23
  assert.equal(result.action, "seeded");
20
24
  assert.equal(result.path, join(dir, "settings.json"));
21
25
  const parsed = JSON.parse(readFileSync(result.path, "utf-8"));
22
- assert.deepEqual(parsed, { permissions: { defaultMode: "bypassPermissions", allow: ["*"], deny: [] } });
26
+ assert.deepEqual(parsed, { permissions: { defaultMode: "bypassPermissions", allow: [], deny: [] } });
23
27
  assert.equal(assertBypassPermissionsSeed(dir).status, "ok");
24
28
  }
25
29
  finally {
@@ -46,7 +50,7 @@ test("existing keys preserved verbatim; permissions added", () => {
46
50
  assert.equal(parsed.inputNeededNotifEnabled, false);
47
51
  assert.equal(parsed.agentPushNotifEnabled, false);
48
52
  assert.equal(parsed.model, "opus");
49
- assert.deepEqual(parsed.permissions, { defaultMode: "bypassPermissions", allow: ["*"], deny: [] });
53
+ assert.deepEqual(parsed.permissions, { defaultMode: "bypassPermissions", allow: [], deny: [] });
50
54
  assert.equal(assertBypassPermissionsSeed(dir).status, "ok");
51
55
  }
52
56
  finally {
@@ -57,7 +61,7 @@ test("idempotent: already-correct block triggers no write", () => {
57
61
  const dir = freshDir();
58
62
  try {
59
63
  const settingsPath = join(dir, "settings.json");
60
- writeFileSync(settingsPath, JSON.stringify({ model: "opus", permissions: { defaultMode: "bypassPermissions", allow: ["*"], deny: [] } }, null, 2));
64
+ writeFileSync(settingsPath, JSON.stringify({ model: "opus", permissions: { defaultMode: "bypassPermissions", allow: [], deny: [] } }, null, 2));
61
65
  const before = statSync(settingsPath).mtimeMs;
62
66
  // Small busy-wait so a same-millisecond write would be detectable on
63
67
  // filesystems with millisecond mtime granularity.
@@ -106,7 +110,33 @@ test("assert: partial block (no wildcard) reports missing", () => {
106
110
  });
107
111
  test("BYPASS_PERMISSIONS_BLOCK frozen literal matches expected shape", () => {
108
112
  assert.equal(BYPASS_PERMISSIONS_BLOCK.defaultMode, "bypassPermissions");
109
- assert.deepEqual([...BYPASS_PERMISSIONS_BLOCK.allow], ["*"]);
113
+ assert.deepEqual([...BYPASS_PERMISSIONS_BLOCK.allow], []);
110
114
  assert.deepEqual([...BYPASS_PERMISSIONS_BLOCK.deny], []);
111
115
  assert.ok(Object.isFrozen(BYPASS_PERMISSIONS_BLOCK));
112
116
  });
117
+ test("assert: wildcard allow reports missing (forces rewrite on upgrade)", () => {
118
+ const dir = freshDir();
119
+ try {
120
+ writeFileSync(join(dir, "settings.json"), JSON.stringify({ permissions: { defaultMode: "bypassPermissions", allow: ["*"], deny: [] } }));
121
+ assert.equal(assertBypassPermissionsSeed(dir).status, "missing");
122
+ }
123
+ finally {
124
+ rmSync(dir, { recursive: true, force: true });
125
+ }
126
+ });
127
+ test("seeding over a wildcard-bearing file rewrites allow to [] preserving other keys", () => {
128
+ const dir = freshDir();
129
+ try {
130
+ const p = join(dir, "settings.json");
131
+ writeFileSync(p, JSON.stringify({ model: "opus", permissions: { defaultMode: "bypassPermissions", allow: ["*"], deny: [] } }));
132
+ const result = seedBypassPermissionsSettings(dir);
133
+ assert.equal(result.action, "seeded");
134
+ const parsed = JSON.parse(readFileSync(p, "utf-8"));
135
+ assert.equal(parsed.model, "opus");
136
+ assert.deepEqual(parsed.permissions, { defaultMode: "bypassPermissions", allow: [], deny: [] });
137
+ assert.equal(assertBypassPermissionsSeed(dir).status, "ok");
138
+ }
139
+ finally {
140
+ rmSync(dir, { recursive: true, force: true });
141
+ }
142
+ });
@@ -1,9 +1,17 @@
1
- // Task 583 — Seed Claude Code's two-stage permission classifier with a
2
- // wildcard allow so claude.ai/code sessions never fall through to the
3
- // Anthropic remote auto-classifier. The classifier routes `Agent`/`Task`
4
- // to `ask`, which surfaces as a permission prompt that an unattended
5
- // operator never clicks — observed as 4-minute dead-air hangs on
6
- // realagent-code.
1
+ // Task 583 / Task 664 — Seed Claude Code's permission block so claude.ai/code
2
+ // sessions never fall through to the Anthropic remote auto-classifier. The
3
+ // classifier routes `Agent`/`Task` to `ask`, which surfaces as a permission
4
+ // prompt that an unattended operator never clicks observed as 4-minute
5
+ // dead-air hangs on realagent-code.
6
+ //
7
+ // `defaultMode: "bypassPermissions"` is the load-bearing key: it resolves to
8
+ // skip_all_permission_checks before any allow/deny matching, short-circuiting
9
+ // Stage 1 for every tool. The Task 583 belt-and-suspenders `allow: ["*"]` was
10
+ // redundant and is now actively rejected by Claude Code >= 2.1.167: `"*"` is
11
+ // not a legal allow token, so the CLI renders a blocking "Settings Warning"
12
+ // screen that, under `--remote-control` in a PTY, no operator dismisses — the
13
+ // stdout bind banner never prints and every rc-spawn times out (Task 664).
14
+ // The allow array is therefore empty and MUST stay "*"-free.
7
15
  //
8
16
  // This module is the single writer for the brand-scoped settings.json
9
17
  // permissions block. Account-scoped settings.json is written by
@@ -12,7 +20,7 @@ import { readFileSync, writeFileSync, renameSync, existsSync, mkdirSync } from "
12
20
  import { join, dirname } from "node:path";
13
21
  export const BYPASS_PERMISSIONS_BLOCK = Object.freeze({
14
22
  defaultMode: "bypassPermissions",
15
- allow: ["*"],
23
+ allow: [],
16
24
  deny: [],
17
25
  });
18
26
  function isAlreadyCorrect(permissions) {
@@ -21,7 +29,9 @@ function isAlreadyCorrect(permissions) {
21
29
  const p = permissions;
22
30
  if (p.defaultMode !== "bypassPermissions")
23
31
  return false;
24
- if (!Array.isArray(p.allow) || !p.allow.includes("*"))
32
+ // Require an empty allow array. A "*"-bearing file (the pre-664 seed) is no
33
+ // longer correct, so this returns false and forces a rewrite on upgrade.
34
+ if (!Array.isArray(p.allow) || p.allow.length !== 0)
25
35
  return false;
26
36
  if (!Array.isArray(p.deny) || p.deny.length !== 0)
27
37
  return false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rubytech/create-realagent-code",
3
- "version": "0.1.257",
3
+ "version": "0.1.259",
4
4
  "description": "Install Real Agent — Built for agents. By agents.",
5
5
  "bin": {
6
6
  "create-realagent-code": "./dist/index.js"
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: platform-architecture
3
3
  description: Use when grounding any documented-surface claim about what Real Agent 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:0c253f3a1c9946f4256daea47978fa363b3a6eef6875b4a417792393c4fc00e9
4
+ content-hash: sha256:d73292d40056e98b2483ca095571c47e72f896e24c979cb40c60b6d99d3335ef
5
5
  brand: realagent-code
6
6
  product-name: Real Agent
7
7
  ---
@@ -302,7 +302,7 @@ If you suspect background processes are piling up, run `grep '\[reaper\]' ~/.{br
302
302
 
303
303
  ## Tool Permissions
304
304
 
305
- Every install seeds a wildcard `permissions.allow:["*"]` plus `defaultMode:"bypassPermissions"` into both the brand-scoped settings file (`~/.{brand}/.claude/settings.json`) and every account-scoped one (`<install>/data/accounts/<id>/.claude/settings.json`). This stops Claude Code from sending tool calls to its remote auto-classifier, which would otherwise surface a permission prompt in the chat that an unattended session never answers. What each subagent is allowed to use is still controlled by the `tools:` line in its agent file, not by a top-level allowlist. To verify after an install: `cat ~/.{brand}/.claude/settings.json | jq '.permissions'`.
305
+ Every install seeds `permissions.allow:[]` plus `defaultMode:"bypassPermissions"` into both the brand-scoped settings file (`~/.{brand}/.claude/settings.json`) and every account-scoped one (`<install>/data/accounts/<id>/.claude/settings.json`). `bypassPermissions` alone stops Claude Code from sending tool calls to its remote auto-classifier (it skips all permission checks), which would otherwise surface a permission prompt in the chat that an unattended session never answers. The `allow` array stays empty and **must never contain `"*"`**: Claude Code ≥ 2.1.167 rejects `"*"` as an allow token and renders a blocking Settings Warning that, under `--remote-control`, stops every `/rc-spawn` from binding (Task 664). What each subagent is allowed to use is still controlled by the `tools:` line in its agent file, not by a top-level allowlist. To verify after an install: `cat ~/.{brand}/.claude/settings.json | jq '.permissions'` (expect `allow: []`). To repair an install seeded before Task 664: `bash <install>/platform/scripts/backfill-bypass-permissions.sh ~/.{brand}`.
306
306
 
307
307
  ---
308
308
  # Plugins Guide
@@ -54,7 +54,10 @@ Load the relevant reference when the task requires it:
54
54
 
55
55
  - **Triage & escalation** → `references/escalation.md` — urgency classification (RED/AMBER/GREEN), customer handoff protocol, voice IVR handling
56
56
  - **Scheduling & briefings** → `references/scheduling.md` — booking protocol, geographic clustering, travel time estimation, morning briefings, holiday mode, GPS integration
57
- - **Quotes** → `references/quoting.md` — photo-to-quote, formatting, follow-up timings
57
+ - **Quotes (lightweight)** → `references/quoting.md` — photo-to-quote, formatting, follow-up timings
58
+ - **Pricing method** → invoke the `pricing-method` skill (via the Skill tool) to build, edit, re-learn, or verify an operator's own pricing method — the items, quantity rules, pricing rules, and roll-up the quote flow consumes
59
+ - **Priced quote compute** → `references/quote-engine.md` — generate & run the owner's bespoke pricing engine (measured/counted work → priced result), domain-agnostic compute contract
60
+ - **Quote generation (engine-driven outputs)** → `references/quote-generation.md` — the output contract for a priced job: internal margin-bearing view + client breakdown + client quote, each reconciling to one engine total and rendered to PDF
58
61
  - **Invoices & payment** → `references/invoicing.md` — HTML invoice creation, PDF generation via `browser-navigate` + `browser-pdf-save`, sending, payment chase protocol
59
62
  - **Customer records** → `references/crm.md` — contact record management, `contact_lookup`/`contact_update` usage, pipeline tracking, when to create/update records
60
63
  - **Document filing** → `references/document-management.md` — dual-scope filing system, naming conventions, folder structure for customer-facing vs internal documents
@@ -0,0 +1,122 @@
1
+ # Priced-quote compute engine
2
+
3
+ This reference is the **compute core** for a structured, priced quote: it turns a job (the items the
4
+ owner selected, with their measurements or counts) into priced figures — line prices, group
5
+ subtotals, and a final total — computed by **this owner's own pricing method**.
6
+
7
+ It carries no items, no units, no rates, no margins, no overheads, no taxes, and no roll-up sequence.
8
+ Every such value and rule is the owner's, captured as data. A trade pricing by the square metre, a
9
+ caterer pricing by the head, and a printer pricing by per-unit break are all priced by the same
10
+ approach — only their captured method differs.
11
+
12
+ The compute engine itself is **not shipped**. You generate a small bespoke engine for this owner the
13
+ first time they need a priced quote, and regenerate it whenever their business changes. The captured
14
+ method is data the engine reads. Both persist in the owner's workspace and are re-run on demand.
15
+
16
+ ## When this path applies
17
+
18
+ Use this for a **priced quote computed from the owner's method** — measured or counted work that has
19
+ to be priced the way this owner prices it, then totalled. This is distinct from the simple
20
+ photo-to-quote / chat-to-quote flow in `references/quoting.md`, which formats a quote the owner has
21
+ already priced. If the owner has already worked out the numbers, stay in `quoting.md`. Come here when
22
+ the numbers have to be **computed**.
23
+
24
+ **Precondition:** a captured method for this owner must exist. Its *content* (the owner's items,
25
+ rules, dials, roll-up, and past-job fixtures) is produced and maintained by the method-capture flow —
26
+ Task 659 — which populates the method/job shape **this reference defines** below. If no captured method
27
+ exists, capture it first; do not invent rates or rules here.
28
+
29
+ ## Where the engine and method live
30
+
31
+ The agent runs in the owner's workspace (the spawn cwd, `$ACCOUNT_DIR`). Keep the engine and the
32
+ captured method together under a `quoting/` subdirectory there, addressed by relative path:
33
+
34
+ - the **method** — the owner's items, dials, and roll-up, as data;
35
+ - the **engine** — the bespoke script that reads a method and a job and prices it;
36
+ - the owner's **past jobs**, each with the figures they actually issued, used to verify the engine.
37
+
38
+ These persist across sessions, are edited when the business changes, and are the engine's only inputs.
39
+ Nothing about the owner's business lives in this reference or in the plugin.
40
+
41
+ ## The method shape (owner data)
42
+
43
+ This section is the authoritative definition of the method/job shape; the method-capture flow (Task
44
+ 659) fills it with one owner's content. The method describes how this owner prices. For each
45
+ **priceable item** it records:
46
+
47
+ - what the item is (an identifier, a human label, a unit);
48
+ - **how a quantity arises** — the item's own rule for turning a measurement into a quantity (for
49
+ example, an area from supplied dimensions, a length, a volume), or that the item carries **no
50
+ measurement rule** because it is counted or taken as a single unit. This is the item's *default*
51
+ quantity rule; a quantity entered on the job line always overrides it (see the job shape);
52
+ - **how a line price is computed** — the owner's own rule for that item, in whatever form their
53
+ history shows it takes;
54
+ - which **group** the line rolls into (the owner's own section headings);
55
+ - whether the item is **standing-rule priced** (a rule fixes its price) or **judgement priced** (the
56
+ owner sets the price per job) — so the quote flow knows when it must ask the owner for a figure
57
+ rather than computing one.
58
+
59
+ Alongside the items the method holds:
60
+
61
+ - named **dials** — the owner's own scalars that their rules reference (a labour day-rate, a margin
62
+ multiplier, a percentage — whatever their method uses, named by them);
63
+ - the **roll-up** — an ordered list of steps applied above the line items to reach the final total.
64
+ Each step names its rule, the prior figures it reads, and the label of the figure it produces. This
65
+ is whatever sequence the owner actually applies. It is **not** a fixed margin-then-overhead-then-tax
66
+ chain: an owner may apply an overhead, a contingency, a discount, a tax, or nothing, in their own
67
+ order. The engine implements exactly the step kinds this owner's roll-up uses, and no others.
68
+
69
+ ## The job shape (per quote)
70
+
71
+ A job is the work selected for one quote: a list of lines, each naming an item and supplying **either**
72
+ measurements **or** a directly-entered quantity, plus — for a judgement-priced item — the price the
73
+ owner set for this line. The quantity source is decided per line by precedence: a quantity entered on
74
+ the line is used as-is; otherwise the engine derives the quantity from the line's measurements via the
75
+ item's measurement rule. An item with no measurement rule therefore requires an entered quantity (or
76
+ its single-unit default). The job also carries the header the issued quote needs (who it is for, the
77
+ address, the reference).
78
+
79
+ ## What the generated engine must do
80
+
81
+ The engine reads one method and one job and produces the priced result. It must:
82
+
83
+ 1. **Resolve each line's quantity** — an entered quantity takes precedence; otherwise derive it from
84
+ the line's measurements via the item's measurement rule — and record which source was used.
85
+ 2. **Resolve each line's price** — by the item's own rule, or by the per-line figure the owner gave for
86
+ a judgement item — and record which was used.
87
+ 3. **Group** the priced lines under the owner's own headings.
88
+ 4. **Run the roll-up** steps in their defined order to reach the final total.
89
+ 5. **Emit observability** so a wrong figure is diagnosable from the log, not just the document: per
90
+ line, the resolved quantity and its source (measurement-derived vs entered) and the price rule
91
+ applied and its source (item rule vs per-line override); and each roll-up step with its result.
92
+ 6. **Reconcile** the sum of the group subtotals against the running line-item total and report it as an
93
+ explicit pass/fail — not a silent assumption.
94
+ 7. **Verify** on demand: given one of the owner's own past jobs, compare every computed figure
95
+ (line, group, final) against the figures the owner actually issued and report each as a match or
96
+ a mismatch.
97
+
98
+ ## Expressiveness — bounded by the owner's history
99
+
100
+ Build the engine to support **exactly** the quantity, pricing, and roll-up forms the owner's own
101
+ history exercises — discovered when their method was captured. Do not add quantity bases, price-rule
102
+ forms, or roll-up step kinds the owner does not use. Unused generality is a liability: it is untested
103
+ against their figures and invites a wrong quote later. If a new job needs a form the method has never
104
+ expressed, that is a change to the captured method (and a re-verification), not a guess made here.
105
+
106
+ ## Acceptance — reproduction of the owner's own figures
107
+
108
+ The engine is correct only when it **reproduces the owner's own past jobs** — line prices, group
109
+ subtotals, and final totals — to the owner's own precision. The fixtures are that owner's
110
+ history, supplied with their method; there are no built-in figures to test against. Reproducing their
111
+ history, not parsing cleanly, is the signal that the engine prices the way they do. Verify on every
112
+ generate and every method change; a reproduction failure blocks issuing a computed quote.
113
+
114
+ ## Boundaries
115
+
116
+ - **Capturing and maintaining** the content of the method (items, rules, dials, roll-up, past-job
117
+ fixtures) is the method-capture flow — Task 659; it populates the shape this reference defines. This
118
+ reference consumes that method; it does not build it.
119
+ - **Rendering** the priced result into the documents the owner issues (internal view, client
120
+ breakdown, branded quote) is Task 658. Those documents are printed to PDF through the existing
121
+ `browser-pdf-save` path (see `references/invoicing.md`); this compute reference produces only the
122
+ figures, not the documents.
@@ -0,0 +1,94 @@
1
+ # Quote generation — output contract
2
+
3
+ This is the **output half** of the engine-driven quote path. The engine (the operator's captured
4
+ pricing method applied to a job) produces the figures; this contract turns those figures into the
5
+ documents the operator issues, each rendered to PDF.
6
+
7
+ It holds **no** business content. There are no fixed section names, no fixed wording, no fixed brand,
8
+ and no fixed page layout here. What each document contains and how it looks is the operator's, held
9
+ as their own data (their headings, their narrative, their charts, their logo, colours, contact and
10
+ terms, their page size). The same contract serves any trade or service business by reading that
11
+ operator's own document definitions.
12
+
13
+ This is distinct from the lightweight WhatsApp quote in `references/quoting.md`. Use that one for a
14
+ quick text quote. Use this one when the operator prices a job from a captured method and issues a set
15
+ of reconciling documents.
16
+
17
+ ## What the engine hands you
18
+
19
+ You render from the engine result. Read it as:
20
+
21
+ - an ordered set of priced **groups** (the operator's own stages or categories);
22
+ - each group an ordered set of **lines**; each line carries its quantity and the quantity's source
23
+ (measurement-derived or entered), its unit, its rate, its cost, and its sell;
24
+ - each group's **subtotal**;
25
+ - the operator's named **roll-up** steps — whatever sequence of adjustments and taxes they apply
26
+ above the line items, in order;
27
+ - the **totals**: the client-visible subtotal and the final total.
28
+
29
+ The engine guarantees the reconciliation invariant: the group subtotals carried through the roll-up
30
+ equal the final total. You never recompute these figures — you display them.
31
+
32
+ The engine itself, and how a line's quantity, rate, cost and sell are derived, are out of scope here
33
+ (that is the compute core). This contract only reads the result.
34
+
35
+ ## The three documents
36
+
37
+ The operator defines the content and styling of each; the **roles** are fixed, the **contents** are
38
+ not. A document is **engine-driven**: a section or line appears **only where the job priced it**.
39
+
40
+ | Role | Shows | Withholds | Filing scope |
41
+ |---|---|---|---|
42
+ | **Internal margin-bearing view** | every line's quantity and its source, unit, rate, cost and sell; each group subtotal; the full roll-up; the totals | nothing — this is the operator's own check on profitability before issuing | **admin-only** |
43
+ | **Client breakdown** | how the price is composed, at the granularity the operator chooses: the client-facing amount per group or line, the roll-up steps the client sees, and the headline figure | any cost, margin, or internal rate figure (the operator's own unit rate, not a client-facing per-unit price) | client-facing (shared) |
44
+ | **Client quote document** | the issued document in the operator's brand: their cover, their narrative or schedule, the client breakdown, the headline price, the acceptance block, and their standing terms | any cost, margin, or internal rate figure (the operator's own unit rate, not a client-facing per-unit price) | client-facing (shared) |
45
+
46
+ The internal view exists so the operator can confirm the job is profitable before it goes out; it may
47
+ expose cost and margin freely. The two client documents **never** surface a cost, margin, or
48
+ internal-rate figure. A column or line that shows the price to the client is fine; a figure that
49
+ reveals what the work cost the operator, or the markup applied, is a leak.
50
+
51
+ ### The margin boundary is the filing scope
52
+
53
+ The leak boundary is structural, not a matter of remembering to omit a column. File each document by
54
+ its scope, exactly as `references/document-management.md` defines:
55
+
56
+ - the **internal margin-bearing view** goes to the **admin-only** customer folder
57
+ (`memory/admin/customers/{phone}/`), which the customer never receives;
58
+ - the **client breakdown** and the **client quote** go to the customer-facing documents folder
59
+ (`memory/users/{phone}/documents/`).
60
+
61
+ A document that carries cost or margin must never be written to the shared scope.
62
+
63
+ ## Print fidelity
64
+
65
+ Each document becomes a PDF through the device's Chromium print pipeline, so the HTML must survive
66
+ that pipeline. Start from the A4 print conventions already in `references/invoicing.md` and
67
+ `references/document-management.md` — an `@page` size directive, `@media print` styles, and
68
+ `page-break-inside: avoid` on blocks that must not split — and keep the operator's own page size on
69
+ `@page` (their stationery may not be A4).
70
+
71
+ A multi-page issued quote needs two things beyond what those siblings show; state them explicitly in
72
+ the document:
73
+
74
+ - page-level chrome — a running header or footer and page numbers — via `@page` margin boxes, never
75
+ absolutely-positioned elements that drift across pages;
76
+ - `print-color-adjust: exact` on coloured fills, so the operator's brand fills survive print. The
77
+ renderer prints backgrounds, but make the intent explicit rather than relying on the default.
78
+
79
+ ## Producing the PDF
80
+
81
+ Render the document as self-contained HTML (styles inline, images by absolute local path), then use
82
+ the same path as every other business-assistant document:
83
+
84
+ 1. write the HTML to the document's filing-scope path;
85
+ 2. `browser-navigate` to it via a `file://` absolute URL;
86
+ 3. `browser-pdf-save` to an absolute `.pdf` path alongside the HTML;
87
+ 4. confirm a non-zero byte count from the `Saved PDF to <path> (<bytes>)` line before treating the PDF
88
+ as done.
89
+
90
+ ## Before you hand anything to the PDF tool
91
+
92
+ Log, for the run: which document roles were produced, each one's page count, and the reconciliation —
93
+ each document's displayed total against the engine total. If a document's total does not reconcile to
94
+ the engine total, fail loudly and stop. Never ship a document whose figures disagree with the engine.
@@ -1,5 +1,20 @@
1
1
  # Quoting
2
2
 
3
+ This reference is the **lightweight** quote path: read a handwritten or spoken quote, format it
4
+ cleanly, and send it as a WhatsApp message (or a one-off PDF on request).
5
+
6
+ When the operator prices a job from a captured pricing method and issues a set of documents that must
7
+ reconcile to one engine total — an internal margin-bearing view, a client breakdown that withholds
8
+ cost and margin, and a branded client quote — use `references/quote-generation.md` instead.
9
+
10
+ ## Where the operator's pricing comes from
11
+
12
+ The operator's priceable items, quantity rules, pricing rules, and roll-up are not held here. They are
13
+ captured and maintained by the `pricing-method` skill (invoke it via the Skill tool — see PLUGIN.md).
14
+ That skill is the single source of the operator's own method; the quote flow consumes it. Because the
15
+ method records which items are priced by a standing rule and which by per-job judgement, the quote flow
16
+ applies the standing-rule items itself and asks the operator only about the judgement-priced lines.
17
+
3
18
  ## Photo-to-Quote (Handwritten)
4
19
 
5
20
  1. Business owner sends a photo of their handwritten quote
@@ -22,6 +37,20 @@
22
37
  2. Format as above
23
38
  3. Confirm and send
24
39
 
40
+ ## Priced Quote (Computed from the Operator's Method)
41
+
42
+ The two flows above format a quote the owner has **already priced** — they read the figures off a photo
43
+ or out of a conversation. When the numbers instead have to be **computed** from the owner's own pricing
44
+ method — measured or counted work priced item by item and rolled up to a total the way this owner
45
+ prices — use `references/quote-engine.md`.
46
+
47
+ That reference is the compute core: it generates a bespoke pricing engine from the owner's captured
48
+ method and runs it on the job to produce line prices, group subtotals, and the final total,
49
+ with an observability log and a check against the owner's own past quotes. It carries none of the
50
+ owner's items, rates, or rules — those are the owner's captured data. Reach for it whenever the owner
51
+ hands over measurements or quantities rather than a finished price. If there is no captured method yet,
52
+ capture it first rather than inventing rates here.
53
+
25
54
  ## Quote Formatting Template
26
55
 
27
56
  Quotes sent to customers should be clean, readable, and professional — but delivered as a WhatsApp message (not a PDF, unless the business owner requests one).
@@ -0,0 +1,78 @@
1
+ ---
2
+ name: pricing-method
3
+ description: Use when an operator's own way of pricing jobs needs to become something the assistant can apply — onboarding a new business's pricing, building or editing their priceable items and rules by hand, re-learning their method from past quotes, or checking that the captured method reproduces their past quotes. Triggers include "set up my pricing", "learn how I quote", "add an item / change a rate", "my method's quotes don't match my past jobs", and any work that produces or maintains the method that the quote-generation flow consumes. Not for pricing or troubleshooting a single live quote — that is the quote-generation flow.
4
+ ---
5
+
6
+ # Pricing method
7
+
8
+ This skill ends with a correct, machine-usable representation of **one operator's own** pricing
9
+ method: the things they price (what each is, how it is quantified, how it is priced), the rules that
10
+ roll line prices up to a total, and the documents they issue. The quote-generation flow consumes that
11
+ method; this skill is the only place it is built and maintained.
12
+
13
+ ## Principle: the skill carries no business content
14
+
15
+ This skill ships no item catalogue, no units, no rates, no margin, overhead, contingency or tax
16
+ values, no roll-up formula, no document templates — **and no predetermined schema or data shape.** It
17
+ is a procedure for eliciting and encoding an operator's *own* method. The same skill must onboard a
18
+ trade that prices by the square metre, a caterer who prices per head in tiers, and a printer with
19
+ per-unit price breaks — businesses with nothing in common. Everything that varies between businesses
20
+ is captured as that operator's data, never written into this skill.
21
+
22
+ The operator's structure is **discovered, not imposed.** Capture it faithfully wherever it diverges
23
+ from any shape you might assume — an operator whose margin differs by category, who applies
24
+ adjustments in an unusual order, who quantifies one item from a measurement and another by eye. If you
25
+ find yourself forcing their method into a fixed template, the template is the defect. There is no
26
+ canonical set of fields; there is only what *their* method actually contains.
27
+
28
+ ## Building, verifying, and maintaining the method
29
+
30
+ Two paths build the method:
31
+
32
+ - **Learn from history** — derive the method from the operator's own past pricing artifacts. See
33
+ [references/learning-from-history.md](references/learning-from-history.md).
34
+ - **Define manually** — build or edit the method by hand for an operator with no usable history, or
35
+ making an adjustment. See [references/manual-definition.md](references/manual-definition.md).
36
+
37
+ Two activities keep it correct:
38
+
39
+ - **Verify against the operator's own outputs** — the method is accepted only when it reproduces that
40
+ operator's own past quotes to their own precision. See
41
+ [references/verification.md](references/verification.md).
42
+ - **Maintain** — re-learning or editing preserves prior manual adjustments and re-verifies green. See
43
+ [references/maintenance.md](references/maintenance.md).
44
+
45
+ **Onboarding** a new operator is a build path (learn from history, or define by hand) followed
46
+ *always* by verification: the method is not onboarded until it reproduces their past jobs. A learned
47
+ method and a hand-built method are the same kind of thing in the same captured form; nothing downstream
48
+ should be able to tell which path produced an entry.
49
+
50
+ ## Standing rule vs per-job judgement
51
+
52
+ For every priceable thing, the method records whether the operator prices it by a **standing rule**
53
+ (the same rule applies every time — a rate, a tier, a formula they always use) or by **per-job
54
+ judgement** (they decide the price afresh each job). This distinction is load-bearing: it is what lets
55
+ the quote flow stay quiet about standing-rule items and ask the operator only about judgement-priced
56
+ ones. Derive the classification from the operator's history — an item whose price moves
57
+ unpredictably across past jobs is judgement-priced; one that follows a consistent rule is not — or
58
+ take it from the operator directly when defining by hand.
59
+
60
+ ## Observability
61
+
62
+ Under-capture that hides until it produces a wrong quote is the failure this skill exists to prevent.
63
+ Two things are always visible:
64
+
65
+ - **A capture log.** Every item, rule, and dial derived is logged with where it came from. Just as
66
+ important, every part of a source artifact that could **not** be confidently encoded — skipped,
67
+ ambiguous, or guessed — is logged as such, so gaps are seen at onboarding, not discovered later in a
68
+ wrong quote.
69
+ - **A reconciliation report.** The standing check is reproduction against the operator's own history:
70
+ per past job, the **delta** between the reproduced figures and the operator's own, at line, group,
71
+ and final level — a delta, never a boolean. The verification pillar owns why and how
72
+ ([references/verification.md](references/verification.md)).
73
+
74
+ ## Scope
75
+
76
+ This skill produces and maintains the method only. The compute that applies the method to a job, and
77
+ the documents that present the result, are the quote-generation flow — separate work. This skill owns
78
+ *what the operator's method is*; it does not own pricing a live job or rendering a quote.
@@ -0,0 +1,51 @@
1
+ # Learning the method from history
2
+
3
+ The operator has been pricing jobs for years. That method already exists — it lives in their past
4
+ quotes, estimates, invoices, and spreadsheets. This pillar derives it from those artifacts so the
5
+ assistant can apply the same method going forward. The source is whatever the operator actually used:
6
+ a workbook, a folder of PDFs, photographed handwritten estimates, a price list. Work from what they
7
+ have.
8
+
9
+ ## What a complete derivation captures
10
+
11
+ From the operator's own artifacts, recover four things, and nothing the artifacts do not show:
12
+
13
+ - **The priceable things.** Each distinct thing the operator charges for. Two lines that are the same
14
+ priced thing under different wording are one method entry, not two — collapse repeated instances of
15
+ the same thing across jobs into a single entry, and let the variation across those instances tell you
16
+ how the thing is quantified and priced.
17
+ - **How each is quantified.** For each thing, how a job turns into a quantity. Some quantities are
18
+ derived from a measurement the operator takes (a length, an area, a volume, a weight); some are
19
+ counted directly (how many doors, heads, units); some are simply one of a thing. Read which it is
20
+ from how the past jobs got their numbers — do not assume a unit.
21
+ - **How each is priced.** The rule that turns a quantity into a line price. It may be a rate per unit,
22
+ a tiered or banded price, a price that steps at break quantities, a fixed amount, or something else
23
+ entirely. Capture the form the operator's history actually shows; do not flatten everything into
24
+ "rate × quantity" if their numbers say otherwise.
25
+ - **How lines roll up.** The sequence the operator applies above the line items to reach the total —
26
+ any markups, overheads, contingencies, discounts, and taxes, **in the order their arithmetic
27
+ reveals.** Do not assume a markup-then-overhead-then-tax chain; discover the operator's actual
28
+ sequence, including adjustments that apply to only part of the work.
29
+
30
+ ## Standing rule vs per-job judgement
31
+
32
+ Classify each priceable thing as you derive it (the rule and why it matters are in
33
+ [../SKILL.md](../SKILL.md)). On this path the signal is **variance across the operator's own jobs**: a
34
+ price that follows a consistent rule from job to job is a standing rule; one that moves unpredictably
35
+ is priced by judgement.
36
+
37
+ ## Faithfulness over tidiness
38
+
39
+ The aim is the operator's method as it really is, including its irregularities. If their margin
40
+ differs by category, capture that. If one adjustment applies before tax and another after, capture
41
+ that. A clean, regular method that does **not** reproduce their past quotes is wrong; an irregular one
42
+ that does is right. Reproduction is the test (see [verification.md](verification.md)).
43
+
44
+ ## Provenance and under-capture are recorded
45
+
46
+ Every derived element carries where it came from — which artifact, which job. And every part of a
47
+ source that could not be confidently encoded is logged as skipped, ambiguous, or guessed, rather than
48
+ silently dropped or invented. A derivation that quietly omits a section the operator charges for will
49
+ surface later as a quote that is wrong by exactly that section; logging the gap at onboarding is what
50
+ prevents that. Under-capture you can see is a question to ask the operator; under-capture you cannot
51
+ see is a future wrong quote.
@@ -0,0 +1,32 @@
1
+ # Maintaining the method
2
+
3
+ A pricing method is not set once. Rates change, items come and go, the operator refines how they
4
+ charge, and a better history may arrive later that is worth re-learning from. Maintenance keeps the
5
+ method correct across those changes without losing what was already established.
6
+
7
+ ## Re-learning preserves prior manual adjustments
8
+
9
+ When the method is re-learned from history — because new artifacts arrived, or the first derivation
10
+ was incomplete — the operator's prior manual adjustments are preserved, not overwritten. An operator
11
+ who corrected a derived rate by hand, or added an item the artifacts missed, must not have that
12
+ correction silently reverted by the next derivation. A re-learn that would drop a prior manual
13
+ adjustment is a failure: surface the conflict to the operator and let them decide, rather than quietly
14
+ choosing the freshly-derived value.
15
+
16
+ This is why provenance matters (see [learning-from-history.md](learning-from-history.md)): an element
17
+ the operator set by hand is distinguishable from one derived from an artifact, so a re-learn knows
18
+ which it must not clobber.
19
+
20
+ ## Every change re-verifies
21
+
22
+ Any maintenance pass — a re-learn, an added or retired item, a changed rate or roll-up step — is
23
+ followed by re-running verification against the operator's own history (see
24
+ [verification.md](verification.md)). The reconciliation must still be green to the operator's
25
+ precision. A change that is recorded but breaks reproduction of a past job is not done; either the
26
+ change is wrong, or a past fixture is no longer representative and the operator confirms which.
27
+
28
+ ## The outcome, not a storage scheme
29
+
30
+ Maintenance is defined by two outcomes — prior manual adjustments survive, and reproduction stays
31
+ green — not by any particular way of versioning or storing the method. How the method is held is the
32
+ operator's data; what maintenance guarantees is preservation and re-verification.
@@ -0,0 +1,42 @@
1
+ # Defining and editing the method by hand
2
+
3
+ Not every operator hands over a usable history, and every operator eventually changes how they price.
4
+ This pillar builds or edits the method directly with the operator, by conversation, producing the same
5
+ captured method that learning from history produces. A method built this way and a method learned from
6
+ artifacts are indistinguishable to anything downstream — same kind of thing, same captured form.
7
+
8
+ ## What the operator must be able to express
9
+
10
+ Building by hand means letting the operator state, in their own terms, the same four things a
11
+ derivation recovers:
12
+
13
+ - **A priceable thing** — something they charge for, named the way they name it.
14
+ - **How it is quantified** — whether its quantity comes from a measurement they take, a count they
15
+ enter, or is simply one of a thing.
16
+ - **How it is priced** — the rule that turns its quantity into a line price, in whatever form their
17
+ pricing takes (a rate, a tier, a price break, a fixed amount, or otherwise).
18
+ - **How lines roll up** — the adjustments and taxes they apply above the lines, in the order they
19
+ apply them, and the dials those adjustments use (a markup percentage, a day rate, a tax rate).
20
+
21
+ There is no fixed set of fields to fill and no form to complete. Capture what the operator's method
22
+ contains; if their pricing for one thing needs something another thing's does not, the method holds
23
+ both. Forcing every entry into the same template is the same defect here as it is when learning from
24
+ history.
25
+
26
+ ## Standing rule vs per-job judgement
27
+
28
+ Classify each thing here too (the rule and why it matters are in [../SKILL.md](../SKILL.md)). On this
29
+ path the signal is simplest: **ask the operator directly** whether each thing is priced the same way
30
+ every time or decided per job.
31
+
32
+ ## A hand-defined item is immediately quotable; a hand-edited rule takes effect
33
+
34
+ The point of building by hand is that it works at once. A priceable thing the operator just defined is
35
+ available to a quote with no further step, and a rule the operator just changed governs the next quote
36
+ that uses it. An edit that is recorded but does not change what the next quote does is not an edit.
37
+
38
+ ## Editing is a first-class path, not a fallback
39
+
40
+ Editing an existing method — adding a thing, retiring one, changing a rate or a roll-up step — runs
41
+ through this pillar, and through [maintenance.md](maintenance.md) for preserving prior adjustments and
42
+ re-verifying. A manual edit must survive a later re-learn from history; see maintenance.