---
name: create-tms-manifold
description: Use when the user asks to build, generate, scaffold, or author a TMS entity_profile manifold for an item, supplier, commodity group, or supplier category. Walks a PO-line silver table through the full L0 summary, L1 geometry, L2 telemetry preview, token_budget, and lineage envelope. TMS v1.2.
---

# create-tms-manifold

Build a Tabular Manifold Spec (TMS) v1.2 entity_profile manifold for one
of: `ca_item`, `supplier`, `commodity_group`, `supplier_category`. Output
is a single JSON document that an agent can consume progressively (L0
first, L1 on demand, L2 for forensics).

- Full TMS spec: https://canonicalresearch.dev/spec/tms
- Companion QPS spec (provenance): https://canonicalresearch.dev/spec/qps
- Live worked manifolds: https://canonicalresearch.dev/spec/tms#reference-examples

## When to use this skill

Use this skill when the user wants to ship a manifold for procurement,
spend-intelligence, or supplier-risk workflows and the source is a
transactional PO-line table (silver-layer, one row per PO line) plus
optional supplier-consolidation and item-categorization joins.

## What you produce

A single JSON object with these top-level keys, in this order:

```json
{
  "artifact_type": "tabular_manifold",
  "artifact_version": "1.2",
  "manifold_kind": "entity_profile",
  "subject": { },
  "time_window": { },
  "level_0_summary": { },
  "level_1_geometry": { },
  "level_2_telemetry": { },
  "token_budget": { },
  "lineage": { }
}
```

Use `references/envelope.json` as the empty starting skeleton.

L0 must include `reliability` and `quality_flags`. L1 is optional but
recommended for time-windowed entities. L2 must declare a `strategy` and
a `retrieval` block even when the preview is empty.

## Examples

### Example 1: Supplier manifold

User prompt:

> Build a TMS entity_profile manifold for supplier CAP-7a3f9e2b4d8c1062
> over the last 24 months.

Abbreviated output shape (full file: `examples/supplier-minimal.json`):

```json
{
  "artifact_type": "tabular_manifold",
  "artifact_version": "1.2",
  "manifold_kind": "entity_profile",
  "subject": {
    "entity_type": "supplier",
    "parent_supplier_id": "CAP-7a3f9e2b4d8c1062",
    "final_supplier_name": "Helvian Specialty Polymers GmbH"
  },
  "time_window": {"start": "2024-05-01", "end": "2026-04-30", "granularity": "month"},
  "level_0_summary": {
    "observation_count": 180,
    "distribution": {"cv": 1.06},
    "reliability": {"sample_size_class": "robust", "sample_size_n": 180},
    "quality_flags": {"suspected_outliers": true, "level_1_item_truncated": true},
    "item_concentration": {"item_count": 18, "hhi": 0.42, "top_item_pct": 0.61, "top_3": ["..."]},
    "price_stability": {"weighted_avg_item_cv": 0.04, "discipline_rating": "Excellent"},
    "interpretation_hints": ["...3 actionable sentences..."]
  },
  "level_1_geometry": {"monthly_timeseries": "...", "item_rollup": "...", "commodity_rollup": "..."},
  "level_2_telemetry": {"row_count_total": 180, "strategy": "preview_outliers", "...": "..."},
  "token_budget": {"...": "..."},
  "lineage": {"manifold_id": "mfld_supplier_CAP-7a3f9e2b4d8c1062_20260501", "...": "..."}
}
```

### Example 2: Supplier-category manifold

User prompt:

> Generate a TMS entity_profile manifold for the "Specialty Chemicals &
> Additives" supplier category for the same window.

Distinct features for this kind (vs. supplier), full file at
`examples/supplier-category-minimal.json`:

- `subject.entity_type` = `"supplier_category"`, plus `sub_commodity_label`
- L0 requires `direct_indirect_mix` (direct_spend, indirect_spend,
  unknown_spend, plus `classification` band)
- L0 carries `industry_breakdown` and `item_diversity` instead of
  `item_concentration` / `commodity_concentration`
- L1 adds `industry_rollup` alongside supplier_rollup and item_rollup
- HHI bands read differently at the category grain (~57 suppliers,
  HHI 0.20 is healthy)

## Inputs you need from the user

Ask up front, in one round-trip if possible:

1. **Entity type and ID.** One of `ca_item` (ca_item_number), `supplier`
   (parent_supplier_id), `commodity_group` (commodity_group_label),
   `supplier_category` (sub_commodity_label).
2. **Source table** and dialect (Databricks SQL / Snowflake / DuckDB /
   Postgres). At minimum, a PO-line silver table; ideally also the
   supplier-mapping and item-categorization views.
3. **Time window.** Default to the last 24 months ending on the most
   recent observation.
4. **Granularity** for L1. Default to `month` for windows >= 6 months,
   `day` for shorter windows.

If anything is missing and you can infer a sensible default, state the
default and proceed. Do not stall on optional parameters.

## Process

### 1. Confirm scope and pull headline numbers

Run a single aggregate query to confirm the entity exists in the window
and to size the work:

```sql
SELECT
  COUNT(*)                                AS po_line_count,
  COUNT(DISTINCT po_num)                  AS po_count,
  MIN(purchase_order_date)                AS first_purchase,
  MAX(purchase_order_date)                AS last_purchase,
  SUM(spend)                              AS total_spend_usd
FROM <table>
WHERE <entity_predicate>
  AND purchase_order_date BETWEEN :start AND :end;
```

If `po_line_count < 30`, mark `sample_size_class: "sparse"` and warn the
user that L0 stats will be unreliable. Do not refuse; emit the manifold
anyway with the flag set.

### 2. Build `level_0_summary`

Required sub-blocks:

- **`observation_count`** = po_line_count from step 1.
- **`time_coverage`** = first/last purchase + window_days.
- **`distribution`** = min, max, mean, median, stddev, cv on `unit_price`.
- **`reliability`** = sample_size_class, confidence_in_mean (95%),
  data_quality_score (1 minus the share of rows with imputed or null
  unit_price), staleness block.
- **`quality_flags`** = booleans. Trigger rules:
  - `low_sample_size`: sample_size_class == "sparse"
  - `data_staleness`: staleness.is_stale == true
  - `high_variance`: distribution.cv > 0.3 (item only; skip for
    supplier and supplier_category where global CV is mechanically large)
  - `suspected_outliers`: any row with |z_score| > 3
  - `low_supplier_diversity`: (ca_item only) supplier_count == 1
  - `high_supplier_concentration`: HHI > 0.25
  - `high_item_concentration`: top_item_pct > 0.5
  - `level_1_*_truncated`: set when the Pareto truncation in step 3
    dropped rows
- **`financial_summary`** = total_spend_usd, total_qty_received, po_count,
  po_line_count, avg_spend_per_po_line, pct_of_total_spend,
  population_rank, population_size.
- **Concentration blocks** per entity type:
  - `ca_item` -> `supplier_concentration` (HHI, top_supplier_pct, top_3)
  - `supplier` -> `item_concentration` + `commodity_concentration`
  - `commodity_group` -> `supplier_concentration` + `item_diversity` +
    `sub_category_breakdown`
  - `supplier_category` -> `supplier_concentration` + `item_diversity` +
    `industry_breakdown` + `direct_indirect_mix`
- **`price_stability`** = global cv, weighted_avg_item_cv (spend-weighted
  per-item CV over items with >=2 PO lines), discipline_rating, plus a
  `rating_basis` object documenting thresholds and method. See
  `references/calculations.md` for the bands and weighted-CV formula.
- **`relationship`** = first/last purchase, relationship_days,
  avg_monthly_po_frequency.
- **`interpretation_hints`** = 3-4 sentences naming the dominant signal
  with one quantitative anchor each. Lead with the actionable finding,
  not the metric definition.

### 3. Build `level_1_geometry`

`monthly_timeseries`: one row per month in the window. Columns:
`period`, `n`, `spend`, `qty`, `mean_unit_price`, optional
`stddev_unit_price`. Include `missing_periods: []` if the window is
contiguous; otherwise list the gaps.

Then add one or two rollups per entity type:

- `ca_item` -> `supplier_rollup`
- `supplier` -> `item_rollup` + `commodity_rollup`
- `commodity_group` -> `supplier_rollup` + `item_rollup` +
  `sub_category_rollup`
- `supplier_category` -> `supplier_rollup` + `item_rollup` +
  `industry_rollup`

Apply **Pareto truncation** to each rollup. The algorithm, default
coverage targets, tie-break rule, and tail_summary structure are in
`references/calculations.md`.

### 4. Build `level_2_telemetry`

Set `row_count_total` to the total PO lines in the window. Choose a
`strategy`:

- `preview_outliers` if any |z_score| > 2 rows exist (recommended default)
- `preview_recent` for stable entities with no outliers
- `preview_concentration_evidence` for high-concentration commodities
- `full_inline` only if row_count_total < 50
- `none` if downstream agents will always retrieve

Cap inline_rows at the limit you record in `token_budget`. Each row
should include: rn, po_num, date, site, supplier_name, ca_item_number
(or part_description), qty, qty_uom, unit_price, spend, z_score, flag.
See `references/calculations.md` for the z_score formula and the flag
vocabulary used in the live TechnoFlex examples.

Add a `retrieval` block. For agents, prefer `method: "mcp_tool"` with
tool_name and tool_args. For human auditors, also include
`sql_fallback` with the verbatim query_template and query_params used
to populate the inline preview.

### 5. Write the `token_budget` block

Estimate L0 / L1 / L2 token costs (UTF-8 chars / 4 is a usable
approximation). Always emit `compression_ratio_l1_vs_l2` and
`compression_ratio_l0_vs_l2`. Write a one-sentence
`recommended_strategy` describing what an agent should read first.

### 6. Write the `lineage` block

`manifold_id` format: `mfld_<kind>_<entity_id>_<YYYYMMDD>`.

Include `qps_entry_id` (a parallel ID for the Query Provenance Store
companion spec). List each input dataset with `dataset_id`, `version`
(or as_of_timestamp), `row_count`, and the `as_of_timestamp` used.
List `filters_applied` (each as `{field, operator, value}`) and
`transformations` (one prose line per transformation). Compute a
`checksum` over the sorted, serialized telemetry rows
(`method: "sha256_row_hash"`).

## Validation checklist

Before returning the manifold, verify:

- [ ] `artifact_type == "tabular_manifold"` and `artifact_version` set.
- [ ] `manifold_kind == "entity_profile"`.
- [ ] `level_0_summary.reliability` and `level_0_summary.quality_flags`
      both present.
- [ ] HHI math: sum of `(spend_i / total_spend)^2` across the
      concentration grain, INCLUDING the truncated tail. Sanity: HHI <= 1.0.
- [ ] All top_3 / top_5 rows sorted DESC by spend.
- [ ] `pct_of_spend` in any rollup plus its tail_summary sums to ~1.0
      (allow rounding).
- [ ] Every monthly_timeseries period falls inside `time_window`.
- [ ] `level_2_telemetry.strategy` is one of the documented enum values.
- [ ] `lineage.inputs[]` is non-empty.
- [ ] `quality_flags.level_1_*_truncated` matches whether any rollup
      actually truncated.
- [ ] No null values in required fields. Use empty strings or 0 only
      where the spec allows.
- [ ] `financial_summary` carries `attributed_spend_usd`,
      `unattributed_spend_usd` and `unattributed_spend_pct`, even when
      the kind resolves every row and the residue is zero. Absent is not
      the same as zero, and the consumer cannot tell them apart.
- [ ] `financial_summary.population_total_spend_usd` is present, so the
      consumer never has to invert `pct_of_total_spend` to recover the
      denominator.
- [ ] The three invariants hold (see `references/calculations.md`):
      `pct_of_total_spend * population_total_spend_usd == total_spend_usd`,
      `attributed_spend_usd + unattributed_spend_usd == total_spend_usd`,
      and `1 <= population_rank <= population_size`.

### Machine validation

The checklist above is the manual pass. Run the validator for the
structural and arithmetic checks:

```bash
node scripts/validate-manifolds.mjs path/to/your-manifold.json
```

It validates against `references/tms-1.3.schema.json` and asserts the
invariants that JSON Schema cannot express. With no arguments it checks
the four TechnoFlex reference manifolds, which run in CI on every change
to the spec references.

The first invariant is the one worth internalizing. If
`pct_of_total_spend` is computed on `attributed_spend_usd` rather than
`total_spend_usd`, everything still looks plausible in isolation and the
denominator a consumer recovers is wrong by
`1 / (1 - unattributed_spend_pct)`. At 34% unattributed that is a 52%
error in the headline number, with nothing to notice.

## What not to do

- Do not collapse the global CV and the weighted_avg_item_cv into a
  single number. They measure different things and the user will read
  them differently. Always emit both with `rating_basis.note`.
- Do not emit `quality_flags.high_variance` for supplier and
  supplier_category manifolds based on the global CV. Their product
  mix makes the global metric mechanically large; use the weighted
  per-item metric instead, or omit the flag.
- Do not invent values for fields the source cannot support. Set the
  relevant `quality_flag` and let the agent decide.
- Do not interpolate parameters into the SQL `query_template`. Use
  parameterized templates with `:name` placeholders and provide
  `query_params` separately. The QPS spec rejects interpolated SQL on
  purpose.
- Do not include PII in `level_2_telemetry.inline_rows`. The preview
  is meant for outlier evidence, not full record dumps.

## References

- `references/envelope.json`: empty starting envelope, ready to fill.
- `references/calculations.md`: HHI math (including the truncated-tail
  rule), discipline-rating bands, weighted-CV formula, Pareto truncation
  algorithm with defaults, tail_summary structure, z_score formula,
  attributed vs unattributed spend, the invariants, and the flag
  vocabulary.
- `references/tms-1.3.schema.json`: JSON Schema (draft 2020-12) for
  structural conformance. Accepts `artifact_version` 1.2 and 1.3.
- `examples/supplier-minimal.json`: worked supplier manifold with
  realistic values.
- `examples/supplier-category-minimal.json`: worked supplier-category
  manifold demonstrating direct_indirect_mix and industry_rollup.
- TMS spec: https://canonicalresearch.dev/spec/tms
- QPS spec: https://canonicalresearch.dev/spec/qps
- Live full manifolds: https://canonicalresearch.dev/spec/tms#reference-examples
