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 <noreply@anthropic.com>
This commit is contained in:
2026-07-31 01:36:03 +02:00
co-authored by Claude Opus 5
commit 686e1f8cc8
2 changed files with 286 additions and 0 deletions
+208
View File
@@ -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
35 items. Direct client quotes where you have them. Mark anything you inferred as (inferred).
## Hero features
12. 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.
+78
View File
@@ -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.