refactor(semantics): migrate TIER system to adaptive COMPLEXITY 1-5 scale
- Replaced rigid TIERs with continuous COMPLEXITY 1-5 scale in semantics.md - Updated generate_semantic_map.py to parse and score based on Complexity - Added backward compatibility mapping for legacy TIERs - Migrated all .ai/shots examples to use @COMPLEXITY and updated relation syntax - Added trivial_utility.py shot to demonstrate implicit Complexity 1 token savings
This commit is contained in:
@@ -1,10 +1,9 @@
|
||||
#[DEF:BackendRouteShot:Module]
|
||||
# @TIER: STANDARD
|
||||
# @COMPLEXITY: 3
|
||||
# @SEMANTICS: Route, Task, API, Async
|
||||
# @PURPOSE: Reference implementation of a task-based route using GRACE-Poly.
|
||||
# @LAYER: Interface (API)
|
||||
# @RELATION: IMPLEMENTS -> [DEF:Std:API_FastAPI]
|
||||
# @INVARIANT: TaskManager must be available in dependency graph.
|
||||
# @RELATION: [IMPLEMENTS] ->[API_FastAPI]
|
||||
|
||||
from typing import Dict, Any
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
@@ -25,15 +24,13 @@ class CreateTaskRequest(BaseModel):
|
||||
# [/DEF:CreateTaskRequest:Class]
|
||||
|
||||
# [DEF:create_task:Function]
|
||||
# @COMPLEXITY: 4
|
||||
# @PURPOSE: Create and start a new task using TaskManager. Non-blocking.
|
||||
# @DATA_CONTRACT: Input -> CreateTaskRequest, Output -> Task
|
||||
# @RELATION: [CALLS] ->[task_manager.create_task]
|
||||
# @PRE: plugin_id must match a registered plugin.
|
||||
# @POST: A new task is spawned; Task object returned immediately.
|
||||
# @SIDE_EFFECT: Writes to DB, Triggers background worker.
|
||||
#
|
||||
# @UX_STATE: Success -> 201 Created
|
||||
# @UX_STATE: Error(Validation) -> 400 Bad Request
|
||||
# @UX_STATE: Error(System) -> 500 Internal Server Error
|
||||
# @DATA_CONTRACT: Input -> CreateTaskRequest, Output -> Task
|
||||
@router.post("/tasks", response_model=Task, status_code=status.HTTP_201_CREATED)
|
||||
async def create_task(
|
||||
request: CreateTaskRequest,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# [DEF:TransactionCore:Module]
|
||||
# @TIER: CRITICAL
|
||||
# @COMPLEXITY: 5
|
||||
# @SEMANTICS: Finance, ACID, Transfer, Ledger
|
||||
# @PURPOSE: Core banking transaction processor with ACID guarantees.
|
||||
# @LAYER: Domain (Core)
|
||||
# @RELATION: DEPENDS_ON -> [DEF:Infra:PostgresDB]
|
||||
# @RELATION: [DEPENDS_ON] ->[PostgresDB]
|
||||
#
|
||||
# @INVARIANT: Total system balance must remain constant (Double-Entry Bookkeeping).
|
||||
# @INVARIANT: Negative transfers are strictly forbidden.
|
||||
@@ -33,14 +33,13 @@ class TransferResult(NamedTuple):
|
||||
new_balance: Decimal
|
||||
|
||||
# [DEF:execute_transfer:Function]
|
||||
# @COMPLEXITY: 5
|
||||
# @PURPOSE: Atomically move funds between accounts with audit trails.
|
||||
# @DATA_CONTRACT: Input -> (sender_id: str, receiver_id: str, amount: Decimal), Output -> TransferResult
|
||||
# @RELATION: [CALLS] ->[atomic_transaction]
|
||||
# @PRE: amount > 0; sender != receiver; sender_balance >= amount.
|
||||
# @POST: sender_balance -= amount; receiver_balance += amount; Audit Record Created.
|
||||
# @SIDE_EFFECT: Database mutation (Rows locked), Audit IO.
|
||||
#
|
||||
# @UX_STATE: Success -> Returns 200 OK + Transaction Receipt.
|
||||
# @UX_STATE: Error(LowBalance) -> 422 Unprocessable -> UI shows "Top-up needed" modal.
|
||||
# @DATA_CONTRACT: Input -> (sender_id: str, receiver_id: str, amount: Decimal), Output -> TransferResult
|
||||
def execute_transfer(sender_id: str, receiver_id: str, amount: Decimal) -> TransferResult:
|
||||
# Guard: Input Validation (Вне belief_scope, так как это trivial проверка)
|
||||
if amount <= Decimal("0.00"):
|
||||
@@ -54,7 +53,6 @@ def execute_transfer(sender_id: str, receiver_id: str, amount: Decimal) -> Trans
|
||||
logger.reason("Initiating transfer", extra={"from": sender_id, "to": receiver_id, "amount": amount})
|
||||
|
||||
try:
|
||||
# @RELATION: CALLS -> atomic_transaction
|
||||
with atomic_transaction():
|
||||
current_balance = get_balance(sender_id, for_update=True)
|
||||
|
||||
|
||||
@@ -1,18 +1,27 @@
|
||||
<!-- [DEF:FrontendComponentShot:Component] -->
|
||||
<!--
|
||||
/**
|
||||
* @TIER: CRITICAL
|
||||
* @COMPLEXITY: 5
|
||||
* @SEMANTICS: Task, Button, Action, UX
|
||||
* @PURPOSE: Action button to spawn a new task with full UX feedback cycle.
|
||||
* @LAYER: UI (Presentation)
|
||||
* @RELATION: CALLS -> postApi
|
||||
* @RELATION: [CALLS] ->[postApi]
|
||||
*
|
||||
* @INVARIANT: Must prevent double-submission while loading.
|
||||
* @INVARIANT: Loading state must always terminate (no infinite spinner).
|
||||
* @INVARIANT: User must receive feedback on both success and failure.
|
||||
*
|
||||
* @SIDE_EFFECT: Sends network request and emits toast notifications.
|
||||
* @DATA_CONTRACT: Input -> { plugin_id: string, params: object }, Output -> { task_id?: string }
|
||||
*
|
||||
* @UX_REACTIVITY: Props -> $props(), LocalState -> $state(isLoading).
|
||||
*
|
||||
* @UX_STATE: Idle -> Button enabled, primary color, no spinner.
|
||||
* @UX_STATE: Loading -> Button disabled, spinner visible, aria-busy=true.
|
||||
* @UX_STATE: Success -> Toast success displayed.
|
||||
* @UX_STATE: Error -> Toast error displayed.
|
||||
* @UX_FEEDBACK: toast.success, toast.error
|
||||
* @UX_RECOVERY: Error -> Keep form interactive and allow retry after failure.
|
||||
*
|
||||
* @TEST_CONTRACT: ComponentState ->
|
||||
* {
|
||||
* required_fields: { isLoading: bool },
|
||||
@@ -21,26 +30,13 @@
|
||||
* "isLoading=true implies aria-busy=true"
|
||||
* ]
|
||||
* }
|
||||
*
|
||||
* @TEST_FIXTURE: idle_state -> { isLoading: false }
|
||||
* @TEST_FIXTURE: successful_response -> { task_id: "task_123" }
|
||||
*
|
||||
* @TEST_EDGE: api_failure -> raises Error("Network")
|
||||
* @TEST_EDGE: empty_response -> {}
|
||||
* @TEST_EDGE: rapid_double_click -> special: concurrent_click
|
||||
*
|
||||
* @TEST_INVARIANT: prevent_double_submission -> VERIFIED_BY:[rapid_double_click]
|
||||
* @TEST_INVARIANT: feedback_always_emitted -> VERIFIED_BY:[successful_response, api_failure]
|
||||
*
|
||||
* @UX_STATE: Idle -> Button enabled, primary color, no spinner.
|
||||
* @UX_STATE: Loading -> Button disabled, spinner visible, aria-busy=true.
|
||||
* @UX_STATE: Success -> Toast success displayed.
|
||||
* @UX_STATE: Error -> Toast error displayed.
|
||||
*
|
||||
* @UX_FEEDBACK: toast.success, toast.error
|
||||
*
|
||||
* @UX_TEST: Idle -> {click: spawnTask, expected: isLoading=true}
|
||||
* @UX_TEST: Loading -> {double_click: ignored, expected: single_api_call}
|
||||
*/
|
||||
-->
|
||||
<script>
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
# [DEF:PluginExampleShot:Module]
|
||||
# @TIER: STANDARD
|
||||
# @COMPLEXITY: 3
|
||||
# @SEMANTICS: Plugin, Core, Extension
|
||||
# @PURPOSE: Reference implementation of a plugin following GRACE standards.
|
||||
# @LAYER: Domain (Business Logic)
|
||||
# @RELATION: INHERITS -> PluginBase
|
||||
# @INVARIANT: get_schema must return valid JSON Schema.
|
||||
# @RELATION: [INHERITS] ->[PluginBase]
|
||||
|
||||
from typing import Dict, Any, Optional
|
||||
from ..core.plugin_base import PluginBase
|
||||
@@ -14,6 +13,7 @@ from ..core.logger import logger, belief_scope
|
||||
|
||||
# [DEF:ExamplePlugin:Class]
|
||||
# @PURPOSE: A sample plugin to demonstrate execution context and logging.
|
||||
# @RELATION: [INHERITS] ->[PluginBase]
|
||||
class ExamplePlugin(PluginBase):
|
||||
@property
|
||||
def id(self) -> str:
|
||||
@@ -21,7 +21,6 @@ class ExamplePlugin(PluginBase):
|
||||
|
||||
#[DEF:get_schema:Function]
|
||||
# @PURPOSE: Defines input validation schema.
|
||||
# @DATA_CONTRACT: Input -> None, Output -> Dict (JSON Schema draft 7)
|
||||
def get_schema(self) -> Dict[str, Any]:
|
||||
return {
|
||||
"type": "object",
|
||||
@@ -36,8 +35,9 @@ class ExamplePlugin(PluginBase):
|
||||
#[/DEF:get_schema:Function]
|
||||
|
||||
# [DEF:execute:Function]
|
||||
# @COMPLEXITY: 4
|
||||
# @PURPOSE: Core plugin logic with structured logging and scope isolation.
|
||||
# @DATA_CONTRACT: Input -> (params: Dict, context: Optional[TaskContext]), Output -> None
|
||||
# @RELATION: [BINDS_TO] ->[context.logger]
|
||||
# @PRE: params must be validated against get_schema() before calling.
|
||||
# @POST: Plugin payload is processed; progress is reported if context exists.
|
||||
# @SIDE_EFFECT: Emits logs to centralized system and TaskContext.
|
||||
|
||||
40
.ai/shots/trivial_utility.py
Normal file
40
.ai/shots/trivial_utility.py
Normal file
@@ -0,0 +1,40 @@
|
||||
# [DEF:TrivialUtilityShot:Module]
|
||||
# @COMPLEXITY: 1
|
||||
# @PURPOSE: Reference implementation of a zero-overhead utility using implicit Complexity 1.
|
||||
|
||||
import re
|
||||
from datetime import datetime, timezone
|
||||
from typing import Optional
|
||||
|
||||
# [DEF:slugify:Function]
|
||||
# @PURPOSE: Converts a string to a URL-safe slug.
|
||||
def slugify(text: str) -> str:
|
||||
if not text:
|
||||
return ""
|
||||
text = text.lower().strip()
|
||||
text = re.sub(r'[^\w\s-]', '', text)
|
||||
return re.sub(r'[-\s]+', '-', text)
|
||||
# [/DEF:slugify:Function]
|
||||
|
||||
# [DEF:get_utc_now:Function]
|
||||
def get_utc_now() -> datetime:
|
||||
"""Returns current UTC datetime (purpose is omitted because it's obvious)."""
|
||||
return datetime.now(timezone.utc)
|
||||
# [/DEF:get_utc_now:Function]
|
||||
|
||||
# [DEF:PaginationDTO:Class]
|
||||
class PaginationDTO:
|
||||
# [DEF:__init__:Function]
|
||||
def __init__(self, page: int = 1, size: int = 50):
|
||||
self.page = max(1, page)
|
||||
self.size = min(max(1, size), 1000)
|
||||
# [/DEF:__init__:Function]
|
||||
|
||||
# [DEF:offset:Function]
|
||||
@property
|
||||
def offset(self) -> int:
|
||||
return (self.page - 1) * self.size
|
||||
# [/DEF:offset:Function]
|
||||
# [/DEF:PaginationDTO:Class]
|
||||
|
||||
# [/DEF:TrivialUtilityShot:Module]
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
## III. ТОПОЛОГИЯ ФАЙЛА (СТРОГИЙ ПОРЯДОК)
|
||||
1. **HEADER (Заголовок):**[DEF:filename:Module]
|
||||
@TIER: [CRITICAL | STANDARD | TRIVIAL]
|
||||
@COMPLEXITY: [1|2|3|4|5] *(алиас: `@C:`; legacy `@TIER` допустим только для обратной совместимости)*
|
||||
@SEMANTICS: [keywords]
|
||||
@PURPOSE: [Однострочная суть]
|
||||
@LAYER: [Domain | UI | Infra]
|
||||
@@ -40,7 +40,7 @@
|
||||
3. **FOOTER (Подвал):** [/DEF:filename:Module]
|
||||
|
||||
## IV. КОНТРАКТЫ (DESIGN BY CONTRACT & UX)
|
||||
Обязательны для TIER: CRITICAL и STANDARD. Заменяют стандартные Docstrings.
|
||||
Контракты требуются адаптивно по уровню сложности, а не по жесткому tier.
|
||||
|
||||
**[CORE CONTRACTS]:**
|
||||
- `@PURPOSE:` Суть функции/компонента.
|
||||
@@ -62,11 +62,40 @@
|
||||
- `@TEST_EDGE: [Название] ->[Сбой]` (Минимум 3: missing_field, invalid_type, external_fail).
|
||||
- `@TEST_INVARIANT: [Имя] -> VERIFIED_BY: [scenario_1, ...]`
|
||||
|
||||
## V. УРОВНИ СТРОГОСТИ (TIERS)
|
||||
Степень контроля задается в Header.
|
||||
- **CRITICAL** (Ядро/Деньги/Безопасность): 100% покрытие тегами GRACE. Обязательны: Граф, Инварианты, Логи `logger.reason/reflect`, все `@UX` и `@TEST` теги. Использование `belief_scope` строго обязательно.
|
||||
- **STANDARD** (Бизнес-логика / Типовые формы): Базовый уровень. Обязательны: `@PURPOSE`, `@UX_STATE`, `@RELATION`, базовое логирование.
|
||||
- **TRIVIAL** (Утилиты / DTO / Атомы UI): Минимальный каркас. Только якоря `[DEF]...[/DEF]` и `@PURPOSE`.
|
||||
## V. ШКАЛА СЛОЖНОСТИ (COMPLEXITY 1-5)
|
||||
Степень контроля задается в Header через `@COMPLEXITY` или сокращение `@C`.
|
||||
Если тег отсутствует, сущность по умолчанию считается **Complexity 1**. Это сделано специально для экономии токенов и снижения шума на очевидных утилитах.
|
||||
|
||||
- **1 — ATOMIC**
|
||||
- Примеры: DTO, исключения, геттеры, простые утилиты, короткие адаптеры.
|
||||
- Обязательны только якоря `[DEF]...[/DEF]`.
|
||||
- `@PURPOSE` желателен, но не обязателен.
|
||||
|
||||
- **2 — SIMPLE**
|
||||
- Примеры: простые helper-функции, небольшие мапперы, UI-атомы.
|
||||
- Обязателен `@PURPOSE`.
|
||||
- Остальные контракты опциональны.
|
||||
|
||||
- **3 — FLOW**
|
||||
- Примеры: стандартная бизнес-логика, API handlers, сервисные методы, UI с загрузкой данных.
|
||||
- Обязательны: `@PURPOSE`, `@RELATION`.
|
||||
- Для UI дополнительно обязателен `@UX_STATE`.
|
||||
|
||||
- **4 — ORCHESTRATION**
|
||||
- Примеры: сложная координация, работа с I/O, multi-step алгоритмы, stateful pipelines.
|
||||
- Обязательны: `@PURPOSE`, `@RELATION`, `@PRE`, `@POST`, `@SIDE_EFFECT`.
|
||||
- Для Python обязателен осмысленный путь логирования через `logger.reason()` / `logger.reflect()` или аналогичный belief-state механизм.
|
||||
|
||||
- **5 — CRITICAL**
|
||||
- Примеры: auth, security, database boundaries, migration core, money-like invariants.
|
||||
- Обязателен полный контракт: уровень 4 + `@DATA_CONTRACT` + `@INVARIANT`.
|
||||
- Для UI требуются UX-контракты.
|
||||
- Использование `belief_scope` строго обязательно.
|
||||
|
||||
**Legacy mapping (обратная совместимость):**
|
||||
- `@TIER: TRIVIAL` -> Complexity 1
|
||||
- `@TIER: STANDARD` -> Complexity 3
|
||||
- `@TIER: CRITICAL` -> Complexity 5
|
||||
|
||||
## VI. ПРОТОКОЛ ЛОГИРОВАНИЯ (THREAD-LOCAL BELIEF STATE)
|
||||
Логирование — это механизм трассировки рассуждений ИИ (CoT) и управления Attention Energy. Архитектура использует Thread-local storage (`_belief_state`), поэтому `ID` прокидывается автоматически.
|
||||
@@ -90,11 +119,11 @@
|
||||
|
||||
## VII. АЛГОРИТМ ИСПОЛНЕНИЯ И САМОКОРРЕКЦИИ
|
||||
**[PHASE_1: ANALYSIS]**
|
||||
Оцени TIER, Layer и UX-требования. При слепоте контекста -> `yield [NEED_CONTEXT: id]`.
|
||||
Оцени Complexity, Layer и UX-требования. При слепоте контекста -> `yield [NEED_CONTEXT: id]`.
|
||||
**[PHASE_2: SYNTHESIS]**
|
||||
Сгенерируй каркас из `[DEF]`, Header и Контрактов.
|
||||
Сгенерируй каркас из `[DEF]`, Header и только тех контрактов, которые соответствуют уровню сложности.
|
||||
**[PHASE_3: IMPLEMENTATION]**
|
||||
Напиши код строго по Контракту. Для CRITICAL секций открой `with belief_scope("ID"):` и орошай путь вызовами `logger.reason()` и `logger.reflect()`.
|
||||
Напиши код строго по Контракту. Для Complexity 5 секций открой `with belief_scope("ID"):` и орошай путь вызовами `logger.reason()` и `logger.reflect()`.
|
||||
**[PHASE_4: CLOSURE]**
|
||||
Убедись, что все `[DEF]` закрыты соответствующими `[/DEF]`.
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ import json
|
||||
import datetime
|
||||
import fnmatch
|
||||
import argparse
|
||||
from enum import Enum
|
||||
from enum import Enum, IntEnum
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Dict, List, Optional, Any, Pattern, Tuple, Set
|
||||
|
||||
@@ -59,13 +59,24 @@ class belief_scope:
|
||||
class Tier(Enum):
|
||||
# [DEF:Tier:Class]
|
||||
# @TIER: TRIVIAL
|
||||
# @PURPOSE: Enumeration of semantic tiers defining validation strictness.
|
||||
# @PURPOSE: Legacy tier buckets retained for backward-compatible reporting.
|
||||
CRITICAL = "CRITICAL"
|
||||
STANDARD = "STANDARD"
|
||||
TRIVIAL = "TRIVIAL"
|
||||
# [/DEF:Tier:Class]
|
||||
|
||||
|
||||
class Complexity(IntEnum):
|
||||
# [DEF:Complexity:Class]
|
||||
# @PURPOSE: Adaptive semantic complexity scale used for validation strictness.
|
||||
ONE = 1
|
||||
TWO = 2
|
||||
THREE = 3
|
||||
FOUR = 4
|
||||
FIVE = 5
|
||||
# [/DEF:Complexity:Class]
|
||||
|
||||
|
||||
class Severity(Enum):
|
||||
# [DEF:Severity:Class]
|
||||
# @TIER: TRIVIAL
|
||||
@@ -95,31 +106,53 @@ OUTPUT_COMPRESSED_MD = ".ai/PROJECT_MAP.md"
|
||||
OUTPUT_MODULE_MAP_MD = ".ai/MODULE_MAP.md"
|
||||
REPORTS_DIR = "semantics/reports"
|
||||
|
||||
# Tier-based mandatory tags aligned with .ai/standards/semantics.md
|
||||
TIER_MANDATORY_TAGS = {
|
||||
Tier.CRITICAL: {
|
||||
"Module": ["PURPOSE", "LAYER", "SEMANTICS", "TIER", "INVARIANT", "RELATION"],
|
||||
"Component": ["PURPOSE", "LAYER", "SEMANTICS", "TIER", "INVARIANT", "RELATION", "UX_STATE", "UX_FEEDBACK", "UX_RECOVERY", "UX_REACTIVITY"],
|
||||
"Function": ["PURPOSE", "PRE", "POST", "SIDE_EFFECT", "DATA_CONTRACT", "TEST_CONTRACT", "TEST_SCENARIO", "TEST_FIXTURE", "TEST_EDGE", "TEST_INVARIANT"],
|
||||
"Class": ["PURPOSE", "TIER", "INVARIANT"],
|
||||
"Store": ["PURPOSE", "TIER", "INVARIANT"],
|
||||
"Block": ["PURPOSE", "TIER"]
|
||||
# Complexity-based mandatory tags aligned with .ai/standards/semantics.md
|
||||
LEGACY_TIER_TO_COMPLEXITY = {
|
||||
"TRIVIAL": Complexity.ONE,
|
||||
"STANDARD": Complexity.THREE,
|
||||
"CRITICAL": Complexity.FIVE,
|
||||
}
|
||||
|
||||
COMPLEXITY_MANDATORY_TAGS = {
|
||||
Complexity.ONE: {
|
||||
"Module": [],
|
||||
"Component": [],
|
||||
"Function": [],
|
||||
"Class": [],
|
||||
"Store": [],
|
||||
"Block": []
|
||||
},
|
||||
Tier.STANDARD: {
|
||||
"Module": ["PURPOSE", "LAYER", "SEMANTICS", "TIER", "RELATION"],
|
||||
"Component": ["PURPOSE", "LAYER", "SEMANTICS", "TIER", "RELATION", "UX_STATE", "UX_REACTIVITY"],
|
||||
"Function": ["PURPOSE", "PRE", "POST", "SIDE_EFFECT", "DATA_CONTRACT"],
|
||||
"Class": ["PURPOSE", "TIER"],
|
||||
"Store": ["PURPOSE", "TIER"],
|
||||
"Block": ["PURPOSE"]
|
||||
},
|
||||
Tier.TRIVIAL: {
|
||||
Complexity.TWO: {
|
||||
"Module": ["PURPOSE"],
|
||||
"Component": ["PURPOSE"],
|
||||
"Function": ["PURPOSE"],
|
||||
"Class": ["PURPOSE"],
|
||||
"Store": ["PURPOSE"],
|
||||
"Block": ["PURPOSE"]
|
||||
},
|
||||
Complexity.THREE: {
|
||||
"Module": ["PURPOSE", "LAYER", "SEMANTICS", "RELATION"],
|
||||
"Component": ["PURPOSE", "RELATION", "UX_STATE"],
|
||||
"Function": ["PURPOSE", "RELATION"],
|
||||
"Class": ["PURPOSE", "RELATION"],
|
||||
"Store": ["PURPOSE", "RELATION"],
|
||||
"Block": ["PURPOSE"]
|
||||
},
|
||||
Complexity.FOUR: {
|
||||
"Module": ["PURPOSE", "LAYER", "SEMANTICS", "RELATION", "PRE", "POST", "SIDE_EFFECT"],
|
||||
"Component": ["PURPOSE", "RELATION", "UX_STATE", "UX_REACTIVITY", "SIDE_EFFECT"],
|
||||
"Function": ["PURPOSE", "RELATION", "PRE", "POST", "SIDE_EFFECT"],
|
||||
"Class": ["PURPOSE", "RELATION", "PRE", "POST", "SIDE_EFFECT"],
|
||||
"Store": ["PURPOSE", "RELATION", "PRE", "POST", "SIDE_EFFECT"],
|
||||
"Block": ["PURPOSE", "SIDE_EFFECT"]
|
||||
},
|
||||
Complexity.FIVE: {
|
||||
"Module": ["PURPOSE", "LAYER", "SEMANTICS", "RELATION", "INVARIANT", "PRE", "POST", "SIDE_EFFECT", "DATA_CONTRACT"],
|
||||
"Component": ["PURPOSE", "RELATION", "INVARIANT", "UX_STATE", "UX_FEEDBACK", "UX_RECOVERY", "UX_REACTIVITY", "SIDE_EFFECT", "DATA_CONTRACT"],
|
||||
"Function": ["PURPOSE", "RELATION", "PRE", "POST", "SIDE_EFFECT", "DATA_CONTRACT"],
|
||||
"Class": ["PURPOSE", "RELATION", "INVARIANT", "PRE", "POST", "SIDE_EFFECT", "DATA_CONTRACT"],
|
||||
"Store": ["PURPOSE", "RELATION", "INVARIANT", "PRE", "POST", "SIDE_EFFECT", "DATA_CONTRACT"],
|
||||
"Block": ["PURPOSE", "SIDE_EFFECT"]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,11 +160,13 @@ ALLOWED_RELATION_PREDICATES = {
|
||||
"DEPENDS_ON", "CALLS", "INHERITS", "IMPLEMENTS", "DISPATCHES", "BINDS_TO"
|
||||
}
|
||||
|
||||
# Tier-based belief state requirements
|
||||
TIER_BELIEF_REQUIRED = {
|
||||
Tier.CRITICAL: True,
|
||||
Tier.STANDARD: True,
|
||||
Tier.TRIVIAL: False
|
||||
# Complexity-based belief state requirements
|
||||
COMPLEXITY_BELIEF_REQUIRED = {
|
||||
Complexity.ONE: False,
|
||||
Complexity.TWO: False,
|
||||
Complexity.THREE: False,
|
||||
Complexity.FOUR: True,
|
||||
Complexity.FIVE: True,
|
||||
}
|
||||
|
||||
# [/SECTION]
|
||||
@@ -157,7 +192,7 @@ class ComplianceIssue:
|
||||
# [DEF:SemanticEntity:Class]
|
||||
# @TIER: CRITICAL
|
||||
# @PURPOSE: Represents a code entity (Module, Function, Component) found during parsing.
|
||||
# @INVARIANT: start_line is always set; end_line is set upon closure; tier defaults to STANDARD.
|
||||
# @INVARIANT: start_line is always set; end_line is set upon closure; complexity defaults to 1 unless explicitly raised.
|
||||
class SemanticEntity:
|
||||
# [DEF:__init__:Function]
|
||||
# @TIER: STANDARD
|
||||
@@ -187,18 +222,26 @@ class SemanticEntity:
|
||||
self.has_runes: bool = False
|
||||
# [/DEF:__init__:Function]
|
||||
|
||||
# [DEF:get_tier:Function]
|
||||
# @TIER: STANDARD
|
||||
# @PURPOSE: Returns the tier of the entity, defaulting to STANDARD.
|
||||
# @PRE: tags dictionary is accessible.
|
||||
# @POST: Returns Tier enum value.
|
||||
def get_tier(self) -> Tier:
|
||||
with belief_scope("get_tier"):
|
||||
tier_str = self.tags.get("TIER", "STANDARD").upper()
|
||||
try:
|
||||
base_tier = Tier(tier_str)
|
||||
except ValueError:
|
||||
base_tier = Tier.STANDARD
|
||||
# [DEF:has_explicit_complexity:Function]
|
||||
# @PURPOSE: Returns whether the entity explicitly declares complexity metadata.
|
||||
def has_explicit_complexity(self) -> bool:
|
||||
return any(tag.upper() in {"COMPLEXITY", "C", "TIER"} for tag in self.tags)
|
||||
|
||||
# [DEF:get_complexity:Function]
|
||||
# @PURPOSE: Returns effective complexity with backward compatibility for legacy tiers.
|
||||
# @PRE: tags dictionary is accessible.
|
||||
# @POST: Returns Complexity enum value.
|
||||
def get_complexity(self) -> Complexity:
|
||||
with belief_scope("get_complexity"):
|
||||
complexity_value = self.tags.get("COMPLEXITY") or self.tags.get("C")
|
||||
if complexity_value is not None:
|
||||
try:
|
||||
base_complexity = Complexity(int(str(complexity_value).strip()))
|
||||
except (ValueError, TypeError):
|
||||
base_complexity = Complexity.ONE
|
||||
else:
|
||||
legacy_tier = str(self.tags.get("TIER", "")).upper().strip()
|
||||
base_complexity = LEGACY_TIER_TO_COMPLEXITY.get(legacy_tier, Complexity.ONE)
|
||||
|
||||
file_path_lower = self.file_path.lower()
|
||||
is_test_entity = (
|
||||
@@ -207,36 +250,44 @@ class SemanticEntity:
|
||||
or self.name.startswith("test_")
|
||||
)
|
||||
|
||||
# 1. Tests should never be higher than STANDARD
|
||||
if is_test_entity and base_tier == Tier.CRITICAL:
|
||||
return Tier.STANDARD
|
||||
if is_test_entity and base_complexity > Complexity.THREE:
|
||||
base_complexity = Complexity.THREE
|
||||
|
||||
# 2. Non-route Svelte entities should not be escalated beyond STANDARD by path heuristics.
|
||||
if self.file_path.endswith(".svelte"):
|
||||
is_route_level_svelte = any(
|
||||
marker in self.name for marker in ["+page", "+layout", "Page", "Layout"]
|
||||
)
|
||||
if not is_route_level_svelte and base_tier == Tier.CRITICAL:
|
||||
return Tier.STANDARD
|
||||
if not is_route_level_svelte and base_complexity > Complexity.THREE:
|
||||
base_complexity = Complexity.THREE
|
||||
|
||||
# 3. Tooling scripts should not be escalated beyond STANDARD.
|
||||
if ("scripts/" in self.file_path or "_tui.py" in self.file_path) and base_tier == Tier.CRITICAL:
|
||||
return Tier.STANDARD
|
||||
if ("scripts/" in self.file_path or "_tui.py" in self.file_path) and base_complexity > Complexity.THREE:
|
||||
base_complexity = Complexity.THREE
|
||||
|
||||
# 4. Promote only module-like entities in critical domains by path heuristic.
|
||||
# This prevents path segments like "migration" from forcing every nested
|
||||
# Block/Function/Component in a route file into CRITICAL validation.
|
||||
critical_keywords = ["auth", "security", "jwt", "database", "migration", "config", "session"]
|
||||
module_like_types = {"Module", "Class", "Store"}
|
||||
if (
|
||||
self.type in module_like_types
|
||||
not self.has_explicit_complexity()
|
||||
and self.type in module_like_types
|
||||
and any(keyword in file_path_lower for keyword in critical_keywords)
|
||||
and not is_test_entity
|
||||
and base_tier != Tier.TRIVIAL
|
||||
):
|
||||
return Tier.CRITICAL
|
||||
return Complexity.FIVE
|
||||
|
||||
return base_tier
|
||||
return base_complexity
|
||||
|
||||
# [DEF:get_tier:Function]
|
||||
# @TIER: STANDARD
|
||||
# @PURPOSE: Returns legacy tier bucket derived from effective complexity.
|
||||
# @PRE: tags dictionary is accessible.
|
||||
# @POST: Returns Tier enum value.
|
||||
def get_tier(self) -> Tier:
|
||||
with belief_scope("get_tier"):
|
||||
complexity = self.get_complexity()
|
||||
if complexity >= Complexity.FIVE:
|
||||
return Tier.CRITICAL
|
||||
if complexity >= Complexity.THREE:
|
||||
return Tier.STANDARD
|
||||
return Tier.TRIVIAL
|
||||
# [/DEF:get_tier:Function]
|
||||
|
||||
# [DEF:to_dict:Function]
|
||||
@@ -249,6 +300,7 @@ class SemanticEntity:
|
||||
result = {
|
||||
"name": self.name,
|
||||
"type": self.type,
|
||||
"complexity": int(self.get_complexity()),
|
||||
"tier": self.get_tier().value,
|
||||
"start_line": self.start_line,
|
||||
"end_line": self.end_line,
|
||||
@@ -272,15 +324,16 @@ class SemanticEntity:
|
||||
|
||||
# [DEF:validate:Function]
|
||||
# @TIER: CRITICAL
|
||||
# @PURPOSE: Checks for semantic compliance based on TIER requirements.
|
||||
# @PRE: Entity structure is complete; tier is determined.
|
||||
# @PURPOSE: Checks for semantic compliance based on complexity requirements.
|
||||
# @PRE: Entity structure is complete; complexity is determined.
|
||||
# @POST: Populates self.compliance_issues with severity levels.
|
||||
# @SIDE_EFFECT: Modifies self.compliance_issues list.
|
||||
def validate(self):
|
||||
with belief_scope("validate"):
|
||||
complexity = self.get_complexity()
|
||||
tier = self.get_tier()
|
||||
|
||||
# 1. Check Closure (required for ALL tiers)
|
||||
# 1. Check Closure (required for ALL complexity levels)
|
||||
if self.end_line is None:
|
||||
self.compliance_issues.append(ComplianceIssue(
|
||||
f"Unclosed Anchor: [DEF:{self.name}:{self.type}] started at line {self.start_line}",
|
||||
@@ -288,8 +341,8 @@ class SemanticEntity:
|
||||
self.start_line
|
||||
))
|
||||
|
||||
# 2. Check Mandatory Tags based on TIER
|
||||
required = TIER_MANDATORY_TAGS.get(tier, {}).get(self.type, [])
|
||||
# 2. Check Mandatory Tags based on complexity
|
||||
required = COMPLEXITY_MANDATORY_TAGS.get(complexity, {}).get(self.type, [])
|
||||
for req_tag in required:
|
||||
found = False
|
||||
if req_tag == "RELATION" and len(self.relations) > 0:
|
||||
@@ -300,9 +353,9 @@ class SemanticEntity:
|
||||
found = True
|
||||
break
|
||||
if not found:
|
||||
severity = Severity.ERROR if tier == Tier.CRITICAL else Severity.WARNING
|
||||
severity = Severity.ERROR if complexity >= Complexity.FOUR else Severity.WARNING
|
||||
self.compliance_issues.append(ComplianceIssue(
|
||||
f"Missing Mandatory Tag: @{req_tag} (required for {tier.value} tier)",
|
||||
f"Missing Mandatory Tag: @{req_tag} (required for complexity {int(complexity)})",
|
||||
severity,
|
||||
self.start_line
|
||||
))
|
||||
@@ -313,40 +366,39 @@ class SemanticEntity:
|
||||
if rel_type and rel_type not in ALLOWED_RELATION_PREDICATES:
|
||||
self.compliance_issues.append(ComplianceIssue(
|
||||
f"Invalid @RELATION predicate: {rel_type}. Allowed: {', '.join(sorted(ALLOWED_RELATION_PREDICATES))}",
|
||||
Severity.ERROR if tier == Tier.CRITICAL else Severity.WARNING,
|
||||
Severity.ERROR if complexity >= Complexity.FOUR else Severity.WARNING,
|
||||
self.start_line
|
||||
))
|
||||
|
||||
# 4. Check for Belief State Logging based on TIER
|
||||
# 4. Check for Belief State Logging based on complexity
|
||||
if self.type == "Function":
|
||||
belief_required = TIER_BELIEF_REQUIRED.get(tier, False)
|
||||
belief_required = COMPLEXITY_BELIEF_REQUIRED.get(complexity, False)
|
||||
if belief_required:
|
||||
is_python = self.file_path.endswith(".py")
|
||||
has_belief = self.has_belief_scope if is_python else self.has_console_log
|
||||
|
||||
if not has_belief:
|
||||
# Check if it's a special case (logger.py or mock functions)
|
||||
if "logger.py" not in self.file_path and "__" not in self.name:
|
||||
severity = Severity.ERROR if tier == Tier.CRITICAL else Severity.WARNING
|
||||
severity = Severity.ERROR if complexity >= Complexity.FOUR else Severity.WARNING
|
||||
log_type = "belief_scope / molecular methods" if is_python else "console.log with [ID][STATE]"
|
||||
self.compliance_issues.append(ComplianceIssue(
|
||||
f"Missing Belief State Logging: Function should use {log_type} (required for {tier.value} tier)",
|
||||
f"Missing Belief State Logging: Function should use {log_type} (required for complexity {int(complexity)})",
|
||||
severity,
|
||||
self.start_line
|
||||
))
|
||||
|
||||
# 5. Check for @INVARIANT in CRITICAL tier
|
||||
if tier == Tier.CRITICAL and self.type in ["Module", "Component", "Class"]:
|
||||
# 5. Check for @INVARIANT in maximum complexity
|
||||
if complexity == Complexity.FIVE and self.type in ["Module", "Component", "Class"]:
|
||||
if "INVARIANT" not in [k.upper() for k in self.tags.keys()]:
|
||||
self.compliance_issues.append(ComplianceIssue(
|
||||
f"Missing @INVARIANT tag (required for CRITICAL tier)",
|
||||
"Missing @INVARIANT tag (required for complexity 5)",
|
||||
Severity.ERROR,
|
||||
self.start_line
|
||||
))
|
||||
|
||||
# 6. Validate modern Svelte reactivity protocol
|
||||
if self.type == "Component" and self.file_path.endswith(".svelte"):
|
||||
strict_severity = Severity.ERROR if tier == Tier.CRITICAL else Severity.WARNING
|
||||
strict_severity = Severity.ERROR if complexity >= Complexity.FOUR else Severity.WARNING
|
||||
|
||||
if self.has_export_let:
|
||||
self.compliance_issues.append(ComplianceIssue(
|
||||
@@ -379,7 +431,7 @@ class SemanticEntity:
|
||||
|
||||
# [DEF:get_score:Function]
|
||||
# @TIER: STANDARD
|
||||
# @PURPOSE: Calculates a compliance score (0.0 to 1.0) based on tier requirements.
|
||||
# @PURPOSE: Calculates a compliance score (0.0 to 1.0) based on complexity requirements.
|
||||
# @PRE: validate() has been called.
|
||||
# @POST: Returns a float score.
|
||||
def get_score(self) -> float:
|
||||
@@ -387,11 +439,11 @@ class SemanticEntity:
|
||||
if self.end_line is None:
|
||||
return 0.0
|
||||
|
||||
tier = self.get_tier()
|
||||
complexity = self.get_complexity()
|
||||
score = 1.0
|
||||
|
||||
# Dynamic penalties based on Tier
|
||||
error_penalty = 0.5 if tier == Tier.CRITICAL else 0.3
|
||||
# Dynamic penalties based on complexity
|
||||
error_penalty = 0.5 if complexity >= Complexity.FOUR else 0.3
|
||||
warning_penalty = 0.15
|
||||
|
||||
# Count issues by severity
|
||||
@@ -403,7 +455,7 @@ class SemanticEntity:
|
||||
score -= warnings * warning_penalty
|
||||
|
||||
# Check mandatory tags
|
||||
required = TIER_MANDATORY_TAGS.get(tier, {}).get(self.type, [])
|
||||
required = COMPLEXITY_MANDATORY_TAGS.get(complexity, {}).get(self.type, [])
|
||||
if required:
|
||||
found_count = 0
|
||||
for req_tag in required:
|
||||
@@ -687,7 +739,7 @@ def parse_file(full_path: str, rel_path: str, lang: str) -> Tuple[List[SemanticE
|
||||
# Create orphan function entity
|
||||
orphan = SemanticEntity(func_name, "Function", lineno, rel_path)
|
||||
orphan.tags["PURPOSE"] = f"Auto-detected function (orphan)"
|
||||
orphan.tags["TIER"] = "TRIVIAL"
|
||||
orphan.tags["COMPLEXITY"] = "1"
|
||||
orphan.end_line = lineno # Mark as closed immediately
|
||||
orphan_functions.append(orphan)
|
||||
|
||||
@@ -770,7 +822,7 @@ def parse_file(full_path: str, rel_path: str, lang: str) -> Tuple[List[SemanticE
|
||||
rel_path
|
||||
)
|
||||
synthetic_module.tags["PURPOSE"] = f"Auto-generated module for {rel_path}"
|
||||
synthetic_module.tags["TIER"] = "TRIVIAL"
|
||||
synthetic_module.tags["COMPLEXITY"] = "1"
|
||||
synthetic_module.tags["LAYER"] = "Unknown"
|
||||
synthetic_module.end_line = len(lines)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user