From 8e9a0b47c1b81c2c173d229004b88129a3ea7a13 Mon Sep 17 00:00:00 2001 From: Loretta Date: Mon, 27 Apr 2026 14:42:10 +0200 Subject: [PATCH] [LOADED_DOCS: 3 files, no new loads] Expand META-TODO scope; add BINARY_TODO entries; doc updates - Broadened ACCORE-META-T-F8R3 to cover SKILL.md/registry text drift, not just summary staleness - Added concrete SKILL.md drift examples and clarified fix direction - Added BINARY_TODOs for Id detection (convention/attribute) and serializer-native ignore attribute - Updated SIGNALR_BINARY_PROTOCOL_TODO.md and ADR 0001 to clarify deferral of decorator base/handshake TODOs - Minor topic code length and JSON-in-Binary tech debt clarifications - Synced references and cross-links with latest protocol decisions --- .github/LLM_PROTOCOL_DECISIONS.md | 2 + .github/META_TODO.md | 354 +++++++++++++++++- .github/copilot-instructions.md | 4 +- .../docs-check/references/TOPIC_CODES.md | 5 +- .github/skills/protocol-audit/SKILL.md | 14 +- AyCode.Core/docs/BINARY/BINARY_TODO.md | 34 ++ .../docs/SIGNALR_DATASOURCE/README.md | 259 +++++++++++++ .../SIGNALR_DATASOURCE_ISSUES.md | 56 +++ .../SIGNALR_DATASOURCE_TODO.md | 29 ++ .../docs/SIGNALR_BINARY_PROTOCOL/README.md | 4 + .../SIGNALR_BINARY_PROTOCOL_TODO.md | 8 +- ...acbinary-decorator-feature-stack-design.md | 12 +- docs/adr/0001-user-bearer-token-flow.md | 1 - 13 files changed, 754 insertions(+), 28 deletions(-) create mode 100644 AyCode.Services.Server/docs/SIGNALR_DATASOURCE/README.md create mode 100644 AyCode.Services.Server/docs/SIGNALR_DATASOURCE/SIGNALR_DATASOURCE_ISSUES.md create mode 100644 AyCode.Services.Server/docs/SIGNALR_DATASOURCE/SIGNALR_DATASOURCE_TODO.md diff --git a/.github/LLM_PROTOCOL_DECISIONS.md b/.github/LLM_PROTOCOL_DECISIONS.md index 534bf82..ac8e5d6 100644 --- a/.github/LLM_PROTOCOL_DECISIONS.md +++ b/.github/LLM_PROTOCOL_DECISIONS.md @@ -177,6 +177,8 @@ The "primary" vs "inherit" distinction is defined in `protocol-audit/references/ | LLMP-DEC-60 | 2026-04-26 | `## Current protocol state (quick reference)` summary section refreshed to reflect LLMP-DEC-50..59 outcomes (ID-format migration + Framework-First retightening + 7-instruction-file workspace) | After Phases 1-6 of the ID-format migration, the top-of-file summary section (introduced by LLMP-DEC-25 as a "what's currently active" snapshot, separate from the append-only dated entry table) had become stale on multiple counts: (a) "Active as of 2026-04-24" pre-dated all the migration entries; (b) ID format section described the OLD 3-component `--` format pre-LLMP-DEC-50; (c) instruction file count "8 total: 5 primary + 3 inherit" pre-dated LLMP-DEC-51 (`Mango.FruitBank` deletion → 7 total: 5 primary + 2 inherit); (d) skill list mentioned only 3 skills (was outdated since LLMP-DEC-43 introduced the 5-skill 2-reactive/3-user-gated matrix), used `vN.M` version labels (banned by LLMP-DEC-47), and described `protocol-audit` as reading the central `references/REPOS.md` (Phase 5.1 / LLMP-DEC-58 redesigned it for runtime discovery via `own-dep-repos` walking + content-based file-type classification); (e) Scope codes table referenced `3× inherit` and `all 8`. **Updates applied**: date → 2026-04-26; ID format → 4-component `---` with `` per-repo declaration + `` random-suffix spec + LLMP exception + cross-repo prefix-wildcard search note + cross-links to REPO_PREFIXES.md and per-repo TOPIC_CODES.md; instruction file count → 7 total (5 primary + 2 inherit) with brief notes on LLMP-DEC-51/52 (Mango.FruitBank deletion + FBANKNOP re-attribution) and LLMP-DEC-58 (per-repo `prefix` field added to all 7 `@repo` blocks); agent skills → 5 skills (2 reactive pre-loaded + 3 user-gated lazy-loaded) per LLMP-DEC-43, version labels removed; `protocol-audit` description updated for Phase 5.1 runtime-discovery design + new C1/C3 invariants; scope codes table → `2× inherit` + `all 7`; new note: `.github/` files follow Framework-First (LLMP-DEC-58 narrows the previous workspace-meta-tooling exception). **Append-only governance preserved**: dated entry table (LLMP-DEC-1..60) untouched; the summary section is by design a mutable snapshot per LLMP-DEC-25 (*"the dated entries remain the source of truth for *why* each decision was made; the summary is the *what* at a glance"*). | `AyCode.Core/.github/LLM_PROTOCOL_DECISIONS.md` (`## Current protocol state` section — date L9; "Instruction files" L30-33 + 2 new bullets on `prefix` field and Framework-First; ID format L35-40; agent skills L42-45; "Scope codes" table L99-100) | | LLMP-DEC-61 | 2026-04-26 | Phase 7 (closure) of ID-format migration: migration formally complete | Phase 7 of LLMP-DEC-50 migration — **formal closure stamp**. All 7 phases of the original LLMP-DEC-50 plan executed: Phase 1 (LLMP-DEC-53) `REPO_PREFIXES.md` registry creation; Phase 2 (LLMP-DEC-54) `TOPIC_CODES.md` 4-component update; Phase 3 (LLMP-DEC-55) skill `SKILL.md` text updates with placeholders + cross-repo glob; Phase 4 (LLMP-DEC-56) `MIGRATION_ID_MAPPING.md` survey + 79-ID random-suffix generation; Phase 5 (LLMP-DEC-57) per-topic rename across 7 topics (132 edits); Phase 5.1 (LLMP-DEC-58) Framework-First retightening of `.github/` registries + per-repo `prefix` field added to all 7 `@repo` blocks + MIGRATION_ID_MAPPING retention policy revision; Phase 6 (LLMP-DEC-59) cross-file cross-ref cleanup (37 edits, 13 files); summary refresh (LLMP-DEC-60). **Final verification confirmed in LLMP-DEC-59 + cross-checked here**: 0 OLD-format IDs in any non-exception `.md` file or `.cs` file workspace-wide; the 3 allowed-exception files (`MIGRATION_ID_MAPPING.md` transient, `LLM_PROTOCOL_DECISIONS.md` workspace-meta history, `REPO_PREFIXES.md` single pedagogical 'LOG-I-5' example) intentionally retain OLD-format references for documented reasons. **Migration totals across LLMP-DEC-53..61**: ~270 edits, ~25-30 files modified, 79 IDs renamed to 4-component random-suffix format, 7 `@repo` blocks gained `prefix` field, 12+ registry/skill files restructured for Framework-First, 1 new file (`AyCode.Blazor/.github/TOPIC_CODES.md`). **Outstanding scheduled event** (NOT new work — auto-triggered): 2026-05-10 LLM-targeted deletion-review for `MIGRATION_ID_MAPPING.md` per LLMP-DEC-58 retention policy. Any LLM reading the file from that date onward must surface a deletion-confirmation prompt to the user; the prompt text is embedded in the file's header. **Migration formally complete** as of this entry; further protocol-stack work proceeds on the 4-component foundation. | `AyCode.Core/.github/LLM_PROTOCOL_DECISIONS.md` (this entry only — closure stamp; no file edits outside the Decision Log) | | LLMP-DEC-62 | 2026-04-26 | Three protocol-stack additions: (a) `META` topic for meta-tooling issues/TODOs; (b) docs-side Framework-First rule with bounded `CONSUMERS.md` exception; (c) `adr-author` skill complexity-aware ask-back trigger restructure | **User-driven coherent batch** addressing 3 long-tail observations from the Phase 1-6 migration: **(1) Mental tracking failure** for protocol-stack issues. The 5-skill stack + 7 repos + per-repo registries had grown complex enough that issues observed mid-session (e.g., `LOGGING_TODO.md` L88 path/anchor typo, `Current protocol state` summary staleness, `docs-check` runtime-discovery deferral, anchor-mismatch typo class) had no formal home — they either landed in LLMP-DEC entries (which are meant for closed decisions, not open issues) or got lost. **Fix**: new `META` topic registered in `TOPIC_CODES.md` (ACCORE-META-*); `META_ISSUES.md` + `META_TODO.md` created at `AyCode.Core/.github/`; seeded with 3 META-T entries from the migration's deferred items: `ACCORE-META-T-J9N4` (docs-check runtime discovery for per-repo TOPIC_CODES.md), `ACCORE-META-T-K2P7` (protocol-audit anchor-mismatch detection invariant), `ACCORE-META-T-F8R3` (Current protocol state summary auto-staleness detection). Empty-state ISSUES file with template. **(2) Docs-side Framework-First** was implicit but not codified. Phase 5.1 (LLMP-DEC-58) retightened `.github/`, but `docs/` files still allowed cross-layer references in principle. User clarified the rule extends to `docs/` AND added a **bounded consumer-awareness exception**: a single `CONSUMERS.md` per repo MAY list higher-layer consumers for change-impact assessment when working in that repo. **Fix**: new sub-bullet in `AyCode.Core/.github/copilot-instructions.md` `## Framework-First Design Principle` section after the Phase-5.1 workspace-meta-history exception paragraph; new file `AyCode.Core/CONSUMERS.md` listing the 6 known ACCORE consumers (ACBLAZOR, MGNOPLIB, MGNOPCORE, MGFBANKPLUG, FBANKNOP, FBANKAPP) with brief usage notes + change-impact lens. The `CONSUMERS.md` is the SINGLE place in ACCORE allowed to name consumer repos; other docs / code stay consumer-agnostic. Top-of-tree consumer products (FBANKNOP, FBANKAPP) need no own CONSUMERS.md. **(3) `adr-author` skill over-trigger / under-trigger.** The previous "When to invoke" section had a flat list of trigger phrases ("let's plan X", "design Y", "döntsük el Y vagy Z") that fired too liberally for trivial planning AND missed architectural intent without the explicit "ADR" keyword. User proposed a 3-tier model: explicit ADR-keyword → auto-trigger; implicit planning + complexity-sniff → ASK-BACK; trivial → no trigger. **Fix**: `adr-author/SKILL.md` `## When to invoke` section restructured into 4 sub-cases — "Explicit ADR invocation (auto-trigger)" / "Implicit planning request (ASK BACK)" / "Trivial — no ADR" / "Proactive flag (drift)". Complexity-sniff heuristics enumerated explicitly (cross-cutting, cross-repo, reversibility-low, ≥2 alternatives, vocabulary signals, re-openable, new-API). Ask-back template is bilingual (Hungarian + English). Default behaviour: ASK if complexity-sniff fires; never silently auto-invoke. **Coherence**: the 3 changes reinforce each other — META topic gives meta-tooling issues a durable home; CONSUMERS.md formalizes a Phase 5.1 implicit gap with a Framework-First-respecting design; adr-author refinement reduces over-classification of "design discussion" as ADR-worthy (which would otherwise pollute the META topic with mis-classified entries). **Self-evidence**: this very batch was tested against the new adr-author trigger logic — user said *"de most nem kell ehhez adr"* (= "no ADR needed for this") and asked for the 3 changes done in one go; the new SKILL.md flow correctly handles this as "Implicit planning + ASK BACK + user said 'no'" → no `adr-author` skill invocation, just structured execution. | new files: `AyCode.Core/.github/META_ISSUES.md`, `AyCode.Core/.github/META_TODO.md` (3 seeded TODOs), `AyCode.Core/CONSUMERS.md`; modified: `AyCode.Core/.github/skills/docs-check/references/TOPIC_CODES.md` (META row added between XCUT and LLMP), `AyCode.Core/.github/copilot-instructions.md` (Framework-First section sub-bullet for docs-side rule + CONSUMERS.md exception), `AyCode.Core/.github/skills/adr-author/SKILL.md` (`## When to invoke` section restructured into 4 sub-cases) | +| LLMP-DEC-63 | 2026-04-26 | Filed 7 META-TODO entries from cross-source improvement review (3 parallel Copilot reviews + own analysis); codified workspace cost-function principle: **correctness > token-economy** | **User-initiated meta-review of protocol-stack improvement opportunities**, surfaced via 3 parallel Copilot session reviews after the LLMP-DEC-62 batch landed. The reviewers proposed ~17 candidate improvements across docs structure, skill behaviour, ADR governance. Cross-source convergence analysis (3-of-3, 2-of-3, 1-of-3, already-covered) identified genuine signal vs. individual ideas vs. overengineering. **User reframing of the cost function** — the reviewers' suggestions implicitly assumed "minimize tokens" as calibration goal, but user clarified: *"felesleges kör költsége >> tokenköltség. inkább több, mint hibás."* (wasted-round cost dominates token cost; prefer broader loading over wrong-answer-then-retry). This **extends the existing amortization principle (LLMP-DEC-43) from session-start-only to per-turn decisions** — every turn faces (a) read-then-answer vs. (b) guess-then-maybe-be-wrong; default toward (a). **7 new META-TODO entries filed** in `META_TODO.md` (LLMP-DEC-62's `META` topic infrastructure proven useful in its first week of existence): `ACCORE-META-T-M5T8` (TL;DR / "When to read" blocks on topic READMEs — 3-of-3 reviewer convergence + user agreement, P2); `ACCORE-META-T-Y4Q9` (Hot-path / Fast-path tagging — single-source, **recalibrated to P3** by user reframing because "load more" preference reduces fast-path criticality); `ACCORE-META-T-V7L2` (ADR quality lint, warning-mode not hard-fail, P3); `ACCORE-META-T-N8K3` (Last-verified-commit opt-in metadata for anti-hallucination drift detection, P3); `ACCORE-META-T-P9F4` (pre-load expansion with foundational reference docs — direct application of user reframing, P2); `ACCORE-META-T-R2W6` (workspace user-preference statement codifying "correctness > token-economy" — the meta-rule, P2); `ACCORE-META-T-C5J7` (docs-check / docs-discovery cap revisit refining LLMP-DEC-27, direct application, P3). **Implementation dependency clusters**: the P2 trio R2W6 → P9F4 → C5J7 should land together (R2W6 codifies the preference; P9F4 applies it to session-start; C5J7 applies it to skill behaviour) — half-state would be inconsistent; M5T8 → Y4Q9 are parent-child convention pair (TL;DR is the bigger signal, fast-path is finer-grained child); V7L2 + LLMP-DEC-62-era K2P7 are lint-family synergy candidates (one validates ADR structure, the other validates cross-ref anchors). **No implementation in this entry** — pure filing. The entries themselves are the actionable plan; **this LLMP-DEC documents the cost-function clarification** as a new protocol principle (which belongs in the Decision Log even before R2W6 codifies it into `copilot-instructions.md`, because the principle now governs subsequent agent behaviour as a known user preference even when the rule text isn't yet present). **Self-evidence again**: this very review — 3 Copilot reviews + user reframing → my recalibration → 7 META-T filed — exercised the LLMP-DEC-62 adr-author flow correctly: planning-language was used (`mi a véleményed`), complexity-sniff fired (≥2 alternatives, cross-cutting impact, new-API), I asked back (`Akarod hogy LLMP-DEC-63-at is felírjam`), user said yes → execute. No spurious ADR; no ad-hoc structural decision without confirmation. | `AyCode.Core/.github/META_TODO.md` (7 new entries `M5T8 / Y4Q9 / V7L2 / N8K3 / P9F4 / R2W6 / C5J7` inserted between `ACCORE-META-T-B6V2` and the TODO entry template — total META-T entries now 11) | +| LLMP-DEC-64 | 2026-04-27 | Fixed `protocol-audit/SKILL.md` C4 + X1 invariant text drift (5-skill matrix wording stale since LLMP-DEC-43); broadened `ACCORE-META-T-F8R3` scope to cover SKILL.md / registry text drift class | A parallel Claude session ran the post-Phase-5.1 `protocol-audit` skill against the workspace and surfaced **3 stale text references in `protocol-audit/SKILL.md` itself** — a positive self-evidence event for the runtime-discovery audit design (LLMP-DEC-58). All 3 share the SAME root cause: **LLMP-DEC-43** introduced the 5-skill 2-reactive/3-user-gated matrix (2 mandatory pre-loaded: `docs-discovery` + `docs-check`; 3 lazy-loaded: `protocol-audit` + `adr-author` + `docs-archive`); the `copilot-instructions.md` files (5 primary + 2 inherit) updated correctly to list all 5 skills + classification, but `protocol-audit/SKILL.md` invariant texts did not. **Drifts identified**: (i) C4 invariant text said *"all three skills"* — stale since 3-skill era; (ii) C4 expected `[LOADED_DOCS]` counts said *"4 for primary, 5 for inherit"* — stale (correct counts after LLMP-DEC-43: **3 for primary, 4 for inherit**, since 3 user-gated skills no longer pre-load); (iii) X1 invariant said *"all three bullets must be listed"* — same stale 3-skill list. **Audit impact**: the actual instruction files PASS (their `## Shared Agent Skills` now has 5 bullets, which is a SUPERSET of the stale 3-bullet check), but the invariant TEXT was misleading and would mislead future maintainers. **Patches applied**: 2 Edits to `protocol-audit/SKILL.md` — C4 rewritten to require reactive/user-gated classification + correct counts (3/4); X1 rewritten to require all 5 bullets with reactive/user-gated labels. **Broader META observation**: this is a **SKILL.md text drift class** parallel to the `Current protocol state` summary drift class previously captured in `ACCORE-META-T-F8R3`. F8R3's description was broadened (3 Edits to `META_TODO.md`) to cover BOTH classes — the auto-staleness detection (when implemented) catches `LLM_PROTOCOL_DECISIONS.md` summary drift AND any SKILL.md / registry text describing 'current state'. **Self-evidence (positive)**: the protocol-audit skill flagged its OWN SKILL.md content drift — exactly the design intent of LLMP-DEC-58 runtime-discovery audit (catches real maintenance debt). The Copilot session's report explicitly noted *"a runtime-discovery audit design (LLMP-DEC-58) is working as intended, surfacing real maintenance debt"* — independent confirmation. | `protocol-audit/SKILL.md` (C4 + X1 invariant text rewrites); `META_TODO.md` (`ACCORE-META-T-F8R3` description + parallel-drift-class section + Fix-direction options widened) | ## Known follow-ups diff --git a/.github/META_TODO.md b/.github/META_TODO.md index 9dbeb75..6453afc 100644 --- a/.github/META_TODO.md +++ b/.github/META_TODO.md @@ -74,10 +74,10 @@ Skill stays read-only; reports findings; user approves any patch (Rule #5). --- -## ACCORE-META-T-F8R3: `Current protocol state` summary auto-staleness detection +## ACCORE-META-T-F8R3: Static-text auto-staleness detection — `Current protocol state` summary AND SKILL.md / registry text drift **Priority:** P3 · **Type:** Skill enhancement · **Status:** Open · **Area:** `protocol-audit/SKILL.md` (or `docs-check/SKILL.md`) -**Origin:** Phase 6 + LLMP-DEC-60 summary refresh +**Origin:** Phase 6 + LLMP-DEC-60 summary refresh; **scope broadened by LLMP-DEC-64** to include SKILL.md text drift (parallel class) ### Description @@ -90,18 +90,25 @@ Drift instances observed during the Phase 1-6 migration (all caught only at Phas - Skill version labels (`v2.2`, `v1.0`) violating LLMP-DEC-47. - `protocol-audit` description still mentioned `references/REPOS.md` central read after LLMP-DEC-58 redesigned to runtime discovery. -Manual refresh worked (LLMP-DEC-60), but a periodic/automated reminder would catch drift earlier. +**Parallel drift class — SKILL.md / invariant text staleness** (LLMP-DEC-64 broadened the scope): +- `protocol-audit/SKILL.md` C4 invariant said "all three skills" + `[LOADED_DOCS]` counts "4 for primary, 5 for inherit" — outdated since LLMP-DEC-43 introduced 5-skill 2-reactive/3-user-gated matrix (correct counts: 3/4). +- `protocol-audit/SKILL.md` X1 invariant said "all three bullets must be listed" — same root cause. +- Caught 2026-04-27 by a re-audit using the post-Phase-5.1 skill itself (positive self-evidence). + +Manual refresh worked (LLMP-DEC-60 for summary; LLMP-DEC-64 for SKILL.md), but a periodic/automated reminder would catch drift earlier. ### Fix direction -Two complementary options (pick one or combine): +Two complementary options (pick one or combine), now covering BOTH drift classes: -**(a) `protocol-audit` invariant `M1`** (meta-summary integrity, optional): -- Parse `Current protocol state` section claims (date, file count, skill list, ID format). -- Compare against ground truth (filesystem walk, file content). -- Surface drift with suggested patch. +**(a) `protocol-audit` invariant `M1`** (meta-text integrity, optional) — applies to: +- `LLM_PROTOCOL_DECISIONS.md` `Current protocol state` section claims (date, file count, skill list, ID format). +- `protocol-audit/SKILL.md` invariant texts that describe protocol-state quantities (e.g., skill counts in C4, bullet-list expectations in X1). +- Other SKILL.md / registry texts describing "current" state (`docs-check/SKILL.md`, `docs-discovery/SKILL.md`, `REPO_PREFIXES.md`, `TOPIC_CODES.md` cross-link counts). -**(b) `docs-check` skill** flags the summary section as stale if N (e.g., N ≥ 3) new LLMP-DEC entries have been added since the summary's last refresh date. +Compare against ground truth (filesystem walk + file content) and surface drift with suggested patch. + +**(b) `docs-check` skill** flags any of the above as stale if N (e.g., N ≥ 3) new LLMP-DEC entries have been added since the file's last touch — heuristic but catches the common drift pattern. ### Acceptance criteria @@ -117,6 +124,335 @@ Two complementary options (pick one or combine): --- +## ACCORE-META-T-B6V2: General-purpose `scheduler` skill — workspace-side scheduled-task abstraction + +**Priority:** P3 · **Type:** New skill · **Status:** Open · **Area:** new skill (proposed: `AyCode.Core/.github/skills/scheduler/`) +**Origin:** 2026-04-26 — discussion about `MIGRATION_ID_MAPPING.md` 2026-05-10 deletion-review (LLMP-DEC-58) + +### Description + +The current LLMP-DEC-58 retention pattern uses **passive LLM-self-reminding** (any LLM reading the file from 2026-05-10 onward surfaces a deletion prompt). Works only if the file gets read; not guaranteed timing. Anthropic's host environment offers a `schedule` skill (one-time/recurring remote agents) — useful for the specific 2026-05-10 case, but ties the workspace process to a specific host's capability. + +A **workspace-side `scheduler` skill** would generalize the pattern: +- File-based registry (e.g., `AyCode.Core/.github/SCHEDULED_TASKS.md` or similar) — declared scheduled events: due-date / cadence + trigger-action + description. +- Skill scans the registry on session start; surfaces "due / overdue" tasks to the user (gated, not spammy). +- Host-agnostic (Claude Code, Copilot, future LLM hosts). +- Optional integration: when the host has a native scheduler, the skill can offer to register the task there too — workspace-side registry stays source of truth. + +Use cases beyond migration retention: +- Archive-eligibility scans (`docs-archive` periodic runs). +- Periodic `protocol-audit` runs. +- Planned-deletion of transient artifacts (more migrations, generated outputs, etc.). +- Deadline-bound TODOs that should auto-surface near their date. + +### Fix direction + +1. Create skill: `AyCode.Core/.github/skills/scheduler/SKILL.md` (with `references/` for the registry). +2. Define the registry format — likely simple table (`task-id | type (one-time/recurring) | due-date or cadence | trigger-action | description | status`). +3. Skill triggers: + - **Reactive** (session-start scan, like `docs-discovery` / `docs-check`) — surface only if there ARE due/overdue tasks. + - **User-invoked**: "list scheduled tasks", "what's due", "schedule a task" (interview-style add). +4. Migrate the 2026-05-10 `MIGRATION_ID_MAPPING.md` deletion-review to this system once it exists; the per-file LLM-self-reminder header note then trims to a cross-ref pointing at the registry. + +### Acceptance criteria + +- Registry-driven, host-agnostic. +- Session-start surfacing of due / overdue tasks (low-friction, not noise). +- Optional one-click "register at host-native scheduler too" for hosts that support it. +- The 2026-05-10 deletion-review event migrated to this system as the inaugural entry. + +### Related + +- LLMP-DEC-58 (introduces the per-file LLM-self-reminder pattern this would replace). +- LLMP-DEC-43 (5-skill matrix — adding a 6th skill is a workspace-meta change; will need an LLMP-DEC entry when implemented). +- Anthropic-host `schedule` skill (parallel capability to integrate with, not replace). + +--- + +## ACCORE-META-T-M5T8: TL;DR / "When to read this file" block on every topic README + +**Priority:** P2 · **Type:** Convention update + bulk doc edit · **Status:** Open · **Area:** All topic `/README.md` files +**Origin:** 2026-04-26 — Cross-source convergence (3/3 Copilot reviewers + my own analysis); user-reframing strengthened the case (correctness > token-economy) + +### Description + +Topic README files (`LOGGING/README.md`, `BINARY/README.md`, etc.) currently vary in opening style. The agent has to scan the whole README to decide "is this topic relevant?", "which sister docs to load deeper?", "when is this NOT what I'm looking for?". A consistent **TL;DR / "When to read" block** at the top would make load-decisions cheaper and more accurate. **3-of-3 Copilot reviewer convergence** is unusually strong signal. + +Anti-hallucination angle: the block helps agents pick the RIGHT topic to load, not just MORE topics — relevance still matters even under "load broader" preference (`ACCORE-META-T-R2W6`). + +### Fix direction + +Standardize a block at the top of every topic README (after `# Title`, before first `## ` section): + +```markdown +> **Mikor olvasd ezt a fájlt:** ha a kérdés érinti ``, ``, ``. +> **Először olvass:** `_.md`, `_.md` (a 80% case-eket fedik). +> **Mikor NE olvasd:** ha pl. csak `` → `/README.md`. +``` + +Apply to all ACCORE topic READMEs (BINARY, LOGGING, SIGNALR, SBP, TOON, XCUT, AUTH). Pattern propagates to higher-layer repos as their topic READMEs are written. + +### Acceptance criteria + +- Every ACCORE topic README has the block, formatted consistently. +- The block is referenced in `docs-discovery/SKILL.md` as a load-decision aid. +- Optional: `docs-check` invariant flags missing block on new topic READMEs. + +### Related + +- Cross-source: 3-of-3 Copilot reviewer convergence (Set 2 #1+#2, Set 3 #2) +- LLMP-DEC-21 (Pattern B docs layout — established the topic-README pattern) +- `ACCORE-META-T-Y4Q9` (Hot-path tagging — finer-grained version of the same idea) + +--- + +## ACCORE-META-T-Y4Q9: Hot-path / Fast-path tagging in topic READMEs + +**Priority:** P3 · **Type:** Convention update · **Status:** Open · **Area:** Topic README files +**Origin:** 2026-04-26 — Set 3 #3 reviewer suggestion; user-reframing recalibrated importance to **secondary** (less critical when default is "load more") + +### Description + +The TL;DR (`ACCORE-META-T-M5T8`) lists the FIRST 2-3 docs to load. A `### Fast-path docs` sub-section (or `@fastpath` tag) declaratively marks the docs covering the 80% case — distinct from "Először olvass" (which is conversational guidance) by being machine-friendly + parsable. + +User's recalibration: with "load more upfront" preference (`ACCORE-META-T-R2W6`), agent will tend to load broader anyway → fast-path tagging becomes **secondary**, not critical. Still useful as a SIGNAL for which docs to start with when the agent does decide to be selective. + +### Fix direction + +Add a small `### Fast-path docs` section in topic READMEs listing 2-3 docs that suffice for 80% of agent queries. Example: + +```markdown +### Fast-path docs +- `BINARY_FORMAT.md` — wire format spec (most queries land here) +- `BINARY_OPTIONS.md` — configuration reference +- `BINARY_WRITERS.md` — IBufferWriter integration +``` + +`docs-discovery/SKILL.md` honours: when topic identified, fast-path docs load first; expanded set (full topic-pair + sister docs) on demand or per `R2W6` preference. + +### Acceptance criteria + +- ACCORE topic READMEs have the block where applicable. +- `docs-discovery/SKILL.md` parses and honours the fast-path. +- Marker convention defined and consistent. + +### Related + +- `ACCORE-META-T-M5T8` (TL;DR — predecessor / parent convention) +- Cross-source: Set 3 #3 (only 1 source mentioned this — weaker convergence than M5T8) + +--- + +## ACCORE-META-T-V7L2: ADR quality lint / structural validator + +**Priority:** P3 · **Type:** Skill enhancement · **Status:** Open · **Area:** `adr-author/SKILL.md` Step 8 extension (or new Step 10) + optional `references/ADR_LINT.md` +**Origin:** 2026-04-26 — Set 1 #7 + 2-of-3 Copilot reviewer agreement (one explicit suggestion: warning, not hard-fail) + +### Description + +`adr-author/SKILL.md` Steps 5-8 produce ADRs but don't VALIDATE them post-write. A lint pass would catch: +- Missing required sections (`## Status`, `## Context`, `## Decision`, `## Consequences`, `## Alternatives considered`, `## Related`) +- Status field format: `Proposed (YYYY-MM-DD) | Accepted (...) | Superseded by ADR-XXXX | Rejected (...)` +- Alternatives section: ≥ 2 alternatives present (or explicit "no alternatives — implementation path" justification) +- Decision section: explicit decision criterion, not just "we'll do X" without why +- Cross-ref consistency: `Supersedes` / `Superseded by` / `Related ADRs` IDs valid (file exists, anchor exists) + +### Fix direction + +Add Step 10 to `adr-author/SKILL.md` (or extend Step 8): after Write, run lint over the generated ADR. Surface any FAIL as polite suggestion (**warning mode**, not blocker — per Copilot reviewer #2). + +Could alternatively be a `protocol-audit` invariant `D2` (docs-link integrity) extended to validate ADR structure — synergy with `ACCORE-META-T-K2P7` (path/anchor mismatch detection). + +### Acceptance criteria + +- Generated ADRs pass lint by construction (if user fills in placeholders correctly). +- Lint reports are constructive (suggested fix, not just "FAIL"). +- Doesn't block ADR creation — quality nudge, not gate. + +### Related + +- `adr-author/SKILL.md` Steps 5-8 (existing structure) +- `ACCORE-META-T-K2P7` (parallel "anchor mismatch" lint family — synergy candidate) +- Cross-source: Set 1 #7 + Copilot reviewer #2 ("warning, not hard fail") + +--- + +## ACCORE-META-T-N8K3: `Last verified against commit` opt-in metadata field + +**Priority:** P3 · **Type:** Convention (opt-in metadata) · **Status:** Open · **Area:** Long-form `docs/` files (opt-in only) +**Origin:** 2026-04-26 — Set 2 #3 + user anti-hallucination framing (drift detection prevents stale-context hallucination loops) + +### Description + +Drift between `.md` docs and source code is a real hallucination cause: agent reads a doc, trusts it, the doc was written 6 months ago against an old API → wrong answer. A simple `` HTML-comment metadata field would let the agent know: + +- "This doc was verified against commit `cdd54d3` (2026-04-05) — current HEAD is later — possibly stale, double-check the relevant `.cs` file." +- vs. "Verified yesterday — trust it." + +User's recalibration: anti-hallucination angle **strengthens** this entry's importance vs. the original "marginal utility" framing. Drift-detection IS hallucination prevention. + +### Fix direction + +**Opt-in only** — not all docs need this. Suggested for: +- Long-form architecture docs (`ARCHITECTURE.md`, `BINARY/README.md`, etc. — less frequent churn) +- Reference docs (`CONVENTIONS.md`, `GLOSSARY.md`) +- Stable API docs / wire-format docs + +NOT for: +- TODO / ISSUES files (high churn — verification not meaningful) +- Decision Log / ADRs (immutable per LLMP-DEC governance) + +Format: HTML-comment line at top: ``. Manual update by author on doc-modifying response. `docs-check` skill flags "doc with last-verified tag, age > N days, code path touched since" as a soft warning. + +### Acceptance criteria + +- Convention documented in `CONVENTIONS.md` or `META_ISSUES.md` reference section. +- Opt-in — never required, never auto-fail. +- `docs-check` skill respects the marker if present. +- Anti-hallucination value: agent expresses "stale, verifying" instead of guessing. + +### Related + +- Set 2 #3 (Copilot suggestion) +- `ACCORE-META-T-R2W6` (anti-hallucination framing motivates this) + +--- + +## ACCORE-META-T-P9F4: Pre-load expansion with foundational reference docs + +**Priority:** P2 · **Type:** Protocol change (Session Setup section + Rule #1) · **Status:** Open · **Area:** `copilot-instructions.md` Session Setup + `docs-discovery/SKILL.md` +**Origin:** 2026-04-26 — User reframing (correctness > token-economy); current 3-file pre-load may be too narrow + +### Description + +Current session-start pre-load is **3 files** (per LLMP-DEC-43): the active repo's `copilot-instructions.md` + 2 reactive skill SKILL.md (`docs-discovery`, `docs-check`). The 3 user-gated skills lazy-load on demand. + +User feedback: this is too narrow. Foundational reference docs (`ARCHITECTURE.md`, `GLOSSARY.md`, `CONVENTIONS.md`, top-level project `README.md`) get queried in nearly every session. Lazy-loading them per-turn means each topic-specific question pays the load cost separately. **Pre-loading at session-start amortizes once across the entire session.** + +User cost function (explicit): 5-8K extra session-start tokens are CHEAPER than 1 hallucination loop caused by missing foundational context. + +### Fix direction + +Expand session-start pre-load to include foundational reference docs from active repo's `docs/` root (when they exist): + +1. Active repo's `copilot-instructions.md` (current — keeps Rule #1 baseline) +2. `docs-discovery/SKILL.md` (current — reactive) +3. `docs-check/SKILL.md` (current — reactive) +4. Active repo's root `README.md` (NEW) +5. `docs/ARCHITECTURE.md` (NEW, if exists) +6. `docs/CONVENTIONS.md` (NEW, if exists) +7. `docs/GLOSSARY.md` (NEW, if exists — workspace-glossary) + +Result: 4-7 files at session start (depending on which exist in the active repo). LOADED_DOCS prefix shows accurate count. `## Session Setup` section in `copilot-instructions.md` updated. + +For inherit files: pre-load also AyCode.Core's foundational docs (since this repo doesn't host them) — adjust the count similarly. + +### Acceptance criteria + +- Session-start LOADED_DOCS count: 4-7 files (was 3). +- Foundational docs available without explicit user query → reduces "had to grep for context" loops. +- `docs-check`'s base context broader → more accurate drift detection. +- Skill descriptions and Session Setup section in `copilot-instructions.md` updated. +- LLMP-DEC entry recording the change (refines LLMP-DEC-43). + +### Related + +- LLMP-DEC-43 (current 5-skill matrix with 2 reactive pre-loaded) +- `ACCORE-META-T-R2W6` (user-preference statement — same anti-hallucination motivation) +- LLMP-DEC-58 / 60 (Framework-First retightening — pre-load expansion respects this: only same-or-lower-layer docs) + +--- + +## ACCORE-META-T-R2W6: Workspace user-preference statement (correctness > token-economy) + +**Priority:** P2 · **Type:** Protocol change (new sub-rule or `## Workspace Load-Policy Preference` section) · **Status:** Open · **Area:** `copilot-instructions.md` +**Origin:** 2026-04-26 — User reframing of cost function: *"felesleges kör költsége >> tokenköltség. inkább több, mint hibás."* + +### Description + +The current AI AGENT CORE PROTOCOL implicitly assumes "minimize tokens" as a calibration goal. User's actual cost function is different: + +> "Felesleges kör költsége >> tokenköltség. Inkább több, mint hibás." + +Agent should **prefer reading docs over guessing** when uncertain. The amortization principle (LLMP-DEC-43 era) partially codifies this for session-start; doesn't extend explicitly to per-turn decisions. + +Without an explicit preference statement, agents calibrate toward "minimum loading" defaults observed in their training. The workspace's actual preference is opposite: **load broader, ask when unsure, never guess critical context**. + +### Fix direction + +Add a new section / Rule sub-bullet to `copilot-instructions.md` (location TBD: Rule #2 extension, new Rule #7, or a `## Workspace Load-Policy Preference` section between `## Session Setup` and the Rules). Suggested text: + +``` +**WORKSPACE LOAD-POLICY PREFERENCE:** This workspace prioritizes correctness +over token-economy. When uncertain whether you have current/sufficient +context on a topic, READ the relevant `.md` file first — do NOT guess from +memory or attempt to infer purely from code-search. Loading 5-10K extra +tokens upfront is cheaper than a wrong answer that requires user correction ++ retry. The amortization principle extends to ALL turns, not just session +start: every turn has the choice between (a) read-then-answer and (b) +guess-then-maybe-be-wrong; default to (a). + +This explicit preference shifts agent calibration toward "load when uncertain". +It does NOT mean "load everything" — relevance still matters (Rule #2's docs- +before-code stays). It means "within the relevance scope, prefer broader +loading over narrower". +``` + +### Acceptance criteria + +- Rule text in AyCode.Core canonical `copilot-instructions.md` (inherited by reference for non-Core primary files, or duplicated to all 5 — TBD per protocol-audit invariant style). +- `docs-discovery/SKILL.md` and `docs-check/SKILL.md` reference this preference where it affects skill behaviour (cap revisit per `ACCORE-META-T-C5J7`). +- LLMP-DEC entry recording the codification. + +### Related + +- `ACCORE-META-T-P9F4` (pre-load expansion — direct application) +- `ACCORE-META-T-C5J7` (cap revision — also direct application) +- LLMP-DEC-43 (amortization principle — this preference extends it) +- `ACCORE-META-T-N8K3` (drift-detection — anti-hallucination companion) + +--- + +## ACCORE-META-T-C5J7: `docs-check` and `docs-discovery` cap revision (revisit fixed limits) + +**Priority:** P3 · **Type:** Skill behaviour tuning · **Status:** Open · **Area:** `docs-check/SKILL.md` (volume cap), `docs-discovery/SKILL.md` (per-topic limit) +**Origin:** 2026-04-26 — User reframing; LLMP-DEC-27 (volume cap = 3) calibration revisit + +### Description + +`docs-check/SKILL.md` has a "max 3 items per response" volume cap (per LLMP-DEC-27, originally calibrated for "noise-aversion / silence beats noise"). `docs-discovery/SKILL.md` has per-topic file limits implicit in glob patterns. + +User's recalibration: the noise-averse 3-cap may be too conservative under the "correctness > token-economy" preference (`ACCORE-META-T-R2W6`). Options: + +1. **Higher fixed default** (e.g., 5 or 7) — simple, predictable. +2. **Confidence-based** (no cap if all proposals are high-confidence with quotable evidence; cap kicks in only when speculation creeps in) — better quality control but harder to calibrate. +3. **Configurable per session** via a marker — flexible but adds state. + +NOT dynamic complexity-based tuning (rejected per Set 1 #1 skip rationale — inconsistent across agents). + +### Fix direction + +Two-step exploration: +1. Update `docs-check/SKILL.md` volume cap: try `max 5` instead of `max 3`. Observe over a few sessions: is signal/noise ratio better, worse, same? +2. If "better" → codify as new default with LLMP-DEC entry refining LLMP-DEC-27. If "worse" → revert and consider confidence-based alternative. + +For `docs-discovery/SKILL.md`: revisit per-topic file limit. Currently glob loads paired main+ISSUES+TODO. Consider expanding to load related sub-topic docs by default if they exist (e.g., `LOGGING/README.md` triggers also loads `LOGGING_SERVER.md` if present). + +### Acceptance criteria + +- Cap is documented numerically (no vague "as many as needed"). +- `docs-check` skill respects new cap. +- LLMP-DEC entry recording the cap change (refines LLMP-DEC-27). +- A short session-log validates the new cap doesn't introduce noise (signal/noise ≥ pre-change). + +### Related + +- LLMP-DEC-27 (original 3-cap rationale — "silence beats noise") +- `ACCORE-META-T-R2W6` (preference shift — motivates this) +- `ACCORE-META-T-P9F4` (pre-load expansion — parallel "load more" direction) + +--- + ## TODO entry template ``` diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index e6c4d89..602bc5c 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -149,7 +149,7 @@ Full doctrine: `../docs/ARCHITECTURE.md#framework-vs-consumer-boundary` ## 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` 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. +9. ~~**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 10. **PasswordHasher** — PBKDF2-HMAC-SHA512. Do NOT modify the salt logic or iteration count — existing passwords become unverifiable. @@ -162,7 +162,7 @@ Full doctrine: `../docs/ARCHITECTURE.md#framework-vs-consumer-boundary` 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. +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, 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. 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. diff --git a/.github/skills/docs-check/references/TOPIC_CODES.md b/.github/skills/docs-check/references/TOPIC_CODES.md index ff436e4..5a5d7d6 100644 --- a/.github/skills/docs-check/references/TOPIC_CODES.md +++ b/.github/skills/docs-check/references/TOPIC_CODES.md @@ -16,6 +16,7 @@ To make IDs like `ACCORE-LOG-I-K7M2`, `ACCORE-SIG-B-3R9P`, `ACCORE-XCUT-I-A4B7` | `AUTH` | AUTH | User authentication: bearer tokens, JWT, login flow, hub authorization | `AyCode.Core/docs/AUTH/` | | `SIG` | SIGNALR | SignalR transport: tags, client base, dispatch, session | `AyCode.Core/AyCode.Services/docs/SIGNALR/` (+ variant in `AyCode.Services.Server`) | | `SBP` | SIGNALR_BINARY_PROTOCOL | Binary wire protocol over SignalR: framing, chunking, argument read | `AyCode.Core/AyCode.Services/docs/SIGNALR_BINARY_PROTOCOL/` | +| `SIGDS` | SIGNALR_DATASOURCE | Client-server DataSource on SignalR transport: change tracking, rollback, sync state, load lifecycle, IList wrapper | `AyCode.Core/AyCode.Services.Server/docs/SIGNALR_DATASOURCE/` | | `BIN` | BINARY | AcBinary serializer: features, format, writers, source generator | `AyCode.Core/AyCode.Core/docs/BINARY/` | | `TOON` | TOON | Toon serializer: LLM-optimized format with @meta/@types/@data sections | `AyCode.Core/AyCode.Core/docs/TOON/` | | `XCUT` | cross-cutting | Issues / TODOs spanning ≥2 ACCORE topics — one canonical home, referenced from each affected topic | `AyCode.Core/AyCode.Core/docs/XCUT/` | @@ -65,7 +66,7 @@ The framework (this file) does NOT enumerate higher-layer topics — that would ## Registry maintenance — adding a new ACCORE topic To add a new topic code **for AyCode.Core specifically**: -1. Propose the code (2-5 uppercase chars), short and mnemonic, scoped to ACCORE's domain (framework concerns only). +1. Propose the code (2-6 uppercase chars), short and mnemonic, scoped to ACCORE's domain (framework concerns only). 2. Check it doesn't collide with C# class-name prefixes (`Ac*` / `Mg*`) — the topic code should be visually distinct in mixed code/markdown content. 3. Check it doesn't collide with existing ACCORE topic codes in the table above. 4. Add a row to the "Framework's own (ACCORE) topic codes" table. @@ -80,7 +81,7 @@ C# code conventions in this workspace: - `Ac*` — AyCode.Core framework types (e.g., `AcLoggerBase`, `AcBinarySerializer`) - `Mg*` — Mango company types (e.g., `MgGrid`, `MgDbTableBase`, `MgEntityBase`) -Topic codes intentionally avoid these 2-char prefixes (`Ac`, `Mg`) to prevent visual confusion in mixed content. Topic codes are 2-5 chars and SHOULD NOT start with `Ac` or `Mg`. (Example principle: a hypothetical 2-char `MG` topic code would visually collide with `Mg*` class names; choose a more distinctive ≥3-char code.) +Topic codes intentionally avoid these 2-char prefixes (`Ac`, `Mg`) to prevent visual confusion in mixed content. Topic codes are 2-6 chars and SHOULD NOT start with `Ac` or `Mg`. (Example principle: a hypothetical 2-char `MG` topic code would visually collide with `Mg*` class names; choose a more distinctive ≥3-char code.) ## Examples (ACCORE only) diff --git a/.github/skills/protocol-audit/SKILL.md b/.github/skills/protocol-audit/SKILL.md index 86dddc6..b89ce4a 100644 --- a/.github/skills/protocol-audit/SKILL.md +++ b/.github/skills/protocol-audit/SKILL.md @@ -56,8 +56,14 @@ For each `": "` entry, resolve `//.github/LLM_PROTOCOL_DECISIONS.md`; the concrete path is resolvable from REPOS.md). diff --git a/AyCode.Core/docs/BINARY/BINARY_TODO.md b/AyCode.Core/docs/BINARY/BINARY_TODO.md index 9bdba57..c0eb34d 100644 --- a/AyCode.Core/docs/BINARY/BINARY_TODO.md +++ b/AyCode.Core/docs/BINARY/BINARY_TODO.md @@ -62,3 +62,37 @@ After ACCORE-BIN-T-W9F1 lands, JIT of generated `WriteProperties` / `ScanObject` **Acceptance:** - Benchmark a realistic entity graph (≥ 3 referenced child types) and show first-call time within ~10% of steady-state after ACCORE-BIN-T-W9F1 + chosen mitigation(s). - Document which combination is recommended for SignalR hot-path workloads vs. batch serialization. + +## ACCORE-BIN-T-Z3K8: Replace `IId` interface dependency with convention/attribute-based Id detection +**Priority:** P1 · **Type:** Refactor + +The binary serializer currently detects Id-tracking properties via the `IId` interface (`AyCode.Interfaces`). This couples the serializer to a framework-specific abstraction and forces consumer types to implement the interface for tracking participation. Move to a POCO-friendly detection scheme: + +- **`IdDetectionMode.Convention`** (default) — convention-based; any property named `Id` is treated as the tracking key. Zero-friction onboarding. +- **`IdDetectionMode.Attribute`** — explicit; only properties marked with a serializer-native `[Id]` (or similar) attribute are tracked. +- **`[IgnoreId]`** attribute — escape hatch in `Convention` mode to exclude an Id-named property from tracking when the developer wants explicit opt-out. + +**Implicit contract for `Convention` mode:** within a single class, the `Id` property must be type-level unique. Whether it semantically represents a primary key or a sequence number is irrelevant — the tracker keys by `(Type, Id)`, so per-type uniqueness is the only requirement. Violating this invariant typically signals a domain-modelling problem, not a serializer bug. Design rationale discussed in conversation 2026-04-27. + +**Acceptance:** +- Binary serializer no longer references `IId` in any execution path (no interface checks, no `where T : IId` constraints in the serializer surface). +- Wire format unchanged. +- Existing consumers using `IId`-implementing types still work transparently in `Convention` mode (their `Id` property is detected via convention). +- New consumers can use plain POCOs with no `AyCode.Interfaces` dependency. +- `IdDetectionMode` exposed on `AcBinaryOptions` (or successor options class post-rebrand). +- Default mode = `Convention`. + +## ACCORE-BIN-T-N7V1: Replace `[JsonIgnore]` dependency with serializer-native ignore attribute +**Priority:** P2 · **Type:** Refactor + +Property exclusion from binary serialization currently relies on `[JsonIgnore]` (Newtonsoft.Json). This couples the binary serializer to a third-party JSON library's attribute and is conceptually wrong — a binary serializer should not consult a JSON-specific marker for its exclusion semantics. + +Define a serializer-native ignore attribute (working name `[BinaryIgnore]`; final name TBD pending broader rebrand). For backward compatibility during transition, also continue recognizing `[JsonIgnore]` with a deprecation note. + +**Possible cross-cutting consideration:** if `Toon` and other future serializers also need property-exclusion, a single shared attribute (e.g., `[SerializerIgnore]` in a common abstractions package) may be cleaner than per-serializer attributes. Decide before naming finalizes — this may belong in `XCUT_TODO.md` rather than purely BINARY scope. + +**Acceptance:** +- Native ignore attribute defined in the binary serializer's namespace (or shared abstractions package, pending the cross-cutting decision above). +- Both native attribute and `[JsonIgnore]` recognized during a transitional period; native attribute takes precedence on conflict. +- `[JsonIgnore]` recognition flagged for removal in a future major version (track in a follow-up cleanup TODO once consumer projects have migrated). +- No new code dependency on Newtonsoft.Json for property-exclusion logic. diff --git a/AyCode.Services.Server/docs/SIGNALR_DATASOURCE/README.md b/AyCode.Services.Server/docs/SIGNALR_DATASOURCE/README.md new file mode 100644 index 0000000..fd6d530 --- /dev/null +++ b/AyCode.Services.Server/docs/SIGNALR_DATASOURCE/README.md @@ -0,0 +1,259 @@ +# SignalR DataSource + +Change-tracked real-time collection on top of the SignalR transport layer. Source: `SignalRs/AcSignalRDataSource.cs` in `AyCode.Services.Server`. + +> For the underlying transport (tag system, wire protocol, client base) see `AyCode.Services/docs/SIGNALR/README.md`. +> For server hub infrastructure see `../SIGNALR/README.md`. + +## Overview + +`AcSignalRDataSource` is an **abstract** generic `IList` that synchronizes with the server via CRUD tags. It handles change tracking, rollback, sync state, and pluggable Binary/JSON+GZip deserialization — so consuming code works with a regular list while the DataSource manages communication. + +```csharp +public abstract class AcSignalRDataSource : IList, IList, IReadOnlyList + where TDataItem : class, IId // entity with ID + where TId : struct // Guid, int, etc. + where TIList : class, IList // List or AcObservableCollection +``` + +Consumers must subclass — the class is abstract: + +```csharp +public class MySampleDataSource : AcSignalRDataSource> +{ + public MySampleDataSource(AcSignalRClientBase client, SignalRCrudTags tags) + : base(client, tags) { } +} +``` + +**Constructor:** + +```csharp +new MySampleDataSource( + AcSignalRClientBase signalRClient, // transport client + SignalRCrudTags crudTags, // 5 tags for CRUD operations + object[]? contextIds = null // optional server-side filter context +) +``` + +## SignalRCrudTags + +A `sealed class` that bundles **5 independent tag integers** — one per CRUD operation. Tags are NOT sequential offsets; each tag is independently assigned: + +```csharp +public sealed class SignalRCrudTags( + int getAllTag, + int getItemTag, + int addTag, + int updateTag, + int removeTag) +``` + +**Usage (consuming project — domain-agnostic example):** + +```csharp +public abstract class MyAppSignalRTags : AcSignalRTags +{ + public const int SampleGetAll = 300; + public const int SampleGetItem = 301; + public const int SampleAdd = 302; + public const int SampleUpdate = 303; + public const int SampleRemove = 304; +} + +var crudTags = new SignalRCrudTags( + MyAppSignalRTags.SampleGetAll, + MyAppSignalRTags.SampleGetItem, + MyAppSignalRTags.SampleAdd, + MyAppSignalRTags.SampleUpdate, + MyAppSignalRTags.SampleRemove +); +``` + +**Tag lookup:** `GetMessageTagByTrackingState(TrackingState)` maps tracking state to the corresponding tag via switch expression. + +## Serializer Selection + +```csharp +public AcSerializerType SerializerType { get; set; } = AcSerializerType.Binary; +``` + +Controls the deserialization format on the **raw byte[]** load path (see Data Loading). Must match the server's `SerializerOptions.SerializerType` for that endpoint. Default: `Binary`. Override to `JsonGZip` for JSON datasources. + +## Data Loading + +```csharp +await dataSource.LoadDataSource(clearChangeTracking: true); // sync-wait transport +await dataSource.LoadDataSourceAsync(clearChangeTracking: true); // async callback path +await dataSource.LoadDataSource(fromSource, refreshDataFromDbAsync: false, + setSourceToWorkingReferenceList: false, clearChangeTracking: true); // in-memory load (no transport) +await dataSource.LoadDataSourceFromResponseData(responseData, serializerType, + refreshDataFromDbAsync: false, setSourceToWorkingReferenceList: false, + clearChangeTracking: true); // pre-fetched payload +await dataSource.LoadItem(id); // single item by ID + // fires OnDataSourceItemChanged + // with TrackingState.Get +``` + +**Three deserialization paths** in `LoadDataSourceFromResponseData`: + +1. **Raw byte[]** (`responseData is byte[]`) — consumer-side deserialize: + - `AcSerializerType.Binary` + `IAcObservableCollection`: `BeginUpdate()` → `AcBinaryDeserializer.PopulateMerge` → `EndUpdate()` (single batched UI notification). + - `AcSerializerType.Binary` + plain `IList`: `AcBinaryDeserializer.Populate`. + - `AcSerializerType.JsonGZip` + `IAcObservableCollection`: `GzipHelper.DecompressToString` → `PopulateFromJson`. + - `AcSerializerType.JsonGZip` + plain `IList`: `GzipHelper.DecompressToString` → `JsonTo`. + +2. **Typed object** (`responseData is TIList`) — protocol already deserialized: + - `IAcObservableCollection`: `BeginUpdate` → `Clear` + `foreach Add` → `EndUpdate`. + - Otherwise: direct `Clear` + `foreach Add`. + +3. **Fallback re-serialize** (responseData neither `byte[]` nor `TIList` — e.g., bare `List` in test scenarios without protocol): `AcBinarySerializer.Serialize(responseData)` → bytes → process as Path 1 (Binary). + +**Context/Filtering:** `ContextIds` (`object[]`) and `FilterText` (`string`) are sent with every GetAll request for server-side filtering. + +## Change Tracking + +Each modified item is wrapped in `TrackingItem`: + +| Field | Purpose | +|---|---| +| `TrackingState` | `Add`, `Update`, `Remove`, `Get` | +| `CurrentValue` | Current item reference | +| `OriginalValue` | Clone for rollback (`TrackingItemHelpers.JsonClone` or `ReflectionClone`) | + +The `ChangeTracking` class manages the list of tracked items. + +### CRUD Operations + +``` +Add(item): → TrackingState.Add + InnerList.Add(item) +Add(item, autoSave): → optionally calls SaveItem after +AddOrUpdate(item, autoSave): → exists? Update : Add (TrackingState auto-determined) +Insert(index, item): → TrackingState.Add + InnerList.Insert(index, item) +Insert(index, item, autoSave): → optionally calls SaveItem after +Update(i, item, autoSave): → TrackingState.Update + clone original + InnerList[i] = item +Update(item, autoSave): → as Update(i, ...) by item.Id lookup +Remove(id, autoSave): → TrackingState.Remove + clone original + InnerList.RemoveAt +Remove(item, autoSave): → as Remove(id) by item.Id +TryRemove(id, out item): → no-throw remove (returns bool) +RemoveAt(index, autoSave): → by index +``` + +`autoSave` parameter — if true, immediately calls `SaveItem()` for that single change after the local mutation. + +**Manual tracking:** `SetTrackingStateToUpdate(item)` marks an existing item as modified without replacing it — useful when properties are mutated in-place. + +### Additional collection helpers + +| Member | Returns | Purpose | +|---|---|---| +| `AddRange(IEnumerable)` | `void` | batch insert (observable-aware: `IAcObservableCollection.AddRange` or `List.AddRange`, fallback per-item `Add`) | +| `TryGetValue(id, out item)` | `bool` | ID-keyed lookup | +| `TryGetIndex(id, out index)` | `bool` | index-keyed lookup | +| `IndexOf(id)` / `IndexOf(item)` | `int` | -1 if not found | +| `Contains(item)` | `bool` | by ID | +| `AsReadOnly()` | `ReadOnlyCollection` | wrap | +| `BinarySearch(...)` | `int` | currently throws `NotImplementedException` (see SIGNALR_DATASOURCE_ISSUES.md) | + +### Events + +| Event | Signature | Fires when | +|---|---|---| +| `OnDataSourceItemChanged` | `Func, Task>?` | After each item is saved or `LoadItem` completes (TrackingState in args) | +| `OnDataSourceLoaded` | `Func?` | After `LoadDataSource*` completes | +| `OnSyncingStateChanged` | `Action?` | On 0→1 (true) and 1→0 (false) sync transitions | + +`ItemChangedEventArgs` carries `Item: T` and `TrackingState: TrackingState`. + +## SaveChanges + +| Method | Returns | Transport pattern | Use case | +|---|---|---|---| +| `SaveChanges()` | `Task>` (remaining failures) | Sync-wait via `ContinueWith` | Caller needs to know what failed | +| `SaveChangesAsync()` | `Task` (void) | Fire-and-forget callback (`Action`) | Background save, no result inspection | +| `SaveItem(id)` | `Task` | Sync-wait | Save a single tracked item | +| `SaveItem(id, trackingState)` | `Task` | Sync-wait | Save with explicit state | +| `SaveItem(item, trackingState)` | `Task` | Sync-wait | Save without lookup | + +``` +Both batch flows: + BeginSync() + for each tracked item: + tag = CrudTags.GetMessageTagByTrackingState(state) + response = SignalRClient.PostDataAsync(tag, item) + on success: ProcessSavedResponseItem → CopyTo InnerList item, remove tracking, + fire OnDataSourceItemChanged + on failure: TryRollbackItem() → restore OriginalValue + EndSync() +``` + +## Rollback + +```csharp +bool TryRollbackItem(TId id, out TDataItem? originalValue); +void Rollback(); // reverts all tracked changes +``` + +`TryRollbackItem` restores `OriginalValue` to `InnerList`: +- `TrackingState.Add`: removes item entirely (no original existed). +- `TrackingState.Update` / `Remove`: re-applies `OriginalValue.CopyTo(InnerList[index])`, or re-adds if missing. +- The tracking entry is removed in either case. + +`Rollback()` iterates and calls `TryRollbackItem` for every tracked item. + +## Sync State + +```csharp +private int _activeSyncOperations; // Interlocked counter + +BeginSync(): Interlocked.Increment → fires OnSyncingStateChanged(true) on 0→1 +EndSync(): Interlocked.Decrement → fires OnSyncingStateChanged(false) on 1→0 +IsSyncing: _activeSyncOperations > 0 +``` + +UI binds to `IsSyncing` to show loading indicators. The counter supports nested sync operations. + +## Working Reference List + +```csharp +dataSource.SetWorkingReferenceList(externalList); +// Now dataSource operates directly on externalList — same reference, no copy +TIList innerList = dataSource.GetReferenceInnerList(); +bool hasRef = dataSource.HasWorkingReferenceList; +``` + +Useful when the UI already has a bound collection and you want the DataSource to manage it in-place. + +## Locking Strategy + +| Lock | Scope | Used by | +|---|---|---| +| `object _syncRoot` | Synchronous | Count, Contains, IndexOf, GetEnumerator, Add (no-save), Insert (no-save), CopyTo | +| `SemaphoreSlim _asyncLock` | Asynchronous | LoadDataSource*, AddOrUpdate, Update, Remove (with save), SaveItem, SaveChanges* | + +`GetEnumerator()` returns `InnerList.ToList().GetEnumerator()` — safe copy to avoid mutation during iteration. + +## Relationship to Transport + +The DataSource is a **consumer** of the SignalR transport, not part of it: + +``` +DataSource.SaveChanges() + → CrudTags.GetMessageTagByTrackingState(state) → tag + → AcSignalRClientBase.PostDataAsync(tag, item) ← transport layer + → OnReceiveMessage(tag, bytes, requestId) ← wire protocol + → Server method with [SignalR(tag)] ← tag dispatch +``` + +Projects can also call the transport directly without DataSource — see `AyCode.Services/docs/SIGNALR/README.md`. + +> **Cross-cutting:** `ACCORE-SIG-I-T7S2` (GetAll raw byte[] for populate/merge) lives in `../SIGNALR/SIGNALR_ISSUES.md` because it spans transport + DataSource concerns equally. + +## Key Source Files + +| Component | Path | +|---|---| +| DataSource (abstract base) | `SignalRs/AcSignalRDataSource.cs` (in `AyCode.Services.Server`) | +| Tracking helpers (`JsonClone` / `ReflectionClone`) | `SignalRs/TrackingItemHelpers.cs` (in `AyCode.Services.Server`) | +| CRUD tags | `SignalRs/SignalRCrudTags.cs` (in `AyCode.Services`) | +| Transport client | `SignalRs/AcSignalRClientBase.cs` (in `AyCode.Services`) | diff --git a/AyCode.Services.Server/docs/SIGNALR_DATASOURCE/SIGNALR_DATASOURCE_ISSUES.md b/AyCode.Services.Server/docs/SIGNALR_DATASOURCE/SIGNALR_DATASOURCE_ISSUES.md new file mode 100644 index 0000000..b0912e6 --- /dev/null +++ b/AyCode.Services.Server/docs/SIGNALR_DATASOURCE/SIGNALR_DATASOURCE_ISSUES.md @@ -0,0 +1,56 @@ +# SignalR DataSource — Known Issues + +Issues specific to `AcSignalRDataSource` (state management, change tracking, public API surface). Issues affecting the underlying SignalR transport itself live in `../SIGNALR/SIGNALR_ISSUES.md` (`SIG` topic). + +Cross-cutting transport+DataSource concerns intentionally remain under `SIG` — see `../SIGNALR/SIGNALR_ISSUES.md#accore-sig-i-t7s2` (GetAll raw byte[] for populate/merge). + +--- + +## ACCORE-SIGDS-B-T4K9: SaveChangesAsync silently drops per-item save Tasks + +**Status:** Open +**Affects:** `AcSignalRDataSource.SaveTrackingItemUnsafeAsync` (line 1001-1002) + +```csharp +private async Task SaveTrackingItemUnsafeAsync(TrackingItem trackingItem) + => SaveItemUnsafeAsync(trackingItem.CurrentValue, trackingItem.TrackingState); +``` + +`async Task` with an expression body that produces another `Task` discards the inner Task — the outer state machine wraps a void completion, so the per-item save is fire-and-forget. The caller in `SaveChangesAsync` (line 950) `await SaveTrackingItemUnsafeAsync(trackingItem)` returns immediately; the foreach proceeds to the next item without waiting for the previous server response. + +If this is the intended fire-and-forget batch behaviour, the method should drop `async` and return the inner Task explicitly (consumers can opt into await). If sequential save was the intent, the body needs `await SaveItemUnsafeAsync(...)`. + +**Possible fix direction:** +```csharp +// Option A — explicit fire-and-forget (rename + drop async): +private void DispatchTrackingItemUnsafeAsync(TrackingItem ti) + => SaveItemUnsafeAsync(ti.CurrentValue, ti.TrackingState).Forget(); + +// Option B — sequential (proper await): +private Task SaveTrackingItemUnsafeAsync(TrackingItem ti) + => SaveItemUnsafeAsync(ti.CurrentValue, ti.TrackingState); +// (drop `async`; caller `await` waits the inner Task) +``` + +--- + +## ACCORE-SIGDS-I-W2H6: BinarySearch is public but throws NotImplementedException + +**Status:** Open +**Affects:** `AcSignalRDataSource.BinarySearch` (lines 1179, 1184, 1185) + +```csharp +public int BinarySearch(int index, int count, TDataItem item, IComparer? comparer) +{ + throw new NotImplementedException($"BinarySearch"); +} +public int BinarySearch(TDataItem item) => BinarySearch(0, Count, item, null); +public int BinarySearch(TDataItem item, IComparer? comparer) => BinarySearch(0, Count, item, comparer); +``` + +All three overloads are `public` (visible via IntelliSense, callable by consumers) but throw at runtime. Sort-dependent operation — DataSource ordering is not enforced by the framework, so a meaningful implementation requires an explicit sort contract. + +**Possible fix directions** (mutually exclusive): +- Implement (delegating to `InnerList.BinarySearch` if `InnerList is List`; for `IAcObservableCollection` document the comparer requirement). +- Mark `[Obsolete("Not implemented")]` + change visibility to `protected internal` (keeps source compatibility, hides from external callers). +- Remove the three overloads outright and rely on consumers using `IndexOf` + manual sort if needed. diff --git a/AyCode.Services.Server/docs/SIGNALR_DATASOURCE/SIGNALR_DATASOURCE_TODO.md b/AyCode.Services.Server/docs/SIGNALR_DATASOURCE/SIGNALR_DATASOURCE_TODO.md new file mode 100644 index 0000000..381cfcf --- /dev/null +++ b/AyCode.Services.Server/docs/SIGNALR_DATASOURCE/SIGNALR_DATASOURCE_TODO.md @@ -0,0 +1,29 @@ +# SignalR DataSource — TODO + +Forward-looking work specific to `AcSignalRDataSource`. Transport-side TODOs live in `../SIGNALR/SIGNALR_TODO.md`. + +## Priority legend +- **P0** blocker · **P1** important · **P2** nice-to-have · **P3** idea + +--- + +## ACCORE-SIGDS-T-N5R8: ChangeTracking._trackingItems → Dictionary +**Priority:** P3 · **Type:** Performance refactor · **Status:** Open + +Pre-existing `//TODO: Dictionary... - J.` comment in `AcSignalRDataSource.cs:68`: + +```csharp +private readonly List> _trackingItems = []; +``` + +`ChangeTracking.FindIndex(newValue)` and `TryGetTrackingItem(id)` both linear-scan `_trackingItems`. For DataSources with many concurrent tracked changes (large grids with frequent edits), this is O(N) per operation. A `Dictionary>` would give O(1) lookup; `ToList()` enumeration (the only external read path) is order-agnostic, so no behavioural regression. + +Trade-off: per-tracking-item dictionary overhead; negligible for typical workload. + +**Acceptance:** +- `_trackingItems` switched to `Dictionary>`. +- `FindIndex` removed (caller paths in `AddTrackingItem` re-flow as TryGetValue + Add/Update). +- `TryGetTrackingItem` becomes single dict lookup. +- `ToList()` returns `_trackingItems.Values.ToList()`. +- `Remove(trackingItem)` → `Remove(trackingItem.CurrentValue.Id)`. +- All existing tests pass without change to public API surface. diff --git a/AyCode.Services/docs/SIGNALR_BINARY_PROTOCOL/README.md b/AyCode.Services/docs/SIGNALR_BINARY_PROTOCOL/README.md index 855435a..ff25e66 100644 --- a/AyCode.Services/docs/SIGNALR_BINARY_PROTOCOL/README.md +++ b/AyCode.Services/docs/SIGNALR_BINARY_PROTOCOL/README.md @@ -271,4 +271,8 @@ services.AddSingleton(); // derived from AcSignalRClientBase **Note**: `AcSignalRClientBase` is `HubConnectionBuilder`-injected and calls only `Build()` + dispatch wiring internally. All transport/protocol configuration lives in `Program.cs` — visible, overridable per environment, and identical on both ends of the wire. +## Related ADRs + +- [`adr/0001-acbinary-decorator-feature-stack-design.md`](../adr/0001-acbinary-decorator-feature-stack-design.md) — *AcBinaryHubProtocol optional feature stack — decorator-based composition design* (Status: Proposed). Umbrella ADR for the optional decorator-based feature stack (encryption, compression with `MinSize`, OpenTelemetry tracing, HMAC signing). Each NuGet-competitiveness TODO entry (`ACCORE-SBP-T-H7M5` / `N9F3` / `J5W8` / `B3K6` in `SIGNALR_BINARY_PROTOCOL_TODO.md`) resolves under this umbrella's architectural framework. Forthcoming leaf ADRs (0002-0005) will provide per-feature design + threat model. + **Source:** `AyCode.Services/SignalRs/AcBinaryHubProtocol.cs` (base), `AyCode.Services/SignalRs/AyCodeBinaryHubProtocol.cs` (consumer logic), `AyCode.Services/SignalRs/BinaryProtocolMode.cs` (enum), `AyCode.Services/SignalRs/AcBinaryHubProtocolOptions.cs` (options), `AyCode.Services/SignalRs/AcSignalRProtocolExtensions.cs` (DI extensions) diff --git a/AyCode.Services/docs/SIGNALR_BINARY_PROTOCOL/SIGNALR_BINARY_PROTOCOL_TODO.md b/AyCode.Services/docs/SIGNALR_BINARY_PROTOCOL/SIGNALR_BINARY_PROTOCOL_TODO.md index 8a712ea..fb40543 100644 --- a/AyCode.Services/docs/SIGNALR_BINARY_PROTOCOL/SIGNALR_BINARY_PROTOCOL_TODO.md +++ b/AyCode.Services/docs/SIGNALR_BINARY_PROTOCOL/SIGNALR_BINARY_PROTOCOL_TODO.md @@ -58,7 +58,7 @@ Zero first-message overhead, fully explicit. Both sides advertise their send-mod These ideas were captured because the **wider binary-protocol market is currently silent** on these features (no major .NET binary serializer ships built-in encryption / compression / tracing). Real differentiation potential, but only if executed correctly. ## ACCORE-SBP-T-H7M5: Optional payload encryption (`AcEncryptionOptions`) -**Priority:** P3 — IDEA · **Type:** Feature (NuGet competitiveness) · **Status:** Open +**Priority:** P3 — IDEA · **Type:** Feature (NuGet competitiveness) · **Status:** Open · **Umbrella ADR:** [`adr/0001`](../adr/0001-acbinary-decorator-feature-stack-design.md) ### Niches where TLS alone is insufficient - **SignalR backplane / Azure SignalR Service** — relay sees plaintext; payload encryption gives true end-to-end client↔client. @@ -84,7 +84,7 @@ These ideas were captured because the **wider binary-protocol market is currentl - Decorator API sketch reviewed. ## ACCORE-SBP-T-N9F3: Optional message compression with `MinSize` threshold -**Priority:** P3 — IDEA · **Type:** Feature (NuGet competitiveness) · **Status:** Open +**Priority:** P3 — IDEA · **Type:** Feature (NuGet competitiveness) · **Status:** Open · **Umbrella ADR:** [`adr/0001`](../adr/0001-acbinary-decorator-feature-stack-design.md) ### Niches - **Large structured payloads** (orders, product lists, shipping documents) — typical 50-90% compression on text-heavy DTOs. @@ -148,7 +148,7 @@ In `AsyncSegment` mode the total message size is unknown until `CHUNK_END`, so a - Fallback / negotiation strategy: what happens if peer can't decompress? (Suggested: wire-marker `0x00` always works since it means "uncompressed" — sender can downgrade per-message if peer signals incompatibility, similar to HTTP `Accept-Encoding` semantics.) ## ACCORE-SBP-T-J5W8: OpenTelemetry tracing integration -**Priority:** P3 — IDEA · **Type:** Feature (NuGet competitiveness) · **Status:** Open +**Priority:** P3 — IDEA · **Type:** Feature (NuGet competitiveness) · **Status:** Open · **Umbrella ADR:** [`adr/0001`](../adr/0001-acbinary-decorator-feature-stack-design.md) ### Niches - **Distributed tracing** is a modern .NET observability standard — gRPC has it, MessagePack/MemoryPack/Protobuf-net don't. @@ -166,7 +166,7 @@ In `AsyncSegment` mode the total message size is unknown until `CHUNK_END`, so a - Hot-path branch verification — when `ActivitySource == null`, JIT must eliminate the tracing code completely. ## ACCORE-SBP-T-B3K6: Optional HMAC signing (without encryption) -**Priority:** P3 — IDEA · **Type:** Feature (NuGet competitiveness) · **Status:** Open +**Priority:** P3 — IDEA · **Type:** Feature (NuGet competitiveness) · **Status:** Open · **Umbrella ADR:** [`adr/0001`](../adr/0001-acbinary-decorator-feature-stack-design.md) ### Niches - **Tamper detection where confidentiality is acceptable** — audit log forwarding, telemetry where data isn't sensitive but integrity must be provable. diff --git a/AyCode.Services/docs/adr/0001-acbinary-decorator-feature-stack-design.md b/AyCode.Services/docs/adr/0001-acbinary-decorator-feature-stack-design.md index 430b356..b349f2d 100644 --- a/AyCode.Services/docs/adr/0001-acbinary-decorator-feature-stack-design.md +++ b/AyCode.Services/docs/adr/0001-acbinary-decorator-feature-stack-design.md @@ -24,7 +24,7 @@ Adopt the **decorator-chain pattern** for composing optional features onto `AcBi ### `AcHubProtocolDecoratorBase` abstract base -A new abstract class in `AyCode.Services/SignalRs/` (concrete API spec is impl-level, tracked as a forthcoming TODO in `SIGNALR_BINARY_PROTOCOL_TODO.md`, no ID assigned yet): +A new abstract class in `AyCode.Services/SignalRs/` (concrete API spec is impl-level — see Follow-up #1 for tracking): - Delegates passthrough `IHubProtocol` members (`Name`, `Version`, `TransferFormat`, `IsVersionSupported`, `GetMessageBytes`) to the wrapped inner protocol. - Exposes `protected IHubProtocol Inner { get; }` to derived classes. @@ -72,7 +72,7 @@ At connection setup (1 application-level handshake roundtrip after SignalR's pro **Mismatch handling**: asymmetric registration (one side has decorator, other doesn't) → handshake fails fast with a logged reason at `Error` level. Per-message asymmetry (handshake-skip artefacts) is handled by the decorator-failure unified protocol below. -The app-level handshake mechanism's concrete shape (Option A: a special `__ac_handshake` HubMessage as first message; Option B: wire-level `WriteHandshake` virtual on `AcHubProtocolDecoratorBase`) is **deferred to a follow-up** — the umbrella commits to "handshake-based" but does not pick the mechanism. Tracked as part of the forthcoming TODO. +The app-level handshake mechanism's concrete shape (Option A: a special `__ac_handshake` HubMessage as first message; Option B: wire-level `WriteHandshake` virtual on `AcHubProtocolDecoratorBase`) is **deferred to a follow-up** — the umbrella commits to "handshake-based" but does not pick the mechanism. See Follow-up #3. ### Per-message `IsCompressed` flag — semantic (A): standalone byte @@ -186,9 +186,9 @@ A v1 client (no decorators registered) connecting to a v2 server (decorators in ### Follow-ups required -1. **`AcHubProtocolDecoratorBase` implementation** — abstract class added to `AyCode.Services/SignalRs/`; tracked as a new forthcoming TODO entry in `SIGNALR_BINARY_PROTOCOL_TODO.md` (no ID assigned yet). Implementation deferred until at least one leaf ADR (0002-0005) reaches `Status: Accepted`. -2. **Stacking-order validation in DI** — `AddAcEncryption` / `AddAcSigning` / `AddAcCompression` extension methods should warn or throw if registered in a non-canonical order (concrete impl part of the forthcoming TODO). -3. **App-level handshake mechanism design** — Option A (`__ac_handshake` HubMessage as first message) vs Option B (`WriteHandshake` virtual on `AcHubProtocolDecoratorBase`). Tracked as part of the forthcoming TODO. +1. **`AcHubProtocolDecoratorBase` implementation** — abstract class to be added to `AyCode.Services/SignalRs/`. **Implementation is deferred** until at least one leaf ADR (0002-0005) reaches `Status: Accepted` and triggers the actual feature work. **At that point**, a `SIGNALR_BINARY_PROTOCOL_TODO.md` entry will be added with a freshly-generated `ACCORE-SBP-T-` ID per the `docs-check` skill Step 5 procedure, and this follow-up's references throughout the ADR will be updated. +2. **Stacking-order validation in DI** — `AddAcEncryption` / `AddAcSigning` / `AddAcCompression` extension methods should warn or throw if registered in a non-canonical order. Concrete implementation lands together with Follow-up #1 once that work begins. +3. **App-level handshake mechanism design** — choose between Option A (`__ac_handshake` HubMessage as first message) vs Option B (`WriteHandshake` virtual on `AcHubProtocolDecoratorBase`). Decided + implemented together with Follow-up #1 when that work begins. 4. **Benchmark scaffolding** — message-granularity vtable cost measurement for the "negligible" claim. Each leaf ADR's acceptance criteria includes verifying this in their feature's context. 5. **Cross-references** — Step 8 of the `adr-author` skill: this ADR links to the 4 forthcoming leaf ADRs (0002-0005); `../SIGNALR_BINARY_PROTOCOL/README.md` gets a new `## Related ADRs` section pointing back to `0001-acbinary-decorator-feature-stack-design.md`. @@ -210,6 +210,6 @@ A v1 client (no decorators registered) connecting to a v2 server (decorators in ## Related -- Related TODOs: `../SIGNALR_BINARY_PROTOCOL/SIGNALR_BINARY_PROTOCOL_TODO.md` ACCORE-SBP-T-H7M5 (encryption), ACCORE-SBP-T-N9F3 (compression+MinSize), ACCORE-SBP-T-J5W8 (tracing), ACCORE-SBP-T-B3K6 (signing); the forthcoming `AcHubProtocolDecoratorBase` impl + handshake-mechanism TODO (to be added in Step 8 cross-reference round). +- Related TODOs: `../SIGNALR_BINARY_PROTOCOL/SIGNALR_BINARY_PROTOCOL_TODO.md` ACCORE-SBP-T-H7M5 (encryption), ACCORE-SBP-T-N9F3 (compression+MinSize), ACCORE-SBP-T-J5W8 (tracing), ACCORE-SBP-T-B3K6 (signing). The `AcHubProtocolDecoratorBase` impl + handshake-mechanism TODO is **not yet created** — see Follow-up #1 for the conditions under which it will be added. - Forthcoming ADRs: 0002 (payload encryption), 0003 (message compression with MinSize), 0004 (OpenTelemetry tracing), 0005 (HMAC signing-only) — all will declare `Depends on: ADR-0001`. - Topic-folder cross-ref: `../SIGNALR_BINARY_PROTOCOL/README.md` to add `## Related ADRs` section in Step 8. diff --git a/docs/adr/0001-user-bearer-token-flow.md b/docs/adr/0001-user-bearer-token-flow.md index 3370085..e330c4d 100644 --- a/docs/adr/0001-user-bearer-token-flow.md +++ b/docs/adr/0001-user-bearer-token-flow.md @@ -100,7 +100,6 @@ Adopt a layered bearer-token authentication architecture across all client and s - **Per-tag DI policy registry** (rejected): too much complexity for Layer 0; mixes consumer-specific tag lists into framework. - **Custom token middleware from scratch** (rejected): non-standard, no `[Authorize]` interop, no out-of-the-box JWT validation. - *Future flexibility:* locks us out of standard OAuth2 / external-IdP integration when ADR `0002+` extends scope; the chosen `JwtBearer` pipeline keeps that door open. - - *Future flexibility:* locks out standard OAuth2 / external-IdP integration when ADR `0002+` extends scope; the chosen `JwtBearer` pipeline keeps that door open. ## Related