prizmkit 1.0.103 → 1.0.105

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.
@@ -1,5 +1,5 @@
1
1
  {
2
- "frameworkVersion": "1.0.103",
3
- "bundledAt": "2026-03-24T17:11:59.061Z",
4
- "bundledFrom": "5e837b4"
2
+ "frameworkVersion": "1.0.105",
3
+ "bundledAt": "2026-03-25T15:34:22.423Z",
4
+ "bundledFrom": "c46942e"
5
5
  }
@@ -288,6 +288,18 @@ sys.exit(1)
288
288
  fi
289
289
  fi
290
290
 
291
+ # Check if session produced a failure-log for future retries
292
+ if [[ "$session_status" != "success" && -n "$feature_slug" ]]; then
293
+ local project_root_for_failure
294
+ project_root_for_failure="$(cd "$SCRIPT_DIR/.." && pwd)"
295
+ local failure_log="$project_root_for_failure/.prizmkit/specs/${feature_slug}/failure-log.md"
296
+ if [[ -f "$failure_log" ]]; then
297
+ log_info "FAILURE_LOG: Session wrote failure-log.md — will be available to next retry"
298
+ else
299
+ log_info "FAILURE_LOG: No failure-log.md written by session"
300
+ fi
301
+ fi
302
+
291
303
  # Update feature status
292
304
  local update_output
293
305
  update_output=$(python3 "$SCRIPTS_DIR/update-feature-status.py" \
@@ -8,6 +8,7 @@ to avoid duplication across pipeline scripts.
8
8
  import json
9
9
  import logging
10
10
  import os
11
+ import re
11
12
  import sys
12
13
 
13
14
 
@@ -107,26 +108,95 @@ def _build_progress_bar(percent, width=20):
107
108
  return "{} {:>3}%".format(bar, int(percent))
108
109
 
109
110
 
111
+ def _read_file_safe(filepath):
112
+ """Read a file and return its content, or empty string on error."""
113
+ try:
114
+ with open(filepath, "r", encoding="utf-8") as f:
115
+ return f.read()
116
+ except (IOError, OSError):
117
+ return ""
118
+
119
+
120
+ def _detect_node_runtime(project_root, pkg):
121
+ """Detect Node.js runtime version from engines, .nvmrc, or .node-version."""
122
+ engines = pkg.get("engines", {})
123
+ node_ver = engines.get("node", "")
124
+ if node_ver:
125
+ return "Node.js {}".format(node_ver)
126
+
127
+ for version_file in [".nvmrc", ".node-version"]:
128
+ vpath = os.path.join(project_root, version_file)
129
+ if os.path.isfile(vpath):
130
+ content = _read_file_safe(vpath).strip()
131
+ if content:
132
+ return "Node.js {}".format(content)
133
+
134
+ return "Node.js"
135
+
136
+
137
+ def _parse_python_deps(py_content, req_content):
138
+ """Extract Python package names from pyproject.toml and requirements.txt.
139
+
140
+ Returns a set of lowercased package names (without version specifiers).
141
+ """
142
+ deps = set()
143
+
144
+ # Parse requirements.txt lines: "package==1.0", "package>=2.0", "package"
145
+ for line in req_content.splitlines():
146
+ line = line.strip()
147
+ if not line or line.startswith("#") or line.startswith("-"):
148
+ continue
149
+ # Split on version specifiers and extras
150
+ name = re.split(r"[>=<!~;\[\s]", line, 1)[0].strip()
151
+ if name:
152
+ deps.add(name.lower())
153
+
154
+ # Parse pyproject.toml dependencies (simplified: look for quoted strings
155
+ # in [project.dependencies] and [project.optional-dependencies.*] sections)
156
+ in_deps_section = False
157
+ for line in py_content.splitlines():
158
+ stripped = line.strip()
159
+ if stripped.startswith("["):
160
+ in_deps_section = (
161
+ "dependencies" in stripped.lower()
162
+ and "optional" not in stripped.lower()
163
+ ) or "dependencies" in stripped.lower()
164
+ continue
165
+ if in_deps_section:
166
+ # Match quoted dependency: "flask>=2.0" or 'django~=4.0'
167
+ match = re.match(r"""^[\s"']*([a-zA-Z0-9_-]+)""", stripped)
168
+ if match:
169
+ deps.add(match.group(1).lower())
170
+
171
+ return deps
172
+
173
+
110
174
  def detect_project_context(project_root):
111
175
  """Auto-detect project tech stack from project files.
112
176
 
113
- Reads package.json, pyproject.toml, and common config files
114
- to infer language, test framework, and other stack details.
115
- Returns a dict of detected key-value pairs.
177
+ Reads package.json, pyproject.toml, requirements.txt, docker-compose,
178
+ and common config files to infer language, frameworks, styling,
179
+ database, ORM, bundler, testing, and project type.
180
+
181
+ Returns a dict of detected key-value pairs. Only detected fields are
182
+ included — no empty or null values. Adapts to any project type
183
+ (frontend-only, backend-only, fullstack, library, CLI, monorepo).
116
184
  """
117
185
  detected = {}
118
186
 
119
- # 1. Node.js / JavaScript / TypeScript project
187
+ # ── 1. Node.js / JavaScript / TypeScript project ──
120
188
  pkg_path = os.path.join(project_root, "package.json")
121
189
  if os.path.isfile(pkg_path):
122
190
  try:
123
191
  with open(pkg_path, "r", encoding="utf-8") as f:
124
192
  pkg = json.load(f)
125
193
 
126
- # Language detection
194
+ # All dependencies combined for detection
127
195
  deps = {}
128
196
  deps.update(pkg.get("dependencies", {}))
129
197
  deps.update(pkg.get("devDependencies", {}))
198
+
199
+ # Language
130
200
  if "typescript" in deps or os.path.isfile(
131
201
  os.path.join(project_root, "tsconfig.json")
132
202
  ):
@@ -134,7 +204,10 @@ def detect_project_context(project_root):
134
204
  else:
135
205
  detected["language"] = "JavaScript"
136
206
 
137
- # Test framework detection (order: more specific first)
207
+ # Runtime
208
+ detected["runtime"] = _detect_node_runtime(project_root, pkg)
209
+
210
+ # Test framework (more specific first)
138
211
  scripts = pkg.get("scripts", {})
139
212
  test_script = (
140
213
  scripts.get("test", "")
@@ -150,35 +223,256 @@ def detect_project_context(project_root):
150
223
  elif "--test" in test_script or "node:test" in test_script:
151
224
  detected["testing_framework"] = "Node.js built-in test runner"
152
225
 
153
- # Framework detection
154
- if "next" in deps:
155
- detected["framework"] = "Next.js"
156
- elif "express" in deps:
157
- detected["framework"] = "Express.js"
158
- elif "fastify" in deps:
159
- detected["framework"] = "Fastify"
160
- elif "react" in deps:
161
- detected["framework"] = "React"
162
- elif "vue" in deps:
163
- detected["framework"] = "Vue.js"
226
+ # ── Frontend framework ──
227
+ frontend_frameworks = [
228
+ ("next", "Next.js"),
229
+ ("nuxt", "Nuxt"),
230
+ ("@angular/core", "Angular"),
231
+ ("svelte", "Svelte"),
232
+ ("solid-js", "Solid.js"),
233
+ ("react", "React"),
234
+ ("vue", "Vue.js"),
235
+ ]
236
+ for dep_name, fw_name in frontend_frameworks:
237
+ if dep_name in deps:
238
+ detected["frontend_framework"] = fw_name
239
+ break
240
+
241
+ # ── Backend framework ──
242
+ backend_frameworks = [
243
+ ("@nestjs/core", "NestJS"),
244
+ ("express", "Express.js"),
245
+ ("fastify", "Fastify"),
246
+ ("koa", "Koa"),
247
+ ("hapi", "Hapi"),
248
+ ("hono", "Hono"),
249
+ ]
250
+ for dep_name, fw_name in backend_frameworks:
251
+ if dep_name in deps:
252
+ detected["backend_framework"] = fw_name
253
+ break
254
+
255
+ # Legacy "framework" field for backward compatibility
256
+ if "frontend_framework" in detected:
257
+ detected["framework"] = detected["frontend_framework"]
258
+ elif "backend_framework" in detected:
259
+ detected["framework"] = detected["backend_framework"]
260
+
261
+ # ── Frontend styling ──
262
+ styling_libs = [
263
+ ("tailwindcss", "Tailwind CSS"),
264
+ ("@tailwindcss/vite", "Tailwind CSS"),
265
+ ("styled-components", "Styled Components"),
266
+ ("@emotion/react", "Emotion"),
267
+ ("@emotion/styled", "Emotion"),
268
+ ("@mui/material", "Material UI"),
269
+ ("@chakra-ui/react", "Chakra UI"),
270
+ ("antd", "Ant Design"),
271
+ ("sass", "SCSS/Sass"),
272
+ ("node-sass", "SCSS/Sass"),
273
+ ("less", "Less"),
274
+ ]
275
+ for dep_name, style_name in styling_libs:
276
+ if dep_name in deps:
277
+ detected["frontend_styling"] = style_name
278
+ break
279
+
280
+ # ── Database ──
281
+ db_libs = [
282
+ ("pg", "PostgreSQL"),
283
+ ("postgres", "PostgreSQL"),
284
+ ("mysql2", "MySQL"),
285
+ ("mysql", "MySQL"),
286
+ ("better-sqlite3", "SQLite"),
287
+ ("mongodb", "MongoDB"),
288
+ ("mongoose", "MongoDB"),
289
+ ("redis", "Redis"),
290
+ ("ioredis", "Redis"),
291
+ ]
292
+ for dep_name, db_name in db_libs:
293
+ if dep_name in deps:
294
+ detected["database"] = db_name
295
+ break
296
+
297
+ # ── ORM ──
298
+ orm_libs = [
299
+ ("@prisma/client", "Prisma"),
300
+ ("prisma", "Prisma"),
301
+ ("drizzle-orm", "Drizzle"),
302
+ ("typeorm", "TypeORM"),
303
+ ("sequelize", "Sequelize"),
304
+ ("mongoose", "Mongoose"),
305
+ ("knex", "Knex.js"),
306
+ ("@mikro-orm/core", "MikroORM"),
307
+ ]
308
+ for dep_name, orm_name in orm_libs:
309
+ if dep_name in deps:
310
+ detected["orm"] = orm_name
311
+ break
312
+
313
+ # ── Bundler ──
314
+ bundler_libs = [
315
+ ("vite", "Vite"),
316
+ ("webpack", "Webpack"),
317
+ ("esbuild", "esbuild"),
318
+ ("rollup", "Rollup"),
319
+ ("parcel", "Parcel"),
320
+ ("turbo", "Turborepo"),
321
+ ("@rspack/core", "Rspack"),
322
+ ]
323
+ for dep_name, bundler_name in bundler_libs:
324
+ if dep_name in deps:
325
+ detected["bundler"] = bundler_name
326
+ break
327
+
328
+ # ── Project type inference ──
329
+ has_frontend = "frontend_framework" in detected
330
+ has_backend = "backend_framework" in detected
331
+ has_workspaces = "workspaces" in pkg
332
+ has_bin = "bin" in pkg
333
+
334
+ if has_workspaces:
335
+ detected["project_type"] = "monorepo"
336
+ elif has_frontend and has_backend:
337
+ detected["project_type"] = "fullstack"
338
+ elif has_frontend:
339
+ detected["project_type"] = "frontend"
340
+ elif has_backend:
341
+ detected["project_type"] = "backend"
342
+ elif has_bin:
343
+ detected["project_type"] = "cli"
344
+ elif "main" in pkg or "exports" in pkg:
345
+ detected["project_type"] = "library"
346
+
164
347
  except (json.JSONDecodeError, IOError):
165
348
  pass
166
349
 
167
- # 2. Python project detection
168
- if not detected:
350
+ # ── 2. Python project detection ──
351
+ if "language" not in detected:
352
+ py_content = ""
169
353
  for marker in ["pyproject.toml", "setup.py", "requirements.txt"]:
170
- if os.path.isfile(os.path.join(project_root, marker)):
354
+ marker_path = os.path.join(project_root, marker)
355
+ if os.path.isfile(marker_path):
171
356
  detected["language"] = "Python"
172
- # Check for pytest
173
- toml_path = os.path.join(project_root, "pyproject.toml")
174
- if os.path.isfile(toml_path):
175
- try:
176
- with open(toml_path, "r", encoding="utf-8") as f:
177
- content = f.read()
178
- if "pytest" in content:
179
- detected["testing_framework"] = "pytest"
180
- except IOError:
181
- pass
357
+ if marker == "pyproject.toml":
358
+ py_content = _read_file_safe(marker_path)
359
+ break
360
+
361
+ if detected.get("language") == "Python":
362
+ req_path = os.path.join(project_root, "requirements.txt")
363
+ req_content = _read_file_safe(req_path) if os.path.isfile(req_path) else ""
364
+ py_deps = _parse_python_deps(py_content, req_content)
365
+
366
+ # Runtime version (regex for requires-python = ">=3.11")
367
+ if py_content:
368
+ ver_match = re.search(
369
+ r'requires-python\s*=\s*["\']([^"\']+)["\']', py_content
370
+ )
371
+ if ver_match:
372
+ detected["runtime"] = "Python {}".format(ver_match.group(1))
373
+
374
+ # Testing
375
+ if "pytest" in py_deps:
376
+ detected["testing_framework"] = "pytest"
377
+
378
+ # Backend framework
379
+ py_backend = [
380
+ ("django", "Django"),
381
+ ("fastapi", "FastAPI"),
382
+ ("flask", "Flask"),
383
+ ("starlette", "Starlette"),
384
+ ("tornado", "Tornado"),
385
+ ("aiohttp", "aiohttp"),
386
+ ]
387
+ for dep_name, fw_name in py_backend:
388
+ if dep_name in py_deps:
389
+ detected["backend_framework"] = fw_name
390
+ detected["framework"] = fw_name
391
+ break
392
+
393
+ # Database / ORM
394
+ py_db = [
395
+ ("psycopg2", "PostgreSQL"),
396
+ ("psycopg", "PostgreSQL"),
397
+ ("asyncpg", "PostgreSQL"),
398
+ ("pymysql", "MySQL"),
399
+ ("pymongo", "MongoDB"),
400
+ ("motor", "MongoDB"),
401
+ ]
402
+ for dep_name, db_name in py_db:
403
+ if dep_name in py_deps:
404
+ detected["database"] = db_name
405
+ break
406
+
407
+ py_orm = [
408
+ ("sqlalchemy", "SQLAlchemy"),
409
+ ("tortoise-orm", "Tortoise ORM"),
410
+ ("peewee", "Peewee"),
411
+ ]
412
+ for dep_name, orm_name in py_orm:
413
+ if dep_name in py_deps:
414
+ detected["orm"] = orm_name
415
+ break
416
+ # Django ORM: if django is a dep and no other ORM detected
417
+ if "django" in py_deps and "orm" not in detected:
418
+ detected["orm"] = "Django ORM"
419
+
420
+ # Project type
421
+ if "backend_framework" in detected:
422
+ detected["project_type"] = "backend"
423
+
424
+ # ── 3. Go project detection ──
425
+ if "language" not in detected:
426
+ go_mod_path = os.path.join(project_root, "go.mod")
427
+ if os.path.isfile(go_mod_path):
428
+ detected["language"] = "Go"
429
+ detected["runtime"] = "Go"
430
+ go_content = _read_file_safe(go_mod_path)
431
+ if "gin-gonic" in go_content:
432
+ detected["backend_framework"] = "Gin"
433
+ elif "labstack/echo" in go_content:
434
+ detected["backend_framework"] = "Echo"
435
+ elif "go-chi/chi" in go_content:
436
+ detected["backend_framework"] = "Chi"
437
+ if "backend_framework" in detected:
438
+ detected["framework"] = detected["backend_framework"]
439
+ detected["project_type"] = "backend"
440
+
441
+ # ── 4. Rust / Java / other languages (basic detection) ──
442
+ if "language" not in detected:
443
+ if os.path.isfile(os.path.join(project_root, "Cargo.toml")):
444
+ detected["language"] = "Rust"
445
+ detected["runtime"] = "Rust"
446
+ elif os.path.isfile(os.path.join(project_root, "pom.xml")):
447
+ detected["language"] = "Java"
448
+ detected["runtime"] = "Java (Maven)"
449
+ elif os.path.isfile(os.path.join(project_root, "build.gradle")):
450
+ detected["language"] = "Java/Kotlin"
451
+ detected["runtime"] = "Java (Gradle)"
452
+
453
+ # ── 5. Database from docker-compose (cross-language) ──
454
+ if "database" not in detected:
455
+ for dc_name in [
456
+ "docker-compose.yml",
457
+ "docker-compose.yaml",
458
+ "compose.yml",
459
+ "compose.yaml",
460
+ ]:
461
+ dc_path = os.path.join(project_root, dc_name)
462
+ if os.path.isfile(dc_path):
463
+ dc_content = _read_file_safe(dc_path).lower()
464
+ dc_db = [
465
+ ("postgres", "PostgreSQL"),
466
+ ("mysql", "MySQL"),
467
+ ("mariadb", "MariaDB"),
468
+ ("mongo", "MongoDB"),
469
+ ("redis", "Redis"),
470
+ ("sqlite", "SQLite"),
471
+ ]
472
+ for pattern, db_name in dc_db:
473
+ if pattern in dc_content:
474
+ detected["database"] = db_name
475
+ break
182
476
  break
183
477
 
184
478
  return detected
@@ -199,10 +493,18 @@ def enrich_global_context(global_context, project_root):
199
493
  "language": "language",
200
494
  "testing_framework": "testing_strategy",
201
495
  "framework": "framework",
496
+ "frontend_framework": "frontend_framework",
497
+ "frontend_styling": "frontend_styling",
498
+ "backend_framework": "backend_framework",
499
+ "database": "database",
500
+ "orm": "orm",
501
+ "bundler": "bundler",
502
+ "project_type": "project_type",
503
+ "runtime": "runtime",
202
504
  }
203
505
  # Alternate key names that should block auto-detection
204
506
  alt_keys = {
205
- "testing_strategy": ["testing_framework", "test_framework"],
507
+ "testing_strategy": ["testing_framework", "test_framework", "testing"],
206
508
  }
207
509
  for det_key, ctx_key in key_mapping.items():
208
510
  if det_key not in detected:
@@ -73,6 +73,15 @@ Check `.prizmkit/specs/{{FEATURE_SLUG}}/context-snapshot.md` — if it exists, s
73
73
 
74
74
  ### Phase 1: Build Context Snapshot
75
75
 
76
+ **Check for previous failure log:**
77
+ ```bash
78
+ cat .prizmkit/specs/{{FEATURE_SLUG}}/failure-log.md 2>/dev/null || echo "NO_PREVIOUS_FAILURE"
79
+ ```
80
+ If failure-log.md exists:
81
+ - Read ROOT_CAUSE and SUGGESTION — adjust your approach accordingly
82
+ - Read DISCOVERED_TRAPS — if any are genuine, inject into .prizm-docs/ during Phase 4 retrospective
83
+ - Do NOT delete failure-log.md until this session succeeds
84
+
76
85
  ```bash
77
86
  ls .prizmkit/specs/{{FEATURE_SLUG}}/context-snapshot.md 2>/dev/null && echo "EXISTS" || echo "MISSING"
78
87
  ```
@@ -167,6 +176,23 @@ git commit -m "chore({{FEATURE_ID}}): include session artifacts"
167
176
  | Context Snapshot | `.prizmkit/specs/{{FEATURE_SLUG}}/context-snapshot.md` |
168
177
  | Project Root | {{PROJECT_ROOT}} |
169
178
 
179
+ ## Failure Capture Protocol
180
+
181
+ If you encounter an unrecoverable error, context overflow, or are about to exit without completing all phases:
182
+
183
+ 1. Write `.prizmkit/specs/{{FEATURE_SLUG}}/failure-log.md` BEFORE exiting:
184
+ ```
185
+ FAILURE_TYPE: timeout | test_failure | review_rejected | context_overflow | unknown
186
+ PHASE: <which phase failed>
187
+ ROOT_CAUSE: <1-2 sentence explanation>
188
+ ATTEMPTED: <approaches already tried>
189
+ SUGGESTION: <what the next session should try differently>
190
+ DISCOVERED_TRAPS:
191
+ - [CRITICAL|HIGH|LOW] <gotcha discovered during this failed session> | FIX: <approach>
192
+ ```
193
+
194
+ 2. This file is intentionally lightweight — write it BEFORE context runs out.
195
+
170
196
  ## Reminders
171
197
 
172
198
  - Tier 1: you handle everything directly — no subagents needed
@@ -84,6 +84,15 @@ Check `.prizmkit/specs/{{FEATURE_SLUG}}/context-snapshot.md` — if exists, skip
84
84
 
85
85
  ### Phase 1: Build Context Snapshot (you, the orchestrator)
86
86
 
87
+ **Check for previous failure log:**
88
+ ```bash
89
+ cat .prizmkit/specs/{{FEATURE_SLUG}}/failure-log.md 2>/dev/null || echo "NO_PREVIOUS_FAILURE"
90
+ ```
91
+ If failure-log.md exists:
92
+ - Read ROOT_CAUSE and SUGGESTION — adjust your approach accordingly
93
+ - Read DISCOVERED_TRAPS — if any are genuine, inject into .prizm-docs/ during Phase 5 retrospective
94
+ - Do NOT delete failure-log.md until this session succeeds
95
+
87
96
  ```bash
88
97
  ls .prizmkit/specs/{{FEATURE_SLUG}}/context-snapshot.md 2>/dev/null && echo "EXISTS" || echo "MISSING"
89
98
  ```
@@ -216,6 +225,23 @@ git commit -m "chore({{FEATURE_ID}}): include session artifacts"
216
225
  | Reviewer Agent Def | {{REVIEWER_SUBAGENT_PATH}} |
217
226
  | Project Root | {{PROJECT_ROOT}} |
218
227
 
228
+ ## Failure Capture Protocol
229
+
230
+ If you encounter an unrecoverable error, context overflow, or are about to exit without completing all phases:
231
+
232
+ 1. Write `.prizmkit/specs/{{FEATURE_SLUG}}/failure-log.md` BEFORE exiting:
233
+ ```
234
+ FAILURE_TYPE: timeout | test_failure | review_rejected | context_overflow | unknown
235
+ PHASE: <which phase failed>
236
+ ROOT_CAUSE: <1-2 sentence explanation>
237
+ ATTEMPTED: <approaches already tried>
238
+ SUGGESTION: <what the next session should try differently>
239
+ DISCOVERED_TRAPS:
240
+ - [CRITICAL|HIGH|LOW] <gotcha discovered during this failed session> | FIX: <approach>
241
+ ```
242
+
243
+ 2. This file is intentionally lightweight — write it BEFORE context runs out.
244
+
219
245
  ## Reminders
220
246
 
221
247
  - Tier 2: orchestrator builds context+plan, Dev implements, Reviewer reviews+tests — use direct Agent spawn for agents
@@ -147,6 +147,15 @@ After team setup: check `.prizmkit/specs/{{FEATURE_SLUG}}/context-snapshot.md`
147
147
 
148
148
  ### Phase 1-2: Specify + Plan — Orchestrator (you)
149
149
 
150
+ **Check for previous failure log:**
151
+ ```bash
152
+ cat .prizmkit/specs/{{FEATURE_SLUG}}/failure-log.md 2>/dev/null || echo "NO_PREVIOUS_FAILURE"
153
+ ```
154
+ If failure-log.md exists:
155
+ - Read ROOT_CAUSE and SUGGESTION — adjust your approach accordingly
156
+ - Read DISCOVERED_TRAPS — if any are genuine, inject into .prizm-docs/ during Phase 6 retrospective
157
+ - Do NOT delete failure-log.md until this session succeeds
158
+
150
159
  Check existing artifacts first:
151
160
  ```bash
152
161
  ls .prizmkit/specs/{{FEATURE_SLUG}}/ 2>/dev/null
@@ -396,6 +405,23 @@ git commit -m "chore({{FEATURE_ID}}): include session artifacts"
396
405
  | Project Root | {{PROJECT_ROOT}} |
397
406
  | Feature List Path | {{FEATURE_LIST_PATH}} |
398
407
 
408
+ ## Failure Capture Protocol
409
+
410
+ If you encounter an unrecoverable error, context overflow, or are about to exit without completing all phases:
411
+
412
+ 1. Write `.prizmkit/specs/{{FEATURE_SLUG}}/failure-log.md` BEFORE exiting:
413
+ ```
414
+ FAILURE_TYPE: timeout | test_failure | review_rejected | context_overflow | unknown
415
+ PHASE: <which phase failed>
416
+ ROOT_CAUSE: <1-2 sentence explanation>
417
+ ATTEMPTED: <approaches already tried>
418
+ SUGGESTION: <what the next session should try differently>
419
+ DISCOVERED_TRAPS:
420
+ - [CRITICAL|HIGH|LOW] <gotcha discovered during this failed session> | FIX: <approach>
421
+ ```
422
+
423
+ 2. This file is intentionally lightweight — write it BEFORE context runs out.
424
+
399
425
  ## Reminders
400
426
 
401
427
  - Tier 3: full team — Dev (implementation) → Reviewer (review + test) — spawn agents directly via Agent tool
@@ -151,7 +151,16 @@
151
151
  "description": "Global context for all bug fixes in this batch",
152
152
  "properties": {
153
153
  "tech_stack": { "type": "string" },
154
- "testing_framework": { "type": "string" },
154
+ "language": { "type": "string" },
155
+ "runtime": { "type": "string" },
156
+ "frontend_framework": { "type": "string" },
157
+ "frontend_styling": { "type": "string" },
158
+ "backend_framework": { "type": "string" },
159
+ "database": { "type": "string" },
160
+ "orm": { "type": "string" },
161
+ "testing_strategy": { "type": "string" },
162
+ "bundler": { "type": "string" },
163
+ "project_type": { "type": "string" },
155
164
  "ci_pipeline": { "type": "string" }
156
165
  }
157
166
  }
@@ -108,8 +108,17 @@
108
108
  "type": "object",
109
109
  "properties": {
110
110
  "tech_stack": { "type": "string" },
111
+ "language": { "type": "string" },
112
+ "runtime": { "type": "string" },
113
+ "frontend_framework": { "type": "string" },
114
+ "frontend_styling": { "type": "string" },
115
+ "backend_framework": { "type": "string" },
116
+ "database": { "type": "string" },
117
+ "orm": { "type": "string" },
111
118
  "design_system": { "type": "string" },
112
- "testing_strategy": { "type": "string" }
119
+ "testing_strategy": { "type": "string" },
120
+ "bundler": { "type": "string" },
121
+ "project_type": { "type": "string" }
113
122
  }
114
123
  }
115
124
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.0.103",
2
+ "version": "1.0.105",
3
3
  "skills": {
4
4
  "prizm-kit": {
5
5
  "description": "Full-lifecycle dev toolkit. Covers spec-driven development, Prizm context docs, code quality, debugging, deployment, and knowledge management.",
@@ -47,8 +47,15 @@ Do NOT use this skill when:
47
47
 
48
48
  Before questions, check optional context files (never block if absent):
49
49
  - `.prizm-docs/root.prizm` (architecture/project context)
50
- - `.prizmkit/config.json` (existing stack preferences)
50
+ - `.prizmkit/config.json` (existing stack preferences and detected tech stack)
51
51
  - existing `feature-list.json` (required for incremental mode)
52
+
53
+ **Tech stack auto-population from config.json:**
54
+ - If `.prizmkit/config.json` contains a `tech_stack` object, use it to pre-fill `global_context` fields in the generated `feature-list.json`.
55
+ - Map config fields to global_context: `language`, `runtime`, `frontend_framework`, `frontend_styling`, `backend_framework`, `database`, `orm`, `testing` → `testing_strategy`, `bundler`, `project_type`.
56
+ - Do NOT re-ask the user for tech stack info that is already present in config.json. Instead, show the detected stack and ask: "Is this correct? Any changes?"
57
+ - If config.json has no `tech_stack`, fall back to asking during Phase 2 (constraints and tech assumptions).
58
+
52
59
  Note:
53
60
  - This skill **reads** `.prizmkit/config.json` if present.
54
61
  - This skill does **not** create `.prizmkit/config.json` directly.
@@ -43,8 +43,8 @@ Launch the interactive bug planning process through 4 phases.
43
43
  ### Phase 1: Project Context
44
44
 
45
45
  1. **Identify project**: Read project name and description from existing `feature-list.json` or ask user
46
- 2. **Identify tech stack**: Read from `feature-list.json` global_context or `.prizm-docs/root.prizm`, or ask user
47
- 3. **Identify testing framework**: Auto-detect from package.json/requirements.txt/etc., or ask user
46
+ 2. **Identify tech stack**: Read from `.prizmkit/config.json` `tech_stack` (preferred), then `feature-list.json` global_context, then `.prizm-docs/root.prizm`. Only ask user if none of these sources provide tech stack info.
47
+ 3. **Identify testing framework**: Read from `.prizmkit/config.json` `tech_stack.testing`, or auto-detect from package.json/requirements.txt/etc., or ask user
48
48
 
49
49
  Output: `project_name`, `project_description`, `global_context` fields populated.
50
50
 
@@ -35,6 +35,8 @@ Project takeover and bootstrap skill. Scans any project (brownfield or greenfiel
35
35
 
36
36
  MODE DETECTION:
37
37
  - If `.prizm-docs/` exists: Ask user if they want to reinitialize or update
38
+ - **Reinitialize**: overwrites `.prizm-docs/` and `config.json` tech_stack (fresh start)
39
+ - **Update**: re-scans tech stack and merges changes into existing `config.json` (see Step 3b merge strategy); updates `root.prizm` TECH_STACK if changed; preserves existing `.prizm-docs/` L1/L2 docs
38
40
  - If project has source code: brownfield mode
39
41
  - If project is nearly empty: greenfield mode
40
42
 
@@ -50,6 +52,18 @@ BROWNFIELD WORKFLOW (existing project):
50
52
  3. Identify entry points by language convention
51
53
  4. Catalog dependencies (external packages)
52
54
  5. Count source files per directory
55
+ 6. Detect detailed tech stack (adaptive — only include fields that apply to this project):
56
+ - **Language & Runtime**: e.g. TypeScript + Node.js 20, Python 3.11, Go 1.22
57
+ - **Frontend framework** (if applicable): React, Vue.js, Angular, Next.js, Svelte, etc.
58
+ - **Frontend styling** (if applicable): Tailwind CSS, SCSS, Styled Components, Material UI, etc.
59
+ - **Backend framework** (if applicable): Express.js, FastAPI, Django, NestJS, Gin, etc.
60
+ - **Database** (if applicable): PostgreSQL, MySQL, MongoDB, Redis, SQLite — detected from deps or `docker-compose.yml`
61
+ - **ORM** (if applicable): Prisma, Drizzle, TypeORM, SQLAlchemy, Mongoose, etc.
62
+ - **Bundler** (if applicable): Vite, Webpack, esbuild, Rollup, Turborepo
63
+ - **Testing**: Vitest, Jest, pytest, Go test, etc.
64
+ - **Project type** (inferred): `frontend` | `backend` | `fullstack` | `library` | `cli` | `monorepo`
65
+
66
+ **IMPORTANT**: Not all projects have all fields. A pure backend API will have no `frontend_framework` or `frontend_styling`. A library may have no database. Only record what is actually detected — never generate empty or placeholder values.
53
67
 
54
68
  **Step 2: Prizm Documentation Generation**
55
69
  Invoke prizmkit-prizm-docs (Init operation), passing the two-tier module structure from Step 1:
@@ -64,6 +78,57 @@ Invoke prizmkit-prizm-docs (Init operation), passing the two-tier module structu
64
78
  - `.prizmkit/config.json` (adoption_mode, speckit_hooks_enabled, platform)
65
79
  - `.prizmkit/specs/` (empty)
66
80
 
81
+ 3b. Write detected tech stack to `.prizmkit/config.json`:
82
+
83
+ **Merge strategy** (handles re-init without losing user edits):
84
+ - Read existing `config.json` if present
85
+ - If `tech_stack` field exists AND `_auto_detected` is `false` or absent:
86
+ → **SKIP** — user has manually configured tech stack, preserve their settings
87
+ - If `tech_stack` field exists AND `_auto_detected` is `true`:
88
+ → **MERGE** — overwrite auto-detected values with new detection results, but preserve any keys the user added manually (keys not in the new detection result)
89
+ - If `tech_stack` field does NOT exist:
90
+ → **WRITE** full detected tech stack with `"_auto_detected": true`
91
+ - Only include fields that were actually detected (no empty/null values)
92
+
93
+ Example config.json after init (fullstack project):
94
+ ```json
95
+ {
96
+ "adoption_mode": "passive",
97
+ "platform": "claude",
98
+ "tech_stack": {
99
+ "language": "TypeScript",
100
+ "runtime": "Node.js 20",
101
+ "frontend_framework": "React",
102
+ "frontend_styling": "Tailwind CSS",
103
+ "backend_framework": "Express.js",
104
+ "database": "PostgreSQL",
105
+ "orm": "Prisma",
106
+ "testing": "Vitest",
107
+ "bundler": "Vite",
108
+ "project_type": "fullstack",
109
+ "_auto_detected": true
110
+ }
111
+ }
112
+ ```
113
+
114
+ Example config.json after init (pure Python backend):
115
+ ```json
116
+ {
117
+ "adoption_mode": "passive",
118
+ "platform": "claude",
119
+ "tech_stack": {
120
+ "language": "Python",
121
+ "runtime": "Python >=3.11",
122
+ "backend_framework": "FastAPI",
123
+ "database": "PostgreSQL",
124
+ "orm": "SQLAlchemy",
125
+ "testing": "pytest",
126
+ "project_type": "backend",
127
+ "_auto_detected": true
128
+ }
129
+ }
130
+ ```
131
+
67
132
  **Step 4: Hook & Settings Configuration**
68
133
 
69
134
  4a. Read or create platform settings file (`.codebuddy/settings.json` or `.claude/settings.json`)
@@ -74,15 +139,44 @@ Invoke prizmkit-prizm-docs (Init operation), passing the two-tier module structu
74
139
  4d. Preserve any existing hooks and settings — never overwrite user's custom configuration
75
140
 
76
141
  **Step 5: Report**
77
- Output summary: platform detected, tech stack detected, modules discovered, L1 docs generated, platform-specific configuration applied, next recommended steps.
142
+ Output summary: platform detected, tech stack detected (with detail), modules discovered, L1 docs generated, platform-specific configuration applied, next recommended steps.
143
+
144
+ Tech stack report format (only show detected fields, adapt to project type):
145
+ ```
146
+ Tech stack detected:
147
+ Language: TypeScript
148
+ Runtime: Node.js 20
149
+ Frontend: React + Tailwind CSS
150
+ Backend: Express.js
151
+ Database: PostgreSQL (Prisma)
152
+ Testing: Vitest
153
+ Bundler: Vite
154
+ Project type: fullstack
155
+ ```
156
+
157
+ For a pure backend Python project, the report would show:
158
+ ```
159
+ Tech stack detected:
160
+ Language: Python
161
+ Runtime: Python >=3.11
162
+ Backend: FastAPI
163
+ Database: PostgreSQL (SQLAlchemy)
164
+ Testing: pytest
165
+ Project type: backend
166
+ ```
167
+
168
+ Saved to: `.prizmkit/config.json` → `tech_stack` field
78
169
 
79
170
  Include platform-specific guidance:
80
171
  - CodeBuddy: "Use `/prizmkit-specify` to start your first feature"
81
172
  - Claude Code: "Use `/prizmkit-specify` to start your first feature"
82
173
 
83
174
  GREENFIELD WORKFLOW (new project):
84
- - Skip Step 1 (no code to scan)
85
- - Step 2: Create minimal `.prizm-docs/` with just `root.prizm` skeleton
175
+ - Skip Step 1 (no code to scan) — but ask the user about their intended tech stack:
176
+ - "What language/framework will you use?" (e.g. React + Node.js, Python + FastAPI, etc.)
177
+ - Record answers in `config.json` `tech_stack` with `"_auto_detected": false` (user-provided, not auto-detected)
178
+ - If user is unsure, skip tech_stack — it can be populated later on re-init after code exists
179
+ - Step 2: Create minimal `.prizm-docs/` with just `root.prizm` skeleton (populate TECH_STACK from user answers if provided)
86
180
  - Steps 3-5: Same as brownfield (Step 5 Report recommends starting with specify for first feature)
87
181
 
88
182
  ### Gradual Adoption Path
@@ -107,14 +201,23 @@ User can change mode in `.prizmkit/config.json`: `"adoption_mode": "passive" | "
107
201
 
108
202
  ## Example
109
203
 
110
- **Brownfield init on a Node.js project:**
204
+ **Brownfield init on a fullstack Node.js project:**
111
205
  ```
112
206
  $ /prizmkit-init
113
207
 
114
208
  Platform detected: Claude Code
115
- Tech stack: TypeScript, Node.js, Express
116
209
  Mode: Brownfield (154 source files found)
117
210
 
211
+ Tech stack detected:
212
+ Language: TypeScript
213
+ Runtime: Node.js 20
214
+ Frontend: React + Tailwind CSS
215
+ Backend: Express.js
216
+ Database: PostgreSQL (Prisma)
217
+ Testing: Vitest
218
+ Bundler: Vite
219
+ Project type: fullstack
220
+
118
221
  Modules discovered:
119
222
  src/routes/ → .prizm-docs/routes.prizm (12 files)
120
223
  src/models/ → .prizm-docs/models.prizm (8 files)
@@ -123,8 +226,25 @@ Modules discovered:
123
226
 
124
227
  Generated: root.prizm + 4 L1 docs + changelog.prizm
125
228
  Configured: .claude/rules/ (2 files), hooks in settings.json
229
+ Saved: .prizmkit/config.json (tech_stack recorded)
126
230
 
127
231
  Next: Use /prizmkit-specify to start your first feature
128
232
  ```
129
233
 
234
+ **Re-init after PrizmKit upgrade (existing config preserved):**
235
+ ```
236
+ $ /prizmkit-init
237
+
238
+ .prizm-docs/ already exists. Reinitialize or update?
239
+ > Update (preserve existing docs)
240
+
241
+ Tech stack changes detected:
242
+ + bundler: Vite (newly detected)
243
+ ~ testing: Jest → Vitest (updated)
244
+ = language: TypeScript (unchanged)
245
+ = frontend: React (unchanged)
246
+
247
+ Merged into .prizmkit/config.json (2 fields updated, user overrides preserved)
248
+ ```
249
+
130
250
  IMPORTANT: Use `${SKILL_DIR}` placeholder for all path references. Never hardcode absolute paths.
@@ -121,7 +121,7 @@ Check format compliance and consistency of all .prizm docs.
121
121
  PRECONDITION: .prizm-docs/ exists.
122
122
 
123
123
  STEPS:
124
- 1. FORMAT CHECK: Verify all .prizm files use KEY: value format. Flag any prose paragraphs, code blocks (```), markdown headers (##), emoji, ASCII art, or horizontal rules.
124
+ 1. FORMAT CHECK: Verify all .prizm files use KEY: value format. Flag any prose paragraphs, code blocks (```), markdown headers (##), emoji, ASCII art, or horizontal rules. Flag TRAPS entries missing severity prefix ([CRITICAL], [HIGH], or [LOW]). Note: [REVIEW] preceding severity (e.g., `[REVIEW][HIGH]`) is a valid temporary staleness marker, not a format violation.
125
125
  2. SIZE CHECK: Verify size limits: L0 <= 4KB, L1 <= 3KB, L2 <= 5KB. Report files exceeding limits with current size.
126
126
  3. POINTER CHECK: Verify all arrow (->) references resolve to existing .prizm files. Report broken pointers.
127
127
  4. TIMESTAMP CHECK: Verify all docs have UPDATED field. Flag docs with UPDATED older than 30 days as potentially stale.
@@ -1,4 +1,4 @@
1
- PRIZM_SPEC_VERSION: 2
1
+ PRIZM_SPEC_VERSION: 3
2
2
  PURPOSE: AI-only documentation framework for vibe coding projects
3
3
  AUDIENCE: AI agents (not humans)
4
4
  FORMAT: KEY: value pairs, ALL CAPS section headers, arrow pointers
@@ -180,7 +180,7 @@ TEMPLATE:
180
180
  - PREFER: <module-specific preference>
181
181
 
182
182
  TRAPS:
183
- - <what looks safe but is dangerous> | FIX: <correct approach>
183
+ - [CRITICAL|HIGH|LOW] <what looks safe but is dangerous> | FIX: <correct approach>
184
184
 
185
185
  DECISIONS:
186
186
  - <what was decided> — <rationale>
@@ -195,6 +195,16 @@ CONSTRAINTS:
195
195
  - SUBDIRS entries must have arrow pointer (->) if L2 doc exists
196
196
  - KEY_FILES lists only the most important files (max 10-15)
197
197
  - RULES may only SUPPLEMENT root.prizm RULES with module-specific exceptions, never contradict them
198
+ - TRAPS entries MUST have severity prefix ([CRITICAL], [HIGH], or [LOW]). [REVIEW] may precede severity as a temporary staleness marker.
199
+ - TRAPS optional fields: append `| REF: <7-char-hash>` for traceability, `| STALE_IF: <glob>` for auto-expiry detection
200
+
201
+ TRAPS_FORMAT_REFERENCE (spec-only — do NOT include this block in generated .prizm files):
202
+ - Severity levels: CRITICAL = data loss/security/financial/crash, HIGH = functional failure/silent error, LOW = naming/minor quality
203
+ - Temporary prefix: [REVIEW] may precede severity (e.g., `[REVIEW][HIGH]`) — signals the TRAP needs re-validation. Consumed by the next retrospective: verify and either remove [REVIEW] or delete the TRAP.
204
+ - REF: first 7 chars of the commit where the trap was discovered (optional, for traceability)
205
+ - STALE_IF: glob pattern — when matched files are deleted or heavily rewritten, this trap needs re-validation (optional)
206
+ - Minimal valid format: `- [SEVERITY] <description> | FIX: <approach>`
207
+ - Full format: `- [SEVERITY] <description> | FIX: <approach> | REF: <hash> | STALE_IF: <glob>`
198
208
 
199
209
  ## 3.3 L2: detail.prizm
200
210
 
@@ -220,8 +230,8 @@ TEMPLATE:
220
230
  - REJECTED: <approach that was tried/considered and abandoned + why>
221
231
 
222
232
  TRAPS:
223
- - <gotcha: something that looks correct but is wrong or dangerous>
224
- - <non-obvious coupling, race condition, or side effect>
233
+ - [CRITICAL|HIGH|LOW] <gotcha: something that looks correct but is wrong or dangerous> | FIX: <correct approach>
234
+ - [CRITICAL|HIGH|LOW] <non-obvious coupling, race condition, or side effect> | FIX: <approach>
225
235
 
226
236
  CHANGELOG:
227
237
  - YYYY-MM-DD | <verb>: <description of recent change to this module>
@@ -238,6 +248,9 @@ CONSTRAINTS:
238
248
  - DOMAIN-SPECIFIC SECTIONS are flexible, not prescribed
239
249
  - DECISIONS is append-only (never delete, archive if >20 entries)
240
250
  - TRAPS section is CRITICAL for preventing AI from making known mistakes
251
+ - TRAPS entries MUST have severity prefix ([CRITICAL], [HIGH], or [LOW]). [REVIEW] may precede severity as a temporary staleness marker.
252
+ - TRAPS optional fields: append `| REF: <7-char-hash>` for traceability, `| STALE_IF: <glob>` for auto-expiry detection
253
+ - TRAPS severity guide: CRITICAL = data loss/security/financial/crash, HIGH = functional failure/silent error, LOW = naming/minor quality
241
254
  - REJECTED entries prevent AI from re-proposing failed approaches
242
255
  - FILES lists all files, not just key ones
243
256
  - RULES may only SUPPLEMENT root.prizm RULES with module-specific exceptions, never contradict them
@@ -440,6 +453,7 @@ NEVER: Session-specific context or conversation history (docs are session-indepe
440
453
  NEVER: Flowcharts, diagrams, mermaid blocks, or ASCII art (wastes tokens, AI cannot parse visually)
441
454
  NEVER: Markdown headers (## / ###) inside .prizm files (use ALL CAPS KEY: format instead)
442
455
  NEVER: Rewrite entire .prizm files on update (modify only affected sections)
456
+ NEVER: TRAPS entries without severity prefix ([CRITICAL], [HIGH], or [LOW])
443
457
 
444
458
  ---
445
459
 
@@ -821,6 +835,16 @@ V2 (2026-03-02): Enhanced specification
821
835
  - Enhanced Section 9.1 with ON_DEEP_READ trigger alongside ON_MODIFY
822
836
  - Updated PRIZM_SPEC_VERSION to 2
823
837
 
838
+ V3 (2026-03-25): Knowledge quality and resilience
839
+ - TRAPS entries now MUST have severity prefix: [CRITICAL], [HIGH], or [LOW]
840
+ - TRAPS optional fields: REF (commit hash for traceability), STALE_IF (glob for auto-expiry detection)
841
+ - Added TRAPS staleness check in retrospective (step 1g): auto-archive stale TRAPS when count > 10
842
+ - Added Failure Capture Protocol: bootstrap templates write failure-log.md on session failure for cross-session learning
843
+ - Added retrospective data source: failure-log.md DISCOVERED_TRAPS are prioritized for .prizm-docs/ injection
844
+ - Added TRAPS_FORMAT_REFERENCE as spec-only block (must NOT appear in generated .prizm files)
845
+ - Added V2→V3 migration: auto-tag legacy TRAPS with [LOW] default severity
846
+ - Updated PRIZM_SPEC_VERSION to 3
847
+
824
848
  ---
825
849
 
826
850
  # SECTION 15: CONFLICT RESOLUTION
@@ -884,8 +908,8 @@ BEST_PRACTICE: Coordinate on MODULE_INDEX changes (adding/removing modules) to a
884
908
 
885
909
  ## 16.1 Migration Principles
886
910
 
887
- BACKWARD_COMPATIBLE: V2 can read V1 docs without modification
888
- FORWARD_COMPATIBLE: V1 tools will ignore V2-only fields they do not recognize
911
+ BACKWARD_COMPATIBLE: V3 can parse V2 docs without errors, but Validate will flag TRAPS entries missing severity prefixes and auto-migration (Section 16.3) will add [LOW] defaults
912
+ FORWARD_COMPATIBLE: V2 tools will ignore V3-only fields they do not recognize
889
913
  MIGRATION_TRIGGER: AI detects PRIZM_VERSION in root.prizm and applies migration if needed
890
914
 
891
915
  ## 16.2 V1 to V2 Migration
@@ -911,7 +935,32 @@ ALGORITHM: prizm_migrate_v1_to_v2
911
935
  4. UPDATE_CHANGELOG:
912
936
  APPEND: - YYYY-MM-DD | root | update: migrated from PRIZM_VERSION 1 to 2
913
937
 
914
- ## 16.3 Future Version Migration Pattern
938
+ ## 16.3 V2 to V3 Migration
939
+
940
+ TRIGGER: Automatic on first prizmkit-prizm-docs Update or Validate operation after spec upgrade
941
+
942
+ ALGORITHM: prizm_migrate_v2_to_v3
943
+
944
+ 1. UPDATE_VERSION:
945
+ Change: PRIZM_VERSION: 2 -> PRIZM_VERSION: 3
946
+ In: root.prizm
947
+
948
+ 2. MIGRATE_TRAPS_FORMAT:
949
+ SCAN: All L1 and L2 .prizm files for TRAPS entries
950
+ FOR EACH TRAPS entry without severity prefix ([CRITICAL], [HIGH], or [LOW]):
951
+ PREPEND: [LOW] as conservative default severity
952
+ EXAMPLE: `- foo is dangerous | FIX: use bar` becomes `- [LOW] foo is dangerous | FIX: bar`
953
+ RATIONALE: [LOW] is the safest default — it preserves the trap without over-alarming.
954
+ AI or user can manually upgrade severity to [HIGH] or [CRITICAL] during next retrospective.
955
+
956
+ 3. VALIDATE:
957
+ Run full Validate operation
958
+ REPORT: Migration results — number of TRAPS entries migrated, files updated
959
+
960
+ 4. UPDATE_CHANGELOG:
961
+ APPEND: - YYYY-MM-DD | root | update: migrated from PRIZM_VERSION 2 to 3 (TRAPS severity prefixes added)
962
+
963
+ ## 16.4 Future Version Migration Pattern
915
964
 
916
965
  FOR any future version N to N+1:
917
966
 
@@ -63,6 +63,9 @@ git diff --name-status
63
63
  - **L1**: Update FILES count, KEY_FILES (if major files added/removed), INTERFACES (if public API changed), UPDATED timestamp
64
64
  - **L0 root.prizm**: Update MODULE_INDEX file counts only if counts changed. Update UPDATED only if structural change (module added/removed).
65
65
 
66
+ **1d-migrate.** Legacy TRAPS format migration (opportunistic):
67
+ While updating an affected L1/L2 doc, if you encounter TRAPS entries **without** a severity prefix (e.g., `- foo | FIX: bar` instead of `- [LOW] foo | FIX: bar`), prepend `[LOW]` as a conservative default. This clears legacy format debt incrementally — only in files already being touched, never as a bulk operation.
68
+
66
69
  **1e.** If new directory with 3+ source files matches no existing module: create L1 doc immediately, add to MODULE_INDEX, defer L2.
67
70
 
68
71
  **1f.** Enforce size limits:
@@ -72,6 +75,15 @@ git diff --name-status
72
75
 
73
76
  **SKIP structural sync if**: only internal implementation changed (no interface/dependency impact), only comments/whitespace, only test files, only .prizm files, bug fixes with no interface change.
74
77
 
78
+ **1g. TRAPS staleness check** (only when an L2 doc's TRAPS section has > 10 entries):
79
+
80
+ Perform a quick staleness scan on existing TRAPS to prevent unbounded accumulation:
81
+ 1. If a TRAP has `STALE_IF:` and the glob-matched files no longer exist (verified via `ls` or `git status`) → delete the TRAP entry, append CHANGELOG: `remove: archived stale TRAP - <summary>`
82
+ 2. If a TRAP has `REF:` and the referenced code has been substantially rewritten (>80% of the original diff is gone, checked via `git log --follow`) → prepend `[REVIEW]` to the severity, signaling it needs manual verification
83
+ 3. Process at most 5 of the oldest TRAPS per L2 doc per session (to bound context cost)
84
+
85
+ This step is lightweight — it only triggers when TRAPS exceed 10 entries, and processes at most 5 per run.
86
+
75
87
  ---
76
88
 
77
89
  ## Job 2: Architecture Knowledge Injection → `.prizm-docs/`
@@ -95,14 +107,29 @@ Extract TRAPS, RULES, and DECISIONS from development work and inject into `.priz
95
107
  - `.prizmkit/specs/###-feature-name/context-snapshot.md` — read the '## Implementation Log' section (Dev's changes, decisions, discoveries) and '## Review Notes' section (Reviewer's findings). These are the **preferred source** for pre-categorized decisions and findings. If these sections exist, prefer them over re-extracting from git diff.
96
108
  - `.prizmkit/specs/###-feature-name/plan.md` — if feature work, read planned vs actual
97
109
  - `.prizmkit/bugfix/<id>/fix-report.md` — if bugfix, read what was discovered
110
+ - `.prizmkit/specs/###-feature-name/failure-log.md` — if a previous session failed, read DISCOVERED_TRAPS. These are high-value TRAPS because they come from actual failures — prioritize injecting them into `.prizm-docs/`
98
111
  - The relevant `.prizm-docs/` L1/L2 docs for affected modules
99
112
 
100
113
  **2b.** Extract knowledge from what was **observed in code**, not invented:
101
114
 
102
115
  **TRAPS** (highest priority) — things that look safe but break:
103
- - Format: `- <what looks safe but is dangerous> | FIX: <correct approach>`
116
+ - Minimal format: `- [SEVERITY] <description> | FIX: <approach>`
117
+ - Full format: `- [SEVERITY] <description> | FIX: <approach> | REF: <hash> | STALE_IF: <glob>`
104
118
  - Source: actual bugs hit, surprising behavior discovered in code, non-obvious coupling
105
119
 
120
+ **TRAPS severity classification guide**:
121
+ - `[CRITICAL]`: data loss, security vulnerability, financial error, system crash
122
+ - `[HIGH]`: functional failure, silent error, interface incompatibility
123
+ - `[LOW]`: misleading naming, non-intuitive API usage, minor performance issue
124
+
125
+ When writing TRAPS:
126
+ - Severity prefix is MANDATORY (e.g., `[CRITICAL]`, `[HIGH]`, `[LOW]`)
127
+ - OPTIONAL: append `| REF: <7-char-hash>` when you know the relevant commit (for traceability)
128
+ - OPTIONAL: append `| STALE_IF: <glob>` when the TRAP is tightly coupled to specific files (for auto-expiry detection)
129
+
130
+ **Consuming [REVIEW] markers** (from staleness check 1g):
131
+ - If you encounter a TRAP prefixed with `[REVIEW]` (e.g., `[REVIEW][HIGH] ...`), verify whether the trap is still valid by checking the current code. If still valid: remove the `[REVIEW]` prefix, keeping the severity. If no longer relevant: delete the TRAP entry and append CHANGELOG.
132
+
106
133
  **RULES** — conventions established or constraints discovered:
107
134
  - Format: `- MUST/NEVER/PREFER: <rule>`
108
135
  - Source: patterns that proved necessary during implementation
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prizmkit",
3
- "version": "1.0.103",
3
+ "version": "1.0.105",
4
4
  "description": "Create a new PrizmKit-powered project with clean initialization — no framework dev files, just what you need.",
5
5
  "type": "module",
6
6
  "bin": {