@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.
- package/dist/__tests__/installer-settings-permissions.test.js +39 -9
- package/dist/permissions-seed.js +18 -8
- package/package.json +1 -1
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +2 -2
- package/payload/platform/plugins/business-assistant/PLUGIN.md +4 -1
- package/payload/platform/plugins/business-assistant/references/quote-engine.md +122 -0
- package/payload/platform/plugins/business-assistant/references/quote-generation.md +94 -0
- package/payload/platform/plugins/business-assistant/references/quoting.md +29 -0
- package/payload/platform/plugins/business-assistant/skills/pricing-method/SKILL.md +78 -0
- package/payload/platform/plugins/business-assistant/skills/pricing-method/references/learning-from-history.md +51 -0
- package/payload/platform/plugins/business-assistant/skills/pricing-method/references/maintenance.md +32 -0
- package/payload/platform/plugins/business-assistant/skills/pricing-method/references/manual-definition.md +42 -0
- package/payload/platform/plugins/business-assistant/skills/pricing-method/references/verification.md +37 -0
- package/payload/platform/plugins/docs/references/platform.md +1 -1
- package/payload/platform/plugins/email/PLUGIN.md +2 -2
- package/payload/platform/plugins/email/mcp/dist/index.js +16 -0
- package/payload/platform/plugins/email/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/smtp.d.ts +8 -0
- package/payload/platform/plugins/email/mcp/dist/lib/smtp.d.ts.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/smtp.js +22 -2
- package/payload/platform/plugins/email/mcp/dist/lib/smtp.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/tools/email-reply.d.ts +2 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-reply.d.ts.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/tools/email-reply.js +4 -2
- package/payload/platform/plugins/email/mcp/dist/tools/email-reply.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/tools/email-send.d.ts +2 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-send.d.ts.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/tools/email-send.js +4 -2
- package/payload/platform/plugins/email/mcp/dist/tools/email-send.js.map +1 -1
- package/payload/platform/plugins/email/references/email-reference.md +5 -0
- package/payload/platform/scripts/backfill-bypass-permissions.sh +58 -0
- package/payload/platform/scripts/setup-account.sh +15 -10
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.js +103 -31
- package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/jsonl-path.d.ts +26 -0
- package/payload/platform/services/claude-session-manager/dist/jsonl-path.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/jsonl-path.js +36 -0
- package/payload/platform/services/claude-session-manager/dist/jsonl-path.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +27 -22
- 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=
|
|
2
|
-
// and permissions.allow=[
|
|
3
|
-
// claude.ai/code sessions never fall through to the
|
|
4
|
-
// auto-classifier.
|
|
5
|
-
//
|
|
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: [
|
|
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: [
|
|
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: [
|
|
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
|
+
});
|
package/dist/permissions-seed.js
CHANGED
|
@@ -1,9 +1,17 @@
|
|
|
1
|
-
// Task 583 — Seed Claude Code's
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
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
|
-
|
|
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,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:
|
|
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
|
|
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.
|
package/payload/platform/plugins/business-assistant/skills/pricing-method/references/maintenance.md
ADDED
|
@@ -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.
|