fix
This commit is contained in:
@@ -8,8 +8,8 @@ description: Structured logging protocol for agent-driven development, based on
|
||||
@RELATION DEPENDS_ON -> [Std.Semantics.Core]
|
||||
@RELATION DISPATCHES -> [Std.Semantics.Python]
|
||||
@RELATION DISPATCHES -> [Std.Semantics.Svelte]
|
||||
@RATIONALE Long CoT chains need stabilisation through explicit reasoning bonds. The three-marker system (REASON/REFLECT/EXPLORE) maps directly to the molecular CoT paper and produces machine-readable execution traces that LLM agents can parse, analyse, and use for fine-tuning (MoLE-Syn bond distributions).
|
||||
@REJECTED Legacy Entry/Exit/Action/Coherence markers rejected — they are too generic, do not map to reasoning structure, and prevent traceability graph analysis. Plain-text logging rejected — JSON lines are mandatory for agent parsing.
|
||||
@RATIONALE Long CoT chains need stabilisation through explicit reasoning bonds. The three-marker system (REASON/REFLECT/EXPLORE) maps directly to the molecular CoT paper and produces machine-readable execution traces that LLM agents can parse, analyse, and use for fine-tuning (MoLE-Syn bond distributions). Without structured markers, agent-generated code exhibits invisible failures: a function returns `None` instead of raising — the agent's attention never sees it because there's no log; a fallback path activates silently — no EXPLORE marker, no trace. JSON-line format ensures every log entry is a self-contained, parseable unit that survives log rotation, aggregation, and agent parsing — unlike plain-text logs that require regex heuristics.
|
||||
@REJECTED Legacy Entry/Exit/Action/Coherence markers rejected — they are too generic, do not map to reasoning structure, and prevent traceability graph analysis. Plain-text logging rejected — JSON lines are mandatory for agent parsing. Unstructured printf-style logging rejected — agents cannot reliably extract structured fields (trace_id, marker, intent) from free-form text, making automated diagnosis impossible.
|
||||
@DATA_CONTRACT LogEntry -> { ts: str, level: str, trace_id: str, span_id?: str, src: str, marker: REASON|REFLECT|EXPLORE, intent: str, payload?: object, error?: str }
|
||||
@INVARIANT Every log line MUST carry exactly one valid marker (REASON | REFLECT | EXPLORE). No markerless log lines in C4/C5 code.
|
||||
@INVARIANT trace_id MUST propagate via ContextVar across async boundaries. Every incoming request or background job seeds a new trace_id.
|
||||
|
||||
@@ -6,6 +6,10 @@ description: Methodology reference: Design by Contract enforcement, Fractal Deci
|
||||
#region Std.Semantics.Contracts [C:5] [TYPE Skill] [SEMANTICS methodology,contracts,adr,decision-memory,anti-erosion]
|
||||
@BRIEF HOW to enforce PRE/POST, write ADRs, prevent structural erosion, execute verifiable edit loops, and maintain anchor safety (anti-corruption) across Python + Svelte.
|
||||
@RELATION DEPENDS_ON -> [Std.Semantics.Core]
|
||||
@RELATION DISPATCHES -> [Std.Semantics.Python]
|
||||
@RELATION DISPATCHES -> [Std.Semantics.Svelte]
|
||||
@RATIONALE Design by Contract is the ONLY mechanism that prevents Transformer agents from silently corrupting code over long horizons. Without @PRE/@POST enforcement, agents optimize for token-likelihood rather than correctness — adding null checks where @PRE already guarantees non-null, re-implementing @REJECTED paths because KV-cache evicted the rejection, and growing functions past the CC=10 threshold because no structural limit is visible in the attention window. The anti-corruption protocol (§VIII) exists because a single broken #region/#endregion pair cascades silently through the entire semantic graph — rendering all downstream contracts invisible to every agent.
|
||||
@REJECTED Trusting agents to self-police code quality without contracts was rejected — they optimize for immediate token likelihood, not long-term invariants. Linter-only enforcement was rejected — linters cannot see cross-file dependency graphs or detect rejected-path regression. Implicit contracts (naming conventions alone) were rejected — without explicit @PRE/@POST in the attention-dense header region, agents default to their pre-trained behavior of adding defensive checks everywhere.
|
||||
|
||||
**Protocol Reference:** Tier definitions, tag catalog, and anchor syntax are defined in `semantics-core`. This skill assumes you have loaded it. All rules below reference `semantics-core` §III for tier semantics — tiers are descriptive, not tag-gating.
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ description: Reference manual for GRACE-Poly v2.6 — syntax formats, complexity
|
||||
@RELATION DISPATCHES -> [Std.Semantics.Python]
|
||||
@RELATION DISPATCHES -> [Std.Semantics.Svelte]
|
||||
@RELATION DISPATCHES -> [Std.Semantics.Testing]
|
||||
@RATIONALE GRACE-Poly exists because autoregressive Transformers suffer from four architectural defects that make them unreliable at scale: (1) KV-cache eviction — after ~8K tokens early context is lost, so decisions from file #1 are forgotten by file #4; (2) attention sink — in files >400 LOC attention weights diffuse, making nested structures invisible; (3) hallucination by design — when a dependency is missing the model confabulates a plausible one instead of signaling uncertainty; (4) copy-paste regression — similar code is duplicated including rejected patterns. The protocol's anchors, relations, and decision-memory tags form an external cognitive exoskeleton that survives context compression and provides structured navigation where raw prose fails.
|
||||
@REJECTED Trusting natural language comments for navigation was rejected — they lack syntactic density and are the first to be evicted under CSA compression. Docstring-only contracts were rejected — they are invisible to the semantic index and cannot be verified structurally. Ad-hoc conventions per agent were rejected — 44% orphan rate in this project proves that without a dedicated curator, the semantic graph degenerates within 3-4 sessions.
|
||||
|
||||
## 0. SSOT DECLARATION
|
||||
|
||||
@@ -152,4 +154,84 @@ All agents use Axiom MCP for GRACE-semantic operations. This is the canonical to
|
||||
- `skill({name="semantics-svelte"})` — Svelte 5 (Runes), UX state machines, Tailwind
|
||||
- `skill({name="semantics-testing"})` — pytest/vitest test constraints, external ontology
|
||||
|
||||
## VIII. ATTENTION ARCHITECTURE & OPTIMIZATION RULES
|
||||
|
||||
The GRACE anchor format is not arbitrary — it is optimized for the specific attention compression mechanisms in the underlying model (MLA → CSA → HCA → DSA → sliding window). Understanding these mechanisms is critical: a contract that violates these rules becomes invisible to the model after context compression, causing downstream hallucination.
|
||||
|
||||
### Attention Compression Pipeline
|
||||
|
||||
| Layer | Compression | Mechanism | What Survives | What Dies |
|
||||
|-------|:----------:|-----------|---------------|-----------|
|
||||
| **MLA** | 3.5× | KV vectors compressed to 576d latent codes. Information density per token is paramount. | Dense tokens (symbols, brackets, semantic tags). | Verbose prose, long descriptions. |
|
||||
| **CSA** | 4× + top‑k sparse | Every ~4 tokens pooled into 1 KV record. Only top‑k records selected per query. | Contracts in 1-2 anchor lines. | Contracts spread across 15+ lines — details lost in pooling. |
|
||||
| **HCA** | 128× | Aggressive pooling over distant context. Dense attention computed on compressed records. | Statistical signatures: hierarchical IDs (`Core.Auth.Login`), repeated `@SEMANTICS` keywords. | Flat IDs (`LoginFunction`) — become noise. One-off tag values. |
|
||||
| **DSA** | Lightning Indexer | Fast linear scorer estimates relevance of each compressed record to query keywords. | Records whose `@SEMANTICS` match query keywords. | Records with different naming than the query. |
|
||||
| **Sliding window** | None (preserved) | Small window of recent uncompressed tokens for local detail. | Contracts ≤150 lines fit entirely in the window. | Contracts >150 lines partially invisible. |
|
||||
|
||||
### ATTN_1 — FIRST-LINE DENSITY (CSA + MLA)
|
||||
|
||||
The opening anchor MUST pack maximum signal into one line:
|
||||
|
||||
```
|
||||
#region Domain.Sub.ContractId [C:N] [TYPE TypeName] [SEMANTICS tag1,tag2,tag3]
|
||||
```
|
||||
|
||||
- ID, complexity, type, and semantic tags on ONE line → survives CSA 4× pooling as a single KV record.
|
||||
- `@BRIEF` on line 2 is secondary — it may be pooled separately.
|
||||
- **NEVER** spread the anchor signature across multiple lines in a CSA-sensitive context.
|
||||
|
||||
### ATTN_2 — HIERARCHICAL IDS (HCA 128×)
|
||||
|
||||
Contract IDs MUST use dot-separated domain prefixes with 2-3 levels of hierarchy:
|
||||
|
||||
- `Core.Auth.Login` → after HCA 128×, `Core.Auth` survives as a statistical signature.
|
||||
- `Core.Auth.Session` → same domain group; `Auth` signature reinforced.
|
||||
- `users_login` → **dies** at 128×, indistinguishable from noise.
|
||||
|
||||
**Rule:** Every non-C1 contract ID carries at least 2 levels: `Domain.Name`. C1 contracts (DTOs, constants) inside a hierarchical parent module may use single-level IDs — the parent provides the domain context.
|
||||
|
||||
**Good:** `Core.Auth.Login`, `Migration.RunTask`, `Users.ListModel`, `Tasks.TaskCard`, `Test.Migration.RunTask`
|
||||
**Bad:** `login_handler`, `migrate`, `format_timestamp`, `UserListModel` (missing domain prefix)
|
||||
|
||||
**Stack disambiguation:** Use domain prefix, not stack prefix. The file path already encodes the stack (`backend/src/` vs `frontend/src/`):
|
||||
- Backend: `Core.Auth.Login`, `Api.Dashboards.List`, `Plugin.Translate.Execute`
|
||||
- Frontend: `Users.ListModel`, `Tasks.TaskCard`, `Dashboards.Hub`
|
||||
- Tests: `Test.Core.Auth`, `Test.Users.ListModel`
|
||||
|
||||
### ATTN_3 — SEMANTIC GROUPING (DSA Lightning Indexer)
|
||||
|
||||
The DSA Indexer scores compressed records by keyword match against the query. `@SEMANTICS` keywords are your index keys:
|
||||
|
||||
- All contracts in the `auth` domain MUST share `[SEMANTICS auth, ...]`.
|
||||
- `grep "@SEMANTICS.*auth"` → Indexer scores all auth records high.
|
||||
- If one auth contract uses `@SEMANTICS login` and another `@SEMANTICS authentication`, the Indexer may fail to group them.
|
||||
|
||||
**Rule:** Identical domain = identical primary keyword in `@SEMANTICS`. Secondary keywords can vary.
|
||||
|
||||
### ATTN_4 — FRACTAL BOUNDARIES (Sliding Window)
|
||||
|
||||
The sliding window preserves recent tokens without compression. A contract ≤150 lines fits entirely in the window and is fully visible to the attention mechanism:
|
||||
|
||||
- Contract ≤150 lines → guaranteed full visibility.
|
||||
- Module ≤400 lines → manageable in a few attention passes.
|
||||
- INV_7 (Module < 400 lines, CC ≤ 10) is not just a style rule — it ensures the model can physically see the entire contract structure.
|
||||
|
||||
### Grep Heuristics (Zombie Mode — when MCP tools are unavailable)
|
||||
|
||||
When Axiom MCP is down, these grep patterns exploit the DSA Indexer's keyword sensitivity:
|
||||
|
||||
```bash
|
||||
# Find all contracts in a domain (Indexer matches @SEMANTICS keywords)
|
||||
grep -r "@SEMANTICS.*<domain>" src/
|
||||
|
||||
# Find API type binding (cross-stack traceability)
|
||||
grep -r "@DATA_CONTRACT.*<ModelName>" src/
|
||||
|
||||
# Extract full contract body (awk, respecting fractal boundaries)
|
||||
awk '/#region <ContractID>/,/#endregion <ContractID>/' file.py
|
||||
|
||||
# Find all contracts BIND_TO a store
|
||||
grep -r "BINDS_TO.*\[<StoreId>\]" src/
|
||||
```
|
||||
|
||||
#endregion Std.Semantics.Core
|
||||
|
||||
@@ -7,7 +7,10 @@ description: Python-specific GRACE-Poly protocol: few-shot complexity examples,
|
||||
@BRIEF Python-specific HOW: few-shot complexity examples, belief runtime patterns, module decomposition, and FastAPI/SQLAlchemy conventions for the GRACE-Poly protocol in ss-tools.
|
||||
@RELATION DEPENDS_ON -> [Std.Semantics.Core]
|
||||
@RELATION DEPENDS_ON -> [Std.Semantics.Contracts]
|
||||
@RELATION DISPATCHES -> [MolecularCoTLogging]
|
||||
@RESTRICTION EXAMPLES ONLY — this file provides language-specific code patterns. All protocol rules (tier definitions, tag catalog, anchor syntax) are defined exclusively in `semantics-core`. This file MUST NOT redefine or contradict any rule from `semantics-core`.
|
||||
@RATIONALE Python's async/await model, FastAPI dependency injection, and SQLAlchemy session management create unique failure modes for Transformer agents: (1) async/await boundary confusion — agents write sync code in async contexts or forget `await` on ORM calls, producing silent no-ops; (2) dependency injection blindness — FastAPI's `Depends()` creates implicit call graphs that the agent's attention cannot trace without explicit @RELATION edges; (3) session lifecycle drift — SQLAlchemy sessions have strict boundaries that agents violate by passing detached objects across function calls. Concrete examples at each complexity tier act as few-shot anchors that override the agent's pre-trained (and often wrong) Python patterns.
|
||||
@REJECTED Generic Python patterns without GRACE anchors were rejected — agents produce working code that violates module size limits (INV_7), omits belief runtime markers, and creates orphan contracts invisible to the semantic index. Relying on the agent's pre-trained FastAPI/SQLAlchemy knowledge without project-specific examples was rejected — ss-tools has specific conventions (trace_id propagation, plugin architecture, WebSocket logging) that general training data cannot capture.
|
||||
|
||||
## 0. WHEN TO USE THIS SKILL
|
||||
|
||||
@@ -54,34 +57,34 @@ def belief_scope(contract_id: str):
|
||||
|
||||
### C1 (Atomic) — DTOs, Pydantic schemas, simple constants
|
||||
```python
|
||||
# #region UserResponseSchema [C:1] [TYPE Class]
|
||||
# #region Users.UserResponseSchema [C:1] [TYPE Class]
|
||||
from pydantic import BaseModel
|
||||
|
||||
class UserResponseSchema(BaseModel):
|
||||
id: str
|
||||
username: str
|
||||
email: str
|
||||
# #endregion UserResponseSchema
|
||||
# #endregion Users.UserResponseSchema
|
||||
```
|
||||
|
||||
### C2 (Simple) — Pure functions, utility helpers
|
||||
```python
|
||||
# #region format_timestamp [C:2] [TYPE Function] [SEMANTICS time,formatting]
|
||||
# #region Time.FormatTimestamp [C:2] [TYPE Function] [SEMANTICS time,formatting]
|
||||
# @BRIEF Format a UTC datetime into a human-readable ISO-8601 string.
|
||||
from datetime import datetime
|
||||
|
||||
def format_timestamp(ts: datetime) -> str:
|
||||
return ts.strftime("%Y-%m-%dT%H:%M:%SZ")
|
||||
# #endregion format_timestamp
|
||||
# #endregion Time.FormatTimestamp
|
||||
```
|
||||
|
||||
### C3 (Flow) — Module with nested functions, service layer
|
||||
```python
|
||||
# #region dashboard_migration [C:3] [TYPE Module] [SEMANTICS migration,dashboard]
|
||||
# #region Migration.Dashboard [C:3] [TYPE Module] [SEMANTICS migration,dashboard]
|
||||
# @BRIEF Dashboard migration service — export/import dashboards with validation.
|
||||
# @LAYER Service
|
||||
|
||||
# #region migrate_dashboard [C:3] [TYPE Function] [SEMANTICS migration,dashboard]
|
||||
# #region Migration.Dashboard.Migrate [C:3] [TYPE Function] [SEMANTICS migration,dashboard]
|
||||
# @BRIEF Migrate a single dashboard from source to target Superset instance.
|
||||
# @RELATION DEPENDS_ON -> [SupersetClient]
|
||||
# @RELATION DEPENDS_ON -> [DashboardValidator]
|
||||
@@ -91,14 +94,14 @@ def migrate_dashboard(source_client, target_client, dashboard_id: str, db_mappin
|
||||
mapped = apply_db_mapping(dashboard, db_mapping)
|
||||
result = target_client.import_dashboard(mapped)
|
||||
return result
|
||||
# #endregion migrate_dashboard
|
||||
# #endregion Migration.Dashboard.Migrate
|
||||
|
||||
# #endregion dashboard_migration
|
||||
# #endregion Migration.Dashboard
|
||||
```
|
||||
|
||||
### C4 (Orchestration) — Stateful operations with belief runtime
|
||||
```python
|
||||
# #region run_migration_task [C:4] [TYPE Function] [SEMANTICS migration,task,state]
|
||||
# #region Migration.RunTask [C:4] [TYPE Function] [SEMANTICS migration,task,state]
|
||||
# @BRIEF Execute a full migration task with rollback capability and progress reporting.
|
||||
# @PRE Database connection is established. Task record exists with valid migration plan.
|
||||
# @POST Task status updated to COMPLETED or FAILED. Migration audit log written.
|
||||
@@ -107,35 +110,35 @@ def migrate_dashboard(source_client, target_client, dashboard_id: str, db_mappin
|
||||
# @RELATION DEPENDS_ON -> [MigrationService]
|
||||
# @RELATION DEPENDS_ON -> [WebSocketNotifier]
|
||||
async def run_migration_task(task_id: str, db_session) -> dict:
|
||||
log("run_migration_task", "REASON", "Starting migration task", {"task_id": task_id})
|
||||
log("Migration.RunTask", "REASON", "Starting migration task", {"task_id": task_id})
|
||||
task = await db_session.get(Task, task_id)
|
||||
if not task:
|
||||
log("run_migration_task", "EXPLORE", "Task not found", error="TaskNotFound")
|
||||
log("Migration.RunTask", "EXPLORE", "Task not found", error="TaskNotFound")
|
||||
raise TaskNotFoundError(task_id)
|
||||
try:
|
||||
task.status = "RUNNING"
|
||||
await db_session.commit()
|
||||
log("run_migration_task", "REASON", "Task status set to RUNNING", {"task_id": task_id})
|
||||
log("Migration.RunTask", "REASON", "Task status set to RUNNING", {"task_id": task_id})
|
||||
result = await execute_migration_plan(task.migration_plan)
|
||||
task.status = "COMPLETED"
|
||||
task.result = result
|
||||
await db_session.commit()
|
||||
await notify_frontend(task_id, "completed", result)
|
||||
log("run_migration_task", "REFLECT", "Migration completed successfully", {"task_id": task_id, "dashboards": len(result)})
|
||||
log("Migration.RunTask", "REFLECT", "Migration completed", {"task_id": task_id, "dashboards": len(result)})
|
||||
return result
|
||||
except Exception as e:
|
||||
log("run_migration_task", "EXPLORE", "Migration failed, rolling back", {"task_id": task_id}, error=str(e))
|
||||
log("Migration.RunTask", "EXPLORE", "Migration failed, rolling back", {"task_id": task_id}, error=str(e))
|
||||
task.status = "FAILED"
|
||||
task.error = str(e)
|
||||
await db_session.commit()
|
||||
await notify_frontend(task_id, "failed", {"error": str(e)})
|
||||
raise
|
||||
# #endregion run_migration_task
|
||||
# #endregion Migration.RunTask
|
||||
```
|
||||
|
||||
### C5 (Critical) — With decision memory
|
||||
```python
|
||||
# #region rebuild_index [C:5] [TYPE Function] [SEMANTICS indexing,recovery,semantic]
|
||||
# #region Index.Rebuild [C:5] [TYPE Function] [SEMANTICS indexing,recovery,semantic]
|
||||
# @BRIEF Rebuild the full semantic index from source with atomic swap and rollback.
|
||||
# @PRE Workspace root is accessible. Source files exist.
|
||||
# @POST New index atomically swapped; old preserved for rollback.
|
||||
@@ -149,19 +152,19 @@ async def run_migration_task(task_id: str, db_session) -> dict:
|
||||
# @REJECTED Incremental-only update was rejected — it leaves stale edges when contracts
|
||||
# are deleted; only full scan guarantees consistency.
|
||||
def rebuild_index(root_path: str) -> dict:
|
||||
log("rebuild_index", "REASON", "Scanning source files", {"root": root_path})
|
||||
log("Index.Rebuild", "REASON", "Scanning source files", {"root": root_path})
|
||||
contracts = []
|
||||
for filepath in scan_files(root_path):
|
||||
try:
|
||||
parsed = parse_contract(filepath)
|
||||
contracts.append(parsed)
|
||||
except Exception as e:
|
||||
log("rebuild_index", "EXPLORE", "Parse failure, skipping file", {"file": filepath}, error=str(e))
|
||||
log("Index.Rebuild", "EXPLORE", "Parse failure, skipping file", {"file": filepath}, error=str(e))
|
||||
snapshot = {"contracts": contracts, "timestamp": datetime.utcnow().isoformat()}
|
||||
write_checkpoint(root_path, snapshot)
|
||||
log("rebuild_index", "REFLECT", "Rebuild complete", {"contracts": len(contracts)})
|
||||
log("Index.Rebuild", "REFLECT", "Rebuild complete", {"contracts": len(contracts)})
|
||||
return snapshot
|
||||
# #endregion rebuild_index
|
||||
# #endregion Index.Rebuild
|
||||
```
|
||||
|
||||
## III. PYTHON MODULE PATTERNS
|
||||
@@ -196,7 +199,7 @@ backend/
|
||||
|
||||
### FastAPI route pattern
|
||||
```python
|
||||
# #region dashboard_routes [C:3] [TYPE Module] [SEMANTICS api,dashboard]
|
||||
# #region Api.Dashboards [C:3] [TYPE Module] [SEMANTICS api,dashboard]
|
||||
# @BRIEF Dashboard CRUD and migration API routes.
|
||||
# @RELATION DEPENDS_ON -> [DashboardService]
|
||||
# @RELATION DEPENDS_ON -> [AuthMiddleware]
|
||||
@@ -204,7 +207,7 @@ from fastapi import APIRouter, Depends
|
||||
|
||||
router = APIRouter(prefix="/api/dashboards", tags=["dashboards"])
|
||||
|
||||
# #region list_dashboards [C:2] [TYPE Function] [SEMANTICS api,query]
|
||||
# #region Dashboards.List [C:2] [TYPE Function] [SEMANTICS api,query]
|
||||
# @BRIEF List dashboards with optional filters.
|
||||
@router.get("/")
|
||||
async def list_dashboards(
|
||||
@@ -213,14 +216,14 @@ async def list_dashboards(
|
||||
service=Depends(get_dashboard_service)
|
||||
):
|
||||
return await service.list_dashboards(page, page_size)
|
||||
# #endregion list_dashboards
|
||||
# #endregion Dashboards.List
|
||||
|
||||
# #endregion dashboard_routes
|
||||
# #endregion Api.Dashboards
|
||||
```
|
||||
|
||||
### SQLAlchemy model pattern
|
||||
```python
|
||||
# #region Dashboard [C:1] [TYPE Class]
|
||||
# #region Models.Dashboard [C:1] [TYPE Class]
|
||||
from sqlalchemy import Column, String, DateTime, JSON
|
||||
from sqlalchemy.orm import declarative_base
|
||||
|
||||
@@ -232,7 +235,7 @@ class Dashboard(Base):
|
||||
title = Column(String, nullable=False)
|
||||
metadata = Column(JSON)
|
||||
created_at = Column(DateTime, server_default="now()")
|
||||
# #endregion Dashboard
|
||||
# #endregion Models.Dashboard
|
||||
```
|
||||
|
||||
## IV. PYTHON VERIFICATION
|
||||
|
||||
@@ -7,9 +7,10 @@ description: Svelte 5 (Runes) protocol for ss-tools: UX State Machines, Tailwind
|
||||
@BRIEF HOW to build Svelte 5 (Runes) Components for ss-tools with UX State Machines, Tailwind CSS, store topology, and visual-interactive validation.
|
||||
@RELATION DEPENDS_ON -> [Std.Semantics.Core]
|
||||
@RELATION DEPENDS_ON -> [MolecularCoTLogging]
|
||||
@RELATION DISPATCHES -> [Std.Semantics.Testing]
|
||||
@RESTRICTION EXAMPLES ONLY — this file provides language-specific code patterns. All protocol rules (tier definitions, tag catalog, anchor syntax) are defined exclusively in `semantics-core`. UX contract tags are defined here as examples; the tag catalog lives in `semantics-core` §III. This file MUST NOT redefine or contradict any rule from `semantics-core`.
|
||||
@RATIONALE Svelte 5 runes ($state, $derived, $effect, $props) chosen for reactive precision and native compiler optimisations over Svelte 4 legacy reactivity ($:). Tailwind CSS selected for zero-runtime utility-first styling and rapid visual validation via chrome-devtools MCP. FSM-based UX contracts (@UX_STATE, @UX_FEEDBACK, @UX_RECOVERY) chosen to create verifiable state-transition tests that the browser Judge Agent can execute deterministically. ss-tools internal API wrappers (fetchApi/requestApi) chosen over native fetch to enforce auth, error normalisation, and trace_id propagation.
|
||||
@REJECTED React (JSX) rejected — Svelte's compiler-first approach yields smaller bundles and native reactivity without virtual DOM overhead. Vue rejected — Svelte 5 runes provide simpler mental model. Legacy Svelte 4 syntax (export let, $:, on:event) rejected — incompatible with Svelte 5 runes mode. CSS Modules / styled-components rejected in favour of Tailwind's utility-first approach, which avoids style leakage and simplifies chrome-devtools visual diffing. Native fetch() rejected — bypasses ss-tools middleware chain (auth, trace_id, error normalisation). Plain-text logging rejected per MolecularCoTLogging §VII — JSON lines are mandatory for agent-parsable traces.
|
||||
@RATIONALE Svelte 5 runes ($state, $derived, $effect, $props) chosen for reactive precision and native compiler optimisations over Svelte 4 legacy reactivity ($:). Tailwind CSS selected for zero-runtime utility-first styling and rapid visual validation via chrome-devtools MCP. FSM-based UX contracts (@UX_STATE, @UX_FEEDBACK, @UX_RECOVERY) chosen to create verifiable state-transition tests that the browser Judge Agent can execute deterministically. ss-tools internal API wrappers (fetchApi/requestApi) chosen over native fetch to enforce auth, error normalisation, and trace_id propagation. Model-first architecture chosen because event-handler spaghetti is the #1 Transformer failure mode in UI code: the agent scatters logic across onclick/onchange in 5 files — KV-cache cannot hold cross-component relationships, creating invisible coupling that breaks silently.
|
||||
@REJECTED React (JSX) rejected — Svelte's compiler-first approach yields smaller bundles and native reactivity without virtual DOM overhead. Vue rejected — Svelte 5 runes provide simpler mental model. Legacy Svelte 4 syntax (export let, $:, on:event) rejected — incompatible with Svelte 5 runes mode and dominates agent training data, causing silent regression. CSS Modules / styled-components rejected in favour of Tailwind's utility-first approach, which avoids style leakage and simplifies chrome-devtools visual diffing. Native fetch() rejected — bypasses ss-tools middleware chain (auth, trace_id, error normalisation). Plain-text logging rejected per MolecularCoTLogging §VII — JSON lines are mandatory for agent-parsable traces. Component-first architecture for complex screens rejected — the Model-first approach (model.svelte.ts → component) keeps system logic in one file where the agent's attention can find it via grep + search_contracts.
|
||||
@INVARIANT Frontend components MUST be verifiable by the browser toolset via `chrome-devtools` MCP.
|
||||
@INVARIANT Use Tailwind CSS exclusively. Raw Tailwind color classes (`blue-600`, `green-500`, `red-600`, `gray-*`, `indigo-*`) are DEPRECATED in page and component code — use semantic tokens from `tailwind.config.js` only (`primary`, `destructive`, `success`, `warning`, `surface-*`, `border-*`, `text-*`).
|
||||
@INVARIANT Page-level UI MUST use `$lib/ui` atoms: `<Button>`, `<Card>`, `<Input>`, `<Select>`, `<PageHeader>`. Raw `<button>` elements and manual card `<div>` containers in page files are a violation.
|
||||
@@ -78,7 +79,7 @@ The component-first approach forces you to encode system logic in event handlers
|
||||
**What this means for you, the agent:**
|
||||
- **Findability:** grep `@semantics.*users` → all models related to users. The contract is single-source, not scattered across HTML.
|
||||
- **Testability:** Model invariants (`@INVARIANT changing filter resets pagination`) are verified in vitest without browser render — milliseconds, not seconds.
|
||||
- **CSA resilience:** `#region ModelName [C:N] [SEMANTICS ...]` on line 1 = maximum density for top‑k attention selection. Closing `#endregion ModelName` duplicates the identifier — safe after aggressive context compression.
|
||||
- **CSA resilience:** `#region Users.ListModel [C:N] [SEMANTICS ...]` on line 1 = maximum density for top‑k attention selection. Closing `#endregion Users.ListModel` duplicates the identifier — safe after aggressive context compression.
|
||||
- **Component simplicity:** When a component contains only `$state`, `$derived`, and `model.action()` calls, its contract is predictable. No guessing which side effect hides in `onchange`.
|
||||
|
||||
### Model Contract Template
|
||||
@@ -88,8 +89,8 @@ A Model is a **contract** — `#region ModelName [C:N] [TYPE Model] [SEMANTICS t
|
||||
Models use the **`.svelte.ts`** extension (not plain `.ts`) because they rely on Svelte 5 reactive primitives (`$state`, `$derived`). The Svelte compiler processes `.svelte.ts` files and transforms these runes into proper reactive code.
|
||||
|
||||
```typescript
|
||||
// frontend/src/lib/models/UserListModel.svelte.ts
|
||||
// #region UserListModel [C:4] [TYPE Model] [SEMANTICS users,list,screen-model]
|
||||
// frontend/src/lib/models/UsersListModel.svelte.ts
|
||||
// #region Users.ListModel [C:4] [TYPE Model] [SEMANTICS users,list,screen-model]
|
||||
// @BRIEF State model for the user list screen — declares atoms, invariants, and actions.
|
||||
// @INVARIANT Changing filter (search, role, status) resets pagination to page 1.
|
||||
// @INVARIANT Deleting a user removes it from the list and decrements total count atomically.
|
||||
@@ -130,7 +131,7 @@ interface UserListResponse {
|
||||
meta: { total: number };
|
||||
}
|
||||
|
||||
export class UserListModel {
|
||||
export class UsersListModel {
|
||||
// ── Atoms (reactive state, all typed) ──────────────────────────
|
||||
users: User[] = $state([]);
|
||||
totalCount: number = $state(0);
|
||||
@@ -204,44 +205,51 @@ export class UserListModel {
|
||||
}
|
||||
}
|
||||
}
|
||||
// #endregion UserListModel
|
||||
// #endregion Users.ListModel
|
||||
```
|
||||
|
||||
### Component Binds to Model (RSM pattern)
|
||||
|
||||
The component contract declares: `@RELATION BINDS_TO -> [ModelId]`. The component code is minimal — it renders model state and calls `model.action()` on user intent. No side-effect logic lives in event handlers.
|
||||
|
||||
For route-level data loading, use SvelteKit `load()` in `+page.ts` — NOT `$effect` (per §0: `$effect` is for browser-side side effects only).
|
||||
|
||||
```svelte
|
||||
<!-- #region UserListPage [C:3] [TYPE Component] [SEMANTICS users,list,page] -->
|
||||
<!-- @BRIEF User list page — renders UserListModel state, delegates all logic to the model. -->
|
||||
<!-- @RELATION BINDS_TO -> [UserListModel] -->
|
||||
<!-- #region Users.ListPage [C:3] [TYPE Component] [SEMANTICS users,list,page] -->
|
||||
<!-- @BRIEF User list page — renders Users.ListModel state, delegates all logic to the model. -->
|
||||
<!-- @RELATION BINDS_TO -> [Users.ListModel] -->
|
||||
<!-- @UX_TEST: Loaded -> {click: "delete", expected: User removed, count decremented}. -->
|
||||
<script>
|
||||
import { UserListModel } from "./UserListModel.js";
|
||||
const model = new UserListModel();
|
||||
// Initial load
|
||||
$effect(() => { model.loadPage(1); });
|
||||
<script lang="ts">
|
||||
import { onMount } from "svelte";
|
||||
import { Button } from "$lib/ui";
|
||||
import { UsersListModel } from "./UsersListModel.svelte.ts";
|
||||
|
||||
const model = new UsersListModel();
|
||||
|
||||
onMount(() => {
|
||||
model.loadPage(1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="max-w-7xl mx-auto px-4 py-6">
|
||||
{#if model.screenState === "error"}
|
||||
<div role="alert" class="text-red-600">{model.error}</div>
|
||||
<button onclick={() => model.retry()}>Retry</button>
|
||||
<div role="alert" class="text-destructive">{model.error}</div>
|
||||
<Button variant="primary" size="sm" onclick={() => model.retry()}>Retry</Button>
|
||||
{:else if model.screenState === "empty"}
|
||||
<p class="text-gray-500">No users found.</p>
|
||||
<p class="text-text-muted">No users found.</p>
|
||||
{:else}
|
||||
<ul>
|
||||
{#each model.users as user (user.id)}
|
||||
<li>
|
||||
{user.name}
|
||||
<button onclick={() => model.deleteUser(user.id)}>Delete</button>
|
||||
<Button variant="ghost" size="sm" onclick={() => model.deleteUser(user.id)}>Delete</Button>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
<nav>Page {model.page} of {model.totalPages}</nav>
|
||||
{/if}
|
||||
</div>
|
||||
<!-- #endregion UserListPage -->
|
||||
<!-- #endregion Users.ListPage -->
|
||||
```
|
||||
|
||||
### Searching for Models
|
||||
@@ -273,10 +281,10 @@ Models accumulate methods as features grow. To prevent "god object" anti-pattern
|
||||
| Model > **400 lines** | Decompose — extract domain helpers or split into submodels |
|
||||
| Model > **40 public methods** | Split into submodels by responsibility (e.g. `FiltersModel`, `SelectionModel`, `GitActionsModel`) |
|
||||
|
||||
**Submodel split example for DashboardHubModel:**
|
||||
- `DashboardFiltersModel` — search, column filters, sort
|
||||
- `DashboardSelectionModel` — checkbox, select all/visible, bulk actions
|
||||
- `DashboardGitActionsModel` — git init, sync, commit, pull, push
|
||||
**Submodel split example for `Dashboards.Hub`:**
|
||||
- `Dashboards.FiltersModel` — search, column filters, sort
|
||||
- `Dashboards.SelectionModel` — checkbox, select all/visible, bulk actions
|
||||
- `Dashboards.GitActionsModel` — git init, sync, commit, pull, push
|
||||
|
||||
Before decomposition, the model MUST carry `@INVARIANT DECOMPOSITION GATE` with the split plan and line count.
|
||||
|
||||
@@ -293,36 +301,7 @@ Before decomposition, the model MUST carry `@INVARIANT DECOMPOSITION GATE` with
|
||||
|
||||
## V. LOGGING (MOLECULAR-COT FOR UI)
|
||||
|
||||
Frontend logging uses `log()` from `$lib/cot-logger`, emitting **JSON lines** per MolecularCoTLogging protocol.
|
||||
Import: `import { log } from "$lib/cot-logger";`
|
||||
|
||||
The logger is a TypeScript module at `frontend/src/lib/cot-logger.ts` with full type support:
|
||||
|
||||
```typescript
|
||||
import { log } from "$lib/cot-logger";
|
||||
|
||||
// Before an operation:
|
||||
log("ComponentName", "REASON", "What we are about to do", { param: value });
|
||||
|
||||
// After successful verification:
|
||||
log("ComponentName", "REFLECT", "Operation completed", { result: value });
|
||||
|
||||
// On error or fallback:
|
||||
log("ComponentName", "EXPLORE", "Operation failed", { param: value }, "Error description");
|
||||
```
|
||||
|
||||
### Marker Reference
|
||||
|
||||
| Marker | When | Signature |
|
||||
|--------|------|-----------|
|
||||
| `REASON` | BEFORE API call or state mutation | `log("ComponentID", "REASON", "intent", payload)` |
|
||||
| `REFLECT` | AFTER successful operation (verification) | `log("ComponentID", "REFLECT", "outcome", payload)` |
|
||||
| `EXPLORE` | ON error, fallback, or violated assumption | `log("ComponentID", "EXPLORE", "message", payload, error="...")` |
|
||||
|
||||
### Invariants
|
||||
- Every log line is a **single JSON object** — no plain-text prefixes.
|
||||
- `trace_id` propagates from HTTP response headers via the ss-tools API wrappers.
|
||||
- One marker per line. No markerless log lines in C4/C5 components.
|
||||
Frontend logging uses `log()` from `$lib/cot-logger` per **MolecularCoTLogging** protocol. Import: `import { log } from "$lib/cot-logger"`. Full wire-format spec, marker reference, and invariants → `molecular-cot-logging` skill §I-VII.
|
||||
|
||||
## VI. CANONICAL SVELTE 5 COMPONENT TEMPLATE (SS-TOOLS)
|
||||
|
||||
@@ -499,84 +478,7 @@ bg-indigo-50 text-indigo-700 bg-gradient-to-br from-slate-50 via-white to-sky-50
|
||||
|
||||
**Rule:** Model invariants MUST be verified without render. UX contracts MAY use render + browser. This eliminates the confusion that slows down the feedback loop — a filter-reset invariant doesn't need a DOM.
|
||||
|
||||
### Model Invariant Tests (No Render)
|
||||
|
||||
```javascript
|
||||
// #region UserListModelTests [C:3] [TYPE Module] [SEMANTICS test,model]
|
||||
// @BRIEF Verify UserListModel @INVARIANT guarantees without DOM rendering.
|
||||
// @RELATION BINDS_TO -> [UserListModel]
|
||||
// @TEST_INVARIANT: filter-resets-pagination -> VERIFIED_BY: [test_filter_resets_pagination]
|
||||
// @TEST_INVARIANT: atomic-delete -> VERIFIED_BY: [test_delete_removes_user_and_decrements]
|
||||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
import { UserListModel } from "../UserListModel.js";
|
||||
|
||||
describe("UserListModel invariants", () => {
|
||||
let model;
|
||||
|
||||
beforeEach(() => {
|
||||
vi.mock("$lib/api", () => ({
|
||||
requestApi: vi.fn().mockResolvedValue({ data: [], meta: { total: 0 } })
|
||||
}));
|
||||
model = new UserListModel();
|
||||
});
|
||||
|
||||
// @INVARIANT: Changing filter resets pagination to page 1.
|
||||
it("resets page to 1 when filter changes", () => {
|
||||
model.page = 5;
|
||||
model.setFilter("role", "admin");
|
||||
expect(model.page).toBe(1);
|
||||
});
|
||||
|
||||
it("resets page to 1 on search", () => {
|
||||
model.page = 3;
|
||||
model.search("john");
|
||||
expect(model.page).toBe(1);
|
||||
});
|
||||
|
||||
// @INVARIANT: Deleting a user removes it and decrements count atomically.
|
||||
it("removes user and decrements count on delete", async () => {
|
||||
model.users = [{ id: "1", name: "Alice" }, { id: "2", name: "Bob" }];
|
||||
model.totalCount = 2;
|
||||
vi.mocked(requestApi).mockResolvedValueOnce({ ok: true });
|
||||
await model.deleteUser("1");
|
||||
expect(model.users).toEqual([{ id: "2", name: "Bob" }]);
|
||||
expect(model.totalCount).toBe(1);
|
||||
});
|
||||
|
||||
// Hardcoded fixture — no logic mirror
|
||||
it("reports empty state when API returns no results", async () => {
|
||||
vi.mocked(requestApi).mockResolvedValueOnce({ data: [], meta: { total: 0 } });
|
||||
await model._fetch();
|
||||
expect(model.screenState).toBe("empty");
|
||||
expect(model.users).toEqual([]);
|
||||
});
|
||||
});
|
||||
// #endregion UserListModelTests
|
||||
```
|
||||
|
||||
### Component UX Tests (With Render)
|
||||
|
||||
```javascript
|
||||
// #region MigrationTaskCardTests [C:1] [TYPE Module]
|
||||
import { render, screen, fireEvent } from "@testing-library/svelte";
|
||||
import { describe, it, expect, vi } from "vitest";
|
||||
import MigrationTaskCard from "./MigrationTaskCard.svelte";
|
||||
|
||||
describe("MigrationTaskCard", () => {
|
||||
it("renders dashboard name and environments", () => {
|
||||
render(MigrationTaskCard, {
|
||||
props: { taskId: "1", dashboardName: "Sales", sourceEnv: "dev", targetEnv: "prod" }
|
||||
});
|
||||
expect(screen.getByText("Sales")).toBeTruthy();
|
||||
expect(screen.getByText(/dev.*prod/)).toBeTruthy();
|
||||
});
|
||||
|
||||
it("shows loading state when action clicked", async () => {
|
||||
// ... button click → loading assertion
|
||||
});
|
||||
});
|
||||
// #endregion MigrationTaskCardTests
|
||||
```
|
||||
Full test templates (Model invariant + Component UX) → `semantics-testing` §VI-VII.
|
||||
|
||||
## IX. FRONTEND VERIFICATION
|
||||
|
||||
|
||||
@@ -6,9 +6,10 @@ description: Core protocol for Test Constraints, External Ontology, Graph Noise
|
||||
#region Std.Semantics.Testing [C:5] [TYPE Skill] [SEMANTICS testing,qa,verification,pytest,vitest]
|
||||
@BRIEF HOW to write tests: constraints, external ontology, graph noise reduction, and invariant traceability for pytest and vitest.
|
||||
@RELATION DEPENDS_ON -> [Std.Semantics.Core]
|
||||
@RELATION DEPENDS_ON -> [Std.Semantics.Svelte]
|
||||
@INVARIANT Test modules must trace back to production @INVARIANT tags without flooding the Semantic Graph with orphan nodes.
|
||||
@RATIONALE Test contracts trace to production @INVARIANT/@POST tags via @TEST_INVARIANT, preventing orphan nodes. pytest+vitest dual stack eliminates cross-language tooling overhead. 3-edge-case floor balances coverage sufficiency against graph noise. Hardcoded fixtures block logic-mirror tautology (dominant LLM test-generation failure mode).
|
||||
@REJECTED Property-based testing — non-deterministic input space creates unbounded graph edges, irreducible to fixed-scenario tracing. Snapshot testing — brittle to CSS/UI changes without invariant signal. Integration-only (no unit tests) — coarse graph edges miss localized @INVARIANT violations. Cucumber/Gherkin BDD — DSL layer breaks direct traceability to Python/Svelte @POST anchors.
|
||||
@RATIONALE Test contracts trace to production @INVARIANT/@POST tags via @TEST_INVARIANT, preventing orphan nodes. pytest+vitest dual stack eliminates cross-language tooling overhead. 3-edge-case floor balances coverage sufficiency against graph noise. Hardcoded fixtures block logic-mirror tautology — the dominant LLM test-generation failure mode where the agent re-implements the production algorithm inside the test as `expected = compute(x)`. The test always passes but proves nothing because it's a copy of what it's testing.
|
||||
@REJECTED Property-based testing — non-deterministic input space creates unbounded graph edges, irreducible to fixed-scenario tracing. Snapshot testing — brittle to CSS/UI changes without invariant signal. Integration-only (no unit tests) — coarse graph edges miss localized @INVARIANT violations. Cucumber/Gherkin BDD — DSL layer breaks direct traceability to Python/Svelte @POST anchors. Dynamic expected-value computation — `expected = production_fn(x)` is a tautology, not a test; hardcoded fixtures are the only valid approach.
|
||||
|
||||
## 0. QA RATIONALE (LLM PHYSICS IN TESTING)
|
||||
|
||||
@@ -33,7 +34,7 @@ When writing code or tests that depend on 3rd-party libraries or shared schemas
|
||||
## II. TEST MARKUP ECONOMY (NOISE REDUCTION)
|
||||
|
||||
To prevent overwhelming Semantic Graph, test files operate under relaxed complexity rules:
|
||||
1. **Short IDs:** Test modules MUST use concise IDs (e.g., `TestDashboardMigration`), not full file paths.
|
||||
1. **Short hierarchical IDs:** Test modules use `Test.Domain.Name` format (e.g., `Test.Migration.RunTask`), not full file paths or flat names. This satisfies ATTN_2: the `Test.` prefix groups all tests under HCA 128× while the domain name provides DSA Indexer grouping.
|
||||
2. **Root Binding (`BINDS_TO`):** Do NOT map the internal call graph of a test file. Instead, anchor the entire test suite to the production module using: `@RELATION BINDS_TO -> [TargetModule]`.
|
||||
3. **Complexity 1 for Helpers:** Small test utilities (e.g., `_setup_mock`, `_build_payload`) are **C1**. They require ONLY the anchor pair. No `@BRIEF` or `@RELATION` allowed.
|
||||
4. **Complexity 2 for Tests:** Actual test functions (e.g., `test_unauthorized_access`) are **C2**. They require anchor + `@BRIEF`. Do not add `@PRE`/`@POST` to individual test functions.
|
||||
@@ -71,9 +72,9 @@ backend/tests/
|
||||
|
||||
### Test module template
|
||||
```python
|
||||
# #region TestDashboardMigration [C:3] [TYPE Module] [SEMANTICS test,migration]
|
||||
# #region Test.Migration.RunTask [C:3] [TYPE Module] [SEMANTICS test,migration]
|
||||
# @BRIEF Verify dashboard migration contracts — @POST guarantees and rejected paths.
|
||||
# @RELATION BINDS_TO -> [dashboard_migration]
|
||||
# @RELATION BINDS_TO -> [Migration.RunTask]
|
||||
# @TEST_EDGE: missing_db_mapping -> Migration fails with MappingError
|
||||
# @TEST_EDGE: invalid_dashboard_id -> Migration fails with NotFoundError
|
||||
# @TEST_EDGE: external_api_timeout -> Migration fails with TimeoutError, rolls back
|
||||
|
||||
Reference in New Issue
Block a user