prizmkit 1.1.155 → 1.1.156

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,51 +1,93 @@
1
- # Knowledge Injection — Detailed Steps (2a–2c)
1
+ # Knowledge Injection — Exact-Scope Value and Cleanup Steps
2
2
 
3
- **2a.** Gather context from the **actual project files changed outside `.prizmkit/`**:
3
+ ## 1. Establish candidate fact authority
4
4
 
5
- - `git diff HEAD -- . ':(exclude).prizmkit/**'` the source of truth for eligible project changes
6
- - Current Main-Agent context for the requirement — reuse it rather than rereading artifacts by default
7
- - Targeted unchanged source, interfaces, callers, dependents, or tests only when an eligible diff raises a concrete ambiguity
8
- - The affected Prizm docs only as update targets and existing-knowledge checks, never as changed project input
5
+ Gather candidate knowledge only from actual project changes in the exact validated non-`.prizmkit/` input paths:
9
6
 
10
- Do not scan or mine generated records for retrospective knowledge. Caller metadata and existing Prizm docs may describe transient or generated material; they must not create a retrospective fact independently of an observed change in the exact non-`.prizmkit/` project paths supplied as input.
7
+ - `git diff HEAD -- <exact validated change_paths>` using literal pathspecs is the source of truth for eligible changes; never broaden the diff to `.` or use repository-wide status/discovery to add paths.
8
+ - Current source content at those exact paths may clarify the diff.
9
+ - Narrowly required unchanged source context (an interface, direct caller, direct dependent, or test) may be read only when an eligible diff raises a concrete ambiguity that cannot be resolved from the changed path itself.
10
+ - Existing affected Prizm docs may be read only as update targets, pointer maps, protected-current-knowledge evidence, and duplicate/staleness checks.
11
11
 
12
- **2b.** Extract knowledge from what was **observed in code**, not invented:
12
+ Generated artifacts, caller metadata, and existing Prizm text must not independently establish a candidate fact, expand `change_paths`, or turn an unrelated document into a target. `change_summary` is supporting context only. Reuse current Main-Agent source understanding when available, but do not treat lifecycle artifacts, completion notes, task IDs, or prior narrative as source evidence.
13
13
 
14
- **TRAPS** (highest priority) things that look safe but break:
15
- - Minimal format: `- [SEVERITY] <description> | FIX: <approach>`
16
- - Full format: `- [SEVERITY] <description> | FIX: <approach> | REF: <hash> | STALE_IF: <glob>`
17
- - Source: actual bugs hit, surprising behavior discovered in code, non-obvious coupling
14
+ ## 2. Apply the future-incorrect-modification Value Gate
18
15
 
19
- **TRAPS severity classification**:
20
- - `[CRITICAL]`: data loss, security, financial error, system crash
21
- - `[HIGH]`: functional failure, silent error, interface incompatibility
22
- - `[LOW]`: misleading naming, non-intuitive API, minor performance issue
16
+ For every candidate, ask exactly:
23
17
 
24
- When writing TRAPS:
25
- - Severity prefix is MANDATORY (e.g., `[CRITICAL]`, `[HIGH]`, `[LOW]`)
26
- - OPTIONAL: append `| REF: <7-char-hash>` when you know the relevant commit (for traceability)
27
- - OPTIONAL: append `| STALE_IF: <glob>` when the TRAP is tightly coupled to specific files (for auto-expiry detection)
18
+ > Could a future AI that lacks this fact make an incorrect modification?
28
19
 
29
- **Consuming [REVIEW] markers** (from staleness check 1g):
30
- - 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.
20
+ Retain it only when the answer is yes and current eligible source proves that it is durable, non-obvious, actionable for future modification, and owned by the target level.
31
21
 
32
- **RULES** conventions established or constraints discovered:
33
- - Format: `- MUST/NEVER/PREFER: <rule>`
34
- - Source: patterns that proved necessary during implementation
22
+ High-value candidate classes include:
35
23
 
36
- **DECISIONS** key design choices that affect future development:
37
- - Format: `- <what was decided> <rationale>`
38
- - Source: non-obvious design choices, interface conventions, cross-module contracts
39
- - Only record decisions that a future AI session would benefit from knowing
40
- - Do NOT record obvious implementation details that can be derived by reading the code
24
+ - non-obvious public interfaces and wire contracts;
25
+ - data flow, side effects, transactions, integrity boundaries, or surprising coupling;
26
+ - cross-module constraints and non-obvious dependencies;
27
+ - security, data-integrity, concurrency, transaction, and compatibility rules;
28
+ - actual traps, race conditions, failure modes, or unsafe-looking alternatives;
29
+ - architectural rules or durable decisions with rationale;
30
+ - observable behavior boundaries whose omission could cause a regression;
31
+ - rejected alternatives that future sessions are likely to propose again.
41
32
 
42
- **QUALITY GATE**: Every item must answer: "If a new AI session reads only `.prizmkit/prizm-docs/` and this entry, does it gain actionable understanding?" If not, discard. Do not record trivially observable code patterns the AI can read the code directly.
33
+ For a likely-to-recur rejected alternative, retain the alternative and its reason only when current eligible source establishes both. Do not retain a rejected option merely as design history.
43
34
 
44
- **MEMORY HYGIENE GATE**: Before writing any `.prizmkit/prizm-docs/` entry, remove transient caller metadata. Never write CHANGELOG sections/files, UPDATED/date metadata, internal execution IDs, branch names, absolute worktree paths, or generated artifact paths. Write only durable product/domain facts established by the supplied project changes.
35
+ Reject candidates that are source-derivable structure or signatures, transient implementation conclusions, historical task/change narrative, duplicate meanings, stale/conflicting statements, test inventories, coverage lists, routine file inventories, long procedures, low-value wording, or behavior already complete in a more specific child.
45
36
 
46
- **2c.** Inject into the correct `.prizmkit/prizm-docs/` file:
47
- - Module-level TRAPS/RULES/DECISIONS → the affected detail `.prizm` file. If the target detail document does not exist, create it from the detail-document generation template before injecting knowledge. These sections belong in detail documentation, not the module index.
48
- - Project-level RULES/PATTERNS → `root.prizm` (respect the current format — MODULE_INDEX or MODULE_GROUPS — do not convert between them during injection)
49
- - Cross-module concerns spanning 2+ modules → `root.prizm` CROSS_CUTTING section
37
+ ## 3. Extract only observed durable knowledge
50
38
 
51
- **RULE**: Only add genuinely new information. Never duplicate existing entries. Never rewrite entire files.
39
+ ### TRAPS
40
+
41
+ Use `- [SEVERITY] <description> | FIX: <approach>` and include only source-established non-obvious failure risks.
42
+
43
+ - `[CRITICAL]`: data loss, security failure, financial error, or crash.
44
+ - `[HIGH]`: functional failure, silent error, data-integrity risk, or interface incompatibility.
45
+ - `[LOW]`: misleading naming, non-intuitive API, or minor quality/performance risk that still passes the Value Gate.
46
+
47
+ Optional `REF` or `STALE_IF` metadata is allowed only when it is durable and source-relevant. For an affected `[REVIEW]` trap, remove the marker only when exact eligible source proves it still valid; delete it only when that source proves it obsolete. Lack of narrow evidence is not permission to delete it.
48
+
49
+ ### RULES
50
+
51
+ Use `- MUST/NEVER/PREFER: <rule>`. Keep only constraints that a future modification must obey and that eligible source establishes.
52
+
53
+ ### DECISIONS and rejected alternatives
54
+
55
+ Use `- <decision> — <durable rationale>` or `- REJECTED: <likely recurring alternative> — <why it remains rejected>`. Do not record obvious implementation choices or historical deliberation.
56
+
57
+ ### Interfaces and behavior
58
+
59
+ Retain only non-obvious public/wire contracts, data flow, side effects, dependencies, and observable boundaries whose absence could cause an incorrect modification. Omit signatures and structure that direct source reading makes obvious.
60
+
61
+ ## 4. Reconcile the current target before insertion
62
+
63
+ For every bounded affected target:
64
+
65
+ 1. Read its current content and the necessary resolving direct parent/child pointers.
66
+ 2. Build the protected set defined by the structural sync procedure before removing anything.
67
+ 3. Match by semantic meaning, not exact wording.
68
+ 4. Update the canonical entry in place when source changes an equivalent or conflicting fact; merge synonyms into one concise meaning.
69
+ 5. Remove only source-proven obsolete facts plus stale, derivable, duplicate, low-value, or parent-copied material outside the protected set.
70
+ 6. Add a candidate only when it passes the Value Gate and no equivalent canonical entry remains.
71
+ 7. Preserve stable section and entry order; do not reorder unchanged entries or append alternate wording merely to make a diff.
72
+
73
+ Append-only injection is prohibited. Cleanup remains limited to the exact affected targets and their necessary direct parent summaries/pointers; do not opportunistically clean siblings.
74
+
75
+ ## 5. Place knowledge at the lowest owning level
76
+
77
+ - Module behavioral details, `INTERFACES`, `DATA_FLOW`, `TRAPS`, complete `RULES`, `DECISIONS`, and rejected alternatives belong in the most specific mirrored or deterministic semantic detail.
78
+ - A real source-submodule boundary uses mirrored detail identity first.
79
+ - A flat module uses a semantic concern detail only when the structural procedure proves stable deterministic identity, explicit non-overlapping source-file ownership, capacity need, complete terminal shape, and a resolving direct-module `DETAILS` pointer.
80
+ - A direct module index keeps concise structure, navigation, critical summaries, and pointers; it does not copy complete child behavior.
81
+ - The root project map keeps only concise navigation and genuinely project-wide/cross-module summaries. An exact eligible change must establish the project-wide fact; existing root text cannot create one.
82
+
83
+ When one eligible change truly spans multiple affected modules, place complete knowledge in the lowest owning details and retain only the shortest useful cross-module summary/pointers at root. Do not infer cross-module scope from unrelated existing documentation.
84
+
85
+ ## 6. Hand candidate results to capacity preflight
86
+
87
+ Do not write directly from extraction. Hand the complete cleaned candidate, protected set, exact target identity, and affected direct pointers to `structural-sync-steps.md` for raw UTF-8 size measurement, minimum safe cleanup or semantic split, bottom-up replacement, re-read validation, and safe restoration.
88
+
89
+ If no candidate survives the Value Gate and structural reconciliation produces no byte change, return a truthful `NO_DOC_CHANGE`. A repeated run over identical source and inputs must preserve byte-identical documentation and must not create duplicate, reorder-only, or synonym-only churn.
90
+
91
+ ## 7. Enforce memory hygiene
92
+
93
+ Before final candidate validation, remove CHANGELOG sections/files, UPDATED/date metadata, feature/bug/refactor/task/session/run/pipeline/workflow IDs, branch names, absolute worktree paths, and `.prizmkit/specs` or `.prizmkit/dev-pipeline` artifact paths. Never trade protected durable product/domain knowledge for traceability noise.
@@ -1,90 +1,153 @@
1
- # Structural Sync — Detailed Steps
1
+ # Structural Sync — Capacity-Safe Detailed Steps
2
2
 
3
3
  ## 1. Validate supplied changed files
4
4
 
5
5
  Use only the caller-supplied `change_paths`. Normalize each repository-relative path, reject paths outside the checkout or under `.prizmkit/`, and verify its current added/modified/deleted/renamed state with exact pathspecs.
6
6
 
7
- Do not expand the path set through repository-wide status or diff discovery. If the validated list is empty, structural sync is not needed.
7
+ Do not expand the path set through repository-wide status or diff discovery. Never run an unconditional repository-wide documentation rewrite. If the validated list is empty, structural sync is not needed.
8
8
 
9
- ## 2. Map files to modules
9
+ ## 2. Map exact changes to a bounded target set
10
10
 
11
- Read `.prizmkit/prizm-docs/root.prizm` and map each changed file to MODULE_INDEX or MODULE_GROUPS entries.
11
+ Read `.prizmkit/prizm-docs/root.prizm` only as the project map, then map each exact changed path through its MODULE_INDEX or MODULE_GROUPS pointer to the most specific existing documentation owner.
12
12
 
13
- If a changed source file maps to no module, evaluate whether its directory qualifies as a new module:
13
+ The writable set contains only:
14
14
 
15
- - Contains source files forming a logical unit
16
- - Contains entry/config/interface files
17
- - Contains qualifying submodules
18
- - Is referenced by multiple modules as a dependency
15
+ - the most specific detail targets that own eligible changed paths;
16
+ - their necessary direct parent summary or pointer at each affected level;
17
+ - a new detail target only when the eligible source change proves meaningful durable behavior and deterministic identity;
18
+ - root only when module structure or its resolving module/group pointer changes.
19
19
 
20
- ## 3. Classify changes
20
+ Affected docs may be read to reconcile current content and resolve pointers, but they cannot independently add source facts or pull sibling docs into the writable set. Do not clean unrelated targets merely because they are stale or near a limit.
21
21
 
22
- - `A` added add to KEY_FILES and check for new public interfaces
23
- - `D` deleted → remove from KEY_FILES and update file counts
24
- - `M` modified → check public interfaces, dependencies, data flow, and durable traps/decisions
25
- - `R` renamed → update path references
22
+ If a changed source path maps to no module, evaluate only that path and narrowly required directory context to determine whether its directory is a real logical module with entry/config/interface files or cross-module dependency use. Do not perform a repository scan to manufacture a module.
26
23
 
27
- ## 4. Decide whether sync is needed
24
+ ## 3. Classify eligible structural changes
28
25
 
29
- Skip structural sync when changes are limited to:
26
+ - `A` added: update navigation only when the file passes the Value Gate; check source-established public/wire contracts.
27
+ - `D` deleted: remove stale owned navigation and source-invalidated knowledge.
28
+ - `M` modified: check non-obvious public/wire interfaces, dependencies, data flow, behavior, traps, rules, and decisions.
29
+ - `R` renamed: update only implicated ownership and resolving paths.
30
30
 
31
- - comments, whitespace, or formatting
32
- - internal implementation details with no interface, dependency, data-flow, or module mapping impact
33
- - test-only changes that reveal no durable boundaries, traps, interface constraints, behavior rules, or regression knowledge
31
+ Skip structural writes for comments, whitespace, formatting, internal implementation detail with no durable contract/behavior impact, and test-only changes that reveal no durable boundary. Do not skip a bug or test change when its exact source evidence changes durable behavior or constraints.
34
32
 
35
- Do not skip automatically just because the task is a bug fix or test change. Run the relevant part of retrospective when the change affects structure, interfaces, dependencies, observable behavior, or durable knowledge.
33
+ ## 4. Select the most specific detail identity
36
34
 
37
- ## 5. Update affected docs bottom-up
35
+ ### Mirrored source identity
38
36
 
39
- ### Detail documentation
37
+ When identities compete, mirrored source identity takes precedence.
40
38
 
41
- If a relevant detail document exists, update only sections affected by supplied changed files in that module.
39
+ A real source submodule uses its mirrored detail path. Mirrored source identity takes precedence over a semantic concern with the same path or meaning. Update an existing mirrored detail before considering a semantic split.
42
40
 
43
- If detail documentation does not exist and the supplied changes include Added or Modified source files with meaningful logic, create it with:
41
+ A new mirrored detail uses these required sections:
44
42
 
45
43
  ```text
46
44
  MODULE
47
45
  FILES
48
46
  RESPONSIBILITY
47
+ INTERFACES
49
48
  KEY_FILES
50
49
  DEPENDENCIES
51
- INTERFACES
52
50
  TRAPS
53
51
  ```
54
52
 
55
- Add DATA_FLOW, RULES, or DECISIONS only when the changed files provide durable information for those sections. Populate from the diff files and targeted source reads, not from an unbounded full-module rescan.
53
+ Add `DATA_FLOW`, `RULES`, `DECISIONS`, rejected alternatives, or domain sections only for facts that pass the Value Gate.
54
+
55
+ ### Deterministic semantic concern identity
56
+
57
+ For a flat source module, create a semantic concern detail only when current eligible source proves multiple stable product/domain behavior concerns and protected durable knowledge cannot fit one 5120B detail after safe cleanup. A semantic concern is not another documentation level.
58
+
59
+ Derive one deterministic concern slug:
60
+
61
+ 1. Choose a concise stable product/domain concern name from current source responsibility; task, change, file-count, or temporary initiative labels are invalid.
62
+ 2. Normalize the name with Unicode NFKC.
63
+ 3. Apply Unicode lowercase.
64
+ 4. Replace each maximal run of characters that is neither a Unicode letter nor a Unicode decimal digit with one ASCII hyphen.
65
+ 5. Trim leading/trailing hyphens and require one non-empty, unambiguous lowercase kebab-case result.
66
+ 6. Compare it with sibling semantic concerns, sibling `DETAILS` entries, and mirrored submodule paths. Task IDs, numeric suffixes, timestamps, and branch names are prohibited as collision workarounds.
67
+
68
+ The detail path is `.prizmkit/prizm-docs/<module>/<concern-slug>.prizm`. It has `MODULE`, `CONCERN`, `FILES`, `RESPONSIBILITY`, `INTERFACES`, `DATA_FLOW`, `KEY_FILES`, `DEPENDENCIES`, `RULES`, `TRAPS`, and `DECISIONS`; use `<SECTION>: none` rather than invented filler when a required behavioral section has no qualifying fact.
69
+
70
+ `FILES` is a non-empty exhaustive normalized source-file ownership list for the concern. Sibling semantic details require explicit non-overlapping `FILES` ownership. The direct module parent has exactly one `DETAILS` pointer:
71
+
72
+ ```text
73
+ - <concern-slug>: <concise stable behavior summary> -> .prizmkit/prizm-docs/<module>/<concern-slug>.prizm
74
+ ```
75
+
76
+ The pointer path, target `MODULE`, normalized `CONCERN` slug, and `FILES` ownership must agree. Mirrored source identity takes precedence; ambiguity, collision, or overlapping ownership blocks the split. Never add a suffix or partial semantic document to guess around it.
77
+
78
+ ## 5. Preflight every target before replacement
79
+
80
+ For each target in the bounded writable set:
81
+
82
+ 1. Read current content and resolving parent/child pointers. For an existing target, preserve its exact pre-write bytes; for a new target, record that no pre-write file exists.
83
+ 2. Measure the current target before replacement using exact raw UTF-8 bytes, never characters, lines, rounded kilobytes, or locale-dependent encoding.
84
+ 3. Establish a protected set before cleanup. It includes every still-valid public or wire contract, CRITICAL/HIGH trap, data-flow or data-integrity constraint, security/concurrency/transaction/compatibility rule, cross-module rule, non-obvious dependency, side effect, and durable decision with necessary rationale.
85
+ 4. Build the complete candidate result before replacement. Match existing and candidate facts by meaning; update the canonical entry in place, merge synonyms, remove equivalent, stale, derivable, conflicting, duplicate, or parent-copied material, and never append another version of the same knowledge.
86
+ 5. Measure the candidate as exact raw UTF-8 bytes and classify both current and candidate against the target's level.
87
+
88
+ Capacity limits and bands:
89
+
90
+ - Exact root project map: hard limit 4096B.
91
+ - Direct module index: hard limit 4096B.
92
+ - Nested mirrored or semantic detail: hard limit 5120B.
93
+ - normal: `size * 100 < limit * 80`.
94
+ - warning: `size * 100 >= limit * 80` and `size * 100 < limit * 90`.
95
+ - strong-warning: `size * 100 >= limit * 90` and `size <= limit`.
96
+ - error: `size > limit`.
97
+
98
+ When the current or candidate reaches at least 80%, perform the minimum safe cleanup or semantic split before final replacement:
99
+
100
+ 1. trim stale, source-derivable, procedural, historical, transient, and low-value wording;
101
+ 2. merge synonymous facts and remove duplicate meanings;
102
+ 3. move child-owned behavior from the root/module index into the existing or newly justified resolving detail;
103
+ 4. if one detail still cannot retain protected durable knowledge within 5120B, use the deterministic semantic split only when its stable identity and non-overlapping ownership are unambiguous.
104
+
105
+ Aim for 3277–3686B for a root/module index and 4096–4607B for a detail document, preserving approximately 10% headroom. Do not pad naturally concise output and do not trim merely to one byte below a hard limit. A post-remediation warning-range result is valid; do not force a needless split when the minimum safe cleanup establishes the target range.
106
+
107
+ Never remove protected knowledge solely to pass capacity validation. If it cannot fit after concise wording, safe movement, or an unambiguous split, do not replace that target.
108
+
109
+ ## 6. Write bottom-up
56
110
 
57
- ### Module index
111
+ Prepare all candidates before the first replacement and write only after each candidate passes its local format, identity, protected-set, and capacity preflight.
58
112
 
59
- Update FILES count, KEY_FILES, SUBDIRS, and DEPENDENCIES when module-level structure changes.
113
+ 1. Create or update the most specific detail first.
114
+ 2. Re-read and validate that detail before modifying its parent.
115
+ 3. Update the direct module index only as concise summaries and resolving pointers, using `SUBDIRS` or `DETAILS` as applicable.
116
+ 4. Update the root project map last and only when module structure or its concise resolving pointer changed.
60
117
 
61
- Module indexes do not contain INTERFACES, DATA_FLOW, TRAPS, or DECISIONS; those belong in detail documentation.
118
+ Complete `INTERFACES`, `DATA_FLOW`, `TRAPS`, `DECISIONS`, and full RULES owned by a child must not be copied into the module index or root project map. Parent docs retain the shortest useful summary and pointer. Preserve any root `PROJECT_BRIEF:` line because it is managed separately.
62
119
 
63
- ### Root project map
120
+ Before each replacement, compare candidate bytes with current bytes. A byte-identical candidate is not written, reordered, or reported as an update.
64
121
 
65
- Update MODULE_INDEX or MODULE_GROUPS only when module structure changed. Preserve any `PROJECT_BRIEF:` line because it is managed separately.
122
+ ## 7. Re-read and validate the actual final files
66
123
 
67
- ## 6. New module handling
124
+ After every write:
68
125
 
69
- If a new directory qualifies as a module and matches no existing module:
126
+ 1. Re-read the actual bytes from disk and recompute exact raw UTF-8 size.
127
+ 2. Reapply the 4096B/4096B/5120B hard limit and 80%/90% band formulas.
128
+ 3. Validate required KEY/value format and memory hygiene.
129
+ 4. Resolve every affected `MODULE_INDEX`, `MODULE_GROUPS`, `SUBDIRS`, and `DETAILS` pointer; validate both each changed target and its affected direct parent/child.
130
+ 5. Confirm semantic `MODULE`, `CONCERN`, path, slug, and `FILES` ownership agree and do not collide or overlap.
131
+ 6. Review only the targeted documentation diff for duplicate meanings, copied child behavior, unstable reorder, and unintended paths.
70
132
 
71
- 1. Create its module index immediately.
72
- 2. Add it to MODULE_INDEX or MODULE_GROUPS.
73
- 3. Create detail documentation only when the supplied changes include meaningful Added or Modified source files for the module.
133
+ If any post-write check fails, restore every replaced target from its preserved exact pre-write bytes and remove any invalid newly created target. Re-read the restored affected set and ensure no invalid oversized final document remains. Restoration is a bounded file operation; never use Git reset, checkout, or stash.
74
134
 
75
- ## 7. Size limits
135
+ ## 8. Capacity blocker evidence
76
136
 
77
- - Root project map > 4KB consolidate MODULE_INDEX or convert to MODULE_GROUPS when module count is high
78
- - Module index > 4KB → trim KEY_FILES descriptions and keep RULES concise
79
- - Detail document > 5KB → remove derived detail or stale entries; keep durable knowledge only
137
+ When no safe compression or stable split exists, stop with no invalid candidate left in place. The blocked result's `reason` must include:
80
138
 
81
- ## 8. TRAPS staleness check
139
+ - exact target path;
140
+ - measured current bytes and/or candidate bytes, hard limit, and band;
141
+ - protected knowledge that prevented trimming;
142
+ - why attempted cleanup, movement, or identity/ownership checks could not establish a safe split;
143
+ - a concrete recommended split boundary that a future manual decision can evaluate.
82
144
 
83
- Run only when a detail document's TRAPS section has more than 10 entries.
145
+ Its `validation` contains only checks actually attempted. Its `documentation_paths` follows the result schema and never invents a change. `RETRO_COMPLETE` is prohibited after any unresolved capacity, pointer, format, or restoration check.
84
146
 
85
- Process at most 5 oldest TRAPS per detail document:
147
+ ## 9. Bounded TRAPS staleness check
86
148
 
87
- 1. If a TRAP has `STALE_IF:` and matched files no longer exist, delete the TRAP.
88
- 2. If a TRAP has `REF:` and the referenced file is gone or the commit is older than the review horizon, mark it `[REVIEW]` for the next knowledge pass.
149
+ Run only for an affected detail target whose `TRAPS` section has more than 10 entries. Process at most five oldest traps:
89
150
 
90
- This bounds context cost and prevents unbounded trap accumulation.
151
+ 1. Delete a trap only when eligible source evidence proves it stale, including a `STALE_IF` path that no longer exists within the exact scope.
152
+ 2. If a `REF` cannot be validated from eligible evidence, retain the trap or mark it `[REVIEW]`; do not mine repository history or unrelated source to decide.
153
+ 3. Never remove a still-valid CRITICAL/HIGH trap merely for capacity.
@@ -6,6 +6,8 @@ from __future__ import annotations
6
6
  import re
7
7
  import subprocess
8
8
  import sys
9
+ from dataclasses import dataclass
10
+ from functools import cmp_to_key
9
11
  from pathlib import Path, PurePosixPath
10
12
 
11
13
  DOCS_ROOT = Path(".prizmkit/prizm-docs")
@@ -124,24 +126,129 @@ def read_check_content(file_path: str, mode: str) -> tuple[bytes, str] | None:
124
126
  return raw, raw.decode("utf-8", errors="replace")
125
127
 
126
128
 
129
+ @dataclass(frozen=True)
130
+ class CapacityDiagnostic:
131
+ file_path: str
132
+ level: str
133
+ size: int
134
+ limit: int
135
+ band: str
136
+ target_low: int
137
+ target_high: int
138
+ actions: str
139
+
140
+
141
+ def is_root_doc(file_path: str) -> bool:
142
+ return PurePosixPath(file_path) == PurePosixPath(ROOT_PRIZM.as_posix())
143
+
144
+
127
145
  def is_l1_doc(file_path: str) -> bool:
128
146
  parent = PurePosixPath(file_path).parent.as_posix()
129
147
  return parent == DOCS_ROOT.as_posix()
130
148
 
131
149
 
132
150
  def size_limit(file_path: str) -> tuple[int, str, str]:
133
- if file_path.endswith("root.prizm"):
134
- return 4096, "L0", "Consolidate MODULE_INDEX, keep top-5 RULES."
151
+ if is_root_doc(file_path):
152
+ return (
153
+ 4096,
154
+ "L0",
155
+ "trim: lower-priority global wording; "
156
+ "deduplicate: repeated RULES or module summaries; "
157
+ "move-to-child: module detail into an L1 document; "
158
+ "semantic-split: broad module groups into focused L1 documents",
159
+ )
135
160
  if is_l1_doc(file_path):
136
- return 3072, "L1", "Move implementation details to L2."
137
- return 5120, "L2", "Remove stale or trivially derivable entries."
161
+ return (
162
+ 4096,
163
+ "L1",
164
+ "trim: low-value module summaries; "
165
+ "deduplicate: repeated interfaces, rules, or traps; "
166
+ "move-to-child: implementation detail into an L2 document; "
167
+ "semantic-split: distinct submodules into focused L2 documents",
168
+ )
169
+ return (
170
+ 5120,
171
+ "L2",
172
+ "trim: stale or derivable detail; "
173
+ "deduplicate: repeated implementation notes; "
174
+ "move-to-child: one coherent detail set into a focused child document; "
175
+ "semantic-split: unrelated responsibilities into focused L2 documents",
176
+ )
177
+
178
+
179
+ def ceil_percent(limit: int, percent: int) -> int:
180
+ return (limit * percent + 99) // 100
181
+
182
+
183
+ def capacity_band(size: int, limit: int) -> str:
184
+ if size > limit:
185
+ return "error"
186
+ if size * 100 >= limit * 90:
187
+ return "strong-warning"
188
+ if size * 100 >= limit * 80:
189
+ return "warning"
190
+ return "normal"
191
+
192
+
193
+ def capacity_diagnostic(file_path: str, size: int) -> CapacityDiagnostic | None:
194
+ limit, level, actions = size_limit(file_path)
195
+ band = capacity_band(size, limit)
196
+ if band == "normal":
197
+ return None
198
+ return CapacityDiagnostic(
199
+ file_path=file_path,
200
+ level=level,
201
+ size=size,
202
+ limit=limit,
203
+ band=band,
204
+ target_low=ceil_percent(limit, 80),
205
+ target_high=ceil_percent(limit, 90) - 1,
206
+ actions=actions,
207
+ )
208
+
209
+
210
+ def compare_capacity(left: CapacityDiagnostic, right: CapacityDiagnostic) -> int:
211
+ left_ratio = left.size * right.limit
212
+ right_ratio = right.size * left.limit
213
+ if left_ratio > right_ratio:
214
+ return -1
215
+ if left_ratio < right_ratio:
216
+ return 1
217
+ if left.file_path < right.file_path:
218
+ return -1
219
+ if left.file_path > right.file_path:
220
+ return 1
221
+ return 0
222
+
223
+
224
+ def utilization_percent(size: int, limit: int) -> str:
225
+ hundredths = (size * 10000) // limit
226
+ return f"{hundredths // 100}.{hundredths % 100:02d}%"
227
+
228
+
229
+ def emit_capacity(diagnostic: CapacityDiagnostic) -> None:
230
+ severity = {
231
+ "warning": "WARNING",
232
+ "strong-warning": "STRONG WARNING",
233
+ "error": "ERROR",
234
+ }[diagnostic.band]
235
+ print(
236
+ f"{severity}: {diagnostic.file_path} capacity "
237
+ f"level={diagnostic.level} bytes={diagnostic.size}B limit={diagnostic.limit}B "
238
+ f"utilization={diagnostic.size}/{diagnostic.limit} "
239
+ f"({utilization_percent(diagnostic.size, diagnostic.limit)}) "
240
+ f"band={diagnostic.band} "
241
+ f"target={diagnostic.target_low}-{diagnostic.target_high}B (80% to <90%) "
242
+ f"actions={diagnostic.actions}",
243
+ file=sys.stderr,
244
+ )
138
245
 
139
246
 
140
247
  def contains_root_field(text: str, field: str) -> bool:
141
248
  return re.search(rf"^{re.escape(field)}:", text, re.MULTILINE) is not None
142
249
 
143
250
 
144
- def validate_file(file_path: str, raw: bytes, text: str) -> int:
251
+ def validate_file(file_path: str, raw: bytes, text: str) -> tuple[int, CapacityDiagnostic | None]:
145
252
  errors = 0
146
253
 
147
254
  if re.search(r"^#{1,6} ", text, re.MULTILINE):
@@ -195,13 +302,11 @@ def validate_file(file_path: str, raw: bytes, text: str) -> int:
195
302
  emit_error(file_path, "contains branch names. Describe durable product/domain context instead.")
196
303
  errors += 1
197
304
 
198
- limit, level, hint = size_limit(file_path)
199
- size = len(raw)
200
- if size > limit:
201
- emit_error(file_path, f"exceeds {level} limit ({size}B > {limit}B). {hint}")
305
+ diagnostic = capacity_diagnostic(file_path, len(raw))
306
+ if diagnostic is not None and diagnostic.band == "error":
202
307
  errors += 1
203
308
 
204
- if file_path.endswith("root.prizm"):
309
+ if is_root_doc(file_path):
205
310
  for field in ["PRIZM_VERSION", "PROJECT", "LANG", "RULES"]:
206
311
  if not contains_root_field(text, field):
207
312
  emit_error(file_path, f"missing required field {field}:")
@@ -210,7 +315,7 @@ def validate_file(file_path: str, raw: bytes, text: str) -> int:
210
315
  emit_error(file_path, "missing required field MODULE_INDEX: or MODULE_GROUPS:")
211
316
  errors += 1
212
317
 
213
- return errors
318
+ return errors, diagnostic
214
319
 
215
320
 
216
321
  def main(argv: list[str]) -> int:
@@ -223,12 +328,19 @@ def main(argv: list[str]) -> int:
223
328
  return 0
224
329
 
225
330
  errors = 0
331
+ capacity_entries: list[CapacityDiagnostic] = []
226
332
  for file_path in collect_files(mode):
227
333
  content = read_check_content(file_path, mode)
228
334
  if content is None:
229
335
  continue
230
336
  raw, text = content
231
- errors += validate_file(file_path, raw, text)
337
+ file_errors, diagnostic = validate_file(file_path, raw, text)
338
+ errors += file_errors
339
+ if diagnostic is not None:
340
+ capacity_entries.append(diagnostic)
341
+
342
+ for diagnostic in sorted(capacity_entries, key=cmp_to_key(compare_capacity)):
343
+ emit_capacity(diagnostic)
232
344
 
233
345
  if errors > 0:
234
346
  print(f"PrizmKit: {errors} format error(s) in .prizm files. Fix before committing.", file=sys.stderr)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prizmkit",
3
- "version": "1.1.155",
3
+ "version": "1.1.156",
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": {