few shots update

This commit is contained in:
2026-02-20 10:26:01 +03:00
parent fdcbe32dfa
commit d7e4919d54
5 changed files with 168 additions and 75 deletions

View File

@@ -1,7 +1,7 @@
# [DEF:Project_Knowledge_Map:Root] # [DEF:Project_Knowledge_Map:Root]
# @TIER: CRITICAL # @TIER: CRITICAL
# @PURPOSE: Global navigation map for AI-Agent (GRACE Knowledge Graph). # @PURPOSE: Global navigation map for AI-Agent (GRACE Knowledge Graph).
# @LAST_UPDATE: 2026-02-19 # @LAST_UPDATE: 2026-02-20
## 1. SYSTEM STANDARDS (Rules of the Game) ## 1. SYSTEM STANDARDS (Rules of the Game)
Strict policies and formatting rules. Strict policies and formatting rules.
@@ -26,6 +26,8 @@ Use these for code generation (Style Transfer).
* Ref: `.ai/shots/frontend_component.svelte` -> `[DEF:Shot:Svelte_Component]` * Ref: `.ai/shots/frontend_component.svelte` -> `[DEF:Shot:Svelte_Component]`
* **Plugin Module:** Reference implementation of a task plugin. * **Plugin Module:** Reference implementation of a task plugin.
* Ref: `.ai/shots/plugin_example.py` -> `[DEF:Shot:Plugin_Example]` * Ref: `.ai/shots/plugin_example.py` -> `[DEF:Shot:Plugin_Example]`
* **Critical Module:** Core banking transaction processor with ACID guarantees.
* Ref: `.ai/shots/critical_module.py` -> `[DEF:Shot:Critical_Module]`
## 3. DOMAIN MAP (Modules) ## 3. DOMAIN MAP (Modules)
* **Project Map:** `.ai/PROJECT_MAP.md` -> `[DEF:Project_Map]` * **Project Map:** `.ai/PROJECT_MAP.md` -> `[DEF:Project_Map]`

View File

@@ -1,14 +1,18 @@
# [DEF:Shot:FastAPI_Route:Example] # [DEF:BackendRouteShot:Module]
# @TIER: STANDARD
# @SEMANTICS: Route, Task, API, Async
# @PURPOSE: Reference implementation of a task-based route using GRACE-Poly. # @PURPOSE: Reference implementation of a task-based route using GRACE-Poly.
# @LAYER: Interface (API)
# @RELATION: IMPLEMENTS -> [DEF:Std:API_FastAPI] # @RELATION: IMPLEMENTS -> [DEF:Std:API_FastAPI]
# @INVARIANT: TaskManager must be available in dependency graph.
from typing import List, Dict, Any, Optional from typing import Dict, Any
from fastapi import APIRouter, Depends, HTTPException, status from fastapi import APIRouter, Depends, HTTPException, status
from pydantic import BaseModel from pydantic import BaseModel
from ...core.logger import belief_scope from ...core.logger import belief_scope
from ...core.task_manager import TaskManager, Task from ...core.task_manager import TaskManager, Task
from ...core.config_manager import ConfigManager from ...core.config_manager import ConfigManager
from ...dependencies import get_task_manager, get_config_manager, has_permission, get_current_user from ...dependencies import get_task_manager, get_config_manager, get_current_user
router = APIRouter() router = APIRouter()
@@ -21,37 +25,41 @@ class CreateTaskRequest(BaseModel):
# @PURPOSE: Create and start a new task using TaskManager. Non-blocking. # @PURPOSE: Create and start a new task using TaskManager. Non-blocking.
# @PARAM: request (CreateTaskRequest) - Plugin and params. # @PARAM: request (CreateTaskRequest) - Plugin and params.
# @PARAM: task_manager (TaskManager) - Async task executor. # @PARAM: task_manager (TaskManager) - Async task executor.
# @PARAM: config (ConfigManager) - Centralized configuration. # @PRE: plugin_id must match a registered plugin.
# @PRE: plugin_id must exist; config must be initialized.
# @POST: A new task is spawned; Task ID returned immediately. # @POST: A new task is spawned; Task ID returned immediately.
# @SIDE_EFFECT: Writes to DB, Trigger background worker.
async def create_task( async def create_task(
request: CreateTaskRequest, request: CreateTaskRequest,
task_manager: TaskManager = Depends(get_task_manager), task_manager: TaskManager = Depends(get_task_manager),
config: ConfigManager = Depends(get_config_manager), config: ConfigManager = Depends(get_config_manager),
current_user = Depends(get_current_user) current_user = Depends(get_current_user)
): ):
# RBAC: Dynamic permission check # Context Logging
has_permission(f"plugin:{request.plugin_id}", "EXECUTE")(current_user)
with belief_scope("create_task"): with belief_scope("create_task"):
try: try:
# 1. Action: Resolve setting using ConfigManager (Example) # 1. Action: Configuration Resolution
timeout = config.get("TASKS_DEFAULT_TIMEOUT", 3600) timeout = config.get("TASKS_DEFAULT_TIMEOUT", 3600)
# 2. Action: Spawn async task via TaskManager # 2. Action: Spawn async task
# @RELATION: CALLS -> task_manager.create_task # @RELATION: CALLS -> task_manager.create_task
task = await task_manager.create_task( task = await task_manager.create_task(
plugin_id=request.plugin_id, plugin_id=request.plugin_id,
params={**request.params, "timeout": timeout} params={**request.params, "timeout": timeout}
) )
return task return task
except ValueError as e:
# 3. Recovery: Domain logic error mapping
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=str(e)
)
except Exception as e: except Exception as e:
# Evaluation: Proper error mapping and logging # @UX_STATE: Error feedback -> 500 Internal Error
# @UX_STATE: Error feedback to frontend
raise HTTPException( raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Task creation failed: {str(e)}" detail="Internal Task Spawning Error"
) )
# [/DEF:create_task:Function] # [/DEF:create_task:Function]
# [/DEF:Shot:FastAPI_Route] # [/DEF:BackendRouteShot:Module]

View File

@@ -0,0 +1,79 @@
# [DEF:TransactionCore:Module]
# @TIER: CRITICAL
# @SEMANTICS: Finance, ACID, Transfer, Ledger
# @PURPOSE: Core banking transaction processor with ACID guarantees.
# @LAYER: Domain (Core)
# @RELATION: DEPENDS_ON -> [DEF:Infra:PostgresDB]
# @RELATION: DEPENDS_ON -> [DEF:Infra:AuditLog]
# @INVARIANT: Total system balance must remain constant (Double-Entry Bookkeeping).
# @INVARIANT: Negative transfers are strictly forbidden.
# @TEST_DATA: sufficient_funds -> {"from": "acc_A", "to": "acc_B", "amt": 100.00}
# @TEST_DATA: insufficient_funds -> {"from": "acc_empty", "to": "acc_B", "amt": 1000.00}
# @TEST_DATA: concurrency_lock -> {./fixtures/transactions.json#race_condition}
from decimal import Decimal
from typing import NamedTuple
from ...core.logger import belief_scope
from ...core.db import atomic_transaction, get_balance, update_balance
from ...core.exceptions import BusinessRuleViolation
class TransferResult(NamedTuple):
tx_id: str
status: str
new_balance: Decimal
# [DEF:execute_transfer:Function]
# @PURPOSE: Atomically move funds between accounts with audit trails.
# @PARAM: sender_id (str) - Source account.
# @PARAM: receiver_id (str) - Destination account.
# @PARAM: amount (Decimal) - Positive amount to transfer.
# @PRE: amount > 0; sender != receiver; sender_balance >= amount.
# @POST: sender_balance -= amount; receiver_balance += amount; Audit Record Created.
# @SIDE_EFFECT: Database mutation (Rows locked), Audit IO.
#
# @UX_STATE: Success -> Returns 200 OK + Transaction Receipt.
# @UX_STATE: Error(LowBalance) -> 422 Unprocessable -> UI shows "Top-up needed" modal.
# @UX_STATE: Error(System) -> 500 Internal -> UI shows "Retry later" toast.
def execute_transfer(sender_id: str, receiver_id: str, amount: Decimal) -> TransferResult:
# Guard: Input Validation
if amount <= Decimal("0.00"):
raise BusinessRuleViolation("Transfer amount must be positive.")
if sender_id == receiver_id:
raise BusinessRuleViolation("Cannot transfer to self.")
with belief_scope("execute_transfer") as context:
context.logger.info("Initiating transfer", data={"from": sender_id, "to": receiver_id})
try:
# 1. Action: Atomic DB Transaction
# @RELATION: CALLS -> atomic_transaction
with atomic_transaction():
# Guard: State Validation (Strict)
current_balance = get_balance(sender_id, for_update=True)
if current_balance < amount:
# @UX_FEEDBACK: Triggers specific UI flow for insufficient funds
context.logger.warn("Insufficient funds", data={"balance": current_balance})
raise BusinessRuleViolation("INSUFFICIENT_FUNDS")
# 2. Action: Mutation
new_src_bal = update_balance(sender_id, -amount)
new_dst_bal = update_balance(receiver_id, +amount)
# 3. Action: Audit
tx_id = context.audit.log_transfer(sender_id, receiver_id, amount)
context.logger.info("Transfer committed", data={"tx_id": tx_id})
return TransferResult(tx_id, "COMPLETED", new_src_bal)
except BusinessRuleViolation as e:
# Logic: Explicit re-raise for UI mapping
raise e
except Exception as e:
# Logic: Catch-all safety net
context.logger.error("Critical Transfer Failure", error=e)
raise RuntimeError("TRANSACTION_ABORTED") from e
# [/DEF:execute_transfer:Function]
# [/DEF:TransactionCore:Module]

View File

@@ -1,19 +1,23 @@
<!-- [DEF:Shot:Svelte_Component:Example] --> <!-- [DEF:FrontendComponentShot:Component] -->
# @PURPOSE: Reference implementation of a task-spawning component using Constitution rules.
# @RELATION: IMPLEMENTS -> [DEF:Std:UI_Svelte]
<script> <script>
/** /**
* @TIER: STANDARD * @TIER: CRITICAL
* @PURPOSE: Action button to spawn a new task. * @SEMANTICS: Task, Button, Action, UX
* @LAYER: UI * @PURPOSE: Action button to spawn a new task with full UX feedback cycle.
* @SEMANTICS: Task, Creation, Button * @LAYER: UI (Presentation)
* @RELATION: CALLS -> postApi * @RELATION: CALLS -> postApi
* @INVARIANT: Must prevent double-submission while loading.
* *
* @UX_STATE: Idle -> Button enabled with primary color. * @TEST_DATA: idle_state -> {"isLoading": false}
* @UX_STATE: Loading -> Button disabled with spinner while postApi resolves. * @TEST_DATA: loading_state -> {"isLoading": true}
* @UX_FEEDBACK: toast.success on completion; toast.error on failure. *
* @UX_TEST: Idle -> {click: spawnTask, expected: loading state then success} * @UX_STATE: Idle -> Button enabled, primary color.
* @UX_STATE: Loading -> Button disabled, spinner visible.
* @UX_STATE: Error -> Toast notification triggers.
*
* @UX_FEEDBACK: Toast success/error.
* @UX_TEST: Idle -> {click: spawnTask, expected: isLoading=true}
* @UX_TEST: Success -> {api_resolve: 200, expected: toast.success called}
*/ */
import { postApi } from "$lib/api.js"; import { postApi } from "$lib/api.js";
import { t } from "$lib/i18n"; import { t } from "$lib/i18n";
@@ -24,40 +28,43 @@
let isLoading = false; let isLoading = false;
async def spawnTask() { // [DEF:spawnTask:Function]
async function spawnTask() {
isLoading = true; isLoading = true;
console.log("[FrontendComponentShot][Loading] Spawning task...");
try { try {
// 1. Action: Constitution Rule - MUST use postApi wrapper // 1. Action: API Call
const response = await postApi("/api/tasks", { const response = await postApi("/api/tasks", {
plugin_id, plugin_id,
params params
}); });
// 2. Feedback: UX state management // 2. Feedback: Success
if (response.task_id) { if (response.task_id) {
console.log("[FrontendComponentShot][Success] Task created.");
toast.success($t.tasks.spawned_success); toast.success($t.tasks.spawned_success);
} }
} catch (error) { } catch (error) {
// 3. Recovery: Evaluation & UI reporting // 3. Recovery: User notification
console.log("[FrontendComponentShot][Error] Failed:", error);
toast.error(`${$t.errors.task_failed}: ${error.message}`); toast.error(`${$t.errors.task_failed}: ${error.message}`);
} finally { } finally {
isLoading = false; isLoading = false;
} }
} }
// [/DEF:spawnTask:Function]
</script> </script>
<button <button
on:click={spawnTask} on:click={spawnTask}
disabled={isLoading} disabled={isLoading}
class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg flex items-center gap-2" class="btn-primary flex items-center gap-2"
aria-busy={isLoading}
> >
{#if isLoading} {#if isLoading}
<span class="animate-spin text-sm">🌀</span> <span class="animate-spin" aria-label="Loading">🌀</span>
{/if} {/if}
<span>{$t.actions.start_task}</span> <span>{$t.actions.start_task}</span>
</button> </button>
<!-- [/DEF:FrontendComponentShot:Component] -->
<style>
/* Local styles minimized as per Constitution Rule III */
</style>
<!-- [/DEF:Shot:Svelte_Component] -->

View File

@@ -1,6 +1,10 @@
# [DEF:Shot:Plugin_Example:Example] # [DEF:PluginExampleShot:Module]
# @TIER: STANDARD
# @SEMANTICS: Plugin, Core, Extension
# @PURPOSE: Reference implementation of a plugin following GRACE standards. # @PURPOSE: Reference implementation of a plugin following GRACE standards.
# @RELATION: IMPLEMENTS -> [DEF:Std:Plugin] # @LAYER: Domain (Business Logic)
# @RELATION: INHERITS -> PluginBase
# @INVARIANT: get_schema must return valid JSON Schema.
from typing import Dict, Any, Optional from typing import Dict, Any, Optional
from ..core.plugin_base import PluginBase from ..core.plugin_base import PluginBase
@@ -11,28 +15,15 @@ class ExamplePlugin(PluginBase):
def id(self) -> str: def id(self) -> str:
return "example-plugin" return "example-plugin"
@property
def name(self) -> str:
return "Example Plugin"
@property
def description(self) -> str:
return "A simple plugin that demonstrates structured logging and progress tracking."
@property
def version(self) -> str:
return "1.0.0"
# [DEF:get_schema:Function] # [DEF:get_schema:Function]
# @PURPOSE: Defines input validation schema. # @PURPOSE: Defines input validation schema.
# @POST: Returns dict compliant with JSON Schema draft 7.
def get_schema(self) -> Dict[str, Any]: def get_schema(self) -> Dict[str, Any]:
return { return {
"type": "object", "type": "object",
"properties": { "properties": {
"message": { "message": {
"type": "string", "type": "string",
"title": "Message",
"description": "A message to log.",
"default": "Hello, GRACE!", "default": "Hello, GRACE!",
} }
}, },
@@ -41,27 +32,33 @@ class ExamplePlugin(PluginBase):
# [/DEF:get_schema:Function] # [/DEF:get_schema:Function]
# [DEF:execute:Function] # [DEF:execute:Function]
# @PURPOSE: Core plugin logic with structured logging and progress reporting. # @PURPOSE: Core plugin logic with structured logging and scope isolation.
# @PARAM: params (Dict) - Validated input parameters. # @PARAM: params (Dict) - Validated input parameters.
# @PARAM: context (TaskContext) - Execution context with logging and progress tools. # @PARAM: context (TaskContext) - Execution tools (log, progress).
async def execute(self, params: Dict[str, Any], context: Optional[TaskContext] = None): # @SIDE_EFFECT: Emits logs to centralized system.
message = params["message"] async def execute(self, params: Dict, context: Optional = None):
message = params
# 1. Action: Structured Logging with Source Attribution # 1. Action: System-level tracing (Rule VI)
if context: with belief_scope("example_plugin_exec") as b_scope:
log = context.logger.with_source("example_plugin") if context:
log.info(f"Starting execution with message: {message}") # Task Logs: Пишем в пользовательский контекст выполнения задачи
# @RELATION: BINDS_TO -> context.logger
# 2. Action: Progress Reporting log = context.logger.with_source("example_plugin")
log.progress("Processing step 1...", percent=25)
# Simulating some async work... b_scope.logger.info("Using provided TaskContext") # System log
# await some_async_op() log.info("Starting execution", data={"msg": message}) # Task log
log.progress("Processing step 2...", percent=75) # 2. Action: Progress Reporting
log.info("Execution completed successfully.") log.progress("Processing...", percent=50)
else:
# Fallback for manual/standalone execution # 3. Action: Finalize
print(f"Standalone execution: {message}") log.info("Execution completed.")
else:
# Standalone Fallback: Замыкаемся на системный scope
b_scope.logger.warning("No TaskContext provided. Running standalone.")
b_scope.logger.info("Standalone execution", data={"msg": message})
print(f"Standalone: {message}")
# [/DEF:execute:Function] # [/DEF:execute:Function]
# [/DEF:Shot:Plugin_Example] # [/DEF:PluginExampleShot:Module]