semantic protocol condense + script update
This commit is contained in:
@@ -1,8 +1,10 @@
|
|||||||
# [DEF:generate_semantic_map:Module]
|
# [DEF:generate_semantic_map:Module]
|
||||||
#
|
#
|
||||||
# @SEMANTICS: semantic_analysis, parser, map_generator, compliance_checker
|
# @TIER: CRITICAL
|
||||||
|
# @SEMANTICS: semantic_analysis, parser, map_generator, compliance_checker, tier_validation, svelte_props, data_flow
|
||||||
# @PURPOSE: Scans the codebase to generate a Semantic Map and Compliance Report based on the System Standard.
|
# @PURPOSE: Scans the codebase to generate a Semantic Map and Compliance Report based on the System Standard.
|
||||||
# @LAYER: DevOps/Tooling
|
# @LAYER: DevOps/Tooling
|
||||||
|
# @INVARIANT: All DEF anchors must have matching closing anchors; TIER determines validation strictness.
|
||||||
# @RELATION: READS -> FileSystem
|
# @RELATION: READS -> FileSystem
|
||||||
# @RELATION: PRODUCES -> semantics/semantic_map.json
|
# @RELATION: PRODUCES -> semantics/semantic_map.json
|
||||||
# @RELATION: PRODUCES -> specs/project_map.md
|
# @RELATION: PRODUCES -> specs/project_map.md
|
||||||
@@ -14,12 +16,15 @@ import re
|
|||||||
import json
|
import json
|
||||||
import datetime
|
import datetime
|
||||||
import fnmatch
|
import fnmatch
|
||||||
|
from enum import Enum
|
||||||
|
from dataclasses import dataclass, field
|
||||||
from typing import Dict, List, Optional, Any, Pattern, Tuple, Set
|
from typing import Dict, List, Optional, Any, Pattern, Tuple, Set
|
||||||
|
|
||||||
# Mock belief_scope for the script itself to avoid import issues
|
# Mock belief_scope for the script itself to avoid import issues
|
||||||
class belief_scope:
|
class belief_scope:
|
||||||
# [DEF:__init__:Function]
|
# [DEF:__init__:Function]
|
||||||
# @PURPOSE: Mock init.
|
# @TIER: TRIVIAL
|
||||||
|
# @PURPOSE: Mock init for self-containment.
|
||||||
# @PRE: name is a string.
|
# @PRE: name is a string.
|
||||||
# @POST: Instance initialized.
|
# @POST: Instance initialized.
|
||||||
def __init__(self, name):
|
def __init__(self, name):
|
||||||
@@ -27,6 +32,7 @@ class belief_scope:
|
|||||||
# [/DEF:__init__:Function]
|
# [/DEF:__init__:Function]
|
||||||
|
|
||||||
# [DEF:__enter__:Function]
|
# [DEF:__enter__:Function]
|
||||||
|
# @TIER: TRIVIAL
|
||||||
# @PURPOSE: Mock enter.
|
# @PURPOSE: Mock enter.
|
||||||
# @PRE: Instance initialized.
|
# @PRE: Instance initialized.
|
||||||
# @POST: Returns self.
|
# @POST: Returns self.
|
||||||
@@ -35,6 +41,7 @@ class belief_scope:
|
|||||||
# [/DEF:__enter__:Function]
|
# [/DEF:__enter__:Function]
|
||||||
|
|
||||||
# [DEF:__exit__:Function]
|
# [DEF:__exit__:Function]
|
||||||
|
# @TIER: TRIVIAL
|
||||||
# @PURPOSE: Mock exit.
|
# @PURPOSE: Mock exit.
|
||||||
# @PRE: Context entered.
|
# @PRE: Context entered.
|
||||||
# @POST: Context exited.
|
# @POST: Context exited.
|
||||||
@@ -44,10 +51,32 @@ class belief_scope:
|
|||||||
# [/SECTION]
|
# [/SECTION]
|
||||||
|
|
||||||
# [SECTION: CONFIGURATION]
|
# [SECTION: CONFIGURATION]
|
||||||
|
|
||||||
|
|
||||||
|
class Tier(Enum):
|
||||||
|
# [DEF:Tier:Class]
|
||||||
|
# @TIER: TRIVIAL
|
||||||
|
# @PURPOSE: Enumeration of semantic tiers defining validation strictness.
|
||||||
|
CRITICAL = "CRITICAL"
|
||||||
|
STANDARD = "STANDARD"
|
||||||
|
TRIVIAL = "TRIVIAL"
|
||||||
|
# [/DEF:Tier:Class]
|
||||||
|
|
||||||
|
|
||||||
|
class Severity(Enum):
|
||||||
|
# [DEF:Severity:Class]
|
||||||
|
# @TIER: TRIVIAL
|
||||||
|
# @PURPOSE: Severity levels for compliance issues.
|
||||||
|
ERROR = "ERROR"
|
||||||
|
WARNING = "WARNING"
|
||||||
|
INFO = "INFO"
|
||||||
|
# [/DEF:Severity:Class]
|
||||||
|
|
||||||
|
|
||||||
PROJECT_ROOT = "."
|
PROJECT_ROOT = "."
|
||||||
IGNORE_DIRS = {
|
IGNORE_DIRS = {
|
||||||
".git", "__pycache__", "node_modules", "venv", ".pytest_cache",
|
".git", "__pycache__", "node_modules", "venv", ".pytest_cache",
|
||||||
".kilocode", "backups", "logs", "semantics", "specs"
|
".kilocode", "backups", "logs", "semantics", "specs", ".venv"
|
||||||
}
|
}
|
||||||
IGNORE_FILES = {
|
IGNORE_FILES = {
|
||||||
"package-lock.json", "poetry.lock", "yarn.lock"
|
"package-lock.json", "poetry.lock", "yarn.lock"
|
||||||
@@ -56,19 +85,62 @@ OUTPUT_JSON = "semantics/semantic_map.json"
|
|||||||
OUTPUT_COMPRESSED_MD = "specs/project_map.md"
|
OUTPUT_COMPRESSED_MD = "specs/project_map.md"
|
||||||
REPORTS_DIR = "semantics/reports"
|
REPORTS_DIR = "semantics/reports"
|
||||||
|
|
||||||
MANDATORY_TAGS = {
|
# Tier-based mandatory tags
|
||||||
"Module": ["PURPOSE", "LAYER", "SEMANTICS"],
|
TIER_MANDATORY_TAGS = {
|
||||||
"Component": ["PURPOSE", "LAYER", "SEMANTICS"],
|
Tier.CRITICAL: {
|
||||||
"Function": ["PURPOSE", "PRE", "POST"],
|
"Module": ["PURPOSE", "LAYER", "SEMANTICS", "TIER", "INVARIANT"],
|
||||||
"Class": ["PURPOSE"]
|
"Component": ["PURPOSE", "LAYER", "SEMANTICS", "TIER", "INVARIANT"],
|
||||||
|
"Function": ["PURPOSE", "PRE", "POST"],
|
||||||
|
"Class": ["PURPOSE", "TIER"]
|
||||||
|
},
|
||||||
|
Tier.STANDARD: {
|
||||||
|
"Module": ["PURPOSE", "LAYER", "SEMANTICS", "TIER"],
|
||||||
|
"Component": ["PURPOSE", "LAYER", "SEMANTICS", "TIER"],
|
||||||
|
"Function": ["PURPOSE", "PRE", "POST"],
|
||||||
|
"Class": ["PURPOSE", "TIER"]
|
||||||
|
},
|
||||||
|
Tier.TRIVIAL: {
|
||||||
|
"Module": ["PURPOSE", "TIER"],
|
||||||
|
"Component": ["PURPOSE", "TIER"],
|
||||||
|
"Function": ["PURPOSE"],
|
||||||
|
"Class": ["PURPOSE", "TIER"]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Tier-based belief state requirements
|
||||||
|
TIER_BELIEF_REQUIRED = {
|
||||||
|
Tier.CRITICAL: True,
|
||||||
|
Tier.STANDARD: True,
|
||||||
|
Tier.TRIVIAL: False
|
||||||
|
}
|
||||||
|
|
||||||
# [/SECTION]
|
# [/SECTION]
|
||||||
|
|
||||||
|
# [DEF:ComplianceIssue:Class]
|
||||||
|
# @TIER: TRIVIAL
|
||||||
|
# @PURPOSE: Represents a single compliance issue with severity.
|
||||||
|
@dataclass
|
||||||
|
class ComplianceIssue:
|
||||||
|
message: str
|
||||||
|
severity: Severity
|
||||||
|
line_number: Optional[int] = None
|
||||||
|
|
||||||
|
def to_dict(self) -> Dict[str, Any]:
|
||||||
|
return {
|
||||||
|
"message": self.message,
|
||||||
|
"severity": self.severity.value,
|
||||||
|
"line_number": self.line_number
|
||||||
|
}
|
||||||
|
# [/DEF:ComplianceIssue:Class]
|
||||||
|
|
||||||
|
|
||||||
# [DEF:SemanticEntity:Class]
|
# [DEF:SemanticEntity:Class]
|
||||||
# @PURPOSE: Represents a code entity (Module, Function, Component) found during parsing.
|
# @TIER: CRITICAL
|
||||||
# @INVARIANT: start_line is always set; end_line is set upon closure.
|
# @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.
|
||||||
class SemanticEntity:
|
class SemanticEntity:
|
||||||
# [DEF:__init__:Function]
|
# [DEF:__init__:Function]
|
||||||
|
# @TIER: STANDARD
|
||||||
# @PURPOSE: Initializes a new SemanticEntity instance.
|
# @PURPOSE: Initializes a new SemanticEntity instance.
|
||||||
# @PRE: name, type_, start_line, file_path are provided.
|
# @PRE: name, type_, start_line, file_path are provided.
|
||||||
# @POST: Instance is initialized with default values.
|
# @POST: Instance is initialized with default values.
|
||||||
@@ -83,43 +155,80 @@ class SemanticEntity:
|
|||||||
self.relations: List[Dict[str, str]] = []
|
self.relations: List[Dict[str, str]] = []
|
||||||
self.children: List['SemanticEntity'] = []
|
self.children: List['SemanticEntity'] = []
|
||||||
self.parent: Optional['SemanticEntity'] = None
|
self.parent: Optional['SemanticEntity'] = None
|
||||||
self.compliance_issues: List[str] = []
|
self.compliance_issues: List[ComplianceIssue] = []
|
||||||
|
self.has_belief_scope: bool = False
|
||||||
|
self.has_console_log: bool = False
|
||||||
|
# New fields for enhanced Svelte analysis
|
||||||
|
self.props: List[Dict[str, Any]] = []
|
||||||
|
self.events: List[str] = []
|
||||||
|
self.data_flow: List[Dict[str, str]] = []
|
||||||
# [/DEF:__init__:Function]
|
# [/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:
|
||||||
|
return Tier(tier_str)
|
||||||
|
except ValueError:
|
||||||
|
return Tier.STANDARD
|
||||||
|
# [/DEF:get_tier:Function]
|
||||||
|
|
||||||
# [DEF:to_dict:Function]
|
# [DEF:to_dict:Function]
|
||||||
|
# @TIER: STANDARD
|
||||||
# @PURPOSE: Serializes the entity to a dictionary for JSON output.
|
# @PURPOSE: Serializes the entity to a dictionary for JSON output.
|
||||||
# @PRE: Entity is fully populated.
|
# @PRE: Entity is fully populated.
|
||||||
# @POST: Returns a dictionary representation.
|
# @POST: Returns a dictionary representation.
|
||||||
# @RETURN: Dict representation of the entity.
|
|
||||||
def to_dict(self) -> Dict[str, Any]:
|
def to_dict(self) -> Dict[str, Any]:
|
||||||
with belief_scope("to_dict"):
|
with belief_scope("to_dict"):
|
||||||
return {
|
result = {
|
||||||
"name": self.name,
|
"name": self.name,
|
||||||
"type": self.type,
|
"type": self.type,
|
||||||
|
"tier": self.get_tier().value,
|
||||||
"start_line": self.start_line,
|
"start_line": self.start_line,
|
||||||
"end_line": self.end_line,
|
"end_line": self.end_line,
|
||||||
"tags": self.tags,
|
"tags": self.tags,
|
||||||
"relations": self.relations,
|
"relations": self.relations,
|
||||||
"children": [c.to_dict() for c in self.children],
|
"children": [c.to_dict() for c in self.children],
|
||||||
"compliance": {
|
"compliance": {
|
||||||
"valid": len(self.compliance_issues) == 0,
|
"valid": len([i for i in self.compliance_issues if i.severity == Severity.ERROR]) == 0,
|
||||||
"issues": self.compliance_issues
|
"issues": [i.to_dict() for i in self.compliance_issues],
|
||||||
|
"score": self.get_score()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if self.props:
|
||||||
|
result["props"] = self.props
|
||||||
|
if self.events:
|
||||||
|
result["events"] = self.events
|
||||||
|
if self.data_flow:
|
||||||
|
result["data_flow"] = self.data_flow
|
||||||
|
return result
|
||||||
# [/DEF:to_dict:Function]
|
# [/DEF:to_dict:Function]
|
||||||
|
|
||||||
# [DEF:validate:Function]
|
# [DEF:validate:Function]
|
||||||
# @PURPOSE: Checks for semantic compliance (closure, mandatory tags, belief state).
|
# @TIER: CRITICAL
|
||||||
# @PRE: Entity structure is complete.
|
# @PURPOSE: Checks for semantic compliance based on TIER requirements.
|
||||||
# @POST: Populates self.compliance_issues.
|
# @PRE: Entity structure is complete; tier is determined.
|
||||||
|
# @POST: Populates self.compliance_issues with severity levels.
|
||||||
|
# @SIDE_EFFECT: Modifies self.compliance_issues list.
|
||||||
def validate(self):
|
def validate(self):
|
||||||
with belief_scope("validate"):
|
with belief_scope("validate"):
|
||||||
# 1. Check Closure
|
tier = self.get_tier()
|
||||||
if self.end_line is None:
|
|
||||||
self.compliance_issues.append(f"Unclosed Anchor: [DEF:{self.name}:{self.type}] started at line {self.start_line}")
|
|
||||||
|
|
||||||
# 2. Check Mandatory Tags
|
# 1. Check Closure (required for ALL tiers)
|
||||||
required = MANDATORY_TAGS.get(self.type, [])
|
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}",
|
||||||
|
Severity.ERROR,
|
||||||
|
self.start_line
|
||||||
|
))
|
||||||
|
|
||||||
|
# 2. Check Mandatory Tags based on TIER
|
||||||
|
required = TIER_MANDATORY_TAGS.get(tier, {}).get(self.type, [])
|
||||||
for req_tag in required:
|
for req_tag in required:
|
||||||
found = False
|
found = False
|
||||||
for existing_tag in self.tags:
|
for existing_tag in self.tags:
|
||||||
@@ -127,13 +236,39 @@ class SemanticEntity:
|
|||||||
found = True
|
found = True
|
||||||
break
|
break
|
||||||
if not found:
|
if not found:
|
||||||
self.compliance_issues.append(f"Missing Mandatory Tag: @{req_tag}")
|
severity = Severity.ERROR if tier == Tier.CRITICAL else Severity.WARNING
|
||||||
|
self.compliance_issues.append(ComplianceIssue(
|
||||||
|
f"Missing Mandatory Tag: @{req_tag} (required for {tier.value} tier)",
|
||||||
|
severity,
|
||||||
|
self.start_line
|
||||||
|
))
|
||||||
|
|
||||||
# 3. Check for Belief State Logging (Python only)
|
# 3. Check for Belief State Logging based on TIER
|
||||||
# Skip check for logger.py to avoid circular dependencies
|
if self.type == "Function":
|
||||||
if self.type == "Function" and self.file_path.endswith(".py") and "backend/src/core/logger.py" not in self.file_path:
|
belief_required = TIER_BELIEF_REQUIRED.get(tier, False)
|
||||||
if not getattr(self, 'has_belief_scope', False):
|
if belief_required:
|
||||||
self.compliance_issues.append("Missing Belief State Logging: Function should use belief_scope context manager.")
|
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
|
||||||
|
log_type = "belief_scope" 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)",
|
||||||
|
severity,
|
||||||
|
self.start_line
|
||||||
|
))
|
||||||
|
|
||||||
|
# 4. Check for @INVARIANT in CRITICAL tier
|
||||||
|
if tier == Tier.CRITICAL 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)",
|
||||||
|
Severity.ERROR,
|
||||||
|
self.start_line
|
||||||
|
))
|
||||||
|
|
||||||
# Recursive validation
|
# Recursive validation
|
||||||
for child in self.children:
|
for child in self.children:
|
||||||
@@ -141,27 +276,37 @@ class SemanticEntity:
|
|||||||
# [/DEF:validate:Function]
|
# [/DEF:validate:Function]
|
||||||
|
|
||||||
# [DEF:get_score:Function]
|
# [DEF:get_score:Function]
|
||||||
# @PURPOSE: Calculates a compliance score (0.0 to 1.0).
|
# @TIER: STANDARD
|
||||||
|
# @PURPOSE: Calculates a compliance score (0.0 to 1.0) based on tier requirements.
|
||||||
# @PRE: validate() has been called.
|
# @PRE: validate() has been called.
|
||||||
# @POST: Returns a float score.
|
# @POST: Returns a float score.
|
||||||
# @RETURN: Float score.
|
|
||||||
def get_score(self) -> float:
|
def get_score(self) -> float:
|
||||||
with belief_scope("get_score"):
|
with belief_scope("get_score"):
|
||||||
if self.end_line is None:
|
if self.end_line is None:
|
||||||
return 0.0
|
return 0.0
|
||||||
|
|
||||||
|
tier = self.get_tier()
|
||||||
score = 1.0
|
score = 1.0
|
||||||
required = MANDATORY_TAGS.get(self.type, [])
|
|
||||||
|
# Count issues by severity
|
||||||
|
errors = len([i for i in self.compliance_issues if i.severity == Severity.ERROR])
|
||||||
|
warnings = len([i for i in self.compliance_issues if i.severity == Severity.WARNING])
|
||||||
|
|
||||||
|
# Penalties
|
||||||
|
score -= errors * 0.3
|
||||||
|
score -= warnings * 0.1
|
||||||
|
|
||||||
|
# Check mandatory tags
|
||||||
|
required = TIER_MANDATORY_TAGS.get(tier, {}).get(self.type, [])
|
||||||
if required:
|
if required:
|
||||||
found_count = 0
|
found_count = 0
|
||||||
for req_tag in required:
|
for req_tag in required:
|
||||||
for existing_tag in self.tags:
|
for existing_tag in self.tags:
|
||||||
if existing_tag.upper() == req_tag:
|
if existing_tag.upper() == req_tag:
|
||||||
found_count += 1
|
found_count += 1
|
||||||
break
|
break
|
||||||
if found_count < len(required):
|
if found_count < len(required):
|
||||||
# Penalty proportional to missing tags
|
score -= 0.2 * (1 - (found_count / len(required)))
|
||||||
score -= 0.5 * (1 - (found_count / len(required)))
|
|
||||||
|
|
||||||
return max(0.0, score)
|
return max(0.0, score)
|
||||||
# [/DEF:get_score:Function]
|
# [/DEF:get_score:Function]
|
||||||
@@ -169,17 +314,17 @@ class SemanticEntity:
|
|||||||
|
|
||||||
|
|
||||||
# [DEF:get_patterns:Function]
|
# [DEF:get_patterns:Function]
|
||||||
|
# @TIER: STANDARD
|
||||||
# @PURPOSE: Returns regex patterns for a specific language.
|
# @PURPOSE: Returns regex patterns for a specific language.
|
||||||
# @PRE: lang is either 'python' or 'svelte_js'.
|
# @PRE: lang is either 'python' or 'svelte_js'.
|
||||||
# @POST: Returns a dictionary of compiled regex patterns.
|
# @POST: Returns a dictionary of compiled regex patterns.
|
||||||
# @PARAM: lang (str) - 'python' or 'svelte_js'
|
# @PARAM: lang (str) - 'python' or 'svelte_js'
|
||||||
# @RETURN: Dict containing compiled regex patterns.
|
|
||||||
def get_patterns(lang: str) -> Dict[str, Pattern]:
|
def get_patterns(lang: str) -> Dict[str, Pattern]:
|
||||||
with belief_scope("get_patterns"):
|
with belief_scope("get_patterns"):
|
||||||
if lang == "python":
|
if lang == "python":
|
||||||
return {
|
return {
|
||||||
"anchor_start": re.compile(r"#\s*\[DEF:(?P<name>[\w\.]+):(?P<type>\w+)\]"),
|
"anchor_start": re.compile(r"#\s*\[DEF:(?P<name>[\w\.]+):(?P<type>\w+)\]"),
|
||||||
"anchor_end": re.compile(r"#\s*\[/DEF:(?P<name>[\w\.]+):(?P<type>\w+)\]"),
|
"anchor_end": re.compile(r"#\s*\[/DEF:(?P<name>[\w\.]+)\]"),
|
||||||
"tag": re.compile(r"#\s*@(?P<tag>[A-Z_]+):\s*(?P<value>.*)"),
|
"tag": re.compile(r"#\s*@(?P<tag>[A-Z_]+):\s*(?P<value>.*)"),
|
||||||
"relation": re.compile(r"#\s*@RELATION:\s*(?P<type>\w+)\s*->\s*(?P<target>.*)"),
|
"relation": re.compile(r"#\s*@RELATION:\s*(?P<type>\w+)\s*->\s*(?P<target>.*)"),
|
||||||
"func_def": re.compile(r"^\s*(async\s+)?def\s+(?P<name>\w+)"),
|
"func_def": re.compile(r"^\s*(async\s+)?def\s+(?P<name>\w+)"),
|
||||||
@@ -188,54 +333,189 @@ def get_patterns(lang: str) -> Dict[str, Pattern]:
|
|||||||
else:
|
else:
|
||||||
return {
|
return {
|
||||||
"html_anchor_start": re.compile(r"<!--\s*\[DEF:(?P<name>[\w\.]+):(?P<type>\w+)\]\s*-->"),
|
"html_anchor_start": re.compile(r"<!--\s*\[DEF:(?P<name>[\w\.]+):(?P<type>\w+)\]\s*-->"),
|
||||||
"html_anchor_end": re.compile(r"<!--\s*\[/DEF:(?P<name>[\w\.]+):(?P<type>\w+)\]\s*-->"),
|
"html_anchor_end": re.compile(r"<!--\s*\[/DEF:(?P<name>[\w\.]+)\]\s*-->"),
|
||||||
"js_anchor_start": re.compile(r"//\s*\[DEF:(?P<name>[\w\.]+):(?P<type>\w+)\]"),
|
"js_anchor_start": re.compile(r"//\s*\[DEF:(?P<name>[\w\.]+):(?P<type>\w+)\]"),
|
||||||
"js_anchor_end": re.compile(r"//\s*\[/DEF:(?P<name>[\w\.]+):(?P<type>\w+)\]"),
|
"js_anchor_end": re.compile(r"//\s*\[/DEF:(?P<name>[\w\.]+)\]"),
|
||||||
"html_tag": re.compile(r"@(?P<tag>[A-Z_]+):\s*(?P<value>.*)"),
|
"html_tag": re.compile(r"@(?P<tag>[A-Z_]+):\s*(?P<value>.*)"),
|
||||||
"jsdoc_tag": re.compile(r"\*\s*@(?P<tag>[a-zA-Z]+)\s+(?P<value>.*)"),
|
"jsdoc_tag": re.compile(r"\*\s*@(?P<tag>[a-zA-Z]+)\s+(?P<value>.*)"),
|
||||||
"relation": re.compile(r"//\s*@RELATION:\s*(?P<type>\w+)\s*->\s*(?P<target>.*)"),
|
"relation": re.compile(r"//\s*@RELATION:\s*(?P<type>\w+)\s*->\s*(?P<target>.*)"),
|
||||||
"func_def": re.compile(r"^\s*(export\s+)?(async\s+)?function\s+(?P<name>\w+)"),
|
"func_def": re.compile(r"^\s*(export\s+)?(async\s+)?function\s+(?P<name>\w+)"),
|
||||||
|
"console_log": re.compile(r"console\.log\s*\(\s*['\"]\[[\w_]+\]\[[\w_]+\]"),
|
||||||
|
# Svelte-specific patterns
|
||||||
|
"export_let": re.compile(r"export\s+let\s+(?P<name>\w+)(?:\s*:\s*(?P<type>[\w\[\]|<>]+))?(?:\s*=\s*(?P<default>[^;]+))?"),
|
||||||
|
"create_event_dispatcher": re.compile(r"createEventDispatcher\s*<\s*\{\s*(?P<events>[^}]+)\s*\}\s*\>"),
|
||||||
|
"dispatch_call": re.compile(r"dispatch\s*\(\s*['\"](?P<event>\w+)['\"]"),
|
||||||
|
"store_subscription": re.compile(r"\$(?P<store>\w+)"),
|
||||||
|
"store_import": re.compile(r"import\s*\{[^}]*\b(?P<store>\w+Store|store)\b[^}]*\}\s*from\s*['\"][^'\"]*stores?[^'\"]*['\"]"),
|
||||||
}
|
}
|
||||||
# [/DEF:get_patterns:Function]
|
# [/DEF:get_patterns:Function]
|
||||||
|
|
||||||
|
|
||||||
|
# [DEF:extract_svelte_props:Function]
|
||||||
|
# @TIER: STANDARD
|
||||||
|
# @PURPOSE: Extracts props from Svelte component script section.
|
||||||
|
# @PRE: lines is a list of file lines, start_idx is the starting line index.
|
||||||
|
# @POST: Returns list of prop definitions.
|
||||||
|
def extract_svelte_props(lines: List[str], start_idx: int) -> List[Dict[str, Any]]:
|
||||||
|
with belief_scope("extract_svelte_props"):
|
||||||
|
props = []
|
||||||
|
pattern = re.compile(r"export\s+let\s+(?P<name>\w+)(?:\s*:\s*(?P<type>[\w\[\]|<>\s]+))?(?:\s*=\s*(?P<default>[^;]+))?;")
|
||||||
|
|
||||||
|
for i in range(start_idx, min(start_idx + 100, len(lines))): # Look ahead 100 lines
|
||||||
|
line = lines[i].strip()
|
||||||
|
# Stop at script end or function definitions
|
||||||
|
if line == "</script>" or line.startswith("function ") or line.startswith("const "):
|
||||||
|
break
|
||||||
|
|
||||||
|
match = pattern.search(line)
|
||||||
|
if match:
|
||||||
|
prop = {
|
||||||
|
"name": match.group("name"),
|
||||||
|
"type": match.group("type") if match.group("type") else "any",
|
||||||
|
"default": match.group("default").strip() if match.group("default") else None
|
||||||
|
}
|
||||||
|
props.append(prop)
|
||||||
|
|
||||||
|
return props
|
||||||
|
# [/DEF:extract_svelte_props:Function]
|
||||||
|
|
||||||
|
|
||||||
|
# [DEF:extract_svelte_events:Function]
|
||||||
|
# @TIER: STANDARD
|
||||||
|
# @PURPOSE: Extracts dispatched events from Svelte component.
|
||||||
|
# @PRE: lines is a list of file lines.
|
||||||
|
# @POST: Returns list of event names.
|
||||||
|
def extract_svelte_events(lines: List[str]) -> List[str]:
|
||||||
|
with belief_scope("extract_svelte_events"):
|
||||||
|
events = set()
|
||||||
|
|
||||||
|
# Pattern 1: createEventDispatcher with type definition
|
||||||
|
dispatcher_pattern = re.compile(r"createEventDispatcher\s*<\s*\{\s*([^}]+)\s*\}\s*\>")
|
||||||
|
# Pattern 2: dispatch('eventName')
|
||||||
|
dispatch_pattern = re.compile(r"dispatch\s*\(\s*['\"](\w+)['\"]")
|
||||||
|
|
||||||
|
for line in lines:
|
||||||
|
line = line.strip()
|
||||||
|
|
||||||
|
# Check for typed dispatcher
|
||||||
|
match = dispatcher_pattern.search(line)
|
||||||
|
if match:
|
||||||
|
events_str = match.group(1)
|
||||||
|
# Extract event names from type definition like: submit: Type; cancel: Type
|
||||||
|
for event_def in events_str.split(";"):
|
||||||
|
if ":" in event_def:
|
||||||
|
event_name = event_def.split(":")[0].strip()
|
||||||
|
if event_name:
|
||||||
|
events.add(event_name)
|
||||||
|
|
||||||
|
# Check for dispatch calls
|
||||||
|
match = dispatch_pattern.search(line)
|
||||||
|
if match:
|
||||||
|
events.add(match.group(1))
|
||||||
|
|
||||||
|
return sorted(list(events))
|
||||||
|
# [/DEF:extract_svelte_events:Function]
|
||||||
|
|
||||||
|
|
||||||
|
# [DEF:extract_data_flow:Function]
|
||||||
|
# @TIER: STANDARD
|
||||||
|
# @PURPOSE: Extracts store subscriptions and data flow from Svelte component.
|
||||||
|
# @PRE: lines is a list of file lines.
|
||||||
|
# @POST: Returns list of data flow descriptors.
|
||||||
|
def extract_data_flow(lines: List[str]) -> List[Dict[str, str]]:
|
||||||
|
with belief_scope("extract_data_flow"):
|
||||||
|
data_flow = []
|
||||||
|
|
||||||
|
# Pattern for store subscriptions: $storeName
|
||||||
|
subscription_pattern = re.compile(r"\$(?P<store>\w+)")
|
||||||
|
# Pattern for store imports
|
||||||
|
import_pattern = re.compile(r"import\s*\{[^}]*\}\s*from\s*['\"][^'\"]*stores?[^'\"]*['\"]")
|
||||||
|
|
||||||
|
store_names = set()
|
||||||
|
|
||||||
|
# First pass: find store imports
|
||||||
|
for line in lines:
|
||||||
|
if import_pattern.search(line):
|
||||||
|
# Extract imported names
|
||||||
|
match = re.search(r"import\s*\{([^}]+)\}", line)
|
||||||
|
if match:
|
||||||
|
imports = match.group(1).split(",")
|
||||||
|
for imp in imports:
|
||||||
|
store_names.add(imp.strip().split()[0])
|
||||||
|
|
||||||
|
# Second pass: find subscriptions
|
||||||
|
for i, line in enumerate(lines):
|
||||||
|
line_stripped = line.strip()
|
||||||
|
|
||||||
|
# Skip comments
|
||||||
|
if line_stripped.startswith("//") or line_stripped.startswith("*"):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Find store subscriptions
|
||||||
|
for match in subscription_pattern.finditer(line):
|
||||||
|
store_name = match.group("store")
|
||||||
|
if store_name not in ["if", "while", "for", "switch"]:
|
||||||
|
flow_type = "READS_FROM"
|
||||||
|
# Check if it's an assignment (write)
|
||||||
|
if "=" in line and line.index("$") > line.index("="):
|
||||||
|
flow_type = "WRITES_TO"
|
||||||
|
|
||||||
|
data_flow.append({
|
||||||
|
"store": store_name,
|
||||||
|
"type": flow_type,
|
||||||
|
"line": i + 1
|
||||||
|
})
|
||||||
|
|
||||||
|
return data_flow
|
||||||
|
# [/DEF:extract_data_flow:Function]
|
||||||
|
|
||||||
|
|
||||||
# [DEF:parse_file:Function]
|
# [DEF:parse_file:Function]
|
||||||
# @PURPOSE: Parses a single file to extract semantic entities.
|
# @TIER: CRITICAL
|
||||||
|
# @PURPOSE: Parses a single file to extract semantic entities with tier awareness and enhanced Svelte analysis.
|
||||||
# @PRE: full_path, rel_path, lang are valid strings.
|
# @PRE: full_path, rel_path, lang are valid strings.
|
||||||
# @POST: Returns extracted entities and list of issues.
|
# @POST: Returns extracted entities and list of issues.
|
||||||
|
# @INVARIANT: Every opened anchor must have a matching closing anchor for valid compliance.
|
||||||
# @PARAM: full_path - Absolute path to file.
|
# @PARAM: full_path - Absolute path to file.
|
||||||
# @PARAM: rel_path - Relative path from project root.
|
# @PARAM: rel_path - Relative path from project root.
|
||||||
# @PARAM: lang - Language identifier.
|
# @PARAM: lang - Language identifier.
|
||||||
# @RETURN: Tuple[List[SemanticEntity], List[str]] - Entities found and global issues.
|
def parse_file(full_path: str, rel_path: str, lang: str) -> Tuple[List[SemanticEntity], List[ComplianceIssue]]:
|
||||||
def parse_file(full_path: str, rel_path: str, lang: str) -> Tuple[List[SemanticEntity], List[str]]:
|
|
||||||
with belief_scope("parse_file"):
|
with belief_scope("parse_file"):
|
||||||
issues: List[str] = []
|
issues: List[ComplianceIssue] = []
|
||||||
try:
|
try:
|
||||||
with open(full_path, 'r', encoding='utf-8') as f:
|
with open(full_path, 'r', encoding='utf-8') as f:
|
||||||
lines = f.readlines()
|
lines = f.readlines()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return [], [f"Could not read file {rel_path}: {e}"]
|
return [], [ComplianceIssue(f"Could not read file {rel_path}: {e}", Severity.ERROR)]
|
||||||
|
|
||||||
stack: List[SemanticEntity] = []
|
stack: List[SemanticEntity] = []
|
||||||
file_entities: List[SemanticEntity] = []
|
file_entities: List[SemanticEntity] = []
|
||||||
|
orphan_functions: List[SemanticEntity] = []
|
||||||
patterns = get_patterns(lang)
|
patterns = get_patterns(lang)
|
||||||
|
|
||||||
|
# Track current module for grouping orphans
|
||||||
|
current_module: Optional[SemanticEntity] = None
|
||||||
|
|
||||||
for i, line in enumerate(lines):
|
for i, line in enumerate(lines):
|
||||||
lineno = i + 1
|
lineno = i + 1
|
||||||
line = line.strip()
|
line_stripped = line.strip()
|
||||||
|
|
||||||
# 1. Check for Anchor Start
|
# 1. Check for Anchor Start
|
||||||
match_start = None
|
match_start = None
|
||||||
if lang == "python":
|
if lang == "python":
|
||||||
match_start = patterns["anchor_start"].search(line)
|
match_start = patterns["anchor_start"].search(line_stripped)
|
||||||
else:
|
else:
|
||||||
match_start = patterns["html_anchor_start"].search(line) or patterns["js_anchor_start"].search(line)
|
match_start = patterns["html_anchor_start"].search(line_stripped) or patterns["js_anchor_start"].search(line_stripped)
|
||||||
|
|
||||||
if match_start:
|
if match_start:
|
||||||
name = match_start.group("name")
|
name = match_start.group("name")
|
||||||
type_ = match_start.group("type")
|
type_ = match_start.group("type")
|
||||||
entity = SemanticEntity(name, type_, lineno, rel_path)
|
entity = SemanticEntity(name, type_, lineno, rel_path)
|
||||||
|
|
||||||
|
# Track module-level entities
|
||||||
|
if type_ == "Module" and not stack:
|
||||||
|
current_module = entity
|
||||||
|
|
||||||
if stack:
|
if stack:
|
||||||
parent = stack[-1]
|
parent = stack[-1]
|
||||||
parent.children.append(entity)
|
parent.children.append(entity)
|
||||||
@@ -249,46 +529,57 @@ def parse_file(full_path: str, rel_path: str, lang: str) -> Tuple[List[SemanticE
|
|||||||
# 2. Check for Anchor End
|
# 2. Check for Anchor End
|
||||||
match_end = None
|
match_end = None
|
||||||
if lang == "python":
|
if lang == "python":
|
||||||
match_end = patterns["anchor_end"].search(line)
|
match_end = patterns["anchor_end"].search(line_stripped)
|
||||||
else:
|
else:
|
||||||
match_end = patterns["html_anchor_end"].search(line) or patterns["js_anchor_end"].search(line)
|
match_end = patterns["html_anchor_end"].search(line_stripped) or patterns["js_anchor_end"].search(line_stripped)
|
||||||
|
|
||||||
if match_end:
|
if match_end:
|
||||||
name = match_end.group("name")
|
name = match_end.group("name")
|
||||||
type_ = match_end.group("type")
|
|
||||||
|
|
||||||
if not stack:
|
if not stack:
|
||||||
issues.append(f"{rel_path}:{lineno} Found closing anchor [/DEF:{name}:{type_}] without opening anchor.")
|
issues.append(ComplianceIssue(
|
||||||
|
f"{rel_path}:{lineno} Found closing anchor [/DEF:{name}] without opening anchor.",
|
||||||
|
Severity.ERROR,
|
||||||
|
lineno
|
||||||
|
))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
top = stack[-1]
|
top = stack[-1]
|
||||||
if top.name == name and top.type == type_:
|
if top.name == name:
|
||||||
top.end_line = lineno
|
top.end_line = lineno
|
||||||
stack.pop()
|
stack.pop()
|
||||||
else:
|
else:
|
||||||
issues.append(f"{rel_path}:{lineno} Mismatched closing anchor. Expected [/DEF:{top.name}:{top.type}], found [/DEF:{name}:{type_}].")
|
issues.append(ComplianceIssue(
|
||||||
|
f"{rel_path}:{lineno} Mismatched closing anchor. Expected [/DEF:{top.name}], found [/DEF:{name}].",
|
||||||
|
Severity.ERROR,
|
||||||
|
lineno
|
||||||
|
))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# 3. Check for Naked Functions (Missing Contracts)
|
# 3. Check for Naked Functions (Missing Contracts) - track as orphans
|
||||||
if "func_def" in patterns:
|
if "func_def" in patterns:
|
||||||
match_func = patterns["func_def"].search(line)
|
match_func = patterns["func_def"].search(line_stripped)
|
||||||
if match_func:
|
if match_func:
|
||||||
func_name = match_func.group("name")
|
func_name = match_func.group("name")
|
||||||
is_covered = False
|
is_covered = False
|
||||||
if stack:
|
if stack:
|
||||||
current = stack[-1]
|
current = stack[-1]
|
||||||
# Check if we are inside a Function anchor that matches the name
|
|
||||||
if current.type == "Function" and current.name == func_name:
|
if current.type == "Function" and current.name == func_name:
|
||||||
is_covered = True
|
is_covered = True
|
||||||
|
|
||||||
if not is_covered:
|
if not is_covered:
|
||||||
issues.append(f"{rel_path}:{lineno} Function '{func_name}' implementation found without matching [DEF:{func_name}:Function] contract.")
|
# 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.end_line = lineno # Mark as closed immediately
|
||||||
|
orphan_functions.append(orphan)
|
||||||
|
|
||||||
# 4. Check for Tags/Relations
|
# 4. Check for Tags/Relations
|
||||||
if stack:
|
if stack:
|
||||||
current = stack[-1]
|
current = stack[-1]
|
||||||
|
|
||||||
match_rel = patterns["relation"].search(line)
|
match_rel = patterns["relation"].search(line_stripped)
|
||||||
if match_rel:
|
if match_rel:
|
||||||
current.relations.append({
|
current.relations.append({
|
||||||
"type": match_rel.group("type"),
|
"type": match_rel.group("type"),
|
||||||
@@ -298,11 +589,11 @@ def parse_file(full_path: str, rel_path: str, lang: str) -> Tuple[List[SemanticE
|
|||||||
|
|
||||||
match_tag = None
|
match_tag = None
|
||||||
if lang == "python":
|
if lang == "python":
|
||||||
match_tag = patterns["tag"].search(line)
|
match_tag = patterns["tag"].search(line_stripped)
|
||||||
elif lang == "svelte_js":
|
elif lang == "svelte_js":
|
||||||
match_tag = patterns["html_tag"].search(line)
|
match_tag = patterns["html_tag"].search(line_stripped)
|
||||||
if not match_tag and ("/*" in line or "*" in line or "//" in line):
|
if not match_tag and ("/*" in line_stripped or "*" in line_stripped or "//" in line_stripped):
|
||||||
match_tag = patterns["jsdoc_tag"].search(line)
|
match_tag = patterns["jsdoc_tag"].search(line_stripped)
|
||||||
|
|
||||||
if match_tag:
|
if match_tag:
|
||||||
tag_name = match_tag.group("tag").upper()
|
tag_name = match_tag.group("tag").upper()
|
||||||
@@ -314,21 +605,68 @@ def parse_file(full_path: str, rel_path: str, lang: str) -> Tuple[List[SemanticE
|
|||||||
if patterns["belief_scope"].search(line):
|
if patterns["belief_scope"].search(line):
|
||||||
current.has_belief_scope = True
|
current.has_belief_scope = True
|
||||||
|
|
||||||
|
# Check for console.log belief state in Svelte
|
||||||
|
if lang == "svelte_js" and "console_log" in patterns:
|
||||||
|
if patterns["console_log"].search(line):
|
||||||
|
current.has_console_log = True
|
||||||
|
|
||||||
# End of file check
|
# End of file check
|
||||||
if stack:
|
if stack:
|
||||||
for unclosed in stack:
|
for unclosed in stack:
|
||||||
unclosed.compliance_issues.append(f"Unclosed Anchor at end of file (started line {unclosed.start_line})")
|
issues.append(ComplianceIssue(
|
||||||
|
f"{rel_path}: Unclosed Anchor [DEF:{unclosed.name}:{unclosed.type}] at end of file (started line {unclosed.start_line})",
|
||||||
|
Severity.ERROR,
|
||||||
|
unclosed.start_line
|
||||||
|
))
|
||||||
if unclosed.parent is None and unclosed not in file_entities:
|
if unclosed.parent is None and unclosed not in file_entities:
|
||||||
file_entities.append(unclosed)
|
file_entities.append(unclosed)
|
||||||
|
|
||||||
|
# Post-processing for Svelte files
|
||||||
|
if lang == "svelte_js":
|
||||||
|
for entity in file_entities:
|
||||||
|
if entity.type == "Component":
|
||||||
|
# Extract props, events, and data flow
|
||||||
|
entity.props = extract_svelte_props(lines, entity.start_line)
|
||||||
|
entity.events = extract_svelte_events(lines)
|
||||||
|
entity.data_flow = extract_data_flow(lines)
|
||||||
|
|
||||||
|
# Group orphan functions under their module
|
||||||
|
if orphan_functions:
|
||||||
|
if current_module:
|
||||||
|
# Add orphans as children of the module
|
||||||
|
for orphan in orphan_functions:
|
||||||
|
orphan.parent = current_module
|
||||||
|
current_module.children.append(orphan)
|
||||||
|
else:
|
||||||
|
# Create a synthetic module for orphans
|
||||||
|
synthetic_module = SemanticEntity(
|
||||||
|
os.path.splitext(os.path.basename(rel_path))[0],
|
||||||
|
"Module",
|
||||||
|
1,
|
||||||
|
rel_path
|
||||||
|
)
|
||||||
|
synthetic_module.tags["PURPOSE"] = f"Auto-generated module for {rel_path}"
|
||||||
|
synthetic_module.tags["TIER"] = "TRIVIAL"
|
||||||
|
synthetic_module.tags["LAYER"] = "Unknown"
|
||||||
|
synthetic_module.end_line = len(lines)
|
||||||
|
|
||||||
|
for orphan in orphan_functions:
|
||||||
|
orphan.parent = synthetic_module
|
||||||
|
synthetic_module.children.append(orphan)
|
||||||
|
|
||||||
|
file_entities.append(synthetic_module)
|
||||||
|
|
||||||
return file_entities, issues
|
return file_entities, issues
|
||||||
# [/DEF:parse_file:Function]
|
# [/DEF:parse_file:Function]
|
||||||
|
|
||||||
|
|
||||||
# [DEF:SemanticMapGenerator:Class]
|
# [DEF:SemanticMapGenerator:Class]
|
||||||
# @PURPOSE: Orchestrates the mapping process.
|
# @TIER: CRITICAL
|
||||||
|
# @PURPOSE: Orchestrates the mapping process with tier-based validation.
|
||||||
|
# @INVARIANT: All entities are validated according to their TIER requirements.
|
||||||
class SemanticMapGenerator:
|
class SemanticMapGenerator:
|
||||||
# [DEF:__init__:Function]
|
# [DEF:__init__:Function]
|
||||||
|
# @TIER: STANDARD
|
||||||
# @PURPOSE: Initializes the generator with a root directory.
|
# @PURPOSE: Initializes the generator with a root directory.
|
||||||
# @PRE: root_dir is a valid path string.
|
# @PRE: root_dir is a valid path string.
|
||||||
# @POST: Generator instance is ready.
|
# @POST: Generator instance is ready.
|
||||||
@@ -337,15 +675,15 @@ class SemanticMapGenerator:
|
|||||||
self.root_dir = root_dir
|
self.root_dir = root_dir
|
||||||
self.entities: List[SemanticEntity] = []
|
self.entities: List[SemanticEntity] = []
|
||||||
self.file_scores: Dict[str, float] = {}
|
self.file_scores: Dict[str, float] = {}
|
||||||
self.global_issues: List[str] = []
|
self.global_issues: List[ComplianceIssue] = []
|
||||||
self.ignored_patterns = self._load_gitignore()
|
self.ignored_patterns = self._load_gitignore()
|
||||||
# [/DEF:__init__:Function]
|
# [/DEF:__init__:Function]
|
||||||
|
|
||||||
# [DEF:_load_gitignore:Function]
|
# [DEF:_load_gitignore:Function]
|
||||||
|
# @TIER: STANDARD
|
||||||
# @PURPOSE: Loads patterns from .gitignore file.
|
# @PURPOSE: Loads patterns from .gitignore file.
|
||||||
# @PRE: .gitignore exists in root_dir.
|
# @PRE: .gitignore exists in root_dir.
|
||||||
# @POST: Returns set of ignore patterns.
|
# @POST: Returns set of ignore patterns.
|
||||||
# @RETURN: Set of patterns to ignore.
|
|
||||||
def _load_gitignore(self) -> Set[str]:
|
def _load_gitignore(self) -> Set[str]:
|
||||||
with belief_scope("_load_gitignore"):
|
with belief_scope("_load_gitignore"):
|
||||||
patterns = set()
|
patterns = set()
|
||||||
@@ -360,17 +698,14 @@ class SemanticMapGenerator:
|
|||||||
# [/DEF:_load_gitignore:Function]
|
# [/DEF:_load_gitignore:Function]
|
||||||
|
|
||||||
# [DEF:_is_ignored:Function]
|
# [DEF:_is_ignored:Function]
|
||||||
|
# @TIER: STANDARD
|
||||||
# @PURPOSE: Checks if a path should be ignored based on .gitignore or hardcoded defaults.
|
# @PURPOSE: Checks if a path should be ignored based on .gitignore or hardcoded defaults.
|
||||||
# @PRE: rel_path is a valid relative path string.
|
# @PRE: rel_path is a valid relative path string.
|
||||||
# @POST: Returns True if the path should be ignored.
|
# @POST: Returns True if the path should be ignored.
|
||||||
# @PARAM: rel_path (str) - Path relative to root.
|
|
||||||
# @RETURN: bool - True if ignored.
|
|
||||||
def _is_ignored(self, rel_path: str) -> bool:
|
def _is_ignored(self, rel_path: str) -> bool:
|
||||||
with belief_scope("_is_ignored"):
|
with belief_scope("_is_ignored"):
|
||||||
# Normalize path for matching
|
|
||||||
rel_path = rel_path.replace(os.sep, '/')
|
rel_path = rel_path.replace(os.sep, '/')
|
||||||
|
|
||||||
# Check hardcoded defaults
|
|
||||||
parts = rel_path.split('/')
|
parts = rel_path.split('/')
|
||||||
for part in parts:
|
for part in parts:
|
||||||
if part in IGNORE_DIRS:
|
if part in IGNORE_DIRS:
|
||||||
@@ -379,21 +714,17 @@ class SemanticMapGenerator:
|
|||||||
if os.path.basename(rel_path) in IGNORE_FILES:
|
if os.path.basename(rel_path) in IGNORE_FILES:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# Check gitignore patterns
|
|
||||||
for pattern in self.ignored_patterns:
|
for pattern in self.ignored_patterns:
|
||||||
# Handle directory patterns like 'node_modules/'
|
|
||||||
if pattern.endswith('/'):
|
if pattern.endswith('/'):
|
||||||
dir_pattern = pattern.rstrip('/')
|
dir_pattern = pattern.rstrip('/')
|
||||||
if rel_path == dir_pattern or rel_path.startswith(pattern):
|
if rel_path == dir_pattern or rel_path.startswith(pattern):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# Check for patterns in frontend/ or backend/
|
|
||||||
if rel_path.startswith("frontend/") and fnmatch.fnmatch(rel_path[9:], pattern):
|
if rel_path.startswith("frontend/") and fnmatch.fnmatch(rel_path[9:], pattern):
|
||||||
return True
|
return True
|
||||||
if rel_path.startswith("backend/") and fnmatch.fnmatch(rel_path[8:], pattern):
|
if rel_path.startswith("backend/") and fnmatch.fnmatch(rel_path[8:], pattern):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# Use fnmatch for glob patterns
|
|
||||||
if fnmatch.fnmatch(rel_path, pattern) or \
|
if fnmatch.fnmatch(rel_path, pattern) or \
|
||||||
fnmatch.fnmatch(os.path.basename(rel_path), pattern) or \
|
fnmatch.fnmatch(os.path.basename(rel_path), pattern) or \
|
||||||
any(fnmatch.fnmatch(part, pattern) for part in parts):
|
any(fnmatch.fnmatch(part, pattern) for part in parts):
|
||||||
@@ -403,6 +734,7 @@ class SemanticMapGenerator:
|
|||||||
# [/DEF:_is_ignored:Function]
|
# [/DEF:_is_ignored:Function]
|
||||||
|
|
||||||
# [DEF:run:Function]
|
# [DEF:run:Function]
|
||||||
|
# @TIER: CRITICAL
|
||||||
# @PURPOSE: Main execution flow.
|
# @PURPOSE: Main execution flow.
|
||||||
# @PRE: Generator is initialized.
|
# @PRE: Generator is initialized.
|
||||||
# @POST: Semantic map and reports are generated.
|
# @POST: Semantic map and reports are generated.
|
||||||
@@ -417,13 +749,13 @@ class SemanticMapGenerator:
|
|||||||
# [/DEF:run:Function]
|
# [/DEF:run:Function]
|
||||||
|
|
||||||
# [DEF:_walk_and_parse:Function]
|
# [DEF:_walk_and_parse:Function]
|
||||||
|
# @TIER: CRITICAL
|
||||||
# @PURPOSE: Recursively walks directories and triggers parsing.
|
# @PURPOSE: Recursively walks directories and triggers parsing.
|
||||||
# @PRE: root_dir exists.
|
# @PRE: root_dir exists.
|
||||||
# @POST: All files are scanned and entities extracted.
|
# @POST: All files are scanned and entities extracted.
|
||||||
def _walk_and_parse(self):
|
def _walk_and_parse(self):
|
||||||
with belief_scope("_walk_and_parse"):
|
with belief_scope("_walk_and_parse"):
|
||||||
for root, dirs, files in os.walk(self.root_dir):
|
for root, dirs, files in os.walk(self.root_dir):
|
||||||
# Optimization: don't enter ignored directories
|
|
||||||
dirs[:] = [d for d in dirs if not self._is_ignored(os.path.relpath(os.path.join(root, d), self.root_dir) + "/")]
|
dirs[:] = [d for d in dirs if not self._is_ignored(os.path.relpath(os.path.join(root, d), self.root_dir) + "/")]
|
||||||
|
|
||||||
for file in files:
|
for file in files:
|
||||||
@@ -448,7 +780,8 @@ class SemanticMapGenerator:
|
|||||||
# [/DEF:_walk_and_parse:Function]
|
# [/DEF:_walk_and_parse:Function]
|
||||||
|
|
||||||
# [DEF:_process_file_results:Function]
|
# [DEF:_process_file_results:Function]
|
||||||
# @PURPOSE: Validates entities and calculates file scores.
|
# @TIER: STANDARD
|
||||||
|
# @PURPOSE: Validates entities and calculates file scores with tier awareness.
|
||||||
# @PRE: Entities have been parsed from the file.
|
# @PRE: Entities have been parsed from the file.
|
||||||
# @POST: File score is calculated and issues collected.
|
# @POST: File score is calculated and issues collected.
|
||||||
def _process_file_results(self, rel_path: str, entities: List[SemanticEntity]):
|
def _process_file_results(self, rel_path: str, entities: List[SemanticEntity]):
|
||||||
@@ -457,6 +790,7 @@ class SemanticMapGenerator:
|
|||||||
count = 0
|
count = 0
|
||||||
|
|
||||||
# [DEF:validate_recursive:Function]
|
# [DEF:validate_recursive:Function]
|
||||||
|
# @TIER: STANDARD
|
||||||
# @PURPOSE: Recursively validates a list of entities.
|
# @PURPOSE: Recursively validates a list of entities.
|
||||||
# @PRE: ent_list is a list of SemanticEntity objects.
|
# @PRE: ent_list is a list of SemanticEntity objects.
|
||||||
# @POST: All entities and their children are validated.
|
# @POST: All entities and their children are validated.
|
||||||
@@ -477,12 +811,12 @@ class SemanticMapGenerator:
|
|||||||
# [/DEF:_process_file_results:Function]
|
# [/DEF:_process_file_results:Function]
|
||||||
|
|
||||||
# [DEF:_generate_artifacts:Function]
|
# [DEF:_generate_artifacts:Function]
|
||||||
# @PURPOSE: Writes output files.
|
# @TIER: CRITICAL
|
||||||
|
# @PURPOSE: Writes output files with tier-based compliance data.
|
||||||
# @PRE: Parsing and validation are complete.
|
# @PRE: Parsing and validation are complete.
|
||||||
# @POST: JSON and Markdown artifacts are written to disk.
|
# @POST: JSON and Markdown artifacts are written to disk.
|
||||||
def _generate_artifacts(self):
|
def _generate_artifacts(self):
|
||||||
with belief_scope("_generate_artifacts"):
|
with belief_scope("_generate_artifacts"):
|
||||||
# 1. Full JSON Map
|
|
||||||
full_map = {
|
full_map = {
|
||||||
"project_root": self.root_dir,
|
"project_root": self.root_dir,
|
||||||
"generated_at": datetime.datetime.now().isoformat(),
|
"generated_at": datetime.datetime.now().isoformat(),
|
||||||
@@ -494,15 +828,13 @@ class SemanticMapGenerator:
|
|||||||
json.dump(full_map, f, indent=2)
|
json.dump(full_map, f, indent=2)
|
||||||
print(f"Generated {OUTPUT_JSON}")
|
print(f"Generated {OUTPUT_JSON}")
|
||||||
|
|
||||||
# 2. Compliance Report
|
|
||||||
self._generate_report()
|
self._generate_report()
|
||||||
|
|
||||||
# 3. Compressed Map (Markdown)
|
|
||||||
self._generate_compressed_map()
|
self._generate_compressed_map()
|
||||||
# [/DEF:_generate_artifacts:Function]
|
# [/DEF:_generate_artifacts:Function]
|
||||||
|
|
||||||
# [DEF:_generate_report:Function]
|
# [DEF:_generate_report:Function]
|
||||||
# @PURPOSE: Generates the Markdown compliance report.
|
# @TIER: CRITICAL
|
||||||
|
# @PURPOSE: Generates the Markdown compliance report with severity levels.
|
||||||
# @PRE: File scores and issues are available.
|
# @PRE: File scores and issues are available.
|
||||||
# @POST: Markdown report is created in reports directory.
|
# @POST: Markdown report is created in reports directory.
|
||||||
def _generate_report(self):
|
def _generate_report(self):
|
||||||
@@ -514,49 +846,64 @@ class SemanticMapGenerator:
|
|||||||
total_files = len(self.file_scores)
|
total_files = len(self.file_scores)
|
||||||
avg_score = sum(self.file_scores.values()) / total_files if total_files > 0 else 0
|
avg_score = sum(self.file_scores.values()) / total_files if total_files > 0 else 0
|
||||||
|
|
||||||
|
# Count issues by severity
|
||||||
|
error_count = len([i for i in self.global_issues if i.severity == Severity.ERROR])
|
||||||
|
warning_count = len([i for i in self.global_issues if i.severity == Severity.WARNING])
|
||||||
|
|
||||||
with open(report_path, 'w', encoding='utf-8') as f:
|
with open(report_path, 'w', encoding='utf-8') as f:
|
||||||
f.write(f"# Semantic Compliance Report\n\n")
|
f.write(f"# Semantic Compliance Report\n\n")
|
||||||
f.write(f"**Generated At:** {datetime.datetime.now().isoformat()}\n")
|
f.write(f"**Generated At:** {datetime.datetime.now().isoformat()}\n")
|
||||||
f.write(f"**Global Compliance Score:** {avg_score:.1%}\n")
|
f.write(f"**Global Compliance Score:** {avg_score:.1%}\n")
|
||||||
f.write(f"**Scanned Files:** {total_files}\n\n")
|
f.write(f"**Scanned Files:** {total_files}\n")
|
||||||
|
f.write(f"**Global Errors:** {error_count} | **Warnings:** {warning_count}\n\n")
|
||||||
|
|
||||||
if self.global_issues:
|
if self.global_issues:
|
||||||
f.write("## Critical Parsing Errors\n")
|
f.write("## Critical Parsing Errors\n")
|
||||||
for issue in self.global_issues:
|
for issue in self.global_issues:
|
||||||
f.write(f"- 🔴 {issue}\n")
|
icon = "🔴" if issue.severity == Severity.ERROR else "🟡" if issue.severity == Severity.WARNING else "ℹ️"
|
||||||
|
f.write(f"- {icon} {issue.message}\n")
|
||||||
f.write("\n")
|
f.write("\n")
|
||||||
|
|
||||||
f.write("## File Compliance Status\n")
|
f.write("## File Compliance Status\n")
|
||||||
f.write("| File | Score | Issues |\n")
|
f.write("| File | Score | Tier | Issues |\n")
|
||||||
f.write("|------|-------|--------|\n")
|
f.write("|------|-------|------|--------|\n")
|
||||||
|
|
||||||
sorted_files = sorted(self.file_scores.items(), key=lambda x: x[1])
|
sorted_files = sorted(self.file_scores.items(), key=lambda x: x[1])
|
||||||
|
|
||||||
for file_path, score in sorted_files:
|
for file_path, score in sorted_files:
|
||||||
issues = []
|
issues = []
|
||||||
self._collect_issues(self.entities, file_path, issues)
|
tier = "N/A"
|
||||||
|
self._collect_issues(self.entities, file_path, issues, tier)
|
||||||
|
|
||||||
status_icon = "🟢" if score == 1.0 else "🟡" if score > 0.5 else "🔴"
|
status_icon = "🟢" if score == 1.0 else "🟡" if score > 0.5 else "🔴"
|
||||||
issue_text = "<br>".join(issues) if issues else "OK"
|
issue_text = "<br>".join([f"{'🔴' if i.severity == Severity.ERROR else '🟡'} {i.message}" for i in issues[:3]])
|
||||||
f.write(f"| {file_path} | {status_icon} {score:.0%} | {issue_text} |\n")
|
if len(issues) > 3:
|
||||||
|
issue_text += f"<br>... and {len(issues) - 3} more"
|
||||||
|
if not issues:
|
||||||
|
issue_text = "OK"
|
||||||
|
|
||||||
|
f.write(f"| {file_path} | {status_icon} {score:.0%} | {tier} | {issue_text} |\n")
|
||||||
|
|
||||||
print(f"Generated {report_path}")
|
print(f"Generated {report_path}")
|
||||||
# [/DEF:_generate_report:Function]
|
# [/DEF:_generate_report:Function]
|
||||||
|
|
||||||
# [DEF:_collect_issues:Function]
|
# [DEF:_collect_issues:Function]
|
||||||
|
# @TIER: STANDARD
|
||||||
# @PURPOSE: Helper to collect issues for a specific file from the entity tree.
|
# @PURPOSE: Helper to collect issues for a specific file from the entity tree.
|
||||||
# @PRE: entities list and file_path are valid.
|
# @PRE: entities list and file_path are valid.
|
||||||
# @POST: issues list is populated with compliance issues.
|
# @POST: issues list is populated with compliance issues.
|
||||||
def _collect_issues(self, entities: List[SemanticEntity], file_path: str, issues: List[str]):
|
def _collect_issues(self, entities: List[SemanticEntity], file_path: str, issues: List[ComplianceIssue], tier: str):
|
||||||
with belief_scope("_collect_issues"):
|
with belief_scope("_collect_issues"):
|
||||||
for e in entities:
|
for e in entities:
|
||||||
if e.file_path == file_path:
|
if e.file_path == file_path:
|
||||||
issues.extend([f"[{e.name}] {i}" for i in e.compliance_issues])
|
issues.extend(e.compliance_issues)
|
||||||
self._collect_issues(e.children, file_path, issues)
|
tier = e.get_tier().value
|
||||||
|
self._collect_issues(e.children, file_path, issues, tier)
|
||||||
# [/DEF:_collect_issues:Function]
|
# [/DEF:_collect_issues:Function]
|
||||||
|
|
||||||
# [DEF:_generate_compressed_map:Function]
|
# [DEF:_generate_compressed_map:Function]
|
||||||
# @PURPOSE: Generates the token-optimized project map.
|
# @TIER: CRITICAL
|
||||||
|
# @PURPOSE: Generates the token-optimized project map with enhanced Svelte details.
|
||||||
# @PRE: Entities have been processed.
|
# @PRE: Entities have been processed.
|
||||||
# @POST: Markdown project map is written.
|
# @POST: Markdown project map is written.
|
||||||
def _generate_compressed_map(self):
|
def _generate_compressed_map(self):
|
||||||
@@ -574,7 +921,8 @@ class SemanticMapGenerator:
|
|||||||
# [/DEF:_generate_compressed_map:Function]
|
# [/DEF:_generate_compressed_map:Function]
|
||||||
|
|
||||||
# [DEF:_write_entity_md:Function]
|
# [DEF:_write_entity_md:Function]
|
||||||
# @PURPOSE: Recursive helper to write entity tree to Markdown.
|
# @TIER: CRITICAL
|
||||||
|
# @PURPOSE: Recursive helper to write entity tree to Markdown with tier badges and enhanced details.
|
||||||
# @PRE: f is an open file handle, entity is valid.
|
# @PRE: f is an open file handle, entity is valid.
|
||||||
# @POST: Entity details are written to the file.
|
# @POST: Entity details are written to the file.
|
||||||
def _write_entity_md(self, f, entity: SemanticEntity, level: int):
|
def _write_entity_md(self, f, entity: SemanticEntity, level: int):
|
||||||
@@ -585,28 +933,66 @@ class SemanticMapGenerator:
|
|||||||
if entity.type == "Component": icon = "🧩"
|
if entity.type == "Component": icon = "🧩"
|
||||||
elif entity.type == "Function": icon = "ƒ"
|
elif entity.type == "Function": icon = "ƒ"
|
||||||
elif entity.type == "Class": icon = "ℂ"
|
elif entity.type == "Class": icon = "ℂ"
|
||||||
|
elif entity.type == "Store": icon = "🗄️"
|
||||||
|
|
||||||
f.write(f"{indent}- {icon} **{entity.name}** (`{entity.type}`)\n")
|
tier_badge = ""
|
||||||
|
tier = entity.get_tier()
|
||||||
|
if tier == Tier.CRITICAL:
|
||||||
|
tier_badge = " `[CRITICAL]`"
|
||||||
|
elif tier == Tier.TRIVIAL:
|
||||||
|
tier_badge = " `[TRIVIAL]`"
|
||||||
|
|
||||||
|
f.write(f"{indent}- {icon} **{entity.name}** (`{entity.type}`){tier_badge}\n")
|
||||||
|
|
||||||
purpose = entity.tags.get("PURPOSE") or entity.tags.get("purpose")
|
purpose = entity.tags.get("PURPOSE") or entity.tags.get("purpose")
|
||||||
layer = entity.tags.get("LAYER") or entity.tags.get("layer")
|
layer = entity.tags.get("LAYER") or entity.tags.get("layer")
|
||||||
|
invariant = entity.tags.get("INVARIANT")
|
||||||
|
|
||||||
if purpose:
|
if purpose:
|
||||||
f.write(f"{indent} - 📝 {purpose}\n")
|
f.write(f"{indent} - 📝 {purpose}\n")
|
||||||
if layer:
|
if layer:
|
||||||
f.write(f"{indent} - 🏗️ Layer: {layer}\n")
|
f.write(f"{indent} - 🏗️ Layer: {layer}\n")
|
||||||
|
if invariant:
|
||||||
|
f.write(f"{indent} - 🔒 Invariant: {invariant}\n")
|
||||||
|
|
||||||
|
# Write Props for Components
|
||||||
|
if entity.props:
|
||||||
|
props_str = ", ".join([f"{p['name']}: {p['type']}" for p in entity.props[:5]])
|
||||||
|
if len(entity.props) > 5:
|
||||||
|
props_str += f"... (+{len(entity.props) - 5})"
|
||||||
|
f.write(f"{indent} - 📥 Props: {props_str}\n")
|
||||||
|
|
||||||
|
# Write Events for Components
|
||||||
|
if entity.events:
|
||||||
|
events_str = ", ".join(entity.events[:5])
|
||||||
|
if len(entity.events) > 5:
|
||||||
|
events_str += f"... (+{len(entity.events) - 5})"
|
||||||
|
f.write(f"{indent} - ⚡ Events: {events_str}\n")
|
||||||
|
|
||||||
|
# Write Data Flow
|
||||||
|
if entity.data_flow:
|
||||||
|
unique_flows = {}
|
||||||
|
for flow in entity.data_flow:
|
||||||
|
key = f"{flow['type']} -> {flow['store']}"
|
||||||
|
unique_flows[key] = flow
|
||||||
|
|
||||||
|
for flow_key, flow in list(unique_flows.items())[:3]:
|
||||||
|
arrow = "⬅️" if flow['type'] == "READS_FROM" else "➡️"
|
||||||
|
f.write(f"{indent} - {arrow} {flow['type']} `{flow['store']}`\n")
|
||||||
|
|
||||||
|
# Write Relations
|
||||||
for rel in entity.relations:
|
for rel in entity.relations:
|
||||||
if rel['type'] in ['DEPENDS_ON', 'CALLS', 'INHERITS_FROM']:
|
if rel['type'] in ['DEPENDS_ON', 'CALLS', 'INHERITS', 'IMPLEMENTS', 'DISPATCHES']:
|
||||||
f.write(f"{indent} - 🔗 {rel['type']} -> `{rel['target']}`\n")
|
f.write(f"{indent} - 🔗 {rel['type']} -> `{rel['target']}`\n")
|
||||||
|
|
||||||
if level < 2:
|
if level < 3:
|
||||||
for child in entity.children:
|
for child in entity.children:
|
||||||
self._write_entity_md(f, child, level + 1)
|
self._write_entity_md(f, child, level + 1)
|
||||||
# [/DEF:_write_entity_md:Function]
|
# [/DEF:_write_entity_md:Function]
|
||||||
|
|
||||||
# [/DEF:SemanticMapGenerator:Class]
|
# [/DEF:SemanticMapGenerator:Class]
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
generator = SemanticMapGenerator(PROJECT_ROOT)
|
generator = SemanticMapGenerator(PROJECT_ROOT)
|
||||||
generator.run()
|
generator.run()
|
||||||
|
|||||||
@@ -1,245 +1,72 @@
|
|||||||
|
РОЛЬ: Архитектор Семантической Когерентности.
|
||||||
|
ЗАДАЧА: Генерация кода (Python/Svelte).
|
||||||
|
РЕЖИМ: Строгий. Детерминированный. Без болтовни.
|
||||||
|
|
||||||
# SYSTEM STANDARD: POLYGLOT CODE GENERATION PROTOCOL (GRACE-Poly)
|
I. ЗАКОН (АКСИОМЫ)
|
||||||
|
1. Смысл первичен. Код вторичен.
|
||||||
|
2. Контракт (@PRE/@POST) — источник истины.
|
||||||
|
3. Структура `[DEF]...[/DEF]` — нерушима.
|
||||||
|
4. Архитектура в Header — неизменяема.
|
||||||
|
5. Сложность фрактала ограничена: модуль < 300 строк.
|
||||||
|
|
||||||
**OBJECTIVE:** Generate Python and Svelte/TypeScript code that strictly adheres to Semantic Coherence standards. Output must be machine-readable, fractal-structured, and optimized for Sparse Attention navigation.
|
II. СИНТАКСИС (ЖЕСТКИЙ ФОРМАТ)
|
||||||
|
ЯКОРЬ (Контейнер):
|
||||||
|
Начало: `# [DEF:id:Type]` (Python) | `<!-- [DEF:id:Type] -->` (Svelte)
|
||||||
|
Конец: `# [/DEF:id]` (ОБЯЗАТЕЛЬНО для аккумуляции)
|
||||||
|
Типы: Module, Class, Function, Component, Store.
|
||||||
|
|
||||||
## I. CORE REQUIREMENTS
|
ТЕГ (Метаданные):
|
||||||
1. **Causal Validity:** Semantic definitions (Contracts) must ALWAYS precede implementation code.
|
Вид: `# @KEY: Value` (внутри DEF, до кода).
|
||||||
2. **Immutability:** Architectural decisions defined in the Module/Component Header are treated as immutable constraints.
|
|
||||||
3. **Format Compliance:** Output must strictly follow the `[DEF:..:...]` / `[/DEF:...:...]` anchor syntax for structure.
|
|
||||||
4. **Logic over Assertion:** Contracts define the *logic flow*. Do not generate explicit `assert` statements unless requested. The code logic itself must inherently satisfy the Pre/Post conditions (e.g., via control flow, guards, or types).
|
|
||||||
5. **Fractal Complexity:** Modules and functions must adhere to strict size limits (~300 lines/module, ~30-50 lines/function) to maintain semantic focus.
|
|
||||||
|
|
||||||
---
|
ГРАФ (Связи):
|
||||||
|
Вид: `# @RELATION: PREDICATE -> TARGET_ID`
|
||||||
|
Предикаты: DEPENDS_ON, CALLS, INHERITS, IMPLEMENTS, DISPATCHES.
|
||||||
|
|
||||||
## II. SYNTAX SPECIFICATION
|
III. СТРУКТУРА ФАЙЛА
|
||||||
|
1. HEADER (Всегда первый):
|
||||||
|
[DEF:filename:Module]
|
||||||
|
@TIER: [CRITICAL|STANDARD|TRIVIAL] (Дефолт: STANDARD)
|
||||||
|
@SEMANTICS: [keywords]
|
||||||
|
@PURPOSE: [Главная цель]
|
||||||
|
@LAYER: [Domain/UI/Infra]
|
||||||
|
@RELATION: [Зависимости]
|
||||||
|
@INVARIANT: [Незыблемое правило]
|
||||||
|
|
||||||
Code structure is defined by **Anchors** (square brackets). Metadata is defined by **Tags** (native comment style).
|
2. BODY: Импорты -> Реализация.
|
||||||
|
3. FOOTER: [/DEF:filename]
|
||||||
|
|
||||||
### 1. Entity Anchors (The "Container")
|
IV. КОНТРАКТ (DBC)
|
||||||
Used to define the boundaries of Modules, Classes, Components, and Functions.
|
Расположение: Внутри [DEF], ПЕРЕД кодом.
|
||||||
|
Стиль Python: Комментарии `# @TAG`.
|
||||||
|
Стиль Svelte: JSDoc `/** @tag */`.
|
||||||
|
|
||||||
* **Python:**
|
Теги:
|
||||||
* Start: `# [DEF:identifier:Type]`
|
@PURPOSE: Суть (High Entropy).
|
||||||
* End: `# [/DEF:identifier:Type]`
|
@PRE: Входные условия.
|
||||||
* **Svelte (Top-level):**
|
@POST: Гарантии выхода.
|
||||||
* Start: `<!-- [DEF:ComponentName:Component] -->`
|
@SIDE_EFFECT: Мутации, IO.
|
||||||
* End: `<!-- [/DEF:ComponentName:Component] -->`
|
|
||||||
* **Svelte (Script/JS/TS):**
|
|
||||||
* Start: `// [DEF:funcName:Function]`
|
|
||||||
* End: `// [/DEF:funcName:Function]`
|
|
||||||
|
|
||||||
**Types:** `Module`, `Component`, `Class`, `Function`, `Store`, `Action`.
|
|
||||||
|
|
||||||
### 2. Graph Relations (The "Map")
|
|
||||||
Defines high-level dependencies.
|
|
||||||
* **Python Syntax:** `# @RELATION: TYPE -> TARGET_ID`
|
|
||||||
* **Svelte/JS Syntax:** `// @RELATION: TYPE -> TARGET_ID`
|
|
||||||
* **Types:** `DEPENDS_ON`, `CALLS`, `INHERITS_FROM`, `IMPLEMENTS`, `BINDS_TO`, `DISPATCHES`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## III. FILE STRUCTURE STANDARD
|
|
||||||
|
|
||||||
### 1. Python Module Header (`.py`)
|
|
||||||
```python
|
|
||||||
# [DEF:module_name:Module]
|
|
||||||
#
|
|
||||||
# @SEMANTICS: [keywords for vector search]
|
|
||||||
# @PURPOSE: [Primary responsibility of the module]
|
|
||||||
# @LAYER: [Domain/Infra/API]
|
|
||||||
# @RELATION: [Dependencies]
|
|
||||||
#
|
|
||||||
# @INVARIANT: [Global immutable rule]
|
|
||||||
# @CONSTRAINT: [Hard restriction, e.g., "No ORM calls here"]
|
|
||||||
|
|
||||||
# [SECTION: IMPORTS]
|
|
||||||
...
|
|
||||||
# [/SECTION]
|
|
||||||
|
|
||||||
# ... IMPLEMENTATION ...
|
|
||||||
|
|
||||||
# [/DEF:module_name:Module]
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Svelte Component Header (`.svelte`)
|
|
||||||
```html
|
|
||||||
<!-- [DEF:ComponentName:Component] -->
|
|
||||||
<!--
|
|
||||||
@SEMANTICS: [keywords]
|
|
||||||
@PURPOSE: [Primary UI responsibility]
|
|
||||||
@LAYER: [Feature/Atom/Layout]
|
|
||||||
@RELATION: [Child components, Stores]
|
|
||||||
|
|
||||||
@INVARIANT: [UI rules, e.g., "Always responsive"]
|
|
||||||
-->
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
// [SECTION: IMPORTS]
|
|
||||||
// ...
|
|
||||||
// [/SECTION: IMPORTS]
|
|
||||||
|
|
||||||
// ... LOGIC IMPLEMENTATION ...
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<!-- [SECTION: TEMPLATE] -->
|
|
||||||
...
|
|
||||||
<!-- [/SECTION: TEMPLATE] -->
|
|
||||||
|
|
||||||
<style>
|
|
||||||
/* ... */
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<!-- [/DEF:ComponentName:Component] -->
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
## IV. CONTRACTS (Design by Contract & Semantic Control)
|
V. АДАПТАЦИЯ (TIERS)
|
||||||
|
Определяется тегом `@TIER` в Header.
|
||||||
|
|
||||||
Contracts are the **Source of Truth** and the **Control Vector** for the code. They must be written with high **semantic density** to ensure the LLM fully "understands" the function's role within the larger Graph without needing to read the implementation body.
|
1. CRITICAL (Core/Security):
|
||||||
|
- Требование: Полный контракт, Граф (@RELATION), Инварианты (@INVARIANT), Строгие Логи.
|
||||||
|
2. STANDARD (BizLogic/UI):
|
||||||
|
- Требование: Базовый контракт (@PURPOSE), Логи, @RELATION (если есть связи).
|
||||||
|
3. TRIVIAL (DTO/Utils):
|
||||||
|
- Требование: Только Якоря [DEF] и @PURPOSE. Логи и Граф не обязательны.
|
||||||
|
|
||||||
### 1. The Anatomy of a Semantic Contract
|
VI. ЛОГИРОВАНИЕ (BELIEF STATE)
|
||||||
|
Цель: Трассировка для самокоррекции.
|
||||||
|
Python: Context Manager `with belief_scope("ID"):`.
|
||||||
|
Svelte: `console.log("[ID][STATE] Msg")`.
|
||||||
|
Состояния: Entry -> Action -> Coherence:OK / Failed -> Exit.
|
||||||
|
|
||||||
Every contract must answer three questions for the AI Agent:
|
VII. АЛГОРИТМ ГЕНЕРАЦИИ
|
||||||
1. **Intent:** *Why* does this exist? (Vector alignment)
|
1. АНАЛИЗ. Оцени TIER и слой.
|
||||||
2. **Boundaries:** *What* are the constraints? (Pre/Post/Invariants)
|
2. КАРКАС. Создай `[DEF]`, Header и Контракты.
|
||||||
3. **Dynamics:** *How* does it change the system state? (Side Effects/Graph)
|
3. РЕАЛИЗАЦИЯ. Напиши логику, удовлетворяющую Контракту.
|
||||||
|
4. ЗАМЫКАНИЕ. Закрой все `[/DEF]`.
|
||||||
|
|
||||||
#### Standard Tags Taxonomy:
|
ЕСЛИ ошибка или противоречие -> СТОП. Выведи `[COHERENCE_CHECK_FAILED]`.
|
||||||
* `@PURPOSE`: (**Mandatory**) A concise, high-entropy summary of functionality.
|
|
||||||
* `@PRE`: (**Mandatory**) Conditions required *before* execution. Defines the valid input space.
|
|
||||||
* `@POST`: (**Mandatory**) Conditions guaranteed *after* execution. Defines the valid output space.
|
|
||||||
* `@PARAM`: Input definitions with strict typing.
|
|
||||||
* `@RETURN`: Output definition.
|
|
||||||
* `@THROW`: Explicit failure modes.
|
|
||||||
* `@SIDE_EFFECT`: (**Critical**) Explicitly lists external state mutations (DB writes, UI updates, events). Vital for "Mental Modeling".
|
|
||||||
* `@INVARIANT`: (**Optional**) Local rules that hold true throughout the function execution.
|
|
||||||
* `@ALGORITHM`: (**Optional**) For complex logic, briefly describes the strategy (e.g., "Two-pointer approach", "Retry with exponential backoff").
|
|
||||||
* `@RELATION`: (**Graph**) Edges to other nodes (`CALLS`, `DISPATCHES`, `DEPENDS_ON`).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 2. Python Contract Style (`.py`)
|
|
||||||
Uses structured comment blocks inside the anchor. Focuses on type hints and logic flow guards.
|
|
||||||
|
|
||||||
```python
|
|
||||||
# [DEF:process_order_batch:Function]
|
|
||||||
# @PURPOSE: Orchestrates the validation and processing of a batch of orders.
|
|
||||||
# Ensures atomic processing per order (failure of one does not stop others).
|
|
||||||
#
|
|
||||||
# @PRE: batch_id must be a valid UUID string.
|
|
||||||
# @PRE: orders list must not be empty.
|
|
||||||
# @POST: Returns a dict mapping order_ids to their processing status (Success/Failed).
|
|
||||||
# @INVARIANT: The length of the returned dict must equal the length of input orders.
|
|
||||||
#
|
|
||||||
# @PARAM: batch_id (str) - The unique identifier for the batch trace.
|
|
||||||
# @PARAM: orders (List[OrderDTO]) - List of immutable order objects.
|
|
||||||
# @RETURN: Dict[str, OrderStatus] - Result map.
|
|
||||||
#
|
|
||||||
# @SIDE_EFFECT: Writes audit logs to DB.
|
|
||||||
# @SIDE_EFFECT: Publishes 'ORDER_PROCESSED' event to MessageBus.
|
|
||||||
#
|
|
||||||
# @RELATION: CALLS -> InventoryService.reserve_items
|
|
||||||
# @RELATION: CALLS -> PaymentGateway.authorize
|
|
||||||
# @RELATION: WRITES_TO -> Database.AuditLog
|
|
||||||
def process_order_batch(batch_id: str, orders: List[OrderDTO]) -> Dict[str, OrderStatus]:
|
|
||||||
# 1. Structural Guard Logic (Handling @PRE)
|
|
||||||
if not orders:
|
|
||||||
return {}
|
|
||||||
|
|
||||||
# 2. Implementation with @INVARIANT in mind
|
|
||||||
results = {}
|
|
||||||
|
|
||||||
for order in orders:
|
|
||||||
# ... logic ...
|
|
||||||
pass
|
|
||||||
|
|
||||||
# 3. Completion (Logic naturally satisfies @POST)
|
|
||||||
return results
|
|
||||||
# [/DEF:process_order_batch:Function]
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. Svelte/JS Contract Style (JSDoc++)
|
|
||||||
Uses enhanced JSDoc. Since JS is less strict than Python, the contract acts as a strict typing and behavioral guard.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// [DEF:handleUserLogin:Function]
|
|
||||||
/**
|
|
||||||
* @purpose Authenticates the user and synchronizes the local UI state.
|
|
||||||
* Handles the complete lifecycle from form submission to redirection.
|
|
||||||
*
|
|
||||||
* @pre LoginForm must be valid (validated by UI constraints).
|
|
||||||
* @pre Network must be available (optimistic check).
|
|
||||||
* @post SessionStore contains a valid JWT token.
|
|
||||||
* @post User is redirected to the Dashboard.
|
|
||||||
*
|
|
||||||
* @param {LoginCredentials} credentials - Email and password object.
|
|
||||||
* @returns {Promise<void>}
|
|
||||||
* @throws {NetworkError} If API is unreachable.
|
|
||||||
* @throws {AuthError} If credentials are invalid (401).
|
|
||||||
*
|
|
||||||
* @side_effect Updates global $session store.
|
|
||||||
* @side_effect Clears any existing error toasts.
|
|
||||||
*
|
|
||||||
* @algorithm 1. Set loading state -> 2. API Call -> 3. Decode Token -> 4. Update Store -> 5. Redirect.
|
|
||||||
*/
|
|
||||||
// @RELATION: CALLS -> api.auth.login
|
|
||||||
// @RELATION: MODIFIES_STATE_OF -> stores.session
|
|
||||||
// @RELATION: DISPATCHES -> 'toast:success'
|
|
||||||
async function handleUserLogin(credentials) {
|
|
||||||
// 1. Guard Clause (@PRE)
|
|
||||||
if (!isValid(credentials)) return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
// ... logic ...
|
|
||||||
} catch (e) {
|
|
||||||
// Error handling (@THROW)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// [/DEF:handleUserLogin:Function]
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 4. Semantic Rules for Contracts
|
|
||||||
1. **Completeness:** A developer (or Agent) must be able to write the function body *solely* by reading the Contract, without guessing.
|
|
||||||
2. **No Implementation Leakage:** Describe *what* happens, not *how* (unless using `@ALGORITHM` for complexity reasons). E.g., say "Persists user" instead of "Inserts into users table via SQL".
|
|
||||||
3. **Active Voice:** Use active verbs (`Calculates`, `Updates`, `Enforces`) to stronger vector alignment.
|
|
||||||
4. **Graph Connectivity:** The `@RELATION` tags must explicitly link to other `[DEF:...]` IDs existing in the codebase. This builds the navigation graph for RAG.
|
|
||||||
---
|
|
||||||
|
|
||||||
## V. LOGGING STANDARD (BELIEF STATE)
|
|
||||||
|
|
||||||
Logs delineate the agent's internal state.
|
|
||||||
|
|
||||||
* **Python:** MUST use a Context Manager (e.g., `with belief_scope("ANCHOR_ID"):`) to ensure state consistency and automatic handling of Entry/Exit/Error states.
|
|
||||||
* Manual logging (inside scope): `logger.info(f"[{ANCHOR_ID}][{STATE}] Msg")`
|
|
||||||
* **Svelte/JS:** `console.log(\`[${ANCHOR_ID}][${STATE}] Msg\`)`
|
|
||||||
|
|
||||||
**Required States:**
|
|
||||||
1. `Entry` (Start of block - Auto-logged by Context Manager)
|
|
||||||
2. `Action` (Key business logic - Manual log)
|
|
||||||
3. `Coherence:OK` (Logic successfully completed - Auto-logged by Context Manager)
|
|
||||||
4. `Coherence:Failed` (Exception/Error - Auto-logged by Context Manager)
|
|
||||||
5. `Exit` (End of block - Auto-logged by Context Manager)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## VI. FRACTAL COMPLEXITY LIMIT
|
|
||||||
|
|
||||||
To maintain semantic coherence and avoid "Attention Sink" issues:
|
|
||||||
* **Module Size:** If a Module body exceeds ~300 lines (or logical complexity), it MUST be refactored into sub-modules or a package structure.
|
|
||||||
* **Function Size:** Functions should fit within a standard attention "chunk" (approx. 30-50 lines). If larger, logic MUST be decomposed into helper functions with their own contracts.
|
|
||||||
|
|
||||||
This ensures every vector embedding remains sharp and focused.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## VII. GENERATION WORKFLOW
|
|
||||||
|
|
||||||
1. **Context Analysis:** Identify language (Python vs Svelte) and Architecture Layer.
|
|
||||||
2. **Scaffolding:** Generate the `[DEF:...:...]` Anchors and Header/Contract **before** writing any logic.
|
|
||||||
3. **Implementation:** Write the code. Ensure the code logic handles the `@PRE` conditions (e.g., via `if/return` or guards) and satisfies `@POST` conditions naturally. **Do not write explicit `assert` statements unless debugging mode is requested.**
|
|
||||||
4. **Closure:** Ensure every `[DEF:...:...]` is closed with `[/DEF:...:...]` to accumulate semantic context.
|
|
||||||
Reference in New Issue
Block a user