AyCode.Core/.github/copilot-instructions.md

151 lines
16 KiB
Markdown

# 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.
1. **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 `.md` files currently in your context (across all loaded docs in this conversation)
- `K` = count of `.md` files 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 `.md` file):**
- **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.md` files:** always include the immediate parent folder as prefix → `TOPIC/README.md` (e.g., `LOGGING/README.md`, `BINARY/README.md`). Never write a bare `README.md` in the prefix because multiple folders have one.
- **Cross-project disambiguation:** if two files share the same short name (rare — e.g., `docs/README.md` from 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 for `copilot-instructions.md` — the `.github/` location is implicit. When multiple are loaded simultaneously (typical in `protocol-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]` — only `copilot-instructions.md` loaded 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.
2. **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`, or `get_file` (for non-markdown files).
- Your VERY FIRST AND ONLY allowed tool calls must be `file_search` or `get_file` targeting the `.md` documentation in the relevant `docs/` folders or `README.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 `.md` files that are already mapped in your context or `LOADED_DOCS` list (strictly maintain rule 3).
- **CROSS-REPO HARD-GATE:** When navigating to an external repo (via `own-dep-repos` paths), read that repo's `docs/` and `README.md` BEFORE 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 `.md` file (folder `README.md`, other repo `docs/`, 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.
3. **STRICT NO-RE-READ POLICY (ANTI-LOOP):**
You are PHYSICALLY FORBIDDEN from calling `get_file` or `file_search` on any `.md` file 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 `.md` file is in your context, it STAYS in your context.
- Re-reading them wastes tokens and breaks the protocol.
- ONLY re-read an `.md` file 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.
4. **CONTEXT RECOVERY (SMART READ):**
If the user asks a domain/architecture specific question and you realize the essential `.md` files 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 the `docs/` 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)
5. **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.
## 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.md` or `SKILL.md` is 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 are NOT in Claude Code's native skill-registry / system-reminder. 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 (~10-13K tokens); 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"
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 in `own-dep-repos` are fully accessible — read their source code, docs, and `.github/copilot-instructions.md` freely 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:**
1. Generic (reusable across any consumer)? → belongs HERE
2. Consumer-specific (business logic, URLs, product names)? → NOT HERE
3. 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-tooling:** Files under `.github/` (LLM-agent skills, `copilot-instructions.md`, `LLM_PROTOCOL_DECISIONS.md`, registry data like `REPOS.md` / `TOPIC_CODES.md`) are **workspace-configuration, not framework code**. They may reference any layer — topics, repos, or consumer products — because they describe *this specific workspace's* agent-tooling layout. The Framework-First rule applies to C# code, namespaces, types, and domain `.md` docs (under `docs/`), **NOT** to `.github/` tooling. If AyCode.Core is ever extracted as a standalone framework, the `.github/` folder is workspace-swapped; the framework code itself stays layer-clean. Do NOT "fix" apparent Framework-First violations inside `.github/` tooling — they are intentional.
Full doctrine: `../docs/ARCHITECTURE.md#framework-vs-consumer-boundary`
## Key Abstractions
1. **IId<T>** is the foundation interface — almost every entity implements it. Always preserve ID integrity.
2. **Generic entity hierarchy**: Interfaces (`AyCode.Interfaces`) → Entities (`AyCode.Entities`) → Models (`AyCode.Models`). Entities are abstract generics; concrete types live in consuming projects.
3. **TrackingState** enum (Added/Modified/Deleted/Unchanged) drives client-side change tracking, not EF Core.
## Serialization
4. **Toon** (Token-Oriented Object Notation) — primary goal is **LLM accuracy**. @meta/@data sections.
5. **AcBinary** — primary goal is **speed**. Two-phase scan+serialize, reference tracking, string interning.
6. **AcJson** — Newtonsoft.Json wrapper with $id/$ref, IId-based reference resolution, and chain API.
## SignalR
7. **Tag-based transport (no conventional hub methods)** — SignalR communication should generally use the generic methods provided by `AcWebSignalRHubBase` (server) and `AcSignalRClientBase` (client). Request types are conventionally identified by `int` tags. Try to avoid adding custom, business-specific, or conventional string-based Hub methods (e.g., `GetUsers()`).
8. **AcSignalRDataSource** — generic `IList<T>` with change tracking, CRUD via `SignalRCrudTags`, binary merge, rollback. See `AyCode.Services.Server/docs/SIGNALR/SIGNALR_DATASOURCE.md`. Transport docs: `AyCode.Services/docs/SIGNALR/README.md`.
9. **JSON-in-Binary tech debt** — client→server request parameters are currently JSON inside a Binary envelope (`SignalPostJsonDataMessage`). Do NOT attempt to fix as a side effect — requires coordinated changes across all consuming projects.
## Critical Warnings
10. **PasswordHasher** — PBKDF2-HMAC-SHA512. Do NOT modify the salt logic or iteration count — existing passwords become unverifiable.
11. **MeasuringStatus.Finnished** — intentional legacy typo in consuming projects. Do NOT fix the spelling.
12. **LogLevel enum values** — synchronized with database. Do NOT renumber.
## Conventions
13. Do not suggest removal/rollback as a solution — find a fix for the problem.
14. Extension methods over instance methods for CRUD operations.
15. Session pattern for reads, Transaction pattern for writes.
16. **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.
17. **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.
18. **Keep all .md documentation in sync** — at the end of EVERY code-modifying response, invoke the **`docs-check`** skill (`AyCode.Core/.github/skills/docs-check/SKILL.md`). It evaluates loaded `.md` files for drift vs code, missing topic coverage, csproj-glob registration gaps for new `.md` files, 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, no-ID rule, status-update-on-fix clause) and empty-state handling. Passive detection + ASK FIRST; all patches require user approval (Rule #5). Do NOT trigger new searches for this check.
19. **Documentation layering** — write `.md` documentation at the **defining layer** (where the code lives). Higher-layer `.md` files 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.
20. **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.
21. **Folder navigation** — start from the root `README.md` for solution-level navigation. When you need to understand a folder's contents or find a type/class, read the `README.md` in that folder first — it indexes the local files and sub-folders. Follow this before grepping or reading source files.