# DFM Knowledge Packs — the design-for-manufacturing database (CADSF-1106)

`ops/dfmRules.js` answers *can this body be made by process X at shop Y?* — a
checkable verdict on geometry. It knows nothing about what a **chair**, a
**keyboard plate** or a **Raspberry Pi enclosure** should be. A text-to-CAD
agent asked for one has to know the seat height, the 19.05 mm key pitch, the
M2.5 standoff pattern — before it calls a single modelling tool.

Knowledge packs are that layer: **sector-organised design facts an agent loads
before it models**, in a shape it can search, in three sizes, one file per
sector so a session pays only for the sectors it uses.

Surface: `ops/designKnowledge.js` (kernel) and the MCP tool
`cad_design_knowledge` (CADSF-1107). Data: `data/dfm/packs/<sector>.json`.
Built artefacts: `data/dfm/dist/` (per-size bundles, byte-counted) and
`data/dfm/packs/index.js` (static requires so the staged kernel ships every
pack). Gate: `tests/test_cadsf-1106.js` validates every pack on every push.

---

## Shipped packs (Data Loop sprint additions)

| Pack | Sector | Entries (core/std/full) | Recipes | Ticket |
|---|---|---|---|---|
| `desk-everyday` | stands, holders, hooks, boxes, organisers (phone/laptop/headphone stand, monitor riser, pen holder, bookend, coaster, cable clip, keychain, wall hook, hinged box, drawer organiser, soap dish, toothbrush holder) | 18 / 51 / 54 | 3 (phone stand, pen holder, coaster) | CADSF-1132 |
| `maker-enclosures` | Raspberry Pi / Arduino / ESP32 dev-board cases and project boxes (Pi Zero, Nano, NodeMCU, Jetson Nano, CM4, HATs, fan grills, DIN-rail, snap/screw lids) — board outlines electronics.json doesn't cover, plus maker-specific fit/vent/lid/material rules | 15 / 39 / 40 | 3 (Pi 4 case base, Pico case, 40 mm fan-grill plate) | CADSF-1136 |
| `mounts-brackets` | VESA/tripod/GoPro/Arca-Swiss mounting interfaces, bike clamp diameters, shelf/L-bracket structural rules, wall-anchor and T-slot extrusion fasteners, materials and the load/vibration/drop tests a mount or bracket is held to | 17 / 47 / 54 | 4 (VESA-100 adapter plate, gusseted L-bracket, GoPro 3-finger mount, sheet-folded L-bracket on the scs_laser+scs_bend route) | CADSF-1137 |
| `kitchen-tableware` | mugs, tumblers, mixing bowls, plates, water bottles, cutlery, funnels, teapots/kettles, chopping boards, knife handles, jar lids — wall-thickness, handle-attachment, thread, nesting/stacking and food-contact-material facts for kitchenware | 22 / 44 / 44 | 3 (coffee mug with D-handle, mixing bowl, cookie cutter) | CADSF-1133 |
| `power-transmission` | spur/helical/bevel/worm/planetary gears, roller chain and sprockets, rack-and-pinion, V-belts and timing belts, cams, clutches, shaft fits and the tooth-bending-fatigue/chain-wear/belt-tension/NVH tests a drivetrain component is held to | 18 / 46 / 46 | 3 (20-tooth module-2 spur gear, 15-tooth #25-chain sprocket, module-2 rack segment) | CADSF-1134 |
| `toys-games` | dice, chess sets, toy cars, fidget spinners, yoyos, frisbees, building bricks, jigsaw puzzles, figurines, marble runs, meeples and the EN71-1/ASTM F963/16 CFR 1501 choking-hazard/drop/edge/bite safety standards a toy or game piece is held to | 19 / 57 / 57 | 3 (16mm die with engraved pips, 2x4 brick-compatible block, revolved chess pawn) | CADSF-1135 |
| `garden-outdoor` | nursery pots, planters, birdhouses, bird feeders, watering cans, mailboxes, hose/sprinkler threads, raised beds, post caps, trellises and the UV/freeze-thaw/drop/water-tightness/wind-load tests an outdoor part is held to | 17 / 45 / 45 | 3 (tapered nursery planter with drainage holes, birdhouse box, 4x4 post cap) | CADSF-1138 |
| `jewellery-wearables` | rings, bangles, bracelets, earrings, pendants, watch cases and glasses — ring-size/band-width dimensions, comfort-fit and prong-setting rules, casting/DMLS processes, hypoallergenic materials and the wear/retention/drop tests a jewellery or wearable part is held to | 18 / 50 / 51 | 3 (plain wedding band, pendant tag with bail, watch bezel ring) | CADSF-1139 |
| `desktop-robots` | desk humanoids/companion robots, STS3215-class smart servos, dev-board eye/camera/speaker cutouts, wheeled rover chassis, quadrupeds and the drop/tip-over/pinch-point tests and ISO 13482/IEC 60335-1 stability standards a small hobby robot is held to | 18 / 43 / 43 | 3 (STS3215 servo mounting bracket, round robot head with eye cutout, two-wheel rover chassis plate) | CADSF-1140 |
| `furniture-construction` | the construction vocabulary of furniture — rail, stretcher, splat, apron, shell, star base, cantilever, lamination, tenon, mortise, dowel, cord seat, cane seat, half-lap, spacer, mount plate, swivel hub — each mapped to the op(s) that build it, plus standard member sections (leg 30–45 mm square, stretcher 20–25, light tube 12–16, veneer shell 8–10, moulded PU shell 25–40), joint-selection rules (leg↔rail mortise-tenon; shell↔frame screws through a mount plate; bent leg↔seat screws) and worked recipes for ten chair construction types. `cad_joint` / `cad_fastener` are marked *planned (CADSF-1370/1371)* with a today's-ops recipe beside each | 20 / 76 / 86 | 12 (2 joinery how-tos + 10 construction types) | CADSF-1381 |
| `turbomachinery` | first-stage HP turbine rotor blade proportions (aero-derivative and industrial-frame classes), fir-tree root and platform geometry, tip-clearance/rotor-speed practice, IN718/IN738LC properties at temperature, investment-casting castability limits (`ops/dfmRules.js`'s new `investment_casting` process) and the disc/blade tests and tolerances a turbogenerator rotor is held to | 16 / 42 / 44 | 3 (fir-tree root profile, platform+shank block, disc sector with a rim slot) | CADSF-1198 |
| `load-driven-detailing` | taking material out the way engineered parts do (LDD epic, CADSF-102461): gear/pulley/sprocket/idler/flywheel bodies (rim backup ratio m_B and AGMA 2001 rim factor K_B with its 0.5 to 1.2 range, web, hub, lightening-hole count and ligaments, web vs spokes, free gears with dog rings), castings (wall, draft, rib, boss, fillet, hot-spot and machined-pad rules for sand, permanent-mould and die-cast aluminium and iron) and fabricated frames (rail taper with moment, flanged lightening holes, Kt, gussets, fish plates, hole-to-weld). Every number is tagged `SRC:` (cited) or `EST:` (basis stated) in its `basis`. Entry ids are stable and prefixed `ldd-`; LDD [2], [7] and the V12 generators cite them | 19 / 50 / 54 | 2 (lightened m3 60-tooth gear body, tapered rail web with lightening holes) | CADSF-102462 |

## One pack, one sector

```
data/dfm/packs/furniture.json
```

```json
{
  "$schema": "nativecad-dfm-pack/1",
  "id": "furniture",
  "title": "Furniture — desks, chairs, tables, shelving, cabinets",
  "sector": "furniture",
  "version": "2026-09-03",
  "summary": "Ergonomic and regulatory dimensions, joinery, materials and the BIFMA/EN tests a piece of furniture is held to.",
  "aliases": ["desk", "chair", "table", "shelf", "cabinet", "office furniture"],
  "sources": [
    { "id": "bifma-x5.1", "title": "ANSI/BIFMA X5.1-2017 General-Purpose Office Chairs", "url": "https://www.bifma.org/...", "accessed": "2026-09-03" }
  ],
  "entries": [ ...see below... ]
}
```

| Field | Rule |
|---|---|
| `id` | lowercase kebab, equals the file name, unique across packs |
| `sector` | the same word; a pack IS a sector (sub-sectors go in `applies` / `tags`) |
| `aliases` | the words a user says that should route to this pack — the MCP `search` matches on them. **Unique across packs** (ids and sectors included): the build refuses a word two packs claim, because the loader can only route it to one. Prefer the specific form (`sheet metal enclosure`) over the shared word (`enclosure`) |
| `sources` | every source an entry cites; `id` unique in the pack, `url` optional but strongly preferred, `accessed` YYYY-MM-DD |
| `entries` | ≥ 40 for a sector pack; see tier minimums |

## One entry, one fact

```json
{
  "id": "seat-height",
  "kind": "dimension",
  "tier": 1,
  "title": "Task chair seat height (adjustable range)",
  "summary": "Adjustable seat height should span at least 380–510 mm above the floor (popliteal height of the 5th–95th percentile adult, ANSI/HFES 100 and BIFMA G1); a fixed-height chair sits at 430–460 mm.",
  "applies": ["task chair", "office chair", "fixed-height chair"],
  "value": { "min": 380, "max": 510, "typical": 450, "unit": "mm" },
  "basis": "Popliteal height 5th–95th percentile, seated, shod",
  "sources": ["bifma-g1", "hfes-100"],
  "tags": ["ergonomics", "seating"]
}
```

| Field | Rule |
|---|---|
| `id` | lowercase kebab, unique in the pack |
| `kind` | `dimension` \| `rule` \| `material` \| `process` \| `feature` \| `fastener` \| `tolerance` \| `test` \| `standard` \| `recipe` \| `note` |
| `tier` | `1` core, `2` standard, `3` full — see *Sizes* |
| `title` | ≤ 90 chars, a noun phrase |
| `summary` | ≤ 280 chars. **Contains the number.** An agent that reads only the summary must be able to act |
| `applies` | product/sub-sector words; an empty list means "the whole sector" |
| `value` | optional but expected on `dimension` / `rule` / `tolerance` / `fastener`: `{min,max,typical,unit}` (any subset, ≥ 1 of min/max/typical) \| `{value,unit}` \| `{options:[...]}` \| `{table:[{...}]}` for small tables |
| `unit` | `mm` `deg` `N` `kg` `g` `MPa` `GPa` `g/cm3` `um` `ratio` `%` `count` `Nm` `C` `mm/s` `Hz` `V` `W` `L` `h` `cycles` `in` — inch-published numbers stay in `in` (the CADSF-633 rule: a reviewer reads the digits the source printed) |
| `basis` | one line: WHY the number is what it is |
| `sources` | ≥ 1 for `dimension` / `rule` / `tolerance` / `test` / `standard` / `fastener`; every id must exist in `pack.sources` |
| `tags` | free words for search |
| `detail` | optional, tier ≥ 2 only, ≤ 1200 chars — the paragraph the summary compressed |
| `check` | optional: `{ "process": "<dfmRules pack>", "rule": "<rule code>", "params": {...} }` — when the fact is measurable by `cad_dfm_check`, say so; the agent can then verify rather than trust |
| `recipe` | required on `kind: recipe`: `{ "prompt": "what a user says", "steps": [ { "tool": "cad_box", "args": {...}, "note": "..." }, ... ], "expect": { "volume": ..., "faceCount": ... } }`. Tool names and argument shapes must be real (`docs/mcp-tools.json`); mm and radians; every step buildable on the current kernel — run it before you commit it |

### Kinds, and what each is for

- **dimension** — a size the product must have (seat height, key pitch, rack width). Value with min/max/typical.
- **rule** — a constraint on the geometry (hole ≥ 2 × wall from an edge, draft ≥ 1°, rib ≤ 60 % of wall). Prefer a `check` when dfmRules can measure it.
- **material** — what things are made of and why (6061-T6 for a bracket, PC for a housing that must pass UL 94 V-0). `value` may carry density / modulus / yield in a `table`.
- **process** — how the sector makes it and the geometry that process imposes (die-cast → draft + wall; laser-cut sheet → kerf + min hole).
- **feature** — a named feature and its conventions (screw boss, snap fit, living hinge, limber hole, cable gland boss) with the numbers.
- **fastener** — screws, inserts, pins, clips: sizes, hole conventions, edge distances.
- **tolerance** — what the sector expects and what the process gives.
- **test** — *things people test*: the test's name, the standard, the load/cycles/height/duration, and **what it demands of the geometry** ("1.2 m drop on corners → ≥ 2 mm corner radius, no unsupported wall > 40 mm").
- **standard** — the standard itself, one entry: what it covers, the clauses that touch geometry.
- **recipe** — a worked text-to-CAD build for the sector's common product, as real tool calls.
- **note** — anything true and useful that is none of the above.

## Sizes

An entry's `tier` says which sizes carry it. The loader and the build emit:

| Size | Carries | Target bytes per sector | Meant for |
|---|---|---|---|
| `core` | tier 1 | ≤ 16 KB | the numbers an agent needs to start a bracket-grade model in this sector; loaded by default |
| `standard` | tiers 1–2 | ≤ 64 KB | a real design session — tables, tests, recipes |
| `full` | tiers 1–3 | unbounded | reference: edge cases, standards prose, `detail` paragraphs |

Tier minimums per sector pack (the gate enforces them): **tier 1 ≥ 15**,
**tier 2 ≥ 15**, and ≥ 3 `test`, ≥ 2 `recipe`, ≥ 3 `material` entries somewhere
in the pack. Cross-sector packs (`hardware`, `materials`, `standards-testing`)
are exempt from the recipe/material minimums.

Pick the tier by asking: *would an agent get the product wrong without this?*
Tier 1 yes; tier 2 it would get it worse; tier 3 it would not notice.

## Modular loading

A pack is a file; the index is a list. `loadPack(['furniture','hardware'],
{size:'core'})` reads two files and nothing else. Cross-sector facts live in
the three cross-sector packs and are *referenced* (`"see": "hardware/m3-clearance"`)
rather than copied, so the number lives in one place.

## Authoring rules that keep a pack honest

1. **Numbers come from a source, and the source is named.** No source → no
   number. A round number you are sure of still needs a basis line.
2. **Say the unit the source said.** Inch-published → `"unit": "in"`.
3. **A range beats a point.** `{min, max, typical}` when the sector has a range.
4. **The summary carries the number.** Prose without a number is a `note` —
   the build refuses any other kind whose summary has no digit.
5. **A recipe was run.** Its `expect` block is what the kernel returned.
6. **A test says what geometry it wants.** A test entry with no geometric
   implication is a `standard`.
7. **Don't restate dfmRules.** Process limits (min wall by process, draft
   floors) live in `ops/dfmRules.js`; a pack entry points at them with `check`.
8. **No vendor pricing, no marketing.** Facts about parts, not about shops.

## The API (`ops/designKnowledge.js`)

```js
const DK = require('./ops/designKnowledge');
DK.listPacks();                                   // [{id, title, sector, aliases, counts:{core,standard,full}, bytes:{...}}]
DK.loadPack('furniture', { size: 'core' });       // the pack with entries filtered to tier <= 1
DK.loadPacks(['furniture','hardware'], { size: 'standard' });
DK.search('seat height', { sectors: ['furniture'], size: 'standard', kinds: ['dimension'], limit: 10 });
DK.resolveSector('office chair');                 // 'furniture' (by id, sector or alias)
DK.SIZES;                                         // { core: 1, standard: 2, full: 3 }
```

`search` scores on title, summary, applies, tags and id (tokenised,
case-folded, prefix-matched) and returns `{pack, id, kind, tier, title,
summary, value, score}` rows — never `detail`, never the whole pack.

## Build

```
node tools/build_dfm_packs.js          # validate, write packs/index.js and dist/
node tools/build_dfm_packs.js --check  # validate only (what the test does)
```

`dist/index.json` lists every pack with entry counts and byte sizes per
size, plus `all.<size>.json` bundles for a consumer who wants one file.
