its-magic 0.1.3-0 → 0.1.3-1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "its-magic",
3
- "version": "0.1.3-0",
3
+ "version": "0.1.3-1",
4
4
  "description": "its-magic - AI dev team workflow for Cursor.",
5
5
  "license": "MIT",
6
6
  "bin": {
@@ -228,6 +228,64 @@ RELEASE_CHANGELOG_PAIRS: tuple[tuple[str, str], ...] = (
228
228
  ),
229
229
  )
230
230
 
231
+ MODEL_TIER_PAIRS: tuple[tuple[str, str], ...] = (
232
+ (
233
+ "scripts/model_tier_lib.py",
234
+ "template/scripts/model_tier_lib.py",
235
+ ),
236
+ (
237
+ "scripts/model_tier_validate.py",
238
+ "template/scripts/model_tier_validate.py",
239
+ ),
240
+ (
241
+ "docs/engineering/runbook.md",
242
+ "template/docs/engineering/runbook.md",
243
+ ),
244
+ (
245
+ ".cursor/scratchpad.md",
246
+ "template/.cursor/scratchpad.md",
247
+ ),
248
+ (
249
+ ".cursor/scratchpad.local.example.md",
250
+ "template/.cursor/scratchpad.local.example.md",
251
+ ),
252
+ (
253
+ "scripts/check_intake_template_parity.py",
254
+ "template/scripts/check_intake_template_parity.py",
255
+ ),
256
+ )
257
+
258
+ MODEL_TIER_OVERRIDES_PAIRS: tuple[tuple[str, str], ...] = (
259
+ (
260
+ ".cursor/scratchpad.md",
261
+ "template/.cursor/scratchpad.md",
262
+ ),
263
+ (
264
+ ".cursor/scratchpad.local.example.md",
265
+ "template/.cursor/scratchpad.local.example.md",
266
+ ),
267
+ (
268
+ ".cursor/model-catalog.local.example.role-based-balanced.json",
269
+ "template/.cursor/model-catalog.local.example.role-based-balanced.json",
270
+ ),
271
+ (
272
+ ".cursor/model-catalog.local.example.role-based-highend.json",
273
+ "template/.cursor/model-catalog.local.example.role-based-highend.json",
274
+ ),
275
+ (
276
+ "scripts/model_tier_lib.py",
277
+ "template/scripts/model_tier_lib.py",
278
+ ),
279
+ (
280
+ "scripts/model_tier_validate.py",
281
+ "template/scripts/model_tier_validate.py",
282
+ ),
283
+ (
284
+ "docs/engineering/runbook.md",
285
+ "template/docs/engineering/runbook.md",
286
+ ),
287
+ )
288
+
231
289
  DOWNSTREAM_CI_GUARD_PAIRS: tuple[tuple[str, str], ...] = (
232
290
  (
233
291
  "scripts/check_downstream_ci_guard.py",
@@ -252,6 +310,8 @@ SCOPES: dict[str, tuple[tuple[str, str], ...]] = {
252
310
  "project-readme": PROJECT_README_PAIRS,
253
311
  "dev-environment": DEV_ENVIRONMENT_PAIRS,
254
312
  "release-changelog": RELEASE_CHANGELOG_PAIRS,
313
+ "model-tier": MODEL_TIER_PAIRS,
314
+ "model-tier-overrides": MODEL_TIER_OVERRIDES_PAIRS,
255
315
  "all": (
256
316
  INTAKE_TEMPLATE_PAIRS
257
317
  + CAVEMAN_COMPRESS_PAIRS
@@ -265,6 +325,8 @@ SCOPES: dict[str, tuple[tuple[str, str], ...]] = {
265
325
  + PROJECT_README_PAIRS
266
326
  + DEV_ENVIRONMENT_PAIRS
267
327
  + RELEASE_CHANGELOG_PAIRS
328
+ + MODEL_TIER_PAIRS
329
+ + MODEL_TIER_OVERRIDES_PAIRS
268
330
  ),
269
331
  }
270
332
 
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  description: "Curator agent"
3
+ model: fast
3
4
  ---
4
5
 
5
6
  You are the Curator. Keep context compact and artifacts current.
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  description: "Product Owner agent"
3
+ model: inherit
3
4
  ---
4
5
 
5
6
  You are the PO. Your job is to clarify requirements and persist them.
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  description: "Release agent"
3
+ model: inherit
3
4
  ---
4
5
 
5
6
  You are Release. Prepare release notes and runbook commands.
@@ -0,0 +1,9 @@
1
+ {
2
+ "schema_version": 1,
3
+ "tiers": {
4
+ "cheap": "composer-2.5-fast",
5
+ "balanced": "composer-2.5-standard",
6
+ "strong": "composer-2.5"
7
+ },
8
+ "notes": "Cursor-integrated models only. Uses Cursor's own Composer 2.5 family via stable Cursor model IDs. No external API key is required; everything routes through Cursor-managed infrastructure. Strong maps to the strongest available Composer 2.5 variant (often the default parent model when model: is omitted)."
9
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "schema_version": 1,
3
+ "tiers": {
4
+ "cheap": "<your-cheap-model-slug>",
5
+ "balanced": "<your-balanced-model-slug>",
6
+ "strong": "<your-strong-model-slug>"
7
+ },
8
+ "notes": "Copy this file to .cursor/model-catalog.local.json and replace placeholder slugs with vendor-specific model IDs. Enable with MODEL_RESOLVE=local_catalog in .cursor/scratchpad.local.md. All three tier keys are required."
9
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "schema_version": 1,
3
+ "tiers": {
4
+ "cheap": "qwen-3.7-plus",
5
+ "balanced": "composer-2.5-standard",
6
+ "strong": "glm-4.7"
7
+ },
8
+ "notes": "Level 1 — small/simple apps (CRUD, dashboard, bot, single-service). Cost-focused: Qwen3.7 Plus handles routine volume, Composer 2.5 Standard is the Cursor default, GLM-4.7 covers occasional architecture/release gates. Based on ai_modell_auslegung_cursor_highend.md 'Kleine unkomplizierte Apps'. Enable with MODEL_RESOLVE=local_catalog."
9
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "schema_version": 1,
3
+ "tiers": {
4
+ "cheap": "qwen-3.7-plus",
5
+ "balanced": "composer-2.5-standard",
6
+ "strong": "glm-5.2"
7
+ },
8
+ "notes": "Level 2 — complex apps (multi-service, auth, DB, FE+BE, Docker, API, worker/queue). Balanced: cheap Qwen for mass DEV, Composer 2.5 Standard as Cursor default, strong GLM-5.2 for architecture/QA/release gates. Based on ai_modell_auslegung_cursor_highend.md 'Komplexere Apps'. Enable with MODEL_RESOLVE=local_catalog."
9
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "schema_version": 1,
3
+ "tiers": {
4
+ "cheap": "qwen-3.7-plus",
5
+ "balanced": "glm-5.2",
6
+ "strong": "gpt-5.5"
7
+ },
8
+ "notes": "Level 3 — mega-complex software / modular monoliths (trading/finance platforms, multi-team systems, long-lived infrastructure). Strong GPT-5.5 for chief-architect/final-gate work, GLM-5.2 for SA/QA/RELEASE, Qwen for volume DEV. Based on ai_modell_auslegung_cursor_highend.md 'Mega-komplexe Software'. Enable with MODEL_RESOLVE=local_catalog."
9
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "schema_version": 1,
3
+ "tiers": {
4
+ "cheap": "qwen-3.7-plus",
5
+ "balanced": "kimi-k2.7-code",
6
+ "strong": "claude-opus-4.8"
7
+ },
8
+ "notes": "Level 4 — super-high-sophisticated / mission-critical (AI operating systems, critical infrastructure, long-horizon agent systems, high-risk security/privacy/finance). Chief-architect quality: Claude Opus 4.8 for critical gates, Kimi K2.7 Code for difficult long-horizon DEV, Qwen for cheap mass work. Based on ai_modell_auslegung_cursor_highend.md 'Super-High-Sophisticated Projects'. Enable with MODEL_RESOLVE=local_catalog."
9
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "schema_version": 2,
3
+ "tiers": {
4
+ "cheap": "<your-cheap-model-slug>",
5
+ "balanced": "<your-balanced-model-slug>",
6
+ "strong": "<your-strong-model-slug>"
7
+ },
8
+ "roles": {
9
+ "po": "<your-po-model-slug>",
10
+ "sa": "<your-sa-model-slug>",
11
+ "dev": "<your-dev-model-slug>",
12
+ "dev_difficult": "<your-dev-difficult-model-slug>",
13
+ "qa": "<your-qa-model-slug>",
14
+ "security": "<your-security-model-slug>",
15
+ "release": "<your-release-model-slug>"
16
+ },
17
+ "notes": "Role-based balanced preset (US-0102 / DEC-0087). Copy to .cursor/model-catalog.local.json and set MODEL_RESOLVE=role_catalog in .cursor/scratchpad.local.md. All tier and role keys required when roles section present."
18
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "schema_version": 2,
3
+ "tiers": {
4
+ "cheap": "<your-cheap-highend-slug>",
5
+ "balanced": "<your-balanced-highend-slug>",
6
+ "strong": "<your-strong-highend-slug>"
7
+ },
8
+ "roles": {
9
+ "po": "<your-po-highend-slug>",
10
+ "sa": "<your-sa-highend-slug>",
11
+ "dev": "<your-dev-highend-slug>",
12
+ "dev_difficult": "<your-dev-difficult-highend-slug>",
13
+ "qa": "<your-qa-highend-slug>",
14
+ "security": "<your-security-highend-slug>",
15
+ "release": "<your-release-highend-slug>"
16
+ },
17
+ "notes": "Role-based high-end preset (US-0102 / DEC-0087). See ai_modell_auslegung_cursor_highend.md for non-normative role recommendations. Enable with MODEL_RESOLVE=role_catalog."
18
+ }
@@ -321,3 +321,58 @@ CAVEMAN_MODE=0
321
321
  CAVEMAN_LEVEL=
322
322
  CAVEMAN_COMPRESS_INPUT=0
323
323
  CAVEMAN_FILE_SCOPE=
324
+
325
+ #
326
+ # ## Per-phase model tier selection (US-0101 / DEC-0086)
327
+ # MODEL_TIER selects LLM model strength (which model runs).
328
+ # MODEL_TIER ≠ TOKEN_PROFILE ≠ DELIVERY_MODE — these are independent axes;
329
+ # none substitutes for the other (DEC-0062 / US-0080 / US-0096).
330
+ # - MODEL_TIER_DEFAULT: cheap|balanced|strong (default balanced)
331
+ # - MODEL_TIER_<PHASE>: cheap|balanced|strong (per-phase override; PHASE = canonical phase id)
332
+ # Default matrix (architecture-locked):
333
+ # cheap — ask, refresh-context, memory-audit, status-reconcile, pause
334
+ # balanced — intake, discovery, research, release, plan-verify
335
+ # strong — architecture, execute, quick, qa, verify-work, security-review
336
+ # (inherit parent) — auto (orchestrator always inherits parent model)
337
+ # - MODEL_CATALOG: path to local slug catalog (default .cursor/model-catalog.local.json)
338
+ # - MODEL_RESOLVE: alias_only|local_catalog|role_catalog (default alias_only)
339
+ # - MODEL_FALLBACK: fallback when catalog lookup fails (default inherit)
340
+ # - MODEL_PROVIDER_MODE: cursor|api (default cursor)
341
+ # cursor = all subagents route through Cursor-managed infrastructure
342
+ # api = operator uses BYOK via Cursor Settings → Models → API Key
343
+ # Known limitation: subagents do NOT inherit custom API keys/base URLs.
344
+ #
345
+ # ## Direct per-phase model slug override (US-0102 / DEC-0087) — set in this local file only
346
+ # Precedence: MODEL_<PHASE> > MODEL_TIER_<PHASE> > MODEL_TIER_DEFAULT > Cursor alias
347
+ # - MODEL_<PHASE>: direct vendor slug (e.g. MODEL_EXECUTE=<your-vendor-slug>, MODEL_ASK=<your-vendor-slug>)
348
+ # - MODEL_RESOLVE=role_catalog enables phase→role→catalog slug lookup (requires v2 catalog with roles section)
349
+ MODEL_TIER_DEFAULT=balanced
350
+ MODEL_CATALOG=.cursor/model-catalog.local.json
351
+ MODEL_RESOLVE=alias_only
352
+ MODEL_FALLBACK=inherit
353
+ MODEL_PROVIDER_MODE=cursor
354
+
355
+ # Per-phase tier overrides for this project (uncomment and adjust as needed):
356
+ #MODEL_TIER_INTAKE=balanced
357
+ #MODEL_TIER_DISCOVERY=cheap
358
+ #MODEL_TIER_RESEARCH=balanced
359
+ #MODEL_TIER_ARCHITECTURE=strong
360
+ #MODEL_TIER_SPRINT-PLAN=strong
361
+ #MODEL_TIER_PLAN-VERIFY=balanced
362
+ #MODEL_TIER_EXECUTE=cheap
363
+ #MODEL_TIER_QA=strong
364
+ #MODEL_TIER_VERIFY-WORK=strong
365
+ #MODEL_TIER_RELEASE=balanced
366
+ #MODEL_TIER_REFRESH-CONTEXT=cheap
367
+
368
+ # Direct per-phase slug overrides (US-0102 — uncomment and set vendor slugs):
369
+ #MODEL_ASK=<your-vendor-slug>
370
+ #MODEL_EXECUTE=<your-vendor-slug>
371
+ #MODEL_QA=<your-vendor-slug>
372
+
373
+ # Use a vendor catalog instead of Cursor aliases:
374
+ # cp .cursor/model-catalog.local.example.level-2-complex.json .cursor/model-catalog.local.json
375
+ #MODEL_RESOLVE=local_catalog
376
+ # Role-based catalog preset (US-0102):
377
+ # cp .cursor/model-catalog.local.example.role-based-balanced.json .cursor/model-catalog.local.json
378
+ #MODEL_RESOLVE=role_catalog
@@ -322,3 +322,65 @@ CAVEMAN_MODE=1
322
322
  CAVEMAN_LEVEL=full
323
323
  CAVEMAN_COMPRESS_INPUT=0
324
324
  CAVEMAN_FILE_SCOPE=
325
+
326
+ #
327
+ # ## Per-phase model tier selection (US-0101 / DEC-0086)
328
+ # MODEL_TIER selects LLM model strength (which model runs).
329
+ # MODEL_TIER ≠ TOKEN_PROFILE ≠ DELIVERY_MODE — these are independent axes;
330
+ # none substitutes for the other (DEC-0062 / US-0080 / US-0096).
331
+ # - MODEL_TIER_DEFAULT: cheap|balanced|strong (default balanced)
332
+ # - MODEL_TIER_<PHASE>: cheap|balanced|strong (per-phase override; PHASE = canonical phase id)
333
+ # Examples: MODEL_TIER_EXECUTE=cheap, MODEL_TIER_QA=strong, MODEL_TIER_RESEARCH=balanced
334
+ # Set in .cursor/scratchpad.local.md to override per phase without touching committed defaults.
335
+ # Default matrix (architecture-locked):
336
+ # cheap — ask, refresh-context, memory-audit, status-reconcile, pause
337
+ # balanced — intake, discovery, research, release, plan-verify
338
+ # strong — architecture, execute, quick, qa, verify-work, security-review
339
+ # (inherit parent) — auto (orchestrator always inherits parent model)
340
+ # - MODEL_CATALOG: path to local slug catalog (default .cursor/model-catalog.local.json)
341
+ # - MODEL_RESOLVE: alias_only|local_catalog|role_catalog (default alias_only)
342
+ # alias_only = use Cursor-stable aliases (cheap->fast, balanced->inherit, strong->omit model:)
343
+ # local_catalog = look up vendor model slugs from MODEL_CATALOG; requires valid JSON catalog
344
+ # role_catalog = opt-in phase→role→catalog slug lookup (US-0102 / DEC-0087); falls through on miss
345
+ # - MODEL_FALLBACK: fallback when catalog lookup fails (default inherit)
346
+ # - MODEL_PROVIDER_MODE: cursor|api (default cursor)
347
+ # cursor = all subagents route through Cursor-managed infrastructure
348
+ # api = operator uses BYOK via Cursor Settings → Models → API Key
349
+ # Known limitation: subagents do NOT inherit custom API keys/base URLs.
350
+ #
351
+ # Example catalogs for 4 software-complexity levels + a Cursor-only variant:
352
+ # .cursor/model-catalog.local.example.json — minimal placeholder template
353
+ # .cursor/model-catalog.local.example.cursor-only.json — only Cursor-integrated Composer models
354
+ # .cursor/model-catalog.local.example.level-1-easy.json — small/simple apps
355
+ # .cursor/model-catalog.local.example.level-2-complex.json — complex multi-service apps
356
+ # .cursor/model-catalog.local.example.level-3-mega.json — mega-complex / modular monoliths
357
+ # .cursor/model-catalog.local.example.level-4-super.json — super-high-sophisticated / mission-critical
358
+ # .cursor/model-catalog.local.example.role-based-balanced.json — v2 role preset (balanced)
359
+ # .cursor/model-catalog.local.example.role-based-highend.json — v2 role preset (high-end)
360
+ # Copy one to .cursor/model-catalog.local.json and set MODEL_RESOLVE=local_catalog or role_catalog to activate.
361
+ MODEL_TIER_DEFAULT=balanced
362
+ MODEL_CATALOG=.cursor/model-catalog.local.json
363
+ MODEL_RESOLVE=alias_only
364
+ MODEL_FALLBACK=inherit
365
+ MODEL_PROVIDER_MODE=cursor
366
+ #
367
+ # ## Direct per-phase model slug override + role catalog (US-0102 / DEC-0087)
368
+ # Composes on US-0101 / DEC-0086 — tier baseline unchanged; overlays are optional.
369
+ # Precedence chain (deterministic, per canonical phase_id):
370
+ # 1. MODEL_<PHASE> — direct vendor slug override (highest priority)
371
+ # 2. MODEL_TIER_<PHASE> — DEC-0086 tier→alias / local_catalog chain
372
+ # 3. role_catalog lookup — only when MODEL_RESOLVE=role_catalog; miss falls through
373
+ # 4. MODEL_TIER_DEFAULT — DEC-0086 tier chain
374
+ # 5. Cursor stable alias — DEC-0086 built-in mapping (fast / inherit / omit)
375
+ # Scratchpad merge precedence for all MODEL_* keys: MODEL_<PHASE> > MODEL_TIER_<PHASE> > MODEL_TIER_DEFAULT
376
+ # - MODEL_<PHASE>: direct vendor slug; <PHASE> = canonical phase id (same list as MODEL_TIER_<PHASE>)
377
+ # Set in .cursor/scratchpad.local.md only — use <your-vendor-slug> placeholders in committed files.
378
+ # Canonical phase ids: ask, refresh-context, memory-audit, status-reconcile, pause,
379
+ # intake, discovery, research, release, plan-verify, architecture, execute, quick,
380
+ # qa, verify-work, security-review, auto
381
+ # Examples (placeholders — replace in scratchpad.local.md):
382
+ # MODEL_ASK=<your-vendor-slug>
383
+ # MODEL_EXECUTE=<your-vendor-slug>
384
+ # MODEL_QA=<your-vendor-slug>
385
+ # MODEL_REFRESH-CONTEXT=<your-vendor-slug>
386
+ # MODEL_ASK participates in step 1 like any other phase (no special-case bypass).
@@ -650,6 +650,203 @@ When **`AUTO_DELIVERY_ROUTING=backlog_then_scratchpad`**, story row may declare
650
650
 
651
651
  **Release status (S0086 / US-0096)**: **`released`** (`2026-06-13T16:00:00Z`); **`US-0096`** **DONE** in canonical backlog. Operator verify: **`handoffs/releases/S0086-release-notes.md`** **## Verify**; publish skipped while **`RELEASE_PUBLISH_MODE=confirm`**.
652
652
 
653
+ ## Per-phase model tier selection (US-0101 / DEC-0086)
654
+
655
+ `MODEL_TIER` selects LLM model strength (which model runs per lifecycle phase).
656
+ Three operator-facing tiers map to stable Cursor aliases — no vendor slugs in
657
+ template files.
658
+
659
+ | Key | Values | Default | Role |
660
+ |-----|--------|---------|------|
661
+ | **`MODEL_TIER_DEFAULT`** | `cheap` \| `balanced` \| `strong` | `balanced` | Fallback when phase-specific key absent |
662
+ | **`MODEL_TIER_<PHASE>`** | `cheap` \| `balanced` \| `strong` | *(per matrix)* | Per-phase tier override |
663
+ | **`MODEL_CATALOG`** | path | `.cursor/model-catalog.local.json` | Path to local slug catalog |
664
+ | **`MODEL_RESOLVE`** | `alias_only` \| `local_catalog` | `alias_only` | Resolution strategy |
665
+ | **`MODEL_FALLBACK`** | `inherit` | `inherit` | Fallback when catalog lookup fails |
666
+ | **`MODEL_PROVIDER_MODE`** | `cursor` \| `api` | `cursor` | Provider routing |
667
+
668
+ ### Default phase→tier matrix (architecture-locked)
669
+
670
+ | Tier | Cursor alias | `model:` field | Phases |
671
+ |------|-------------|----------------|--------|
672
+ | **`cheap`** | `fast` | `model: fast` | `ask`, `refresh-context`, `memory-audit`, `status-reconcile`, `pause` |
673
+ | **`balanced`** | `inherit` | `model: inherit` | `intake`, `discovery`, `research`, `release`, `plan-verify` |
674
+ | **`strong`** | *(omit)* | no `model:` field | `architecture`, `execute`, `quick`, `qa`, `verify-work`, `security-review` |
675
+ | *(inherit parent)* | — | — | `auto` (orchestrator always inherits parent model) |
676
+
677
+ ### Provider mode (MODEL_PROVIDER_MODE)
678
+
679
+ | Mode | Description |
680
+ |------|-------------|
681
+ | **`cursor`** (default) | All subagents route through Cursor-managed infrastructure; tier aliases work as designed |
682
+ | **`api`** | Operator uses BYOK via Cursor Settings → Models → API Key |
683
+
684
+ **Known limitation (confirmed 2026-06)**: Cursor subagents do **NOT** inherit
685
+ custom API keys or base URLs — they always bill against the Cursor plan. When
686
+ `MODEL_PROVIDER_MODE=api`, the local catalog is the only operator-controlled
687
+ surface; subagent `model:` aliases still resolve through Cursor infrastructure.
688
+
689
+ **Workaround recipes**:
690
+
691
+ 1. **Parent model + `inherit`** — set the parent chat model to the desired BYOK
692
+ model; subagents using `model: inherit` (balanced tier) pick it up.
693
+ 2. **Manual phase runs** — run each phase in a separate chat with the desired
694
+ model selected at the chat level (no subagent spawn).
695
+ 3. **Local catalog override** — set `MODEL_RESOLVE=local_catalog` and populate
696
+ `.cursor/model-catalog.local.json` with vendor slugs; materializer applies
697
+ slugs at template materialization time (not at subagent runtime).
698
+
699
+ ### Non-substitution paragraph
700
+
701
+ MODEL_TIER ≠ TOKEN_PROFILE ≠ DELIVERY_MODE
702
+
703
+ > **`MODEL_TIER`** selects LLM model strength (which model runs).
704
+ > **`TOKEN_PROFILE`** selects context breadth / token cost (how much context the model sees).
705
+ > **`DELIVERY_MODE`** selects lifecycle shape (standard / ultra_lean / mega_quick).
706
+ > These are **independent axes** — none substitutes for another. A `strong` tier
707
+ > with `lean` profile is valid; a `cheap` tier with `full` profile is valid.
708
+ > Setting one does not change the others. Combine freely.
709
+
710
+ ### Reason codes — `MODEL_TIER_*` / `MODEL_CATALOG_*` / `MODEL_RESOLVE_*` / `MODEL_SLUG_*`
711
+
712
+ | Code | Trigger |
713
+ |------|---------|
714
+ | **`MODEL_TIER_INVALID`** | Unknown tier value (not `cheap`/`balanced`/`strong`) |
715
+ | **`MODEL_CATALOG_INVALID`** | Malformed catalog JSON (parse error, missing `schema_version`) |
716
+ | **`MODEL_SLUG_UNKNOWN`** | Tier key missing from catalog when `MODEL_RESOLVE=local_catalog` |
717
+ | **`MODEL_RESOLVE_FALLBACK`** | Catalog lookup failed but `MODEL_FALLBACK=inherit` → reason + fallback |
718
+
719
+ ### Template agent defaults
720
+
721
+ | Agent role | Tier | `model:` field |
722
+ |-----------|------|----------------|
723
+ | `curator` | cheap | `model: fast` |
724
+ | `po` | balanced | `model: inherit` |
725
+ | `release` | balanced | `model: inherit` |
726
+ | `tech-lead` | strong | *(omit)* |
727
+ | `dev` | strong | *(omit)* |
728
+ | `qa` | strong | *(omit)* |
729
+ | `security` | strong | *(omit)* |
730
+
731
+ **Forbidden in `template/.cursor/agents/`**: hardcoded vendor slugs
732
+ (`composer-*`, `claude-*`, `gpt-*`, `opus-*`). Template files use aliases only.
733
+
734
+ ### Local catalog schema (v1)
735
+
736
+ ```json
737
+ {
738
+ "schema_version": 1,
739
+ "tiers": {
740
+ "cheap": "<slug>",
741
+ "balanced": "<slug>",
742
+ "strong": "<slug>"
743
+ },
744
+ "notes": "optional free-text"
745
+ }
746
+ ```
747
+
748
+ - Path: `.cursor/model-catalog.local.json` (gitignored)
749
+ - Example: `.cursor/model-catalog.local.example.json` (committed, placeholder values)
750
+ - All three tier keys required; values are opaque vendor slug strings
751
+
752
+ ### Resolver algorithm
753
+
754
+ 1. Read `MODEL_TIER_<PHASE>` from merged scratchpad → tier value
755
+ 2. If `MODEL_RESOLVE=alias_only` (default): use built-in mapping (table above)
756
+ 3. If `MODEL_RESOLVE=local_catalog`: load catalog JSON → lookup tier key → slug
757
+ 4. If key missing → `MODEL_SLUG_UNKNOWN` fail-closed
758
+ 5. If `MODEL_FALLBACK=inherit` and lookup fails → emit `MODEL_RESOLVE_FALLBACK` + use `inherit`
759
+ 6. Unknown tier value → `MODEL_TIER_INVALID` fail-closed
760
+ 7. Malformed catalog JSON → `MODEL_CATALOG_INVALID` fail-closed
761
+
762
+ ### Validation commands
763
+
764
+ - `python scripts/model_tier_validate.py --repo .`
765
+ - `pytest -k us0101 tests/auto_command_contract_test.py`
766
+ - `python scripts/check_intake_template_parity.py --scope=model-tier`
767
+ - `tests/run-tests.ps1` / `tests/run-tests.sh` §26Z
768
+
769
+ Normative architecture: `docs/engineering/architecture.md` (**# US-0101**).
770
+
771
+ ## Direct per-phase model slug override + role catalog (US-0102 / DEC-0087)
772
+
773
+ Composes on **US-0101** / **DEC-0086** — tier-only operators need no migration.
774
+
775
+ ### 5-step precedence chain
776
+
777
+ | Step | Source | Outcome |
778
+ |------|--------|---------|
779
+ | **1** | `MODEL_<PHASE>` | Non-empty slug → return (validate per mode) or `MODEL_OVERRIDE_SLUG_UNKNOWN` |
780
+ | **2** | `MODEL_TIER_<PHASE>` | **DEC-0086** tier→alias / `local_catalog` chain |
781
+ | **3** | `MODEL_RESOLVE=role_catalog` | Phase→logical role → catalog `roles[<key>]`; miss → `MODEL_ROLE_SLUG_UNKNOWN` → fall through |
782
+ | **4** | `MODEL_TIER_DEFAULT` | **DEC-0086** tier chain |
783
+ | **5** | Cursor alias | **DEC-0086** built-in mapping |
784
+
785
+ When `MODEL_RESOLVE` is `alias_only` or `local_catalog`, step **3** is skipped.
786
+
787
+ ### Scratchpad keys (new)
788
+
789
+ | Key | Default | Role |
790
+ |-----|---------|------|
791
+ | **`MODEL_<PHASE>`** | *(absent)* | Direct vendor slug; includes `MODEL_ASK` |
792
+ | **`MODEL_RESOLVE`** | `alias_only` | Extended: `alias_only` \| `local_catalog` \| **`role_catalog`** |
793
+
794
+ Operator slugs live in `.cursor/scratchpad.local.md` and `.cursor/model-catalog.local.json` only (gitignored).
795
+
796
+ ### Role catalog enablement recipe
797
+
798
+ 1. Copy a v2 example catalog:
799
+ - `.cursor/model-catalog.local.example.role-based-balanced.json`, or
800
+ - `.cursor/model-catalog.local.example.role-based-highend.json`
801
+ 2. Save as `.cursor/model-catalog.local.json` with real vendor slugs.
802
+ 3. Set `MODEL_RESOLVE=role_catalog` in `.cursor/scratchpad.local.md`.
803
+ 4. Optional: set `MODEL_<PHASE>=<slug>` for direct overrides (step 1 wins over role lookup).
804
+ 5. Run `python scripts/model_tier_validate.py --repo .` to validate.
805
+
806
+ On role lookup miss → `MODEL_ROLE_SLUG_UNKNOWN` emitted; resolver falls through to `MODEL_TIER_DEFAULT` then Cursor alias.
807
+
808
+ **`dev_difficult`**: no automatic phase routing — use `MODEL_EXECUTE=<slug>` direct override or tier `strong` + catalog tier slug.
809
+
810
+ Non-normative role recommendations: `ai_modell_auslegung_cursor_highend.md`.
811
+
812
+ ### Catalog schema v2 (opt-in)
813
+
814
+ ```json
815
+ {
816
+ "schema_version": 2,
817
+ "tiers": { "cheap": "<slug>", "balanced": "<slug>", "strong": "<slug>" },
818
+ "roles": {
819
+ "po": "<slug>", "sa": "<slug>", "dev": "<slug>", "dev_difficult": "<slug>",
820
+ "qa": "<slug>", "security": "<slug>", "release": "<slug>"
821
+ }
822
+ }
823
+ ```
824
+
825
+ v1 catalogs (tiers only) unchanged. Malformed v2 → `MODEL_CATALOG_SCHEMA_V2_INVALID`.
826
+
827
+ ### New reason codes
828
+
829
+ | Code | Trigger |
830
+ |------|---------|
831
+ | **`MODEL_OVERRIDE_SLUG_UNKNOWN`** | Direct slug validation failure |
832
+ | **`MODEL_ROLE_SLUG_UNKNOWN`** | Role catalog lookup miss |
833
+ | **`MODEL_CATALOG_SCHEMA_V2_INVALID`** | v2 schema validation failure |
834
+
835
+ ### Non-substitution paragraph
836
+
837
+ MODEL_TIER ≠ TOKEN_PROFILE ≠ DELIVERY_MODE — unchanged from **US-0101**; model selection is orthogonal to context breadth.
838
+
839
+ ### Validation commands
840
+
841
+ - `python scripts/model_tier_validate.py --repo .`
842
+ - `pytest -k us0102 tests/auto_command_contract_test.py`
843
+ - `python scripts/check_intake_template_parity.py --scope=model-tier-overrides`
844
+ - `tests/run-tests.ps1` / `tests/run-tests.sh` §26AA
845
+
846
+ Normative architecture: `docs/engineering/architecture.md` (**# US-0102**); decision: **`DEC-0087`**.
847
+
848
+ Binding decision: `decisions/DEC-0086.md`.
849
+
653
850
  Context compaction policy:
654
851
 
655
852
  - `docs/engineering/state.md` is a compact hot surface for current execution
@@ -228,6 +228,64 @@ RELEASE_CHANGELOG_PAIRS: tuple[tuple[str, str], ...] = (
228
228
  ),
229
229
  )
230
230
 
231
+ MODEL_TIER_PAIRS: tuple[tuple[str, str], ...] = (
232
+ (
233
+ "scripts/model_tier_lib.py",
234
+ "template/scripts/model_tier_lib.py",
235
+ ),
236
+ (
237
+ "scripts/model_tier_validate.py",
238
+ "template/scripts/model_tier_validate.py",
239
+ ),
240
+ (
241
+ "docs/engineering/runbook.md",
242
+ "template/docs/engineering/runbook.md",
243
+ ),
244
+ (
245
+ ".cursor/scratchpad.md",
246
+ "template/.cursor/scratchpad.md",
247
+ ),
248
+ (
249
+ ".cursor/scratchpad.local.example.md",
250
+ "template/.cursor/scratchpad.local.example.md",
251
+ ),
252
+ (
253
+ "scripts/check_intake_template_parity.py",
254
+ "template/scripts/check_intake_template_parity.py",
255
+ ),
256
+ )
257
+
258
+ MODEL_TIER_OVERRIDES_PAIRS: tuple[tuple[str, str], ...] = (
259
+ (
260
+ ".cursor/scratchpad.md",
261
+ "template/.cursor/scratchpad.md",
262
+ ),
263
+ (
264
+ ".cursor/scratchpad.local.example.md",
265
+ "template/.cursor/scratchpad.local.example.md",
266
+ ),
267
+ (
268
+ ".cursor/model-catalog.local.example.role-based-balanced.json",
269
+ "template/.cursor/model-catalog.local.example.role-based-balanced.json",
270
+ ),
271
+ (
272
+ ".cursor/model-catalog.local.example.role-based-highend.json",
273
+ "template/.cursor/model-catalog.local.example.role-based-highend.json",
274
+ ),
275
+ (
276
+ "scripts/model_tier_lib.py",
277
+ "template/scripts/model_tier_lib.py",
278
+ ),
279
+ (
280
+ "scripts/model_tier_validate.py",
281
+ "template/scripts/model_tier_validate.py",
282
+ ),
283
+ (
284
+ "docs/engineering/runbook.md",
285
+ "template/docs/engineering/runbook.md",
286
+ ),
287
+ )
288
+
231
289
  DOWNSTREAM_CI_GUARD_PAIRS: tuple[tuple[str, str], ...] = (
232
290
  (
233
291
  "scripts/check_downstream_ci_guard.py",
@@ -252,6 +310,8 @@ SCOPES: dict[str, tuple[tuple[str, str], ...]] = {
252
310
  "project-readme": PROJECT_README_PAIRS,
253
311
  "dev-environment": DEV_ENVIRONMENT_PAIRS,
254
312
  "release-changelog": RELEASE_CHANGELOG_PAIRS,
313
+ "model-tier": MODEL_TIER_PAIRS,
314
+ "model-tier-overrides": MODEL_TIER_OVERRIDES_PAIRS,
255
315
  "all": (
256
316
  INTAKE_TEMPLATE_PAIRS
257
317
  + CAVEMAN_COMPRESS_PAIRS
@@ -265,6 +325,8 @@ SCOPES: dict[str, tuple[tuple[str, str], ...]] = {
265
325
  + PROJECT_README_PAIRS
266
326
  + DEV_ENVIRONMENT_PAIRS
267
327
  + RELEASE_CHANGELOG_PAIRS
328
+ + MODEL_TIER_PAIRS
329
+ + MODEL_TIER_OVERRIDES_PAIRS
268
330
  ),
269
331
  }
270
332