19 KiB
AyCode.Core — Domain Rules
🛑 AI AGENT CORE PROTOCOL (CRITICAL ENFORCEMENT)
You are operating in a multi-repo, documentation-first architecture. You MUST STRICTLY follow this protocol for every response. Failure to do so will break the workspace rules.
-
MANDATORY OUTPUT PREFIX: Your response MUST begin on the very first line with this format:
[LOADED_DOCS: N files (+K this turn: <comma-separated short names>)]N= total count of.mdfiles currently in your context (across all loaded docs in this conversation)K= count of.mdfiles newly loaded during THIS response (may be 0)- If
K > 0: list the newly loaded files as shortest unique short names (see naming rule below) - If
K = 0: write[LOADED_DOCS: N files, no new loads] - If
N = 0: write[LOADED_DOCS: NONE]
Short-name rule (for each loaded
.mdfile):- Default: use the basename only. Works for all topic-prefixed companions (
LOGGING_ISSUES.md,BINARY_FEATURES.md) and flat single-file topics (ARCHITECTURE.md,GLOSSARY.md,CONVENTIONS.md) — these are already unique. - For
README.mdfiles: always include the immediate parent folder as prefix →TOPIC/README.md(e.g.,LOGGING/README.md,BINARY/README.md). Never write a bareREADME.mdin the prefix because multiple folders have one. - Cross-project disambiguation: if two files share the same short name (rare — e.g.,
docs/README.mdfrom two different projects), extend the prefix one more level:PROJECT/docs/README.md. Always use the shortest unique form. - Never use absolute paths. Never include the
.github/prefix forcopilot-instructions.md— the.github/location is implicit. When multiple are loaded simultaneously (typical inprotocol-audit), apply the cross-project disambiguation above:AyCode.Core/copilot-instructions.md,FruitBankHybridApp/copilot-instructions.md, etc.
Examples:
[LOADED_DOCS: NONE]— nothing loaded yet[LOADED_DOCS: 1 files, no new loads]— onlycopilot-instructions.mdloaded earlier, nothing new this turn[LOADED_DOCS: 4 files (+3 this turn: LOGGING/README.md, LOGGING_ISSUES.md, LOGGING_TODO.md)]— logger topic folder loaded[LOADED_DOCS: 7 files (+3 this turn: SIGNALR/README.md, SIGNALR_ISSUES.md, SIGNALR_TODO.md)]— SignalR topic loaded (LOGGING was already in context)[LOADED_DOCS: 5 files (+2 this turn: ARCHITECTURE.md, GLOSSARY.md)]— top-level reference docs loaded (flat, no folder prefix needed)
This prefix is MANDATORY on EVERY response (not just the first, not just when loading happens). It serves two purposes: (a) user-visible compliance signal, and (b) self-commitment for the no-re-read rule — in subsequent turns you read your own prior prefix from the conversation to enforce Rule #3. Dropping the prefix breaks both.
-
HARD-GATE DELAY (DOCS BEFORE CODE) & TOOL EXECUTION BLOCK:
- If
[LOADED_DOCS: NONE]applies, you MUST STOP and you are STRICTLY FORBIDDEN to use the following tools:code_search,get_symbols_by_name,find_symbol, orget_file(for non-markdown files). - Your VERY FIRST AND ONLY allowed tool calls must be
file_searchorget_filetargeting the.mddocumentation in the relevantdocs/folders orREADME.md. - Do not answer the user's core question until the
[LOADED_DOCS]list is populated with the base architecture files. - CRITICAL EXCEPTION: Do NOT re-read
.mdfiles that are already mapped in your context orLOADED_DOCSlist (strictly maintain rule 3). - CROSS-REPO HARD-GATE: When navigating to an external repo (via
own-dep-repospaths), read that repo'sdocs/andREADME.mdBEFORE searching its source code. The hard-gate applies to EVERY repo you enter, not just your own. - PER-QUESTION DOC-FIRST: Before searching source code for any user question, check whether there is a relevant
.mdfile (folderREADME.md, other repodocs/, etc.) that has NOT yet been loaded. Read it first — it tells you where to look in the code, saving searches and tokens. Only after loading relevant docs should you search/read source files.
- If
-
STRICT NO-RE-READ POLICY (ANTI-LOOP): You are PHYSICALLY FORBIDDEN from calling
get_fileorfile_searchon any.mdfile that is already listed in your[LOADED_DOCS]prefix.- Definition: A doc is "in your context" ONLY if you have read its actual file content via a tool call in THIS conversation. Prior session summaries, compacted messages, and memory entries do NOT count — they are lossy compressions.
- Once an
.mdfile is in your context, it STAYS in your context. - Re-reading them wastes tokens and breaks the protocol.
- ONLY re-read an
.mdfile if the user EXPLICITLY states "the file has changed on disk, read it again". - If the user simply mentions a glossary term or requests info found in a loaded doc, answer directly from memory. DO NOT search for it again.
-
CONTEXT RECOVERY (SMART READ): If the user asks a domain/architecture specific question and you realize the essential
.mdfiles are NO LONGER in your current context (they dropped out of memory), you MUST automatically re-read the necessary documentation before answering. Do NOT wait for the user to explicitly tell you to re-read them. Prioritize scanning thedocs/folders to recover the lost context.Auto-detection triggers (MUST treat ALL docs as NOT loaded):
- Session starts with a summary of a previous conversation (context recovery/compaction)
- Message compaction or context compression occurred mid-session
- You cannot quote the exact content of a doc you claim to know
When any trigger fires → reset
[LOADED_DOCS: NONE]and re-read per Rule #2.
Directories to read (when recovering context):
docs/(in this repository root)
-
EXPLICIT CONSENT FOR MODIFICATIONS: NEVER rewrite, create, or delete any file (code, documentation, configuration, memory, or otherwise) without the user's explicit permission. If the user does not specifically request a code modification (e.g., using phrases like "we are just thinking," "what do you think," "let's plan"), you MUST ONLY provide text-based analysis and planning. You are FORBIDDEN from using file-modifying tools (
replace_string_in_file,edit_file,create_file, etc.) until the user explicitly says "ok", "go ahead", "implement", or a similar unambiguous approval. -
AUTHORITY, RULE SCOPE, AND SKILL INVOCATION: When proposing structural changes (new files, conventions, categories, IDs, naming patterns, URL schemes, etc.), follow these three principles:
-
Authority before proposal: Before proposing new structures, conventions, categories, or IDs, verify the proposal against the authoritative source for that domain. If a registry, convention doc, or canonical reference exists, follow it (or its registered maintenance workflow for new entries). Do not invent ad-hoc when an authority exists.
-
Rule implications are part of the rule: Rules have scope implications beyond their literal text. Apply rules conservatively — if a rule prohibits action X, do not work around it with novel patterns Y that achieve the same effect. Working around rule scope with ad-hoc patterns is a process violation, not a creative solution.
-
Skill invocation preferred over reimplementation: If a workspace skill's scope covers the current task, invoke the skill explicitly rather than manually performing similar steps. Skills canonicalize subtleties (validation rules, cross-references, edge cases) that ad-hoc reimplementation risks missing. Improvising a skill's logic is a process violation.
-
Session Setup
Mandatory reads at session start — in addition to this copilot-instructions.md, the agent MUST load the two reactive workspace skills' SKILL.md files (the three user-gated skills are lazy-loaded on demand — see notes below):
docs-discovery/SKILL.md— reactive (triggers on any domain question — must be ready BEFORE the first domain query arrives)docs-check/SKILL.md— reactive (triggers at the end of every code-modifying response)
The remaining three skills are lazy-loaded — SKILL.md is read on demand at invocation, not at session start. The 1-2 line summaries in ## Shared Agent Skills below are sufficient for trigger recognition; the full skill content loads only when needed:
protocol-audit/SKILL.md— user-invoked ("audit protocol"); LLM-suggest-back when a.github/copilot-instructions.mdorSKILL.mdis modified during the session.adr-author/SKILL.md— user-invoked ("let's plan X", "design Y"); LLM-suggest-back on ADR-worthy conversation drift.docs-archive/SKILL.md— user-invoked ("archive ISSUES", "rotate logs"); LLM-suggest-back when active_ISSUES.md/_TODO.md/ DEC log shows many closed prior entries.
Path resolution: if this repo is the canonical protocol host (see @repo block below — typically AyCode.Core), the paths are local: .github/skills/<skill>/SKILL.md. Otherwise, prefix with this repo's own-dep-repos AyCode.Core path (see the ## Shared Agent Skills section below for explicit paths).
Why mandatory: workspace skills live as plain SKILL.md files in .github/skills/ — LLMs do NOT auto-discover them via any built-in skill-registry. Without pre-loading their SKILL.md content, the agent cannot reliably recognize implicit triggers (e.g., "domain question → invoke docs-discovery" at the moment the question arrives, not retroactively). Pre-loading is a one-time cost per session (~5-8K tokens for the 2 reactive SKILL.md files; the 3 user-gated skills are lazy-loaded — no upfront cost); Rule #3 (no-re-read) prevents repeated reads; re-read only if Rule #4 (Context Recovery) fires.
Amortization — critical, do NOT re-evaluate per-turn: the Session Setup cost is measured over the ENTIRE session, not per single turn. A typical session has many turns; the first domain question alone already recoups the investment (alternative — repeated source-code Grep/Read per turn — costs 10-20K tokens per turn with lower output quality). Do NOT flag pre-loaded content as "wasteful" for turns that don't invoke it — the design depends on cross-turn amortization + Rule #3 (no-re-read) + on-demand specificity of Rule #4 (Context Recovery). This amortization is the central token-economics principle of the entire protocol stack.
The first response's [LOADED_DOCS: ...] prefix must reflect 3 files (this copilot-instructions.md + 2 reactive SKILL.md). Lazy-loaded skills add to the count when invoked.
Documentation-first coding
Before running any source-code Grep / get_file / code_search in response to a domain-related user request, invoke the docs-discovery skill (.github/skills/docs-discovery/SKILL.md). It scans docs/ folders via Glob using topic tokens extracted from the request, loads paired main/_ISSUES/_TODO .md sets as a unit, and honours the no-re-read rule. This saves tokens and prevents reintroducing fixed bugs. The skill contains the full procedure; this rule is just the trigger.
Workspace Dependencies
@repo { name = "AyCode.Core" prefix = "ACCORE" type = "framework" layer = 0 own-dep-repos = [] }
This is the single source of truth for domain rules. Do not duplicate these elsewhere. For detailed docs see:
README.md→docs/External repos inown-dep-reposare fully accessible — read their source code, docs, and.github/copilot-instructions.mdfreely when you need type definitions, base classes, or context. Do not limit yourself to the current workspace.
Framework-First Design Principle
🛑 This repo is framework (Layer 0 — Core framework), not a consumer application. Consumers (plural, unknown) reference it.
Before writing code, ask:
- Generic (reusable across any consumer)? → belongs HERE
- Consumer-specific (business logic, URLs, product names)? → NOT HERE
- Same pattern in 2+ consumers? → promote to framework (HERE)
Hard rules:
- ❌ No consumer names or product-specific terms in code, identifiers, namespaces, or docs
- ❌ No framework → consumer references
- ✅ Abstract/virtual base classes — consumers derive/override
- ✅
IOptions<T>for per-consumer configuration - ✅ DI extension methods (
services.AddAcXxx<T>()) that bundle setup
Framework design = "write the base first, derive the specific later". Plan framework placement first, then consumer-specific code.
Exception for workspace-meta history (narrow): Only historical / transient workspace-meta files may reference any layer:
LLM_PROTOCOL_DECISIONS.md— append-only Decision Log; entries document workspace-wide events historically and may reference any layer (a decision affecting a specific consumer is recorded here for the audit trail).- Transient migration artifacts (e.g.,
MIGRATION_ID_MAPPING.mdduring an active ID-format migration) — short-lived, scheduled for deletion review.
All OTHER .github/ files (skills, registries like REPO_PREFIXES.md / REPOS.md / TOPIC_CODES.md, copilot-instructions.md itself, etc.) follow the Framework-First Design Principle: a Layer N file may only reference Layer ≤ N. Cross-layer awareness in tooling uses runtime discovery via own-dep-repos walking (each repo declares its own info; the skill / agent walks the dep tree to gather what's needed), not centralized enumeration in a Layer 0 file.
The Framework-First rule applies to C# code, namespaces, types, domain .md docs (under docs/), AND .github/ tooling — except the narrow workspace-meta history above. If AyCode.Core is ever extracted as a standalone framework, the framework code AND its .github/ tooling stay layer-clean.
Docs-side Framework-First — bounded consumer-awareness exception (LLMP-DEC-62): the same Layer N → Layer ≤ N reference principle applies to ALL .md files (not just .github/). Cross-layer awareness in tooling uses runtime discovery via own-dep-repos walking, not centralized listings. Single bounded exception per repo: a repo with consumers above it MAY have ONE CONSUMERS.md (or similar — single file per repo) listing which higher-layer repos consume it — bounded consumer-awareness for change-impact assessment, NOT framework-on-consumer coupling. Other docs/code MUST stay consumer-agnostic. Top-of-tree consumer products (no higher layer above) need no CONSUMERS.md. AyCode.Core's lives at AyCode.Core/CONSUMERS.md and is the SINGLE place in this repo allowed to name consumer repos.
Full doctrine: ../docs/ARCHITECTURE.md#framework-vs-consumer-boundary
Key Abstractions
- IId is the foundation interface — almost every entity implements it. Always preserve ID integrity.
- Generic entity hierarchy: Interfaces (
AyCode.Interfaces) → Entities (AyCode.Entities) → Models (AyCode.Models). Entities are abstract generics; concrete types live in consuming projects. - TrackingState enum (Added/Modified/Deleted/Unchanged) drives client-side change tracking, not EF Core.
Serialization
- Toon (Token-Oriented Object Notation) — primary goal is LLM accuracy. @meta/@data sections.
- AcBinary — primary goal is speed. Two-phase scan+serialize, reference tracking, string interning.
- AcJson — Newtonsoft.Json wrapper with $id/$ref, IId-based reference resolution, and chain API.
SignalR
- Tag-based transport (no conventional hub methods) — SignalR communication should generally use the generic methods provided by
AcWebSignalRHubBase(server) andAcSignalRClientBase(client). Request types are conventionally identified byinttags. Try to avoid adding custom, business-specific, or conventional string-based Hub methods (e.g.,GetUsers()). - AcSignalRDataSource — generic
IList<T>with change tracking, CRUD viaSignalRCrudTags, binary merge, rollback. SeeAyCode.Services.Server/docs/SIGNALR_DATASOURCE/README.md. Transport docs:AyCode.Services/docs/SIGNALR/README.md. JSON-in-Binary tech debt— REMOVED 2026-04-26 (resolved:AyCode.Core/docs/XCUT/XCUT_ISSUES.md#accore-xcut-i-x8q1). Number kept reserved to prevent renumbering of subsequent items.
Critical Warnings
- PasswordHasher — PBKDF2-HMAC-SHA512. Do NOT modify the salt logic or iteration count — existing passwords become unverifiable.
- MeasuringStatus.Finnished — intentional legacy typo in consuming projects. Do NOT fix the spelling.
- LogLevel enum values — synchronized with database. Do NOT renumber.
Conventions
- Do not suggest removal/rollback as a solution — find a fix for the problem.
- Extension methods over instance methods for CRUD operations.
- Session pattern for reads, Transaction pattern for writes.
- Target framework is net9.0 (set in AyCode.Core.targets). The SourceGenerator targets netstandard2.0. Consuming projects (AyCode.Blazor, FruitBankHybridApp UI) may target net10.0 but reference AyCode.Core DLLs built as net9.0.
- No redundant code — before writing new logic, check whether similar methods already exist in the current context. Reuse or extract shared logic into smaller methods rather than duplicating. If an existing method does most of what you need, split it into composable parts.
- Keep all .md documentation in sync — at the end of EVERY code-modifying response, invoke the
docs-checkskill (AyCode.Core/.github/skills/docs-check/SKILL.md). It evaluates loaded.mdfiles for drift vs code, missing topic coverage, csproj-glob registration gaps for new.mdfiles, and new issue/TODO candidates — then emits the[DOCUMENTATION CHECK]section per its procedure (or[DOCUMENTATION CHECK] None.single-line when nothing to report). The skill encapsulates the full calibration (4 prerequisites, 3-item volume cap, tentative-ID rule, status-update-on-close clause) and empty-state handling. Passive detection + ASK FIRST; all patches require user approval (Rule #5). Do NOT trigger new searches for this check. - Documentation layering — write
.mddocumentation at the defining layer (where the code lives). Higher-layer.mdfiles reference the base docs (e.g.see AyCode.Services/docs/SIGNALR/README.md) and document only project-specific overrides or extensions. Never duplicate base-layer descriptions in consumer-level docs. - Do not re-read .md files already in your context window. They only change if you modify them yourself (new content is already in context) or if the developer tells you they changed — in that case re-read them once.
- Folder navigation — start from the root
README.mdfor solution-level navigation. When you need to understand a folder's contents or find a type/class, read theREADME.mdin that folder first — it indexes the local files and sub-folders. Follow this before grepping or reading source files. - Language Preference: Communicate in Hungarian as requested by the user.