commit 686e1f8cc8e3645364ccd44ccd385d60903f8632 Author: Glenn Date: Fri Jul 31 01:36:03 2026 +0200 Add buildease-demo skill Wraps odoo-demo-architect for BuildEase (callista-tools/mev) construction demos: discovery brief from any source instead of Knowcap, pain-to-module routing onto the 33 BuildEase modules, parallel instance-prep/research agents, throwaway-record verification with a full purge, and the post-demo follow-through set. Co-Authored-By: Claude Opus 5 diff --git a/SKILL.md b/SKILL.md new file mode 100644 index 0000000..43f1471 --- /dev/null +++ b/SKILL.md @@ -0,0 +1,208 @@ +--- +name: buildease-demo +description: Wraps odoo-demo-architect for BuildEase (callista-tools/mev) construction demos. Builds the discovery brief from any source — transcript file, notes, email thread, or a live interview — instead of requiring Knowcap, routes client pains onto BuildEase modules before considering custom work, and after the demo generates the follow-through set (objection sheet, scoped quote, implementation timeline, SOW). Use for "BuildEase demo", "QTO demo", "construction Odoo demo", "prep a demo from these notes", or "write the follow-up after the demo". +--- + +# BuildEase Demo + +Three phases. `odoo-demo-architect` does the middle one; this skill owns the ends. + +**Requires** `odoo-demo-architect` installed (`~/.claude/skills/odoo-demo-architect/`, +from github.com/Smetools/odoo-demo-architect). If it is missing, say so and stop — +do not reimplement its XML-RPC connector or data builder. + +--- + +## Phase A — Discovery brief (no Knowcap) + +Goal: produce `discovery_brief.md` in exactly the shape odoo-demo-architect's step 1 +would have emitted, from whatever the user actually has. + +**Source, in order of preference:** +1. A transcript, meeting notes, or email thread — read it. +2. Knowcap MCP, if it happens to be available and the user wants it. +3. Nothing written down → interview the user (see intake below). + +### Intake — infer first, ask only the gaps + +Before asking anything, **look**: glob the working directory for a transcript, notes, +brief, or `.md`/`.txt`/`.docx` that plausibly is the source, and read it. Then answer +each item below from what you read. Only the items you genuinely cannot answer go to +the user, in **one** `AskUserQuestion` batch — never a serial interrogation, never a +question whose answer is sitting in the file you just read. + +| Item | Infer from | Ask only if | +|---|---|---| +| **Source brief location** | A single obvious candidate file in the working directory | Zero or several candidates — ask which, or paste | +| **Odoo UI language** | Language the brief is written in; client country; `l10n_*` in scope | Brief is English but the client clearly isn't anglophone — ask (nl_BE / fr_BE / en_US / other) | +| **Client website / company info** | URL or company name in the brief — fetch the site, mine it for what they build, size, regions, branding | No name or URL anywhere in the source | +| **Odoo instance** | `demo_config.json`, prior session, the brief | Not found — always ask, never guess a URL or API key | +| **Demo audience** | Attendee list in a transcript header or email thread | Source is notes with no names | +| **Rate card** | A rates file in the project | Only needed at Phase C — don't ask now | + +Anything still missing after that batch: write it into the brief as `(unknown)` and +carry on. One unknown does not block building the demo. + +If nothing written down exists at all, the interview replaces the table — still one +batch: who the client is, what they build, how they quote today, how they invoice, +the top 3 complaints they voiced, who is in the room. + +**Write `discovery_brief.md` with these six sections and no others:** + +```markdown +## Business shape +Entities, product/customer types, volumes, shared financials. +Company name, website, country, what they actually build — from the site if you fetched it. + +## Top pains +3–5 items. Direct client quotes where you have them. Mark anything you inferred as (inferred). + +## Hero features +1–2. Each names the pain it kills and the decision-maker it lands on. + +## Phase 1 scope +Odoo apps + BuildEase modules, from reference/buildease.md. + +## Odoo instance +URL, database, login, API key. Ask if absent — do not guess. +UI language + `l10n_*` localisation to install. + +## Demo audience +Who is in the room and what each one needs to hear. +``` + +Never invent a pain quote. An inferred pain marked `(inferred)` is fine; a fabricated +quote read back to the client in the demo is not. + +Show the brief to the user and get a yes before Phase B. + +--- + +## Phase B — Build, via odoo-demo-architect + +Hand `discovery_brief.md` to `odoo-demo-architect` and let it run its pipeline +(research → connect → install → data → heroes → test → sales docs). + +### Fork the slow half + +Module installation and client research are independent — the install list comes from the +brief plus the routing table, the research feeds hero *building* which happens after both. +Installing over XML-RPC is minutes of waiting and produces log spew nobody needs in context. +So run them as two parallel subagents, launched in **one** message: + +**Agent 1 — instance prep.** Give it the Odoo credentials, the routed module list, and +odoo-demo-architect's `scripts/odoo_connect.py`. It sets the company, currency, UoM and +UI language, installs the localisation, installs `buildease` + the routed modules, then +walks Settings for the BuildEase toggles that default off. It loads **no data** — that is +the join step's job. Returns: what installed, what failed, the settings it changed. + +**Agent 2 — client research.** Give it the brief. It fetches the client's website, and +researches each hero feature **BuildEase → native Odoo 19 → custom**, in that order. +Returns: per hero, which of the three it lands in, and the click-path if it already exists. + +**Join.** Both done → install any module Agent 2's research turned up that Agent 1 didn't +know about (incremental, cheap), then generate and load the client-specific dataset into +the empty database, then build and test heroes. + +Two agents, not five. Do not fan out further — hero building is sequential against one +database and parallel writes will fight. + +Two things to inject before it starts: + +1. **Read `reference/buildease.md`** and pass the routed module list. The research step's + native-vs-custom question becomes **BuildEase → native Odoo → custom**, in that order. + Most construction pains are already solved in mev; custom work is the last resort. +2. **This skill is the demo data loader.** Do not install `buildease_demo_data` — that is + the old, one-size-fits-all Belgian dataset it replaces. The database starts empty and + gets a dataset generated from the brief: the client's own partners, articles, projects + and orders, staged so each hero feature has a record to fire on. Install it only if the + user explicitly asks for the generic sandbox. + +Its step 7 (test every hero on throwaway records) is not optional here either. A QTO → +sale order → project → progress statement → invoice chain that breaks mid-demo is the +one failure mode that loses the deal. + +### Verify on throwaway records, then delete them + +Test against a partner named `ZZ Test…`, never against the client's own records — a hero +you tested is a hero you consumed, and the demo needs its first click to be the first click. + +**Then purge. A demo environment ships with zero test residue.** Debugging the chain takes +several runs, each leaving a full take-off → order → project → statement → invoice trail; +"it's cancelled and the partner is archived" is not clean, those records still list on +screen. Write the purge as a function in the test script and run it until the counts are +zero — not as a one-off you improvise at the end. + +Odoo blocks most of this unless you go in order: + +``` +transient wizard rows → statements (cancelled only, newest first) → invoices +→ timesheet lines → tasks → projects → sale orders → take-offs → the partner +``` + +The traps, all of which cost a debugging round: +- **A take-off with no sale order cannot be deleted at all.** Its state is derived from the + order, so there is nothing to cancel. Convert it to a quotation, cancel that, then delete. +- `search` will not find the partner once archived — pass `context={"active_test": False}` + or the purge silently no-ops and reports success. +- Statements cancel newest-first only; invoices must go to draft before cancel; leftover + `add.dynamic.article.template.line` rows pin take-offs. +- **Delete the partner last, and only once nothing references it.** Deleting it first nulls + `partner_id` on whatever survived, so a `partner_id`-based purge can no longer see those + records and a `partner_id`-based assertion reports clean over an environment that isn't. + Sweep by name as well, and assert on both. +- Anything the tests **copy** inherits the source record's customer, not the throwaway one. + Repoint copies at the throwaway partner or they escape the purge. +- Make every step best-effort with its own `try`, and **print what it kept** — a silent + purge that skipped nine records reads exactly like one that worked. + +Finish by reading back the counts of every model you touched and putting them in the +verification log. "No test residue" is a claim; the counts are the evidence. + +### Sanity-check the staged numbers before calling it done + +A chain that passes every assertion can still look broken on screen. Check the staged data +reads plausibly: **booked hours well under ordered hours** (over 100% makes the progress +statement look wrong), costs under selling prices, dates in the past. Derive staged +quantities from the ordered quantity rather than hard-coding them, so they cannot drift +when a formula changes. + +--- + +## Phase C — Follow-through + +Runs after the demo happened. Needs: the built demo, the verification log, and what +actually came up in the room. Ask the user for the room notes — objections raised, +who pushed back, what they asked for that wasn't there. + +Generate four documents. Reuse odoo-demo-architect's `templates/` branding and its +self-contained-HTML rule (no external assets, single file, prints to A4). + +| Document | Contents | +|---|---| +| **Objection sheet** | Every objection raised → the honest answer. Where BuildEase already covers it, name the module and the screen. Where it doesn't, say so and give the workaround or the build estimate. | +| **Scoped quote** | Line items from the Phase 1 scope: modules, configuration, data migration, custom work, training. Days and rate per line. Flag assumptions separately from committed lines. | +| **Implementation timeline** | Phased, with the client's own dependencies on it (data extraction, sign-offs, who from their side). Weeks, not dates, unless a start date is agreed. | +| **Statement of work** | Deliverables, acceptance criteria, out-of-scope list, change-request process. The out-of-scope list is the point — write it properly. | + +Rules: +- Every number traces to something. No invented day-rates, no invented licence costs — + ask the user for the rate card. +- Anything the demo *didn't* prove goes in the quote as build work, not as "included". + Cross-check against the verification log from step 7. +- The out-of-scope list is where the follow-through earns its keep. Anything discussed + and not quoted goes in it explicitly. + +Show all four to the user before anything leaves the machine. + +--- + +## Guardrails + +- Never claim a BuildEase feature works without having seen it work in the demo DB. +- Never hand over an environment holding test records. Purge them and read back the counts; + do not settle for "cancelled and archived" and do not write the leftovers off as a note + in the verification log. +- Never send anything client-facing without the user reading it first. +- `discovery_brief.md`, `demo_config.json` and the follow-through docs hold client data + and API keys — keep them in the project directory, never in a scratchpad you'd forget. diff --git a/reference/buildease.md b/reference/buildease.md new file mode 100644 index 0000000..c6a298e --- /dev/null +++ b/reference/buildease.md @@ -0,0 +1,78 @@ +# BuildEase (callista-tools/mev) — module map & pain routing + +Odoo **19.0** vertical for construction / contracting. Repo: `git@github.com:callista-tools/mev.git`. +34 modules, all LGPL-3. Umbrella module `buildease` depends on nothing and installs the set. + +## The four core concepts + +| Concept | Model | What it replaces at the client | +|---|---|---| +| **Quantity Take-Off (QTO)** | `quantity.take.off`, `.line`, `.line.calculation` | The measurement/estimating spreadsheet that gets re-keyed into a quote | +| **Sale Progress** | `sale.progress`, `sale.progress.line` | Monthly progress statements (BE: *vorderingsstaat*) and % -complete invoicing | +| **Adjustments** | `adjustment.create` + QTO/SP extensions | Variation orders / change orders (BE: *verrekeningen*) | +| **Revision formula** | `revision.formula`, `revision.scheme`, `revision.parameter`, `index.value.date` | Contractual price-indexation formulas on progress statements | + +A QTO holds lines; lines hold *calculation* lines (the measurement math). QTO → sale order → project → progress statements → invoices is the spine of every demo. + +## Pain → module routing + +Match the client's own words on the left; install the right. + +| Client says | Install | +|---|---| +| "we measure in Excel then re-type it into the quote" | `quantity_take_off_base`, `quantity_take_off_sale` | +| "every project starts from the same standard items" | + `quantity_take_off_base` template sets (`quantity.take.off.line.template.set`) | +| "the same item recalculates from a few parameters" | `quantity_take_off_dynamic_calculation` | +| "we add a % for general costs / site overhead" | `quantity_take_off_overhead` (+ `_dynamic_calculation_overhead` if both) | +| "we invoice monthly on % complete" / "vorderingsstaat" | `sale_progress_base` | +| "prices are indexed per the contract formula" | `sale_progress_revision_formula` | +| "extra work / change orders are a mess" | `adjustments_base` (+ `sale_progress_revision_formula_adjustments` if both) | +| "hours worked must land on the progress statement" | `sale_progress_base_timesheet`, `adjustments_base_timesheet` | +| "subcontractors send us their own progress statements" | `sale_progress_purchase` (`subcontractor.progress.statement.wizard`) | +| "we ask 3 suppliers for prices per item" | `quantity_take_off_purchase` (`purchase.request.wizard`, uses `purchase_requisition`) | +| "made-to-order items priced from their bill of materials" | `buildease_custom_product`, `buildease_custom_product_bom_price_sync` (+ `_overhead`) | +| "the client has to sign off" | `quantity_take_off_sale_signature`, `sale_progress_base_signature` | +| "plans and datasheets must be on the site project" | `project_product_documents_link` | +| "we track material deliveries to site" | `quantity_take_off_sale_stock`, `_sale_delivery` | +| "one project, many purchase orders" | `quantity_take_off_sale_project_purchase`, `_project_stock` | +| "our techs do call-outs / maintenance visits" | `quantity_take_off_sale_industry_fsm` | +| "recurring maintenance contracts" | `quantity_take_off_sale_subscription` | +| "estimates start from a lead" | `quantity_take_off_sale_crm` | +| "supplier article numbers differ from ours" | `quantity_take_off_extra_article_nr` | +| "same item in several sizes/finishes" | `sale_quantity_takeoff_variant` | +| "management wants margin per project" | `sale_progress_quantity_take_off_report` (QTO carries `margin`, `margin_percentage` natively) | +| "we live in spreadsheets" | `quantity_take_off_spreadsheet` | + +## Demo data — `buildease_demo_data` is the OLD way + +**Do not install `buildease_demo_data` or `buildease_demo_data_sale_progress` by default.** +They are the previous generation of environment setup: one fixed Belgian construction +dataset, the same for every prospect. This skill replaces them — demo data is generated +per client from the discovery brief and loaded through odoo-demo-architect's +`build_demo.py` / `demo_config.json`. + +Install them only if the user explicitly asks, or when there is genuinely no client +context and a generic sandbox is wanted. If you do, know what comes with them: +`sale_management, purchase_stock, project, timesheet_grid, sale_timesheet, maintenance, +quantity_take_off_sale, industry_fsm, l10n_be, accountant, uom` — including the Belgian +localisation, which is wrong for most non-BE clients. + +The app-level dependencies that dataset used to drag in (`sale_management`, `project`, +`purchase_stock`, `sale_timesheet`, `industry_fsm`, `accountant`, `uom`) still need +installing on their own — route them from the pain table above, not from the demo bundle. + +## Gotchas + +- **Three dependencies are not in this repo**: `custom_product_sale`, `custom_product_bom`, + `bom_on_sale_order_line`. Anything under `buildease_custom_product*` fails to install + without them. Confirm their source before promising a BOM-pricing hero feature. +- **Odoo 19 only.** Manifests are all `19.0.x`; do not attempt against 17/18. +- `l10n_be` and `accountant` are pulled in by demo data — the demo DB is Belgian-localised. + Wrong-country client → set `demo_config.json` company/fiscal position explicitly, or skip + `buildease_demo_data` and stage minimal data instead. +- `quantity.take.off` inherits `mail.thread` + `mail.activity.mixin` — chatter and activities + work for free, good cheap demo beats. +- `res.config.settings` and `res.company` are extended by 8 modules each. After installing, + walk Settings once; several BuildEase behaviours are off by default. +- `post_migrate.py` sits at repo root — relevant when upgrading an existing demo DB, not a + fresh build.