[LOADED_DOCS: 3 files, no new loads]

Update ID format to use per-repo prefixes and random suffix

Migrated all issue, TODO, and decision IDs to a new 4-part format: <PREFIX>-<TOPIC>-<TYPE>-<RAND>. Added per-repo prefix declarations in copilot-instructions.md and documented conventions in REPO_PREFIXES.md. Updated all topic registries, logs, cross-references, and documentation to use the new format. Introduced MIGRATION_ID_MAPPING.md for old-to-new ID mapping. Enhanced skills and protocol audit logic to validate and enforce per-repo prefixes and topic codes at runtime. Clarified Framework-First doctrine and ensured all references are unambiguous.
This commit is contained in:
Loretta 2026-04-26 19:12:50 +02:00
parent b59b42d381
commit c062ded9a4
36 changed files with 738 additions and 335 deletions

File diff suppressed because one or more lines are too long

236
.github/MIGRATION_ID_MAPPING.md vendored Normal file
View File

@ -0,0 +1,236 @@
# MIGRATION_ID_MAPPING — Phase 4 (deletion-pending)
> ⚠️ **LLM-targeted deletion reminder**: This file was a Phase 4 transient artifact for the ID-format migration. **From 2026-05-10 onward** (2 weeks after Phase 5 completion 2026-04-26), any LLM agent that reads this file MUST surface the following single-line reminder to the user at the start of its next response:
>
> *"`MIGRATION_ID_MAPPING.md` is past its 2-week retention window (since 2026-05-10) — confirm if you want me to delete it now."*
>
> Until 2026-05-10 the file remains untouched as a reverse-lookup aid (git history, old chat transcripts, external references may still mention OLD IDs).
>
> **Phase 4 of the ID-format migration** (see `LLMP-DEC-50`). Maps existing 3-component IDs (`<TOPIC>-<TYPE>-<N>`) to the new 4-component format (`<PREFIX>-<TOPIC>-<TYPE>-<RAND>`).
>
> **Status:** generated 2026-04-26; Phase 5 (per-topic rename) completed 2026-04-26; Phase 6 (cross-ref cleanup) consumes this table. **Original Phase 7 plan (delete this file) is superseded** — this file now persists as a historical reverse-lookup aid, with a 2-week deletion-review window starting 2026-05-10 (per user instruction 2026-04-26).
>
> **Generation rules:**
> - Repo prefix per `REPO_PREFIXES.md` (file → repo → prefix).
> - Random 4-char `[A-Z0-9]` suffix; unique within each `<PREFIX>-<TOPIC>-<TYPE>` triplet.
> - `LLMP-DEC-N` entries are NOT migrated (workspace-meta exception).
> - Template/placeholder IDs (e.g. `## GRID-I-1: ...` example line, `AUTH-I-N` placeholder) are NOT migrated.
> - "Pending" references (mentioned in docs but not yet defined as `_ISSUES.md`/`_TODO.md` entries) are NOT migrated — see "Pending forward-references" section below; they get rewritten/removed in Phase 6.
---
## Summary
| Repo | Prefix | Issue IDs | TODO IDs | Total |
|---|---|---:|---:|---:|
| AyCode.Core | `ACCORE` | 41 | 36 | 77 |
| AyCode.Blazor | `ACBLAZOR` | 0 | 2 | 2 |
| Mango.Nop Libraries | `MGNOPLIB` | 0 | 0 | 0 |
| Mango.Nop.Core (sub-folder) | `MGNOPCORE` | 0 | 0 | 0 |
| Nop.Plugin.Misc.AIPlugin | `MGFBANKPLUG` | 0 | 0 | 0 |
| FruitBank | `FBANKNOP` | 0 | 0 | 0 |
| FruitBankHybridApp | `FBANKAPP` | 0 | 0 | 0 |
| **Total** | | **41** | **38** | **79** |
---
## ACCORE — AyCode.Core
### BINARY (BIN)
Canonical home: `AyCode.Core/docs/BINARY/BINARY_ISSUES.md`, `AyCode.Core/docs/BINARY/BINARY_TODO.md`.
| OLD_ID | FILE (repo-relative) | NEW_ID |
|---|---|---|
| `BIN-I-1` | `AyCode.Core/docs/BINARY/BINARY_ISSUES.md` | `ACCORE-BIN-I-D2J5` |
| `BIN-I-2` | `AyCode.Core/docs/BINARY/BINARY_ISSUES.md` | `ACCORE-BIN-I-G7N3` |
| `BIN-I-3` | `AyCode.Core/docs/BINARY/BINARY_ISSUES.md` | `ACCORE-BIN-I-S1F8` |
| `BIN-I-4` | `AyCode.Core/docs/BINARY/BINARY_ISSUES.md` | `ACCORE-BIN-I-V5L2` |
| `BIN-I-5` | `AyCode.Core/docs/BINARY/BINARY_ISSUES.md` | `ACCORE-BIN-I-K8R4` |
| `BIN-I-6` | `AyCode.Core/docs/BINARY/BINARY_ISSUES.md` | `ACCORE-BIN-I-P3M6` |
| `BIN-I-7` | `AyCode.Core/docs/BINARY/BINARY_ISSUES.md` | `ACCORE-BIN-I-T9X1` |
| `BIN-I-8` | `AyCode.Core/docs/BINARY/BINARY_ISSUES.md` | `ACCORE-BIN-I-B4Y7` |
| `BIN-I-9` | `AyCode.Core/docs/BINARY/BINARY_ISSUES.md` | `ACCORE-BIN-I-H2C5` |
| `BIN-I-10` | `AyCode.Core/docs/BINARY/BINARY_ISSUES.md` | `ACCORE-BIN-I-N6Q3` |
| `BIN-I-11` | `AyCode.Core/docs/BINARY/BINARY_ISSUES.md` | `ACCORE-BIN-I-F1W8` |
| `BIN-I-12` | `AyCode.Core/docs/BINARY/BINARY_ISSUES.md` | `ACCORE-BIN-I-J4D2` |
| `BIN-I-13` | `AyCode.Core/docs/BINARY/BINARY_ISSUES.md` | `ACCORE-BIN-I-R5V9` |
| `BIN-I-14` | `AyCode.Core/docs/BINARY/BINARY_ISSUES.md` | `ACCORE-BIN-I-L7G3` |
| `BIN-I-15` | `AyCode.Core/docs/BINARY/BINARY_ISSUES.md` | `ACCORE-BIN-I-M3K6` |
| `BIN-T-1` | `AyCode.Core/docs/BINARY/BINARY_TODO.md` | `ACCORE-BIN-T-S8P4` |
| `BIN-T-2` | `AyCode.Core/docs/BINARY/BINARY_TODO.md` | `ACCORE-BIN-T-Q2N7` |
| `BIN-T-3` | `AyCode.Core/docs/BINARY/BINARY_TODO.md` | `ACCORE-BIN-T-W9F1` |
| `BIN-T-4` | `AyCode.Core/docs/BINARY/BINARY_TODO.md` | `ACCORE-BIN-T-T5J8` |
### LOGGING (LOG)
Canonical home: `AyCode.Core/docs/LOGGING/LOGGING_ISSUES.md`, `AyCode.Core/docs/LOGGING/LOGGING_TODO.md`.
| OLD_ID | FILE (repo-relative) | NEW_ID |
|---|---|---|
| `LOG-I-1` | `AyCode.Core/docs/LOGGING/LOGGING_ISSUES.md` | `ACCORE-LOG-I-K7M2` |
| `LOG-I-2` | `AyCode.Core/docs/LOGGING/LOGGING_ISSUES.md` | `ACCORE-LOG-I-R9P3` |
| `LOG-I-3` | `AyCode.Core/docs/LOGGING/LOGGING_ISSUES.md` | `ACCORE-LOG-I-L4N8` |
| `LOG-I-4` | `AyCode.Core/docs/LOGGING/LOGGING_ISSUES.md` | `ACCORE-LOG-I-B2H5` |
| `LOG-I-5` | `AyCode.Core/docs/LOGGING/LOGGING_ISSUES.md` | `ACCORE-LOG-I-X7Q1` |
| `LOG-I-6` | `AyCode.Core/docs/LOGGING/LOGGING_ISSUES.md` | `ACCORE-LOG-I-V3J6` |
| `LOG-I-7` | `AyCode.Core/docs/LOGGING/LOGGING_ISSUES.md` | `ACCORE-LOG-I-T8F2` |
| `LOG-I-8` | `AyCode.Core/docs/LOGGING/LOGGING_ISSUES.md` | `ACCORE-LOG-I-M4C9` |
| `LOG-I-9` | `AyCode.Core/docs/LOGGING/LOGGING_ISSUES.md` | `ACCORE-LOG-I-P5W3` |
| `LOG-I-10` | `AyCode.Core/docs/LOGGING/LOGGING_ISSUES.md` | `ACCORE-LOG-I-K1Z7` |
| `LOG-T-1` | `AyCode.Core/docs/LOGGING/LOGGING_TODO.md` | `ACCORE-LOG-T-H6Y4` |
| `LOG-T-2` | `AyCode.Core/docs/LOGGING/LOGGING_TODO.md` | `ACCORE-LOG-T-N2D8` |
| `LOG-T-3` | `AyCode.Core/docs/LOGGING/LOGGING_TODO.md` | `ACCORE-LOG-T-R7L3` |
| `LOG-T-4` | `AyCode.Core/docs/LOGGING/LOGGING_TODO.md` | `ACCORE-LOG-T-F4S6` |
| `LOG-T-5` | `AyCode.Core/docs/LOGGING/LOGGING_TODO.md` | `ACCORE-LOG-T-J9G2` |
| `LOG-T-6` | `AyCode.Core/docs/LOGGING/LOGGING_TODO.md` | `ACCORE-LOG-T-B8K5` |
| `LOG-T-7` | `AyCode.Core/docs/LOGGING/LOGGING_TODO.md` | `ACCORE-LOG-T-X1V4` |
| `LOG-T-8` | `AyCode.Core/docs/LOGGING/LOGGING_TODO.md` | `ACCORE-LOG-T-M7P2` |
| `LOG-T-9` | `AyCode.Core/docs/LOGGING/LOGGING_TODO.md` | `ACCORE-LOG-T-L3T8` |
| `LOG-T-10` | `AyCode.Core/docs/LOGGING/LOGGING_TODO.md` | `ACCORE-LOG-T-Q6Z1` |
| `LOG-T-11` | `AyCode.Core/docs/LOGGING/LOGGING_TODO.md` | `ACCORE-LOG-T-W4H9` |
### SIGNALR_BINARY_PROTOCOL (SBP)
Canonical home: `AyCode.Services/docs/SIGNALR_BINARY_PROTOCOL/SIGNALR_BINARY_PROTOCOL_ISSUES.md`, `..._TODO.md`.
| OLD_ID | FILE (repo-relative) | NEW_ID |
|---|---|---|
| `SBP-I-1` | `AyCode.Services/docs/SIGNALR_BINARY_PROTOCOL/SIGNALR_BINARY_PROTOCOL_ISSUES.md` | `ACCORE-SBP-I-F6T2` |
| `SBP-I-2` | `AyCode.Services/docs/SIGNALR_BINARY_PROTOCOL/SIGNALR_BINARY_PROTOCOL_ISSUES.md` | `ACCORE-SBP-I-G4B5` |
| `SBP-T-1` | `AyCode.Services/docs/SIGNALR_BINARY_PROTOCOL/SIGNALR_BINARY_PROTOCOL_TODO.md` | `ACCORE-SBP-T-P8X9` |
| `SBP-T-2` | `AyCode.Services/docs/SIGNALR_BINARY_PROTOCOL/SIGNALR_BINARY_PROTOCOL_TODO.md` | `ACCORE-SBP-T-K3J7` |
| `SBP-T-3` | `AyCode.Services/docs/SIGNALR_BINARY_PROTOCOL/SIGNALR_BINARY_PROTOCOL_TODO.md` | `ACCORE-SBP-T-L1V4` |
| `SBP-T-4` | `AyCode.Services/docs/SIGNALR_BINARY_PROTOCOL/SIGNALR_BINARY_PROTOCOL_TODO.md` | `ACCORE-SBP-T-R6D2` |
| `SBP-T-5` | `AyCode.Services/docs/SIGNALR_BINARY_PROTOCOL/SIGNALR_BINARY_PROTOCOL_TODO.md` | `ACCORE-SBP-T-H7M5` |
| `SBP-T-6` | `AyCode.Services/docs/SIGNALR_BINARY_PROTOCOL/SIGNALR_BINARY_PROTOCOL_TODO.md` | `ACCORE-SBP-T-N9F3` |
| `SBP-T-7` | `AyCode.Services/docs/SIGNALR_BINARY_PROTOCOL/SIGNALR_BINARY_PROTOCOL_TODO.md` | `ACCORE-SBP-T-J5W8` |
| `SBP-T-8` | `AyCode.Services/docs/SIGNALR_BINARY_PROTOCOL/SIGNALR_BINARY_PROTOCOL_TODO.md` | `ACCORE-SBP-T-B3K6` |
### SIGNALR (SIG)
Canonical home: `AyCode.Services/docs/SIGNALR/SIGNALR_ISSUES.md`, `AyCode.Services/docs/SIGNALR/SIGNALR_TODO.md`.
| OLD_ID | FILE (repo-relative) | NEW_ID |
|---|---|---|
| `SIG-I-1` | `AyCode.Services/docs/SIGNALR/SIGNALR_ISSUES.md` | `ACCORE-SIG-I-R4W7` |
| `SIG-I-2` | `AyCode.Services/docs/SIGNALR/SIGNALR_ISSUES.md` | `ACCORE-SIG-I-L5K3` |
| `SIG-I-3` | `AyCode.Services/docs/SIGNALR/SIGNALR_ISSUES.md` | `ACCORE-SIG-I-H8D6` |
| `SIG-I-4` | `AyCode.Services/docs/SIGNALR/SIGNALR_ISSUES.md` | `ACCORE-SIG-I-P1J4` |
| `SIG-I-5` | `AyCode.Services/docs/SIGNALR/SIGNALR_ISSUES.md` | `ACCORE-SIG-I-N3V8` |
| `SIG-I-6` | `AyCode.Services/docs/SIGNALR/SIGNALR_ISSUES.md` | `ACCORE-SIG-I-T7S2` |
| `SIG-I-7` | `AyCode.Services/docs/SIGNALR/SIGNALR_ISSUES.md` | `ACCORE-SIG-I-B5G9` |
| `SIG-I-8` | `AyCode.Services/docs/SIGNALR/SIGNALR_ISSUES.md` | `ACCORE-SIG-I-K6F1` |
| `SIG-I-9` | `AyCode.Services/docs/SIGNALR/SIGNALR_ISSUES.md` | `ACCORE-SIG-I-X4M7` |
| `SIG-T-1` | `AyCode.Services/docs/SIGNALR/SIGNALR_TODO.md` | `ACCORE-SIG-T-J2P5` |
| `SIG-T-2` | `AyCode.Services/docs/SIGNALR/SIGNALR_TODO.md` | `ACCORE-SIG-T-W8R3` |
| `SIG-T-3` | `AyCode.Services/docs/SIGNALR/SIGNALR_TODO.md` | `ACCORE-SIG-T-D7Q4` |
| `SIG-T-4` | `AyCode.Services/docs/SIGNALR/SIGNALR_TODO.md` | `ACCORE-SIG-T-V9H1` |
| `SIG-T-5` | `AyCode.Services/docs/SIGNALR/SIGNALR_TODO.md` | `ACCORE-SIG-T-M5L6` |
| `SIG-T-6` | `AyCode.Services/docs/SIGNALR/SIGNALR_TODO.md` | `ACCORE-SIG-T-S3N8` |
### TOON (TOON)
Canonical home: `AyCode.Core/docs/TOON/TOON_ISSUES.md`, `AyCode.Core/docs/TOON/TOON_TODO.md`.
| OLD_ID | FILE (repo-relative) | NEW_ID |
|---|---|---|
| `TOON-I-1` | `AyCode.Core/docs/TOON/TOON_ISSUES.md` | `ACCORE-TOON-I-B7L4` |
| `TOON-I-2` | `AyCode.Core/docs/TOON/TOON_ISSUES.md` | `ACCORE-TOON-I-X3H2` |
| `TOON-I-3` | `AyCode.Core/docs/TOON/TOON_ISSUES.md` | `ACCORE-TOON-I-P6V5` |
| `TOON-I-4` | `AyCode.Core/docs/TOON/TOON_ISSUES.md` | `ACCORE-TOON-I-K4Z9` |
| `TOON-T-1` | `AyCode.Core/docs/TOON/TOON_TODO.md` | `ACCORE-TOON-T-D1R7` |
| `TOON-T-2` | `AyCode.Core/docs/TOON/TOON_TODO.md` | `ACCORE-TOON-T-G5M3` |
| `TOON-T-3` | `AyCode.Core/docs/TOON/TOON_TODO.md` | `ACCORE-TOON-T-J8N6` |
| `TOON-T-4` | `AyCode.Core/docs/TOON/TOON_TODO.md` | `ACCORE-TOON-T-V2T4` |
| `TOON-T-5` | `AyCode.Core/docs/TOON/TOON_TODO.md` | `ACCORE-TOON-T-S6B9` |
| `TOON-T-6` | `AyCode.Core/docs/TOON/TOON_TODO.md` | `ACCORE-TOON-T-F3X1` |
| `TOON-T-7` | `AyCode.Core/docs/TOON/TOON_TODO.md` | `ACCORE-TOON-T-M9Q2` |
### XCUT (XCUT)
Canonical home: `AyCode.Core/docs/XCUT/XCUT_ISSUES.md`. Cross-ref pointer entries with the same ID exist in `BINARY_ISSUES.md` (line 148) and `SIGNALR_ISSUES.md` (line 131) — they are renamed to the same NEW_ID in Phase 5 (cross-ref pointers, not duplicate entries).
| OLD_ID | FILE (repo-relative) | NEW_ID |
|---|---|---|
| `XCUT-I-1` | `AyCode.Core/docs/XCUT/XCUT_ISSUES.md` (canonical) + cross-refs in `BINARY_ISSUES.md`, `AyCode.Services/docs/SIGNALR/SIGNALR_ISSUES.md` | `ACCORE-XCUT-I-X8Q1` |
---
## ACBLAZOR — AyCode.Blazor
### MGGRID (GRID)
Canonical home: `AyCode.Blazor.Components/docs/MGGRID/MGGRID_TODO.md`. (No issues yet — `MGGRID_ISSUES.md` only contains a placeholder example line.)
| OLD_ID | FILE (repo-relative) | NEW_ID |
|---|---|---|
| `GRID-T-1` | `AyCode.Blazor.Components/docs/MGGRID/MGGRID_TODO.md` | `ACBLAZOR-GRID-T-V4P7` |
| `GRID-T-2` | `AyCode.Blazor.Components/docs/MGGRID/MGGRID_TODO.md` | `ACBLAZOR-GRID-T-S2L9` |
---
## Pending forward-references (NOT migrated — Phase 6 cleanup)
These IDs are referenced in docs but not yet defined as `_ISSUES.md`/`_TODO.md` entries. They have no OLD_ID body to migrate. Phase 6 must decide per-case: (a) actually create the entry under the new format, or (b) rewrite/remove the reference.
| Referenced ID | Mentioned in | Decision deferred to Phase 6 |
|---|---|---|
| `LOG-T-12` | `docs/AUTH/README.md`, `docs/adr/0001-user-bearer-token-flow.md` | Tentative TODO ("Never log secrets" framework guideline). Either define as `ACCORE-LOG-T-<RAND>` and add to `LOGGING_TODO.md`, or rewrite reference. |
| `SBP-T-9` | `AyCode.Services/docs/adr/0001-acbinary-decorator-feature-stack-design.md` | Reserved for `AcHubProtocolDecoratorBase` impl + handshake; deferred until at least one leaf ADR (0002-0005) reaches `Status: Accepted`. Either define on demand or rewrite reference. |
## Template/placeholder IDs (NOT migrated)
These appear in template/example lines, not as real entries. They stay as-is (or get reformatted to the new placeholder syntax in Phase 5 alongside the topic rename).
| Placeholder | File | Note |
|---|---|---|
| `GRID-I-1` | `AyCode.Blazor.Components/docs/MGGRID/MGGRID_ISSUES.md:7` | Example line: "Add the first `## GRID-I-1: ...` entry below..." — should become "`## ACBLAZOR-GRID-I-<RAND>:`" example. |
| `AUTH-I-N` | `AyCode.Core/docs/AUTH/AUTH_ISSUES.md`, `AyCode.Core/docs/AUTH/README.md` | Generic placeholder — the `N` is literal "N", not a digit. Should become `ACCORE-AUTH-I-<RAND>` example. |
## Workspace-meta IDs (NOT migrated — bare exception)
`LLMP-DEC-N` Decision Log entries do NOT receive a repo prefix per `REPO_PREFIXES.md` "LLMP exception" section. They stay bare. Highest entry as of 2026-04-26: `LLMP-DEC-55` (Phase 3 closure).
---
## Within-triplet duplicate-check (sanity)
| Triplet | Count | Suffixes (sorted) | Unique? |
|---|---:|---|---|
| `ACCORE-BIN-I-` | 15 | B4Y7, D2J5, F1W8, G7N3, H2C5, J4D2, K8R4, L7G3, M3K6, N6Q3, P3M6, R5V9, S1F8, T9X1, V5L2 | ✅ |
| `ACCORE-BIN-T-` | 4 | Q2N7, S8P4, T5J8, W9F1 | ✅ |
| `ACCORE-LOG-I-` | 10 | B2H5, K1Z7, K7M2, L4N8, M4C9, P5W3, R9P3, T8F2, V3J6, X7Q1 | ✅ |
| `ACCORE-LOG-T-` | 11 | B8K5, F4S6, H6Y4, J9G2, L3T8, M7P2, N2D8, Q6Z1, R7L3, W4H9, X1V4 | ✅ |
| `ACCORE-SBP-I-` | 2 | F6T2, G4B5 | ✅ |
| `ACCORE-SBP-T-` | 8 | B3K6, H7M5, J5W8, K3J7, L1V4, N9F3, P8X9, R6D2 | ✅ |
| `ACCORE-SIG-I-` | 9 | B5G9, H8D6, K6F1, L5K3, N3V8, P1J4, R4W7, T7S2, X4M7 | ✅ |
| `ACCORE-SIG-T-` | 6 | D7Q4, J2P5, M5L6, S3N8, V9H1, W8R3 | ✅ |
| `ACCORE-TOON-I-` | 4 | B7L4, K4Z9, P6V5, X3H2 | ✅ |
| `ACCORE-TOON-T-` | 7 | D1R7, F3X1, G5M3, J8N6, M9Q2, S6B9, V2T4 | ✅ |
| `ACCORE-XCUT-I-` | 1 | X8Q1 | ✅ (trivially) |
| `ACBLAZOR-GRID-T-` | 2 | S2L9, V4P7 | ✅ |
All 12 triplets pass uniqueness within-triplet (the only collision domain that matters per `REPO_PREFIXES.md`'s suffix specification — collisions ACROSS triplets are non-issues since the full ID disambiguates).
---
## Phase 5 readiness checklist
For each topic-pair (`_ISSUES.md` + `_TODO.md`) Phase 5 will:
1. Rename headers (`## LOG-I-1: ...` → `## ACCORE-LOG-I-K7M2: ...`).
2. Rewrite intra-file cross-refs (e.g., body text "see LOG-I-3" → "see ACCORE-LOG-I-L4N8").
3. Rewrite TOC/anchor references if present.
4. Verify with grep: zero remaining bare `<TOPIC>-<TYPE>-<N>` matches in the file (post-rename), excluding example/placeholder lines.
5. One commit per topic (12 commits total: BIN, LOG, SBP, SIG, TOON, XCUT, GRID — but BIN/LOG/SIG/SBP/TOON each pair I+T → 5 commits; XCUT and GRID each 1 commit → 7 commits total).
Inter-file cross-refs (e.g., `BINARY_ISSUES.md` mentioning `BIN-T-3`, `BINARY_TODO.md` mentioning `BIN-T-3` from another entry, ADRs mentioning `LOG-I-9` etc.) are handled in Phase 6.
---
## Related
- `REPO_PREFIXES.md` — repo prefix registry (canonical authority for the `<PREFIX>` component).
- `skills/docs-check/references/TOPIC_CODES.md` — topic registry (canonical authority for the `<TOPIC>` component).
- `LLM_PROTOCOL_DECISIONS.md` `LLMP-DEC-50` — migration design decision (7-phase plan).
- `LLM_PROTOCOL_DECISIONS.md` `LLMP-DEC-53`, `LLMP-DEC-54`, `LLMP-DEC-55` — Phases 1-3 closure entries.

106
.github/REPO_PREFIXES.md vendored Normal file
View File

@ -0,0 +1,106 @@
# Repo Prefixes — format spec for repo-level namespace in topic IDs
Specification of the `<PREFIX>` component in the workspace's 4-component ID format. **Each repo declares its own prefix in its own `copilot-instructions.md` `@repo` block** — there is no central prefix listing here, in line with the Framework-First Design Principle (a framework `.md` does not enumerate consumer repos).
This file lives in `AyCode.Core/.github/` because the **format spec** is workspace-meta (used by every repo). It does NOT list non-framework prefixes.
## Full ID format
```
<PREFIX>-<TOPIC>-<TYPE>-<RAND>
```
| Component | Source | Description |
|---|---|---|
| `<PREFIX>` | Each repo's own `copilot-instructions.md` `@repo` block (`prefix = "..."` field) | Repo-level namespace |
| `<TOPIC>` | `docs-check/references/TOPIC_CODES.md` | Topic code (e.g., `LOG`, `BIN`, `SIG`) |
| `<TYPE>` | `docs-check/references/TOPIC_CODES.md` | Entry type (`I` = issue, `T` = TODO, `B` = bug, `C` = critical severity override) |
| `<RAND>` | Generated at creation | 4-character random alphanumeric suffix from `[A-Z0-9]` |
**Format rules**: all uppercase, hyphen-separated, no underscores, no spaces. Hash anchors in markdown cross-refs use lowercase: `accore-log-i-k7m2`.
**Examples** (using this repo's own prefix only — see Framework-First note above):
```
ACCORE-LOG-I-K7M2 # AyCode.Core's logger issue, random suffix K7M2
ACCORE-BIN-T-W9F1 # AyCode.Core's BINARY TODO, random suffix W9F1
ACCORE-XCUT-I-X8Q1 # AyCode.Core's cross-cutting issue
LLMP-DEC-50 # workspace-meta Decision (no prefix — see "LLMP exception" below)
```
## Why per-repo prefixes
Without prefixes, IDs like `LOG-I-5` are not globally unique across repos. Two peers may independently create logger-related issues with colliding IDs. More importantly: **framework docs cannot reference consumer-side issues** per the Framework-First Design Principle (a lower-layer framework cannot depend on a higher-layer consumer). Per-repo prefixes provide:
1. **Globally unique IDs**`ACCORE-LOG-I-K7M2``<other-prefix>-LOG-I-K7M2`, even when topic, type, and random suffix all match.
2. **Layer enforcement is visible** — a framework doc body referencing a higher-layer-prefixed ID becomes an immediate red flag in review (the prefix mismatch reveals the dependency-direction violation).
3. **Cross-repo search via wildcard**`*-LOG-I-*` glob/regex finds all logger issues workspace-wide, with no central registry needed; the LLM filters by prefix after retrieval.
4. **Distributed parallel work** — combined with the random `<RAND>` suffix, multiple developers can create entries in parallel branches without ID-collision at merge time.
## ACCORE — this repo's own prefix
This repo (`AyCode.Core`) uses prefix **`ACCORE`** (declared in this repo's own `copilot-instructions.md` `@repo` block, `prefix = "ACCORE"` field).
## Per-repo prefix declaration convention
Every repo participating in the workspace declares its own prefix in its `.github/copilot-instructions.md` `@repo` block:
```
@repo {
name = "<RepoName>"
prefix = "<PREFIX>" # ← prefix declared here
type = "framework" | "product" | "consumer" | ...
layer = 0..N
own-dep-repos = [...]
}
```
To discover a peer's prefix at agent runtime: read that peer's `copilot-instructions.md` (already loaded if the peer is in `own-dep-repos`). For peers NOT in `own-dep-repos` (i.e., higher-layer consumers from a framework's perspective): cross-repo wildcard search (next section) avoids needing to know the prefix in advance.
## Cross-repo ID search (no central registry needed)
When searching for entries across the workspace (e.g., "all logger issues" — across framework AND any consumer repos), agents use the prefix-wildcard glob:
```
*-LOG-I-* # all logger issues, any prefix
*-BIN-T-* # all BINARY TODOs, any prefix
*-SIG-* # all SIGNALR entries (issues + TODOs + bugs)
```
The wildcard pattern is workspace-discovery-agnostic — no central prefix list required. Result enumeration finds all matches; the LLM filters by prefix per the user's intent. The `docs-discovery` skill includes the cross-repo wildcard convention in its discovery flow.
## Random suffix spec
The `<RAND>` suffix is **4 characters from `[A-Z0-9]`** (36⁴ ≈ 1.7 million combinations per topic-type-prefix triple).
**Generation rules**:
1. Each new entry receives a fresh random suffix at creation time.
2. Before finalizing: the agent globs existing entries (active topic file + all year-bucketed archive files for that topic) and verifies the suffix is not yet used.
3. If collision detected (extremely rare — birthday-paradox 50% probability at ~1300 entries per topic-type-prefix triple): regenerate the suffix.
4. The suffix is **append-only** once assigned — never renumbered, never recycled, never reassigned to a different entry.
**At archive time** (`docs-archive` skill): performs collision-check before moving entries to year-bucketed archive files. If collision detected with an existing archive entry: skill aborts, signals the user, awaits manual resolution — no silent corruption.
## LLMP exception
`LLMP-DEC-N` Decision Log entries **do NOT receive a repo prefix**. They are workspace-meta — there is exactly one `LLM_PROTOCOL_DECISIONS.md` file (in AyCode.Core), and decisions are workspace-wide, not repo-specific. Bare format: `LLMP-DEC-1`, `LLMP-DEC-2`, ...
Sequential numbering for LLMP-DEC entries is **preserved** (legacy from before this registry existed; no random suffix). This is acceptable because the single Decision Log file enforces serialization — no parallel branches create concurrent LLMP-DEC entries that would collide.
If a future scenario emerges where workspace-meta decisions span multiple Decision Logs (unlikely), this exception will be revisited.
## Cross-references
- **Topic codes registry**: `docs-check/references/TOPIC_CODES.md` — the `<TOPIC>` component
- **Decision Log**: `LLM_PROTOCOL_DECISIONS.md` — registry of `LLMP-DEC-N` entries (workspace-meta, no prefix)
- **Per-repo prefix declarations**: each repo's own `.github/copilot-instructions.md` `@repo` block
## Picking a prefix for a new repo
When a new repo joins the workspace, it picks its own prefix (no central approval needed):
1. Choose a prefix (4-12 chars, uppercase, alphanumeric, no hyphens / underscores).
2. Verify it does NOT collide with `Ac*` / `Mg*` C# class-name prefixes (prefix must be ≥ 4 chars to avoid 2-char visual collision in mixed code/markdown content).
3. Verify it is visually distinct from prefixes of repos this new repo will reference or interoperate with (workspace-discovery-time check, not centrally enforced).
4. Declare the prefix in the new repo's `.github/copilot-instructions.md` `@repo` block (`prefix = "<PREFIX>"` field).
5. (Optional) Add an `LLMP-DEC-N` entry recording the new repo's join + prefix choice, if the new repo's existence is workspace-meta-significant.

File diff suppressed because one or more lines are too long

View File

@ -158,7 +158,7 @@ After the primary write:
- **Pre-flight-closed-entry**: if an entry was closed BEFORE the ADR write (a pre-flight fix that this ADR formalizes architecturally), do NOT change its Status — it's already `Closed`. Instead, propose appending a soft cross-ref to the entry's `### Related` section: `**Reference:** ADR-NNNN — formal architectural decision; this entry's Resolution is the pre-flight fix referenced in the ADR's Consequences.` - **Pre-flight-closed-entry**: if an entry was closed BEFORE the ADR write (a pre-flight fix that this ADR formalizes architecturally), do NOT change its Status — it's already `Closed`. Instead, propose appending a soft cross-ref to the entry's `### Related` section: `**Reference:** ADR-NNNN — formal architectural decision; this entry's Resolution is the pre-flight fix referenced in the ADR's Consequences.`
Do not apply either case without user consent. Do not apply either case without user consent.
4. **Follow-up TODOs** — if Step 6 surfaced follow-up work items, propose their addition to the relevant `{TOPIC}_TODO.md` as new `{TOPIC}-T-N` entries. Do not apply without user consent. 4. **Follow-up TODOs** — if Step 6 surfaced follow-up work items, propose their addition to the relevant `<TOPIC>_TODO.md` as new `<PREFIX>-<TOPIC>-T-<RAND>` entries (per `docs-check/references/TOPIC_CODES.md` ID format rules and `REPO_PREFIXES.md`'s prefix scheme). Do not apply without user consent.
## Step 9 — Hand-off after write ## Step 9 — Hand-off after write

View File

@ -78,5 +78,5 @@ Proposed (YYYY-MM-DD)
- Supersedes: <ADR-XXXX, if applicable> - Supersedes: <ADR-XXXX, if applicable>
- Superseded by: <ADR-YYYY, if this ADR was later overturned> - Superseded by: <ADR-YYYY, if this ADR was later overturned>
- Related ADRs: <ADR-ZZZZ, ...> - Related ADRs: <ADR-ZZZZ, ...>
- Related TODOs/Issues: <LOG-T-N, BIN-I-M, ...> - Related TODOs/Issues: <e.g., `ACCORE-LOG-T-K7M2`, `ACCORE-BIN-I-3R9P`, ... per `TOPIC_CODES.md` ID format rules and `REPO_PREFIXES.md` prefix scheme>
- External references: <URLs, RFCs, blog posts that informed this decision> - External references: <URLs, RFCs, blog posts that informed this decision>

View File

@ -37,7 +37,7 @@ User confirmation required. Never auto-invoke.
Move entry X to archive IF `Status: Closed`. Move entry X to archive IF `Status: Closed`.
Year derived from a date in the entry body (e.g., `Fixed 2026-04-25`, `Won't fix 2026-04-25`, `Superseded by LOG-I-X 2026-04-25`). If no parseable date in body, default to current year. Year derived from a date in the entry body (e.g., `Status: Closed (2026-04-25)` or a `### Resolution` sub-section dated `2026-04-25`, or `Superseded by ACCORE-LOG-I-K7M2 (2026-04-25)`). If no parseable date in body, default to current year. Status vocabulary follows `TOPIC_CODES.md` Status field conventions: only `Closed` is archive-eligible; `Open` and `InProgress` stay active.
**Stay in active file**: **Stay in active file**:
- `Status: Open` — including documented-current-behaviour entries (these stay Open with a body callout per `TOPIC_CODES.md`'s Status field conventions) - `Status: Open` — including documented-current-behaviour entries (these stay Open with a body callout per `TOPIC_CODES.md`'s Status field conventions)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -135,7 +135,7 @@ The Step 2 glob patterns target **active** companions only — unsuffixed names.
### On-demand read (no user-confirm needed — read-only operation) ### On-demand read (no user-confirm needed — read-only operation)
Read an archive file when ANY of these signals appears: Read an archive file when ANY of these signals appears:
- A loaded entry references an archived ID (e.g., `Superseded by LOG-I-X` where X resolves only to `_<year>.md`) - A loaded entry references an archived ID (e.g., `Superseded by ACCORE-LOG-I-K7M2` where the random-suffixed ID resolves only to a `_<year>.md` archive)
- A code comment or other doc references an ID resolving only to an archive file - A code comment or other doc references an ID resolving only to an archive file
- The user's request describes a behaviour pattern matching an archived `Fixed` entry's Description (regression suspicion) - The user's request describes a behaviour pattern matching an archived `Fixed` entry's Description (regression suspicion)
- The investigation feels like "this was solved before" — read the topic's archive(s) before re-deriving - The investigation feels like "this was solved before" — read the topic's archive(s) before re-deriving
@ -157,6 +157,20 @@ On-demand archive lookup:
- `**/docs/{TOKEN}/{TOKEN}_TODO_*.md` - `**/docs/{TOKEN}/{TOKEN}_TODO_*.md`
- `**/LLM_PROTOCOL_DECISIONS_*.md` - `**/LLM_PROTOCOL_DECISIONS_*.md`
## Cross-repo ID search
When a request asks about all entries of a topic across the workspace (e.g., "show me all logger issues" — potentially spanning multiple repos, each possibly having its own logger-related topic under its own prefix), use **prefix-wildcard globs** against the entry IDs:
- `**/*-LOG-I-*` — all logger-issue IDs, any repo prefix
- `**/*-LOG-*` — broader: all logger entries (issues + TODOs + bugs across any prefix)
- `**/*-XCUT-I-*` — all cross-cutting issue IDs across any prefix
The wildcard pattern is repo-agnostic — no central prefix list is consulted; the LLM filters by prefix after retrieval.
Active topic files are scanned by default; archive files follow the on-demand rules above. Filter results by the `<PREFIX>` dimension after retrieval to narrow to a specific repo scope.
The skill maintains no cross-repo index — **globs do the work**. This complements topic-folder discovery (Step 2): folder-discovery loads files by token-to-folder mapping; cross-repo ID search resolves specific entries by `<PREFIX>-<TOPIC>-<TYPE>-<RAND>` pattern.
## Step 6 — Proceed to the user's task ## Step 6 — Proceed to the user's task
The response's `[LOADED_DOCS: N files (+K this turn: <basenames>)]` prefix (per the active repo's Rule #1) already surfaces the newly-loaded filenames and the cumulative count. **No separate confirmation line is needed** — the prefix itself is the confirmation. Continue directly to the user's actual request. The response's `[LOADED_DOCS: N files (+K this turn: <basenames>)]` prefix (per the active repo's Rule #1) already surfaces the newly-loaded filenames and the cumulative count. **No separate confirmation line is needed** — the prefix itself is the confirmation. Continue directly to the user's actual request.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -141,18 +141,18 @@ Two-phase:
- `WriteObjectFullMarkerIId` / `WriteObjectFullMarkerAll`: `wrapper.Metadata` cached at entry, reused in ref-handling and non-ref branches - `WriteObjectFullMarkerIId` / `WriteObjectFullMarkerAll`: `wrapper.Metadata` cached at entry, reused in ref-handling and non-ref branches
- `GetWrapper(type, slot)` is O(1) array index after first call, but `value.GetType()` is a virtual call — avoid repeating it - `GetWrapper(type, slot)` is O(1) array index after first call, but `value.GetType()` is a virtual call — avoid repeating it
## Metadata Lifecycle & Cold-Start (planned: BIN-T-3 / BIN-T-4) ## Metadata Lifecycle & Cold-Start (planned: ACCORE-BIN-T-W9F1 / ACCORE-BIN-T-T5J8)
Today `BinarySerializeTypeMetadata` and `BinaryDeserializeTypeMetadata` are built lazily in `GetWrapperSlow` via `GlobalMetadataCache.GetOrAdd(type, MetadataFactory)`. The factory runs reflection property enumeration, attribute scans, and `Expression.Compile` per property — the dominant first-call cost for SGen types (see `BINARY_ISSUES.md#bin-i-10`). Today `BinarySerializeTypeMetadata` and `BinaryDeserializeTypeMetadata` are built lazily in `GetWrapperSlow` via `GlobalMetadataCache.GetOrAdd(type, MetadataFactory)`. The factory runs reflection property enumeration, attribute scans, and `Expression.Compile` per property — the dominant first-call cost for SGen types (see `BINARY_ISSUES.md#accore-bin-i-n6q3`).
**Planned evolution** (`BINARY_TODO.md#bin-t-3`): **Planned evolution** (`BINARY_TODO.md#accore-bin-t-w9f1`):
- **`GeneratedMetadataRegistry`**: `ModuleInit` registers pre-built metadata per `[AcBinarySerializable]` type alongside the existing `GeneratedWriterRegistry` / `GeneratedReaderRegistry` entries. Generator passes references to its static `s_typeNameHash` / `s_propertyHashes` fields — single source of truth, no duplicate computation, no hot-path indirection (generator keeps using its own static fields). - **`GeneratedMetadataRegistry`**: `ModuleInit` registers pre-built metadata per `[AcBinarySerializable]` type alongside the existing `GeneratedWriterRegistry` / `GeneratedReaderRegistry` entries. Generator passes references to its static `s_typeNameHash` / `s_propertyHashes` fields — single source of truth, no duplicate computation, no hot-path indirection (generator keeps using its own static fields).
- **Metadata ctor split**: a second ctor on `BinarySerializeTypeMetadata` / `BinaryDeserializeTypeMetadata` accepts pre-computed values (hashes, `MinWriteSize`, `ComplexPropertyCount`, `IsIId`, `IdAccessorType`, flags). No reflection in this ctor. - **Metadata ctor split**: a second ctor on `BinarySerializeTypeMetadata` / `BinaryDeserializeTypeMetadata` accepts pre-computed values (hashes, `MinWriteSize`, `ComplexPropertyCount`, `IsIId`, `IdAccessorType`, flags). No reflection in this ctor.
- **Lazy `RuntimeInit`**: `TypeMetadataBase` gets `volatile bool _runtimeInitialized` + `internal void RuntimeInit()`. `GetWrapperSlow` calls it only when `wrapper.GeneratedWriter == null || !Options.UseGeneratedCode` — i.e. for runtime-only types and the `UseGeneratedCode=false` edge case. SGen types skip it. Thread-safe by idempotence + `volatile` (no lock). - **Lazy `RuntimeInit`**: `TypeMetadataBase` gets `volatile bool _runtimeInitialized` + `internal void RuntimeInit()`. `GetWrapperSlow` calls it only when `wrapper.GeneratedWriter == null || !Options.UseGeneratedCode` — i.e. for runtime-only types and the `UseGeneratedCode=false` edge case. SGen types skip it. Thread-safe by idempotence + `volatile` (no lock).
- **Hybrid safety**: SGen root path (`WriteObjectProperties` → `generatedWriter.WriteProperties`) never touches the SGen type's own property accessors; non-SGen child types come through the `MetadataFactory` path as today. - **Hybrid safety**: SGen root path (`WriteObjectProperties` → `generatedWriter.WriteProperties`) never touches the SGen type's own property accessors; non-SGen child types come through the `MetadataFactory` path as today.
**Follow-up** (`BINARY_TODO.md#bin-t-4`): after BIN-T-3 removes reflection + `Expression.Compile` from the cold path, JIT of generated methods becomes dominant — mitigated via `[AggressiveOptimization]`, background `RuntimeHelpers.PrepareMethod`, and/or R2R (consumer publish config). **Follow-up** (`BINARY_TODO.md#accore-bin-t-t5j8`): after ACCORE-BIN-T-W9F1 removes reflection + `Expression.Compile` from the cold path, JIT of generated methods becomes dominant — mitigated via `[AggressiveOptimization]`, background `RuntimeHelpers.PrepareMethod`, and/or R2R (consumer publish config).
Wire format unchanged; `UseGeneratedCode=false` fallback continues to work identically (triggers `RuntimeInit` for SGen types on demand). Wire format unchanged; `UseGeneratedCode=false` fallback continues to work identically (triggers `RuntimeInit` for SGen types on demand).

View File

@ -2,7 +2,7 @@
## Deserialization ## Deserialization
### BIN-I-1: Non-array-backed memory — per-segment copy ### ACCORE-BIN-I-D2J5: Non-array-backed memory — per-segment copy
**Status:** Open **Status:** Open
**Affects:** `SequenceBinaryInput` **Affects:** `SequenceBinaryInput`
@ -12,7 +12,7 @@ When `ReadOnlySequence<byte>` segments are backed by native memory (not managed
**Impact:** Negligible. Non-array-backed `ReadOnlyMemory` is extremely rare (custom `MemoryManager<T>` with native memory, memory-mapped files). All standard .NET pools (`ArrayPool`, `MemoryPool.Shared`, Kestrel pipe) are array-backed. **Impact:** Negligible. Non-array-backed `ReadOnlyMemory` is extremely rare (custom `MemoryManager<T>` with native memory, memory-mapped files). All standard .NET pools (`ArrayPool`, `MemoryPool.Shared`, Kestrel pipe) are array-backed.
### BIN-I-2: Cross-boundary scratch buffer is not pooled across calls ### ACCORE-BIN-I-G7N3: Cross-boundary scratch buffer is not pooled across calls
**Status:** Open **Status:** Open
**Affects:** `SequenceBinaryInput._scratchBuffer` **Affects:** `SequenceBinaryInput._scratchBuffer`
@ -23,14 +23,14 @@ The scratch buffer is `ArrayPool.Rent`-ed on first cross-boundary read and reuse
**Possible optimization:** Store the scratch buffer on the pooled `BinaryDeserializationContext` and reuse across deserializations. Low priority — `ArrayPool` overhead is negligible. **Possible optimization:** Store the scratch buffer on the pooled `BinaryDeserializationContext` and reuse across deserializations. Low priority — `ArrayPool` overhead is negligible.
### BIN-I-3: ReadBytes always copies ### ACCORE-BIN-I-S1F8: ReadBytes always copies
**Status:** Open **Status:** Open
**Affects:** `BinaryDeserializationContext.ReadBytes(int length)` **Affects:** `BinaryDeserializationContext.ReadBytes(int length)`
`ReadBytes` allocates a new `byte[]` and copies from the buffer. This is unavoidable because the caller owns the returned array, and the source buffer (pipe segment or serialized data) may be recycled. `ReadBytes` allocates a new `byte[]` and copies from the buffer. This is unavoidable because the caller owns the returned array, and the source buffer (pipe segment or serialized data) may be recycled.
### BIN-I-4: ReadStringUtf8 requires contiguous buffer ### ACCORE-BIN-I-V5L2: ReadStringUtf8 requires contiguous buffer
**Status:** Open **Status:** Open
**Affects:** `BinaryDeserializationContext.ReadStringUtf8(int length)` **Affects:** `BinaryDeserializationContext.ReadStringUtf8(int length)`
@ -41,14 +41,14 @@ The scratch buffer is `ArrayPool.Rent`-ed on first cross-boundary read and reuse
## Serialization ## Serialization
### BIN-I-5: BufferWriterBinaryOutput fallback path allocates per-chunk ### ACCORE-BIN-I-K8R4: BufferWriterBinaryOutput fallback path allocates per-chunk
**Status:** Open **Status:** Open
**Affects:** `BufferWriterBinaryOutput.AcquireChunk` fallback **Affects:** `BufferWriterBinaryOutput.AcquireChunk` fallback
When `MemoryMarshal.TryGetArray` fails on `IBufferWriter.GetMemory()` (native memory-backed writer), a `byte[]` is rented from `ArrayPool` per chunk and copied to the writer on `Grow`/`Flush`. Same as BIN-I-1 — non-array-backed writers are extremely rare. When `MemoryMarshal.TryGetArray` fails on `IBufferWriter.GetMemory()` (native memory-backed writer), a `byte[]` is rented from `ArrayPool` per chunk and copied to the writer on `Grow`/`Flush`. Same as ACCORE-BIN-I-D2J5 — non-array-backed writers are extremely rare.
### BIN-I-6: AsyncPipeWriterOutput uses sync GetResult() for backpressure ### ACCORE-BIN-I-P3M6: AsyncPipeWriterOutput uses sync GetResult() for backpressure
**Status:** Open **Status:** Open
**Affects:** `AsyncPipeWriterOutput.Grow()``_lastFlush.GetAwaiter().GetResult()` **Affects:** `AsyncPipeWriterOutput.Grow()``_lastFlush.GetAwaiter().GetResult()`
@ -59,37 +59,37 @@ When the previous `PipeWriter.FlushAsync()` hasn't completed by the next `Grow()
**Possible optimization:** `AsyncSegment` mode (future) with a custom async `WriteMessageAsync` protocol interface, enabling `await` on flush instead of `GetResult()`. **Possible optimization:** `AsyncSegment` mode (future) with a custom async `WriteMessageAsync` protocol interface, enabling `await` on flush instead of `GetResult()`.
### BIN-I-7: AsyncPipeWriterOutput fallback path — same as BIN-I-5 ### ACCORE-BIN-I-T9X1: AsyncPipeWriterOutput fallback path — same as ACCORE-BIN-I-K8R4
**Status:** Open **Status:** Open
**Affects:** `AsyncPipeWriterOutput.AcquireChunk` fallback **Affects:** `AsyncPipeWriterOutput.AcquireChunk` fallback
Same `TryGetArray` fallback as `BufferWriterBinaryOutput` (BIN-I-5). Kestrel `PipeWriter.GetMemory()` always returns array-backed memory — fallback is for non-standard `PipeWriter` implementations only. Same `TryGetArray` fallback as `BufferWriterBinaryOutput` (ACCORE-BIN-I-K8R4). Kestrel `PipeWriter.GetMemory()` always returns array-backed memory — fallback is for non-standard `PipeWriter` implementations only.
## Deserialization (PipeReader) ## Deserialization (PipeReader)
### BIN-I-8: PipeReaderBinaryInput uses sync ReadAsync().GetResult() ### ACCORE-BIN-I-B4Y7: PipeReaderBinaryInput uses sync ReadAsync().GetResult()
**Status:** Open **Status:** Open
**Affects:** `PipeReaderBinaryInput.Initialize()` and `TryAdvanceSegment()` **Affects:** `PipeReaderBinaryInput.Initialize()` and `TryAdvanceSegment()`
Same constraint as BIN-I-6 — `IBinaryInputBase` interface is synchronous. `ReadAsync().GetAwaiter().GetResult()` blocks when waiting for more data from the pipe. Currently not used in production (SignalR delivers complete messages via `TryParseMessage`). Reserved for future direct-pipe deserialization scenarios. Same constraint as ACCORE-BIN-I-P3M6 — `IBinaryInputBase` interface is synchronous. `ReadAsync().GetAwaiter().GetResult()` blocks when waiting for more data from the pipe. Currently not used in production (SignalR delivers complete messages via `TryParseMessage`). Reserved for future direct-pipe deserialization scenarios.
## Source Generator (SGen) ## Source Generator (SGen)
### BIN-I-9: CS8625 warnings for non-nullable reference types ### ACCORE-BIN-I-H2C5: CS8625 warnings for non-nullable reference types
**Status:** Open **Status:** Open
**Affects:** Generated reader code **Affects:** Generated reader code
The source generator emits `null` assignments for non-nullable reference type properties during deserialization (before the value is read from the stream). This produces CS8625 warnings. Functionally harmless — the property is always assigned before use. The source generator emits `null` assignments for non-nullable reference type properties during deserialization (before the value is read from the stream). This produces CS8625 warnings. Functionally harmless — the property is always assigned before use.
### BIN-I-10: First-run cold-start overhead ### ACCORE-BIN-I-N6Q3: First-run cold-start overhead
**Status:** Open **Status:** Open
**Affects:** First `Serialize<T>`/`Deserialize<T>` per `[AcBinarySerializable]` type, per process **Affects:** First `Serialize<T>`/`Deserialize<T>` per `[AcBinarySerializable]` type, per process
Cold-start cost chain on first use of an SGen type (before BIN-T-3 lands): Cold-start cost chain on first use of an SGen type (before ACCORE-BIN-T-W9F1 lands):
1. `BinarySerializeTypeMetadata` ctor — reflection property enumeration + `GetCustomAttribute` scans 1. `BinarySerializeTypeMetadata` ctor — reflection property enumeration + `GetCustomAttribute` scans
2. `Expression.Compile` per property accessor (dynamic getter + typed getters) — **dominant cost** 2. `Expression.Compile` per property accessor (dynamic getter + typed getters) — **dominant cost**
@ -100,43 +100,43 @@ Cold-start cost chain on first use of an SGen type (before BIN-T-3 lands):
Subsequent calls hit cached metadata/wrappers → only Tier 0→1 JIT transition remains (background, async). Subsequent calls hit cached metadata/wrappers → only Tier 0→1 JIT transition remains (background, async).
**Dominant cost today:** #1#2 (reflection + `Expression.Compile`). After BIN-T-3, the dominant residual cost shifts to #4#5 (JIT), addressed by BIN-T-4. **Dominant cost today:** #1#2 (reflection + `Expression.Compile`). After ACCORE-BIN-T-W9F1, the dominant residual cost shifts to #4#5 (JIT), addressed by ACCORE-BIN-T-T5J8.
**Impact:** Measurable first-call latency — larger for types with many properties or deep graphs. For SignalR workloads the first message per entity type pays this tax. **Impact:** Measurable first-call latency — larger for types with many properties or deep graphs. For SignalR workloads the first message per entity type pays this tax.
### BIN-I-11: Consumer entity with `new` Id shadowing — excluded from SGen ### ACCORE-BIN-I-F1W8: Consumer entity with `new` Id shadowing — excluded from SGen
**Status:** Open **Status:** Open
**Affects:** Any consumer entity whose base class hides `BaseEntity.Id` with `readonly new int Id { get; }` pattern (e.g. `DiscountProductMapping` in Mango.Nop.Core) **Affects:** Any consumer entity whose base class hides `BaseEntity.Id` with `readonly new int Id { get; }` pattern (e.g. `DiscountProductMapping` in Mango.Nop.Core)
When the base class shadows `Id` with a setter-less `new int Id { get; }`, SGen can't emit a setter without CS0200. Runtime falls back to compiled-expression serialization for these types. Low priority — affects a small number of consumer entities. When the base class shadows `Id` with a setter-less `new int Id { get; }`, SGen can't emit a setter without CS0200. Runtime falls back to compiled-expression serialization for these types. Low priority — affects a small number of consumer entities.
**Related TODO:** `BINARY_TODO.md#bin-t-2` **Related TODO:** `BINARY_TODO.md#accore-bin-t-q2n7`
## Buffer Writer (BWO) ## Buffer Writer (BWO)
### BIN-I-12: Struct copy semantics ### ACCORE-BIN-I-J4D2: Struct copy semantics
**Status:** Open **Status:** Open
**Affects:** `BufferWriterBinaryOutput` value-type assignment **Affects:** `BufferWriterBinaryOutput` value-type assignment
Assigning a `BufferWriterBinaryOutput` value creates an independent copy. State changes (e.g. `_committedBytes` via `Grow`/`Flush`) are not reflected in the original. Copy back after use if needed. Assigning a `BufferWriterBinaryOutput` value creates an independent copy. State changes (e.g. `_committedBytes` via `Grow`/`Flush`) are not reflected in the original. Copy back after use if needed.
### BIN-I-13: Initialize resets tracking ### ACCORE-BIN-I-R5V9: Initialize resets tracking
**Status:** Open **Status:** Open
**Affects:** `BufferWriterBinaryOutput.Initialize` (context mode) **Affects:** `BufferWriterBinaryOutput.Initialize` (context mode)
`Initialize` sets `_committedBytes = 0`. Standalone bytes written before are lost if the BWO is then passed to a context. Call `FlushAndReset()` first, or track standalone bytes separately. `Initialize` sets `_committedBytes = 0`. Standalone bytes written before are lost if the BWO is then passed to a context. Call `FlushAndReset()` first, or track standalone bytes separately.
### BIN-I-14: Constructor acquires chunk ### ACCORE-BIN-I-L7G3: Constructor acquires chunk
**Status:** Open **Status:** Open
**Affects:** `BufferWriterBinaryOutput` ctor **Affects:** `BufferWriterBinaryOutput` ctor
`AcquireChunk` runs in ctor for standalone readiness. Redundant if only context mode is used (context `Initialize` acquires its own). Not a leak — consecutive `GetMemory` without `Advance` returns overlapping memory. `AcquireChunk` runs in ctor for standalone readiness. Redundant if only context mode is used (context `Initialize` acquires its own). Not a leak — consecutive `GetMemory` without `Advance` returns overlapping memory.
### BIN-I-15: No mode mixing ### ACCORE-BIN-I-M3K6: No mode mixing
**Status:** Open **Status:** Open
**Affects:** `BufferWriterBinaryOutput` — context vs standalone mode **Affects:** `BufferWriterBinaryOutput` — context vs standalone mode
@ -145,8 +145,8 @@ A single instance must not use context + standalone modes simultaneously — buf
## Cross-cutting (canonical home: `../XCUT/`) ## Cross-cutting (canonical home: `../XCUT/`)
### XCUT-I-1: JSON-in-Binary request parameters — cross-ref ### ACCORE-XCUT-I-X8Q1: JSON-in-Binary request parameters — cross-ref
**Status:** Closed (2026-04-26, see canonical entry). **Status:** Closed (2026-04-26, see canonical entry).
Canonical entry: **`../XCUT/XCUT_ISSUES.md#xcut-i-1`**. Summary: client→server request parameters previously used JSON inside a Binary envelope (`SignalPostJsonDataMessage<T>`); response path was already pure Binary. Migration landed in commits `cdd54d3` + `3b70070` via `SignalParams` (length-prefixed binary pack/unpack) — wire is now Binary in both directions. Migration plan tracked in `BINARY_TODO.md#bin-t-1` (also Closed). Canonical entry: **`../XCUT/XCUT_ISSUES.md#accore-xcut-i-x8q1`**. Summary: client→server request parameters previously used JSON inside a Binary envelope (`SignalPostJsonDataMessage<T>`); response path was already pure Binary. Migration landed in commits `cdd54d3` + `3b70070` via `SignalParams` (length-prefixed binary pack/unpack) — wire is now Binary in both directions. Migration plan tracked in `BINARY_TODO.md#accore-bin-t-s8p4` (also Closed).

File diff suppressed because one or more lines are too long

View File

@ -5,8 +5,8 @@
--- ---
## BIN-T-1: Replace JSON-in-Binary request parameters ## ACCORE-BIN-T-S8P4: Replace JSON-in-Binary request parameters
**Priority:** P1 · **Type:** Refactor · **Status:** Closed (2026-04-26, landed in commits `cdd54d3` 2026-04-05 + `3b70070` 2026-04-06) · **Related:** `../XCUT/XCUT_ISSUES.md#xcut-i-1` (canonical), `AyCode.Services/docs/SIGNALR/SIGNALR_TODO.md` **Priority:** P1 · **Type:** Refactor · **Status:** Closed (2026-04-26, landed in commits `cdd54d3` 2026-04-05 + `3b70070` 2026-04-06) · **Related:** `../XCUT/XCUT_ISSUES.md#accore-xcut-i-x8q1` (canonical), `AyCode.Services/docs/SIGNALR/SIGNALR_TODO.md`
Migrate client→server request parameters from JSON-in-Binary envelope to direct Binary serialization (matching response path). Coordinated change across client, server, and all consuming projects. Do NOT attempt as side-effect of unrelated work. Migrate client→server request parameters from JSON-in-Binary envelope to direct Binary serialization (matching response path). Coordinated change across client, server, and all consuming projects. Do NOT attempt as side-effect of unrelated work.
@ -17,15 +17,15 @@ Migrate client→server request parameters from JSON-in-Binary envelope to direc
- **Where:** `AyCode.Services/SignalRs/AcSignalRClientBase.cs`, `AcWebSignalRHubBase.cs`, `ISignalParams.cs` (server + client dispatch); `IAcSignalRHubClient.cs` (legacy wrappers). - **Where:** `AyCode.Services/SignalRs/AcSignalRClientBase.cs`, `AcWebSignalRHubBase.cs`, `ISignalParams.cs` (server + client dispatch); `IAcSignalRHubClient.cs` (legacy wrappers).
- **Equivalent (not literal `SignalPostBinaryDataMessage<T>`):** `SignalParams` was chosen over a 1:1 binary wrapper class — fewer indirections on the hot path, type-safe pack/unpack, and `DataSerializerType` field on `SignalReceiveParams` for response format indication. - **Equivalent (not literal `SignalPostBinaryDataMessage<T>`):** `SignalParams` was chosen over a 1:1 binary wrapper class — fewer indirections on the hot path, type-safe pack/unpack, and `DataSerializerType` field on `SignalReceiveParams` for response format indication.
- **Wire impact:** No JSON round-trip on the wire for request params; this is a **breaking change** vs. previous JSON-in-Binary clients/servers (see commit message). - **Wire impact:** No JSON round-trip on the wire for request params; this is a **breaking change** vs. previous JSON-in-Binary clients/servers (see commit message).
- **Legacy types:** `SignalPostJsonMessage`, `SignalPostJsonDataMessage<T>`, `SignalPostMessage<T>`, `ISignalPostMessage<T>` all marked `[Obsolete]` in `IAcSignalRHubClient.cs`; deletion tracked separately in `AyCode.Services/docs/SIGNALR/SIGNALR_TODO.md#sig-t-6` (gated on consumer migration). - **Legacy types:** `SignalPostJsonMessage`, `SignalPostJsonDataMessage<T>`, `SignalPostMessage<T>`, `ISignalPostMessage<T>` all marked `[Obsolete]` in `IAcSignalRHubClient.cs`; deletion tracked separately in `AyCode.Services/docs/SIGNALR/SIGNALR_TODO.md#accore-sig-t-s3n8` (gated on consumer migration).
## BIN-T-2: Re-evaluate DiscountProductMapping SGen exclusion ## ACCORE-BIN-T-Q2N7: Re-evaluate DiscountProductMapping SGen exclusion
**Priority:** P3 · **Type:** Investigation · **Related:** `BINARY_ISSUES.md#bin-i-11` **Priority:** P3 · **Type:** Investigation · **Related:** `BINARY_ISSUES.md#accore-bin-i-f1w8`
Investigate whether the `new int Id` shadowing pattern can be handled by SGen (via base-class introspection, property-setter lookup on the base) to eliminate the runtime compiled-expression fallback for this entity class. Investigate whether the `new int Id` shadowing pattern can be handled by SGen (via base-class introspection, property-setter lookup on the base) to eliminate the runtime compiled-expression fallback for this entity class.
## BIN-T-3: Generate `BinarySerializeTypeMetadata` / `BinaryDeserializeTypeMetadata` at compile time ## ACCORE-BIN-T-W9F1: Generate `BinarySerializeTypeMetadata` / `BinaryDeserializeTypeMetadata` at compile time
**Priority:** P1 · **Type:** Performance · **Related:** `BINARY_ISSUES.md#bin-i-10` **Priority:** P1 · **Type:** Performance · **Related:** `BINARY_ISSUES.md#accore-bin-i-n6q3`
Eliminate the dominant first-call cost (reflection + `Expression.Compile` in metadata ctor) for SGen types by emitting pre-built metadata from the source generator. Eliminate the dominant first-call cost (reflection + `Expression.Compile` in metadata ctor) for SGen types by emitting pre-built metadata from the source generator.
@ -48,10 +48,10 @@ Eliminate the dominant first-call cost (reflection + `Expression.Compile` in met
- Deserialize side has parity (same approach for `BinaryDeserializeTypeMetadata`). - Deserialize side has parity (same approach for `BinaryDeserializeTypeMetadata`).
- Existing tests pass; wire format unchanged. - Existing tests pass; wire format unchanged.
## BIN-T-4: JIT Tier 1 warmup for generated hot methods ## ACCORE-BIN-T-T5J8: JIT Tier 1 warmup for generated hot methods
**Priority:** P2 · **Type:** Performance · **Related:** `BINARY_ISSUES.md#bin-i-10` **Priority:** P2 · **Type:** Performance · **Related:** `BINARY_ISSUES.md#accore-bin-i-n6q3`
After BIN-T-3 lands, JIT of generated `WriteProperties` / `ScanObject` / `ScanForDuplicates` becomes the dominant residual first-call cost for SGen types. Options to evaluate (benchmark before committing): After ACCORE-BIN-T-W9F1 lands, JIT of generated `WriteProperties` / `ScanObject` / `ScanForDuplicates` becomes the dominant residual first-call cost for SGen types. Options to evaluate (benchmark before committing):
- **`[MethodImpl(MethodImplOptions.AggressiveOptimization)]`** on the generated hot methods — skips Tier 0, compiles directly at Tier 1. Simple generator change. Trade-off: larger one-time JIT cost in exchange for eliminating the Tier 0→1 recompile step. - **`[MethodImpl(MethodImplOptions.AggressiveOptimization)]`** on the generated hot methods — skips Tier 0, compiles directly at Tier 1. Simple generator change. Trade-off: larger one-time JIT cost in exchange for eliminating the Tier 0→1 recompile step.
- **Background prewarm from `ModuleInit`**: `Task.Run(() => RuntimeHelpers.PrepareMethod(handle))` for each registered writer/reader method. Parallelizes JIT with app startup. Keep it opt-in (option flag) to avoid surprising consumers with extra startup threads. - **Background prewarm from `ModuleInit`**: `Task.Run(() => RuntimeHelpers.PrepareMethod(handle))` for each registered writer/reader method. Parallelizes JIT with app startup. Keep it opt-in (option flag) to avoid surprising consumers with extra startup threads.
@ -60,5 +60,5 @@ After BIN-T-3 lands, JIT of generated `WriteProperties` / `ScanObject` / `ScanFo
- **Native AOT** — out of scope for this TODO; separate architectural decision with deployment-model implications. - **Native AOT** — out of scope for this TODO; separate architectural decision with deployment-model implications.
**Acceptance:** **Acceptance:**
- Benchmark a realistic entity graph (≥ 3 referenced child types) and show first-call time within ~10% of steady-state after BIN-T-3 + chosen mitigation(s). - 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. - Document which combination is recommended for SignalR hot-path workloads vs. batch serialization.

View File

@ -2,7 +2,7 @@
For planned/actionable work see `LOGGING_TODO.md`. For planned/actionable work see `LOGGING_TODO.md`.
## LOG-I-1: NopLogWriter ctor signature mismatch (consumer-specific but framework-exposed) ## ACCORE-LOG-I-K7M2: NopLogWriter ctor signature mismatch (consumer-specific but framework-exposed)
**Severity:** Minor (caught, non-blocking, but noisy) · **Status:** Open · **Area:** Writer-instantiation contract (`AcLoggerBase(string)` config-reading ctor) **Severity:** Minor (caught, non-blocking, but noisy) · **Status:** Open · **Area:** Writer-instantiation contract (`AcLoggerBase(string)` config-reading ctor)
@ -20,9 +20,9 @@ Two logger-construction paradigms coexist:
Console.Error noise tolerated. Alternatively, consumer uses DI-based `AddAcLoggerFactory<TLogger>` (see LOGGING.md) instead of the config-reading ctor — this path doesn't touch `LogWriters[]`. Console.Error noise tolerated. Alternatively, consumer uses DI-based `AddAcLoggerFactory<TLogger>` (see LOGGING.md) instead of the config-reading ctor — this path doesn't touch `LogWriters[]`.
### Related TODO ### Related TODO
`LOGGING_TODO.md#log-t-1` `LOGGING_TODO.md#accore-log-t-h6y4`
## LOG-I-2: AcEnv.AppConfiguration is filesystem-bound, MAUI/WASM-unsafe ## ACCORE-LOG-I-R9P3: AcEnv.AppConfiguration is filesystem-bound, MAUI/WASM-unsafe
**Severity:** Minor · **Status:** Open · **Area:** `AyCode.Core.Consts.AcEnv` **Severity:** Minor · **Status:** Open · **Area:** `AyCode.Core.Consts.AcEnv`
@ -36,9 +36,9 @@ Design predates `IOptions`/DI pattern.
Consumer avoids the config-reading `AcLoggerBase(string)` ctor on these platforms. DI-based `AddAcLoggerFactory<TLogger>` + `services.Configure<AcLoggerOptions>(...)` is the replacement (see LOGGING.md). Consumer avoids the config-reading `AcLoggerBase(string)` ctor on these platforms. DI-based `AddAcLoggerFactory<TLogger>` + `services.Configure<AcLoggerOptions>(...)` is the replacement (see LOGGING.md).
### Related TODO ### Related TODO
`LOGGING_TODO.md#log-t-2` `LOGGING_TODO.md#accore-log-t-n2d8`
## LOG-I-3: Two parallel logger-setup patterns ## ACCORE-LOG-I-L4N8: Two parallel logger-setup patterns
**Severity:** Minor (confusion, not functional) · **Status:** Open · **Area:** LOGGING.md / consumer code **Severity:** Minor (confusion, not functional) · **Status:** Open · **Area:** LOGGING.md / consumer code
@ -47,17 +47,17 @@ Two ways to construct a logger coexist:
1. **Config-reading:** `new Logger(categoryName)``AcLoggerBase` reads `AyCode:Logger` section via `AcEnv.AppConfiguration`, instantiates writers via `Activator.CreateInstance` 1. **Config-reading:** `new Logger(categoryName)``AcLoggerBase` reads `AyCode:Logger` section via `AcEnv.AppConfiguration`, instantiates writers via `Activator.CreateInstance`
2. **DI factory:** `services.Configure<AcLoggerOptions>(...)` + `services.AddAcLoggerFactory<TLogger>()``Func<string, TLogger>` resolved from DI, writers pulled from DI 2. **DI factory:** `services.Configure<AcLoggerOptions>(...)` + `services.AddAcLoggerFactory<TLogger>()``Func<string, TLogger>` resolved from DI, writers pulled from DI
Consumer picks per scenario; no automatic bridge. Risk: mixing patterns causes subtle failures (e.g. `MissingMethodException` — see LOG-I-1). Consumer picks per scenario; no automatic bridge. Risk: mixing patterns causes subtle failures (e.g. `MissingMethodException` — see ACCORE-LOG-I-K7M2).
### Related TODO ### Related TODO
`LOGGING_TODO.md#log-t-3` `LOGGING_TODO.md#accore-log-t-r7l3`
## LOG-I-4: Default LogLevel diverges across the two setup paths ## ACCORE-LOG-I-B2H5: Default LogLevel diverges across the two setup paths
**Severity:** Minor (surprise, not broken) · **Status:** Open · **Area:** `AcLoggerBase` field initializer vs `AcLoggerOptions` **Severity:** Minor (surprise, not broken) · **Status:** Open · **Area:** `AcLoggerBase` field initializer vs `AcLoggerOptions`
### Description ### Description
The two logger-setup paths (see LOG-I-3) ship with **different default LogLevels**: The two logger-setup paths (see ACCORE-LOG-I-L4N8) ship with **different default LogLevels**:
- `AcLoggerBase.cs:18` — field initializer: `LogLevel = Error` - `AcLoggerBase.cs:18` — field initializer: `LogLevel = Error`
- `AcLoggerOptions.cs:27` — DI options default: `LogLevel = Info` - `AcLoggerOptions.cs:27` — DI options default: `LogLevel = Info`
@ -71,9 +71,9 @@ Same consumer code, same "no configuration" state, two different log volumes dep
Historical: field initializer predates the Options class; Options was added as part of the DI-factory refactor with a developer-friendly `Info` default. Historical: field initializer predates the Options class; Options was added as part of the DI-factory refactor with a developer-friendly `Info` default.
### Related TODO ### Related TODO
`LOGGING_TODO.md#log-t-5` `LOGGING_TODO.md#accore-log-t-j9g2`
## LOG-I-5: `AcConsoleLogWriter.Initialize()` runs twice on parameterless ctor ## ACCORE-LOG-I-X7Q1: `AcConsoleLogWriter.Initialize()` runs twice on parameterless ctor
**Severity:** Minor (wasted work, not broken) · **Status:** Open · **Area:** `AcConsoleLogWriter` ctor chain **Severity:** Minor (wasted work, not broken) · **Status:** Open · **Area:** `AcConsoleLogWriter` ctor chain
@ -97,9 +97,9 @@ Chain `: this(null)` invokes the `(string?)` ctor, which calls `Initialize()`. C
Remove the second `Initialize()` call in the parameterless ctor body (the chain already covered it). Remove the second `Initialize()` call in the parameterless ctor body (the chain already covered it).
### Related TODO ### Related TODO
`LOGGING_TODO.md#log-t-6` `LOGGING_TODO.md#accore-log-t-b8k5`
## LOG-I-6: `ILogger.IsEnabled(MsLogLevel.None)` incorrectly reports enabled ## ACCORE-LOG-I-V3J6: `ILogger.IsEnabled(MsLogLevel.None)` incorrectly reports enabled
**Severity:** Low (semantic bug, rare path) · **Status:** Open · **Area:** `AcLoggerBase.IsEnabled` + `MapFromMsLogLevel` **Severity:** Low (semantic bug, rare path) · **Status:** Open · **Area:** `AcLoggerBase.IsEnabled` + `MapFromMsLogLevel`
@ -125,9 +125,9 @@ Either:
- **(b)** Change the comparison to strict `<` with `Disabled` as sentinel (larger refactor — affects semantic of `Disabled` elsewhere) - **(b)** Change the comparison to strict `<` with `Disabled` as sentinel (larger refactor — affects semantic of `Disabled` elsewhere)
### Related TODO ### Related TODO
`LOGGING_TODO.md#log-t-7` `LOGGING_TODO.md#accore-log-t-x1v4`
## LOG-I-7: Misleading inline comment in `AcLoggerBase.Log<TState>` ## ACCORE-LOG-I-T8F2: Misleading inline comment in `AcLoggerBase.Log<TState>`
**Severity:** Trivial (doc-only) · **Status:** Open · **Area:** `AcLoggerBase.cs:210-211` **Severity:** Trivial (doc-only) · **Status:** Open · **Area:** `AcLoggerBase.cs:210-211`
@ -143,11 +143,11 @@ Comment says fallback is `null` (empty display), the code assigns `"Log"`. Contr
Update comment to match code: `// Use eventId.Name:eventId.Id if Name is set, otherwise fallback to "Log" per LOGGING.md convention`. Update comment to match code: `// Use eventId.Name:eventId.Id if Name is set, otherwise fallback to "Log" per LOGGING.md convention`.
### Related TODO ### Related TODO
Folded into `LOGGING_TODO.md#log-t-8` (cleanup batch). Folded into `LOGGING_TODO.md#accore-log-t-m7p2` (cleanup batch).
## LOG-I-8: Server-side NopCommerce plugin still uses legacy config-reading Logger ctor ## ACCORE-LOG-I-M4C9: Server-side NopCommerce plugin still uses legacy config-reading Logger ctor
**Severity:** Minor (works, but inconsistent with modern pattern + triggers LOG-I-1 noise) · **Status:** Open · **Area:** Consumer adoption gap in `Nop.Plugin.Misc.AIPlugin/Infrastructure/PluginNopStartup.cs` **Severity:** Minor (works, but inconsistent with modern pattern + triggers ACCORE-LOG-I-K7M2 noise) · **Status:** Open · **Area:** Consumer adoption gap in `Nop.Plugin.Misc.AIPlugin/Infrastructure/PluginNopStartup.cs`
### Description ### Description
Client side (`FruitBankHybridApp.*` — Web, Web.Client, MAUI) was migrated to the DI-factory pattern: `services.Configure<AcLoggerOptions>(...)` + `services.AddAcLoggerFactory<Logger>()`. The server-side plugin was NOT migrated — it still: Client side (`FruitBankHybridApp.*` — Web, Web.Client, MAUI) was migrated to the DI-factory pattern: `services.Configure<AcLoggerOptions>(...)` + `services.AddAcLoggerFactory<Logger>()`. The server-side plugin was NOT migrated — it still:
@ -158,21 +158,21 @@ Client side (`FruitBankHybridApp.*` — Web, Web.Client, MAUI) was migrated to t
4. Writer registration exists (`services.AddScoped<IAcLogWriterBase, ConsoleLogWriter>()` + `NopLogWriter`) but those DI-registered singletons are NOT the instances the `new Logger(...)` ctor sees — the legacy ctor creates a parallel set via `Activator.CreateInstance`. 4. Writer registration exists (`services.AddScoped<IAcLogWriterBase, ConsoleLogWriter>()` + `NopLogWriter`) but those DI-registered singletons are NOT the instances the `new Logger(...)` ctor sees — the legacy ctor creates a parallel set via `Activator.CreateInstance`.
The legacy config-reading ctor DOES find the appsettings `AyCode:Logger` section via `AcEnv.AppConfiguration` (filesystem-backed, works on server) — so logging functions. But every `new Logger(...)` call: The legacy config-reading ctor DOES find the appsettings `AyCode:Logger` section via `AcEnv.AppConfiguration` (filesystem-backed, works on server) — so logging functions. But every `new Logger(...)` call:
- Triggers LOG-I-1 (NopLogWriter ctor mismatch → Console.Error noise) - Triggers ACCORE-LOG-I-K7M2 (NopLogWriter ctor mismatch → Console.Error noise)
- Reconstructs writer instances via `Activator` (not singleton-shared with DI-registered writers) - Reconstructs writer instances via `Activator` (not singleton-shared with DI-registered writers)
- Is inconsistent with the client-side pattern → two mental models for the same framework - Is inconsistent with the client-side pattern → two mental models for the same framework
### Fix direction ### Fix direction
See `LOGGING_TODO.md#log-t-11`. See `LOGGING_TODO.md#accore-log-t-w4h9`.
### Related ### Related
- `LOG-I-1` (trigger — NopLogWriter ctor mismatch, currently causing Console.Error noise) - `ACCORE-LOG-I-K7M2` (trigger — NopLogWriter ctor mismatch, currently causing Console.Error noise)
- `LOG-I-3` (root cause — two coexisting setup patterns) - `ACCORE-LOG-I-L4N8` (root cause — two coexisting setup patterns)
- `LOG-I-4` (consequence — different defaults between paths; server legacy path silently ships with `Error` default unless `AyCode:Logger:LogLevel` is set) - `ACCORE-LOG-I-B2H5` (consequence — different defaults between paths; server legacy path silently ships with `Error` default unless `AyCode:Logger:LogLevel` is set)
- Sibling gap: `../SIGNALR/SIGNALR_ISSUES.md#sig-i-7` (same server-side plugin, protocol-options adoption gap) - Sibling gap: `../SIGNALR/SIGNALR_ISSUES.md#accore-sig-i-b5g9` (same server-side plugin, protocol-options adoption gap)
- Plugin doc drift: `Nop.Plugin.Misc.AIPlugin/docs/SIGNALR/README.md:22` still documents the pre-migration `new AcBinaryHubProtocol()` registration (actual code uses `.AddAcBinaryProtocol(opts => {...})`). Update needed. - Plugin doc drift: `Nop.Plugin.Misc.AIPlugin/docs/SIGNALR/README.md:22` still documents the pre-migration `new AcBinaryHubProtocol()` registration (actual code uses `.AddAcBinaryProtocol(opts => {...})`). Update needed.
## LOG-I-9: JWT signing key written to log (CRITICAL security) ## ACCORE-LOG-I-P5W3: JWT signing key written to log (CRITICAL security)
**Severity:** 🛑 **Critical (security)** · **Status:** Closed (2026-04-25) · **Area:** `AyCode.Services.Server/Logins/AcLoginServiceServer.cs:192` **Severity:** 🛑 **Critical (security)** · **Status:** Closed (2026-04-25) · **Area:** `AyCode.Services.Server/Logins/AcLoginServiceServer.cs:192`
@ -206,11 +206,11 @@ Remove the `GlobalLogger.Detail($"Key: ...")` line entirely. The signing key mus
### Related ### Related
- **Reference:** ADR [`0001-user-bearer-token-flow.md`](../../../docs/adr/0001-user-bearer-token-flow.md) — formal architectural decision; this entry's Resolution is the pre-flight fix referenced in the ADR's Consequences. - **Reference:** ADR [`0001-user-bearer-token-flow.md`](../../../docs/adr/0001-user-bearer-token-flow.md) — formal architectural decision; this entry's Resolution is the pre-flight fix referenced in the ADR's Consequences.
- `LOG-I-10` (sibling — access token also leaked, same file, same code path) - `ACCORE-LOG-I-K1Z7` (sibling — access token also leaked, same file, same code path)
- `LOGGING/README.md` may need a new "Never log secrets" guideline section (separate `_TODO.md` candidate) - `LOGGING/README.md` may need a new "Never log secrets" guideline section (separate `_TODO.md` candidate)
- **Discovery context**: this issue and `LOG-I-10` emerged during the user-bearer-token ADR context-gathering phase (2026-04-25 Copilot session, while reading `AcLoginServiceServer.cs`). If a dedicated AUTH topic-folder is created later (likely as part of the bearer token ADR follow-ups), these two entries are candidates for relocation to `AUTH_ISSUES.md` with `Status: SUPERSEDED by AUTH-I-N` cross-references. - **Discovery context**: this issue and `ACCORE-LOG-I-K1Z7` emerged during the user-bearer-token ADR context-gathering phase (2026-04-25 Copilot session, while reading `AcLoginServiceServer.cs`). If a dedicated AUTH topic-folder is created later (likely as part of the bearer token ADR follow-ups), these two entries are candidates for relocation to `AUTH_ISSUES.md` with `Status: SUPERSEDED by AUTH-I-N` cross-references.
## LOG-I-10: JWT access token written to log (CRITICAL security) ## ACCORE-LOG-I-K1Z7: JWT access token written to log (CRITICAL security)
**Severity:** 🛑 **Critical (security)** · **Status:** Closed (2026-04-25) · **Area:** `AyCode.Services.Server/Logins/AcLoginServiceServer.cs:212` **Severity:** 🛑 **Critical (security)** · **Status:** Closed (2026-04-25) · **Area:** `AyCode.Services.Server/Logins/AcLoginServiceServer.cs:212`
@ -223,7 +223,7 @@ GlobalLogger.Detail($"AccesToken: {writtenToken}");
(Side note: `AccesToken` is a typo for `AccessToken` — fix while at the line.) (Side note: `AccesToken` is a typo for `AccessToken` — fix while at the line.)
Access tokens are bearer credentials — anyone holding the token can authenticate as the user until token expiry (currently 6h per `GenerateAccessToken` config). Logged tokens leak via the same channels as `LOG-I-9` (live streams, retention archives, screenshots, shared logs during ops). Access tokens are bearer credentials — anyone holding the token can authenticate as the user until token expiry (currently 6h per `GenerateAccessToken` config). Logged tokens leak via the same channels as `ACCORE-LOG-I-P5W3` (live streams, retention archives, screenshots, shared logs during ops).
### Fix direction ### Fix direction
Remove the `GlobalLogger.Detail($"AccesToken: ...")` line entirely. If issuance verification is needed, log only **metadata** — user ID, expiry timestamp, issuer — never the token itself. Remove the `GlobalLogger.Detail($"AccesToken: ...")` line entirely. If issuance verification is needed, log only **metadata** — user ID, expiry timestamp, issuer — never the token itself.
@ -236,15 +236,15 @@ Remove the `GlobalLogger.Detail($"AccesToken: ...")` line entirely. If issuance
**Where:** `AyCode.Services.Server/Logins/AcLoginServiceServer.cs:212` (line numbers shifted slightly due to the inserted comment). **Where:** `AyCode.Services.Server/Logins/AcLoginServiceServer.cs:212` (line numbers shifted slightly due to the inserted comment).
**Why:** Same as `LOG-I-9` — user opted for DEBUG-gating over deletion (2026-04-25, bearer-token ADR session). Typo fix is opportunistic since the line was being touched. **Why:** Same as `ACCORE-LOG-I-P5W3` — user opted for DEBUG-gating over deletion (2026-04-25, bearer-token ADR session). Typo fix is opportunistic since the line was being touched.
**Caveat:** Same DEBUG-build leak constraint as `LOG-I-9`. CI/CD must build with `-c Release` for production. **Caveat:** Same DEBUG-build leak constraint as `ACCORE-LOG-I-P5W3`. CI/CD must build with `-c Release` for production.
### Related ### Related
- **Reference:** ADR [`0001-user-bearer-token-flow.md`](../../../docs/adr/0001-user-bearer-token-flow.md) — formal architectural decision; this entry's Resolution is the pre-flight fix referenced in the ADR's Consequences. - **Reference:** ADR [`0001-user-bearer-token-flow.md`](../../../docs/adr/0001-user-bearer-token-flow.md) — formal architectural decision; this entry's Resolution is the pre-flight fix referenced in the ADR's Consequences.
- `LOG-I-9` (sibling — JWT signing key also leaked, same file) - `ACCORE-LOG-I-P5W3` (sibling — JWT signing key also leaked, same file)
- Same `LOGGING/README.md` "Never log secrets" guideline gap as LOG-I-9 - Same `LOGGING/README.md` "Never log secrets" guideline gap as ACCORE-LOG-I-P5W3
- Same discovery-context note as LOG-I-9 (bearer token ADR session, candidate for AUTH topic relocation if/when that emerges) - Same discovery-context note as ACCORE-LOG-I-P5W3 (bearer token ADR session, candidate for AUTH topic relocation if/when that emerges)
## Evaluated review findings — NOT bugs (by-design) ## Evaluated review findings — NOT bugs (by-design)

View File

@ -5,8 +5,8 @@
--- ---
## LOG-T-1: Fix the writer-ctor mismatch for DI-injected writers ## ACCORE-LOG-T-H6Y4: Fix the writer-ctor mismatch for DI-injected writers
**Priority:** P2 · **Type:** Bug fix · **Related:** `LOGGING_ISSUES.md#log-i-1` **Priority:** P2 · **Type:** Bug fix · **Related:** `LOGGING_ISSUES.md#accore-log-i-k7m2`
Options: Options:
- **A)** Provide a fallback `(AppType, LogLevel, string?)` ctor on consumer writers that have DI-heavy primary ctors, with DI resolution via service-locator - **A)** Provide a fallback `(AppType, LogLevel, string?)` ctor on consumer writers that have DI-heavy primary ctors, with DI resolution via service-locator
@ -15,20 +15,20 @@ Options:
Eliminate the per-startup Console.Error noise regardless. Eliminate the per-startup Console.Error noise regardless.
## LOG-T-2: Expose `AcEnv.AppConfiguration` setter for consumer init ## ACCORE-LOG-T-N2D8: Expose `AcEnv.AppConfiguration` setter for consumer init
**Priority:** P2 · **Type:** Feature · **Related:** `LOGGING_ISSUES.md#log-i-2` **Priority:** P2 · **Type:** Feature · **Related:** `LOGGING_ISSUES.md#accore-log-i-r9p3`
Allow `AcEnv.SetConfiguration(IConfiguration)` so consumer `Program.cs` can do `AcEnv.SetConfiguration(builder.Configuration)` at startup. Enables config-reading pattern on MAUI/WASM without filesystem assumptions. Backward-compat: fall back to filesystem if no explicit set. Allow `AcEnv.SetConfiguration(IConfiguration)` so consumer `Program.cs` can do `AcEnv.SetConfiguration(builder.Configuration)` at startup. Enables config-reading pattern on MAUI/WASM without filesystem assumptions. Backward-compat: fall back to filesystem if no explicit set.
## LOG-T-3: Unify or clearly separate config-reading and DI-based patterns ## ACCORE-LOG-T-R7L3: Unify or clearly separate config-reading and DI-based patterns
**Priority:** P2 · **Type:** Docs / Refactor · **Related:** `LOGGING_ISSUES.md#log-i-3` **Priority:** P2 · **Type:** Docs / Refactor · **Related:** `LOGGING_ISSUES.md#accore-log-i-l4n8`
Decide the canonical direction: Decide the canonical direction:
- **(a)** Deprecate config-reading pattern → all consumers migrate to DI factory - **(a)** Deprecate config-reading pattern → all consumers migrate to DI factory
- **(b)** Keep both, with compile-time guidance (analyzer / XML doc `[Obsolete]` hints / decision tree in LOGGING.md) - **(b)** Keep both, with compile-time guidance (analyzer / XML doc `[Obsolete]` hints / decision tree in LOGGING.md)
- **(c)** Merge: DI-factory internally falls back to config-reading when `TLogger` doesn't match the `Activator` ctor shape - **(c)** Merge: DI-factory internally falls back to config-reading when `TLogger` doesn't match the `Activator` ctor shape
## LOG-T-4: Per-writer LogLevel via appsettings ## ACCORE-LOG-T-F4S6: Per-writer LogLevel via appsettings
**Priority:** P2 · **Type:** Feature **Priority:** P2 · **Type:** Feature
Extend `AcLoggerOptions` with per-writer LogLevel overrides. Example shape: Extend `AcLoggerOptions` with per-writer LogLevel overrides. Example shape:
@ -42,25 +42,25 @@ Extend `AcLoggerOptions` with per-writer LogLevel overrides. Example shape:
``` ```
Factory applies overrides when constructing writers. Currently writer-LogLevel is hardcoded in writer ctors. Factory applies overrides when constructing writers. Currently writer-LogLevel is hardcoded in writer ctors.
## LOG-T-5: Unify default LogLevel across setup paths ## ACCORE-LOG-T-J9G2: Unify default LogLevel across setup paths
**Priority:** P1 · **Type:** Behaviour decision + cleanup · **Related:** `LOGGING_ISSUES.md#log-i-4` **Priority:** P1 · **Type:** Behaviour decision + cleanup · **Related:** `LOGGING_ISSUES.md#accore-log-i-b2h5`
Decide which default wins (`AcLoggerBase.cs:18` = `Error` vs `AcLoggerOptions.cs:27` = `Info`) and make both paths consistent. Recommendation: **`Info`** — matches the DI/Options path already in use for modern consumers (MAUI, WASM, ASP.NET Core) and is the common developer expectation. Decide which default wins (`AcLoggerBase.cs:18` = `Error` vs `AcLoggerOptions.cs:27` = `Info`) and make both paths consistent. Recommendation: **`Info`** — matches the DI/Options path already in use for modern consumers (MAUI, WASM, ASP.NET Core) and is the common developer expectation.
Change: drop the field initializer in `AcLoggerBase` (or set it to `Info`). Update LOGGING.md with a single "Default LogLevel = Info" line so this can't drift again. Change: drop the field initializer in `AcLoggerBase` (or set it to `Info`). Update LOGGING.md with a single "Default LogLevel = Info" line so this can't drift again.
## LOG-T-6: Fix `AcConsoleLogWriter.Initialize()` double-run ## ACCORE-LOG-T-B8K5: Fix `AcConsoleLogWriter.Initialize()` double-run
**Priority:** P3 · **Type:** Bug fix · **Related:** `LOGGING_ISSUES.md#log-i-5` **Priority:** P3 · **Type:** Bug fix · **Related:** `LOGGING_ISSUES.md#accore-log-i-x7q1`
Remove the redundant `Initialize()` call from the parameterless ctor body — the `: this(null)` chain already invokes it. No behaviour change; removes wasted `Console.ForegroundColor` assignment. Remove the redundant `Initialize()` call from the parameterless ctor body — the `: this(null)` chain already invokes it. No behaviour change; removes wasted `Console.ForegroundColor` assignment.
## LOG-T-7: Fix `ILogger.IsEnabled(MsLogLevel.None)` wrongly reporting enabled ## ACCORE-LOG-T-X1V4: Fix `ILogger.IsEnabled(MsLogLevel.None)` wrongly reporting enabled
**Priority:** P2 · **Type:** Bug fix · **Related:** `LOGGING_ISSUES.md#log-i-6` **Priority:** P2 · **Type:** Bug fix · **Related:** `LOGGING_ISSUES.md#accore-log-i-v3j6`
Add `if (logLevel == MsLogLevel.None) return false;` at the top of `AcLoggerBase.IsEnabled`. One-line fix. Optionally add a unit test covering all six MS LogLevel values → expected bool. Add `if (logLevel == MsLogLevel.None) return false;` at the top of `AcLoggerBase.IsEnabled`. One-line fix. Optionally add a unit test covering all six MS LogLevel values → expected bool.
## LOG-T-8: Cleanup batch (low-risk micro-refactor, one commit) ## ACCORE-LOG-T-M7P2: Cleanup batch (low-risk micro-refactor, one commit)
**Priority:** P3 · **Type:** Cleanup · **Related:** `LOGGING_ISSUES.md#log-i-7` **Priority:** P3 · **Type:** Cleanup · **Related:** `LOGGING_ISSUES.md#accore-log-i-t8f2`
Single commit covering: Single commit covering:
- Remove unused usings in `AcLoggerBase.cs` (`System.Security.AccessControl`, `System.Net.Mime.MediaTypeNames`) - Remove unused usings in `AcLoggerBase.cs` (`System.Security.AccessControl`, `System.Net.Mime.MediaTypeNames`)
@ -68,14 +68,14 @@ Single commit covering:
- Fix misleading comment at `AcLoggerBase.cs:210` — current comment says "fallback null" but code assigns `"Log"`. Make comment match code. - Fix misleading comment at `AcLoggerBase.cs:210` — current comment says "fallback null" but code assigns `"Log"`. Make comment match code.
- Decide on the commented-out batch block in `AcLogItemWriterBase.cs:90-119`: either delete (git history preserves) or convert to a single `// TODO: see LOGGING_TODO.md#todo-XX` marker with a new TODO entry for the batch work. - Decide on the commented-out batch block in `AcLogItemWriterBase.cs:90-119`: either delete (git history preserves) or convert to a single `// TODO: see LOGGING_TODO.md#todo-XX` marker with a new TODO entry for the batch work.
## LOG-T-9: Fail-fast ctor validation in `AddAcLoggerFactory<TLogger>` ## ACCORE-LOG-T-L3T8: Fail-fast ctor validation in `AddAcLoggerFactory<TLogger>`
**Priority:** P2 · **Type:** Feature **Priority:** P2 · **Type:** Feature
`AcLoggerServiceExtensions.AddAcLoggerFactory<TLogger>` uses `Activator.CreateInstance(typeof(TLogger), AppType, LogLevel, categoryName, writers)` only on first logger resolution. Ctor-mismatch therefore surfaces only at first log call, not at `services.BuildServiceProvider()` time. `AcLoggerServiceExtensions.AddAcLoggerFactory<TLogger>` uses `Activator.CreateInstance(typeof(TLogger), AppType, LogLevel, categoryName, writers)` only on first logger resolution. Ctor-mismatch therefore surfaces only at first log call, not at `services.BuildServiceProvider()` time.
Add a one-time reflection check at registration: `typeof(TLogger).GetConstructor([typeof(AppType), typeof(LogLevel), typeof(string), typeof(IAcLogWriterBase[])])` — if null, throw `InvalidOperationException` with the expected signature in the message. Cost: one reflection call per registration. Benefit: app fails to start (loud) instead of logging being silently broken (quiet). Add a one-time reflection check at registration: `typeof(TLogger).GetConstructor([typeof(AppType), typeof(LogLevel), typeof(string), typeof(IAcLogWriterBase[])])` — if null, throw `InvalidOperationException` with the expected signature in the message. Cost: one reflection call per registration. Benefit: app fails to start (loud) instead of logging being silently broken (quiet).
## LOG-T-10: Writer exception isolation ## ACCORE-LOG-T-Q6Z1: Writer exception isolation
**Priority:** P2 · **Type:** Resilience **Priority:** P2 · **Type:** Resilience
Currently `AcLoggerBase.Info(...)` and friends do `LogWriters.ForEach(x => x.Info(...))`. A throwing writer (e.g. `SignaRClientLogItemWriter` during network blip, `AcDbLogItemWriter` during a DB outage) takes down the fan-out → subsequent writers in the list never see the message. Currently `AcLoggerBase.Info(...)` and friends do `LogWriters.ForEach(x => x.Info(...))`. A throwing writer (e.g. `SignaRClientLogItemWriter` during network blip, `AcDbLogItemWriter` during a DB outage) takes down the fan-out → subsequent writers in the list never see the message.
@ -84,8 +84,8 @@ Wrap each writer invocation in `try { ... } catch (Exception ex) { Console.Error
Must NOT log to `AcLoggerBase` itself inside the catch (reentrancy / infinite loop risk). Must NOT log to `AcLoggerBase` itself inside the catch (reentrancy / infinite loop risk).
## LOG-T-11: Migrate server-side NopCommerce plugin logger setup to DI-factory ## ACCORE-LOG-T-W4H9: Migrate server-side NopCommerce plugin logger setup to DI-factory
**Priority:** P2 · **Type:** Consumer refactor · **Related:** `LOGGING_ISSUES.md#log-i-8`, `../../../AyCode.Services/docs/SIGNALR/SIGNALR_TODO.md#log-t-5` **Priority:** P2 · **Type:** Consumer refactor · **Related:** `LOGGING_ISSUES.md#accore-log-i-m4c9`, `../../../AyCode.Services/docs/SIGNALR/SIGNALR_TODO.md#accore-sig-t-m5l6`
Align the server-side plugin (`Nop.Plugin.Misc.AIPlugin/Infrastructure/PluginNopStartup.cs`) with the client-side setup pattern already used by MAUI / Web / Web.Client consumers. Align the server-side plugin (`Nop.Plugin.Misc.AIPlugin/Infrastructure/PluginNopStartup.cs`) with the client-side setup pattern already used by MAUI / Web / Web.Client consumers.
@ -114,10 +114,10 @@ services.AddAcLoggerFactory<Logger>();
### Consequences / checklist ### Consequences / checklist
- [ ] Consumers of `new Logger(...)` elsewhere in the plugin code must switch to injecting `Func<string, Logger>` (DI factory). - [ ] Consumers of `new Logger(...)` elsewhere in the plugin code must switch to injecting `Func<string, Logger>` (DI factory).
- [ ] LOG-I-1 Console.Error noise disappears once the config-reading path is no longer invoked. - [ ] ACCORE-LOG-I-K7M2 Console.Error noise disappears once the config-reading path is no longer invoked.
- [ ] Legacy `AyCode:Logger:LogWriters[]` array in `appsettings.json` becomes unused — decide: **(a)** remove (breaking if any other consumer still reads it), **(b)** keep for back-compat with a comment "superseded by DI, see LOGGING.md". - [ ] Legacy `AyCode:Logger:LogWriters[]` array in `appsettings.json` becomes unused — decide: **(a)** remove (breaking if any other consumer still reads it), **(b)** keep for back-compat with a comment "superseded by DI, see LOGGING.md".
- [ ] Update `Nop.Plugin.Misc.AIPlugin/docs/SIGNALR/README.md:22` — current text still describes `services.AddSingleton<IHubProtocol>(new AcBinaryHubProtocol())` which has NOT been the actual registration for months. - [ ] Update `Nop.Plugin.Misc.AIPlugin/docs/SIGNALR/README.md:22` — current text still describes `services.AddSingleton<IHubProtocol>(new AcBinaryHubProtocol())` which has NOT been the actual registration for months.
- [ ] Verify server startup log — `AcLoggerOptions` must bind cleanly (no "missing section" warnings from `IOptionsMonitor`). - [ ] Verify server startup log — `AcLoggerOptions` must bind cleanly (no "missing section" warnings from `IOptionsMonitor`).
### Why this is a separate TODO from LOG-T-3 ### Why this is a separate TODO from ACCORE-LOG-T-R7L3
LOG-T-3 is about the **framework-level decision** (keep both patterns vs deprecate legacy). LOG-T-11 is the **consumer-level execution** of that decision for the server plugin — independent of whether LOG-T-3 ultimately deprecates the legacy path or keeps it alive. Migrating the server plugin removes LOG-I-1 noise regardless of LOG-T-3's outcome. ACCORE-LOG-T-R7L3 is about the **framework-level decision** (keep both patterns vs deprecate legacy). ACCORE-LOG-T-W4H9 is the **consumer-level execution** of that decision for the server plugin — independent of whether ACCORE-LOG-T-R7L3 ultimately deprecates the legacy path or keeps it alive. Migrating the server plugin removes ACCORE-LOG-I-K7M2 noise regardless of ACCORE-LOG-T-R7L3's outcome.

View File

@ -120,7 +120,7 @@ Each writer's constructor signature must accept `(AppType, LogLevel, string?)`.
## DI-Based Factory Pattern ## DI-Based Factory Pattern
Modern, framework-first alternative to the config-reading `AcLoggerBase(string)` ctor. No runtime reflection over writer config; concrete writer types resolved from DI. Recommended for all modern projects (MAUI, WASM, ASP.NET Core) — the config-reading path is filesystem-bound and unsuitable for MAUI/WASM (see `LOGGING_ISSUES.md#log-i-2`). Modern, framework-first alternative to the config-reading `AcLoggerBase(string)` ctor. No runtime reflection over writer config; concrete writer types resolved from DI. Recommended for all modern projects (MAUI, WASM, ASP.NET Core) — the config-reading path is filesystem-bound and unsuitable for MAUI/WASM (see `LOGGING_ISSUES.md#accore-log-i-r9p3`).
### Consumer setup in Program.cs ### Consumer setup in Program.cs
@ -199,7 +199,7 @@ For SignalR client setup that wires the same `AcLoggerBase` instance into Micros
| Typical use | Legacy / server-side config-driven | Modern DI: MAUI, WASM, ASP.NET Core | | Typical use | Legacy / server-side config-driven | Modern DI: MAUI, WASM, ASP.NET Core |
| MAUI/WASM-safe | ❌ (filesystem-bound `AcEnv.AppConfiguration`) | ✅ | | MAUI/WASM-safe | ❌ (filesystem-bound `AcEnv.AppConfiguration`) | ✅ |
Both patterns can coexist — consumer picks per scenario. Related issues: `LOGGING_ISSUES.md`. Planned unification: `LOGGING_TODO.md#log-t-3`. Both patterns can coexist — consumer picks per scenario. Related issues: `LOGGING_ISSUES.md`. Planned unification: `LOGGING_TODO.md#accore-log-t-r7l3`.
## Core Components ## Core Components

View File

@ -1,6 +1,6 @@
# TOON — Token-Oriented Object Notation # TOON — Token-Oriented Object Notation
LLM-optimized serializer. Primary goal: **maximize LLM accuracy** via explicit schema/data separation, rich metadata, and unambiguous structure boundaries. Serialize-only (no deserializer — see `TOON_TODO.md#toon-t-6`). LLM-optimized serializer. Primary goal: **maximize LLM accuracy** via explicit schema/data separation, rich metadata, and unambiguous structure boundaries. Serialize-only (no deserializer — see `TOON_TODO.md#accore-toon-t-f3x1`).
Source: `Serializers/Toons/` in this project. Source: `Serializers/Toons/` in this project.

View File

@ -124,4 +124,4 @@ The context's `StringBuilder` is shared with write helpers:
- **Cached per-type metadata** — reflection runs once per type, then hot cache. - **Cached per-type metadata** — reflection runs once per type, then hot cache.
- **Parallel code paths for `UseIndentation`** — compact mode avoids space emission on `=` and skips blank lines at write time (not a post-process strip). - **Parallel code paths for `UseIndentation`** — compact mode avoids space emission on `=` and skips blank lines at write time (not a post-process strip).
- **Fast-path for primitive values**`Serialize(value)` with a primitive returns without pool access or reflection. - **Fast-path for primitive values**`Serialize(value)` with a primitive returns without pool access or reflection.
- **No deserialization** — Toon is intentionally one-way. See `TOON_TODO.md#toon-t-6`. - **No deserialization** — Toon is intentionally one-way. See `TOON_TODO.md#accore-toon-t-f3x1`.

View File

@ -82,7 +82,7 @@ Useful extra info because LLMs can misjudge narrow integer type sizes:
| read-only property (no public setter) | `readonly` | | read-only property (no public setter) | `readonly` |
| enum backing field detected (see below) | `enum-type: {EnumName}` | | enum backing field detected (see below) | `enum-type: {EnumName}` |
> ⚠️ **`Age` pattern false positives** — the rule uses `string.Contains("Age", OrdinalIgnoreCase)`, which is not word-boundary-aware. Hits `LanguageId`, `Package*`, `Image*`, `Page*`, `Message*`, `Storage*` and similar. See `TOON_ISSUES.md#toon-i-2` for detected cases and fix options. Lesser risk applies to the `Email`, `Phone`, `Address`, `Url`, `Date`, `Version`, `CreatedAt`, `UpdatedAt`, `DeletedAt` patterns too. > ⚠️ **`Age` pattern false positives** — the rule uses `string.Contains("Age", OrdinalIgnoreCase)`, which is not word-boundary-aware. Hits `LanguageId`, `Package*`, `Image*`, `Page*`, `Message*`, `Storage*` and similar. See `TOON_ISSUES.md#accore-toon-i-x3h2` for detected cases and fix options. Lesser risk applies to the `Email`, `Phone`, `Address`, `Url`, `Date`, `Version`, `CreatedAt`, `UpdatedAt`, `DeletedAt` patterns too.
## Enum backing field detection ## Enum backing field detection

View File

@ -2,7 +2,7 @@
For planned/actionable work see `TOON_TODO.md`. For planned/actionable work see `TOON_TODO.md`.
## TOON-I-1: `Compact` preset XML doc contradicts code behaviour ## ACCORE-TOON-I-B7L4: `Compact` preset XML doc contradicts code behaviour
**Severity:** Trivial (doc-only, no runtime effect) · **Status:** Open (direction not decided) · **Area:** `Serializers/Toons/AcToonSerializerOptions.cs` **Severity:** Trivial (doc-only, no runtime effect) · **Status:** Open (direction not decided) · **Area:** `Serializers/Toons/AcToonSerializerOptions.cs`
@ -48,7 +48,7 @@ var trulyCompact = new AcToonSerializerOptions {
### Related TODO ### Related TODO
None yet — fix direction not decided. None yet — fix direction not decided.
## TOON-I-2: `Age` inference substring false positive ## ACCORE-TOON-I-X3H2: `Age` inference substring false positive
**Severity:** Minor (constraint-only, no runtime effect) · **Status:** Open · **Area:** `Serializers/Toons/AcToonSerializer.Descriptions.cs``GetPropertyConstraints` / `GetInferredConstraints` **Severity:** Minor (constraint-only, no runtime effect) · **Status:** Open · **Area:** `Serializers/Toons/AcToonSerializer.Descriptions.cs``GetPropertyConstraints` / `GetInferredConstraints`
@ -83,7 +83,7 @@ Explicit `[ToonDescription(Constraints = "...")]` on the affected property (repl
### Related TODO ### Related TODO
None yet. None yet.
## TOON-I-3: Property override duplicated on inheritance in `@types` ## ACCORE-TOON-I-P6V5: Property override duplicated on inheritance in `@types`
**Severity:** Minor (schema correctness; LLM may misinterpret) · **Status:** Open · **Area:** Property enumeration — likely `AcToonSerializer.ToonSerializeTypeMetadata.cs` or the shared base in `Serializers/` root. **Severity:** Minor (schema correctness; LLM may misinterpret) · **Status:** Open · **Area:** Property enumeration — likely `AcToonSerializer.ToonSerializeTypeMetadata.cs` or the shared base in `Serializers/` root.
@ -118,7 +118,7 @@ None — the schema is wrong as-is. Downstream consumers must tolerate the dupli
### Related TODO ### Related TODO
None yet. None yet.
## TOON-I-4: Property order in `@types` is not pure alphabetical ## ACCORE-TOON-I-K4Z9: Property order in `@types` is not pure alphabetical
**Severity:** Minor (readability / predictability) · **Status:** Open · **Area:** Property ordering — likely `AcToonSerializer.ToonSerializeTypeMetadata.cs`, inherited from the shared base in `Serializers/` root. **Severity:** Minor (readability / predictability) · **Status:** Open · **Area:** Property ordering — likely `AcToonSerializer.ToonSerializeTypeMetadata.cs`, inherited from the shared base in `Serializers/` root.

View File

@ -5,7 +5,7 @@
--- ---
## TOON-T-1: Tabular array mode (CSV-style headered lists) ## ACCORE-TOON-T-D1R7: Tabular array mode (CSV-style headered lists)
**Priority:** P2 · **Type:** Feature (opt-in) · **Origin:** 2026-04-24 LLM-accuracy proposal **Priority:** P2 · **Type:** Feature (opt-in) · **Origin:** 2026-04-24 LLM-accuracy proposal
Emit arrays of homogeneous flat objects with a single column header instead of repeating property names on every element: Emit arrays of homogeneous flat objects with a single column header instead of repeating property names on every element:
@ -27,7 +27,7 @@ When any condition fails → fall back to the current named syntax.
Rationale: the CSV pattern is heavily represented in LLM training data; a single header avoids per-row key repetition. Estimated 30-50% token savings on arrays with 50+ rows. Must remain OFF in `Verbose` preset for debug clarity. Rationale: the CSV pattern is heavily represented in LLM training data; a single header avoids per-row key repetition. Estimated 30-50% token savings on arrays with 50+ rows. Must remain OFF in `Verbose` preset for debug clarity.
## TOON-T-2: Schema-derived type elision in `@data` ## ACCORE-TOON-T-G5M3: Schema-derived type elision in `@data`
**Priority:** P2 · **Type:** Feature · **Origin:** 2026-04-24 LLM-accuracy proposal **Priority:** P2 · **Type:** Feature · **Origin:** 2026-04-24 LLM-accuracy proposal
Omit type names on `@data` list elements when: Omit type names on `@data` list elements when:
@ -47,9 +47,9 @@ GenericAttributes = [
] ]
``` ```
Must stay explicit in `Verbose` mode and in polymorphic collections (type name is the discriminator). The main design tension: this slightly erodes the "explicit is better" principle — schema drift in a long conversation could cause silent misinterpretation. Pair with TOON-T-5 to mitigate. Must stay explicit in `Verbose` mode and in polymorphic collections (type name is the discriminator). The main design tension: this slightly erodes the "explicit is better" principle — schema drift in a long conversation could cause silent misinterpretation. Pair with ACCORE-TOON-T-S6B9 to mitigate.
## TOON-T-3: Schema-declared default values ## ACCORE-TOON-T-J8N6: Schema-declared default values
**Priority:** P2 · **Type:** Feature · **Origin:** 2026-04-24 LLM-accuracy proposal **Priority:** P2 · **Type:** Feature · **Origin:** 2026-04-24 LLM-accuracy proposal
Currently `OmitDefaultValues = true` removes fields equal to `default(T)`, but the LLM must guess what the default is. Extend `@types` with explicit per-property defaults so absence becomes unambiguous: Currently `OmitDefaultValues = true` removes fields equal to `default(T)`, but the LLM must guess what the default is. Extend `@types` with explicit per-property defaults so absence becomes unambiguous:
@ -66,9 +66,9 @@ Currently `OmitDefaultValues = true` removes fields equal to `default(T)`, but t
Implementation: during `@types` emission, detect the property's `default(T)` value and emit `default-value: ...` when `OmitDefaultValues = true`. `@data` omission then carries documented semantics ("use the schema default"), not implicit guessing. Implementation: during `@types` emission, detect the property's `default(T)` value and emit `default-value: ...` when `OmitDefaultValues = true`. `@data` omission then carries documented semantics ("use the schema default"), not implicit guessing.
Deserializer impact: N/A — no deserializer yet (see TOON-T-6). Deserializer impact: N/A — no deserializer yet (see ACCORE-TOON-T-F3X1).
## TOON-T-4: Reference-handling preset audit ## ACCORE-TOON-T-V2T4: Reference-handling preset audit
**Priority:** P3 · **Type:** Configuration review · **Origin:** 2026-04-24 LLM-accuracy proposal **Priority:** P3 · **Type:** Configuration review · **Origin:** 2026-04-24 LLM-accuracy proposal
The `@N` / `@ref:N` syntax exists and works (see `TOON_FORMAT.md#reference-syntax`). Audit whether the presets activate it aggressively enough: The `@N` / `@ref:N` syntax exists and works (see `TOON_FORMAT.md#reference-syntax`). Audit whether the presets activate it aggressively enough:
@ -79,10 +79,10 @@ The `@N` / `@ref:N` syntax exists and works (see `TOON_FORMAT.md#reference-synta
Deliverable: a "When to choose which ReferenceHandling mode" section in `TOON_OPTIONS.md` with concrete examples. Deliverable: a "When to choose which ReferenceHandling mode" section in `TOON_OPTIONS.md` with concrete examples.
## TOON-T-5: Schema hash in `@meta` ## ACCORE-TOON-T-S6B9: Schema hash in `@meta`
**Priority:** P3 · **Type:** Feature (defense-in-depth for TOON-T-2, TOON-T-3) · **Origin:** 2026-04-24 **Priority:** P3 · **Type:** Feature (defense-in-depth for ACCORE-TOON-T-G5M3, ACCORE-TOON-T-J8N6) · **Origin:** 2026-04-24
After TOON-T-2 and TOON-T-3 ship, `@data` payloads rely more heavily on a specific `@types` schema. In multi-turn conversations, the LLM's cached schema may drift out of sync with the sender's. After ACCORE-TOON-T-G5M3 and ACCORE-TOON-T-J8N6 ship, `@data` payloads rely more heavily on a specific `@types` schema. In multi-turn conversations, the LLM's cached schema may drift out of sync with the sender's.
Emit a compact schema fingerprint in `@meta`: Emit a compact schema fingerprint in `@meta`:
@ -96,7 +96,7 @@ Emit a compact schema fingerprint in `@meta`:
The prompt template instructs the LLM: *"only use cached schema when the `schema-hash` matches; otherwise request the full schema again."* Cost: ~20-30 tokens per payload. Benefit: silent schema drift becomes detectable. The prompt template instructs the LLM: *"only use cached schema when the `schema-hash` matches; otherwise request the full schema again."* Cost: ~20-30 tokens per payload. Benefit: silent schema drift becomes detectable.
## TOON-T-6: Deserialization support ## ACCORE-TOON-T-F3X1: Deserialization support
**Priority:** P3 · **Type:** Feature (major) · **Origin:** 2026-04-24 during docs migration **Priority:** P3 · **Type:** Feature (major) · **Origin:** 2026-04-24 during docs migration
Toon is currently **serialize-only** — there is no `AcToonDeserializer`. An LLM can produce Toon-formatted responses, but the framework cannot parse them back into C# objects. Toon is currently **serialize-only** — there is no `AcToonDeserializer`. An LLM can produce Toon-formatted responses, but the framework cannot parse them back into C# objects.
@ -105,9 +105,9 @@ Decision needed before implementation:
- Is Toon deserialization in-scope? (Primary goal is LLM accuracy — deserialization is mostly useful for LLM-generated outputs.) - Is Toon deserialization in-scope? (Primary goal is LLM accuracy — deserialization is mostly useful for LLM-generated outputs.)
- Or is `AcBinary`/`AcJson` the intended deserialization path, with Toon strictly a presentation format for LLM inputs? - Or is `AcBinary`/`AcJson` the intended deserialization path, with Toon strictly a presentation format for LLM inputs?
If in-scope, the parser must handle: `@meta`/`@types`/`@data` sections, reference resolution (`@N` / `@ref:N`), multi-line strings, numeric enum values (requires `@types` for name mapping), tabular arrays (if TOON-T-1 lands), polymorphic collections. If in-scope, the parser must handle: `@meta`/`@types`/`@data` sections, reference resolution (`@N` / `@ref:N`), multi-line strings, numeric enum values (requires `@types` for name mapping), tabular arrays (if ACCORE-TOON-T-D1R7 lands), polymorphic collections.
## TOON-T-7: Validate benchmark claims before re-adding to docs ## ACCORE-TOON-T-M9Q2: Validate benchmark claims before re-adding to docs
**Priority:** P3 · **Type:** Doc correctness · **Origin:** 2026-04-24 during `ToonExtendedInfo.txt` removal **Priority:** P3 · **Type:** Doc correctness · **Origin:** 2026-04-24 during `ToonExtendedInfo.txt` removal
The original `ToonExtendedInfo.txt` (deleted during migration) claimed: The original `ToonExtendedInfo.txt` (deleted during migration) claimed:

View File

@ -4,14 +4,14 @@ Canonical home for issues, TODOs, bugs, and critical items that span **two or mo
## When to use `XCUT-*-N` vs a topic-specific ID ## When to use `XCUT-*-N` vs a topic-specific ID
- **Single topic** (concern, bug, or planned work inside one domain) → `{TOPIC}-{TYPE}-{N}` in that topic's `{TOPIC}_ISSUES.md` / `{TOPIC}_TODO.md` (e.g., `LOG-I-5` in `LOGGING_ISSUES.md`). - **Single topic** (concern, bug, or planned work inside one domain) → `ACCORE-{TOPIC}-{TYPE}-{XXXX}` in that topic's `{TOPIC}_ISSUES.md` / `{TOPIC}_TODO.md` (e.g., `ACCORE-LOG-I-X7Q1` in `LOGGING_ISSUES.md`).
- **Two or more topics** (coordinated change needed across domains, or the issue's root cause spans multiple subsystems) → `XCUT-{TYPE}-{N}` here, with cross-references from each affected topic's file. - **Two or more ACCORE topics** (coordinated change needed across domains, or the issue's root cause spans multiple subsystems) → `ACCORE-XCUT-{TYPE}-{XXXX}` here, with cross-references from each affected topic's file.
## Examples of cross-cutting items ## Examples of cross-cutting items
- **`XCUT-I-1: JSON-in-Binary request parameters`** — affects both the BINARY serializer (wire format) and SIGNALR transport (envelope). Neither side can fix it alone; it needs a coordinated client+server+consumer migration. - **`ACCORE-XCUT-I-X8Q1: JSON-in-Binary request parameters`** — affects both the BINARY serializer (wire format) and SIGNALR transport (envelope). Neither side can fix it alone; it needs a coordinated client+server+consumer migration.
- *(hypothetical)* `XCUT-T-N` for a shared infrastructure refactor that touches Logger, SignalR, and SourceGenerator simultaneously. - *(hypothetical)* `ACCORE-XCUT-T-XXXX` for a shared infrastructure refactor that touches Logger, SignalR, and SourceGenerator simultaneously.
- *(hypothetical)* `XCUT-B-N` for a bug where the symptom appears in one topic but the root cause lives in another. - *(hypothetical)* `ACCORE-XCUT-B-XXXX` for a bug where the symptom appears in one topic but the root cause lives in another.
## Files in this folder ## Files in this folder
@ -21,16 +21,15 @@ Canonical home for issues, TODOs, bugs, and critical items that span **two or mo
## Cross-reference convention ## Cross-reference convention
1. **Canonical entry** lives here. Full body: status, affects (list of topics), rationale, migration plan. 1. **Canonical entry** lives here. Full body: status, affects (list of topics), rationale, migration plan.
2. **Each affected topic** adds a short `### XCUT-{TYPE}-{N}: <title> — cross-ref` section in its own `_ISSUES.md` / `_TODO.md`, pointing back to this canonical entry. 2. **Each affected topic** adds a short `### ACCORE-XCUT-{TYPE}-{XXXX}: <title> — cross-ref` section in its own `_ISSUES.md` / `_TODO.md`, pointing back to this canonical entry (with the SAME 4-char suffix as the canonical, since cross-ref pointers are pointers — not duplicate IDs).
3. The **topic-specific cross-ref** body is one sentence: "See canonical entry at `../XCUT/XCUT_ISSUES.md#xcut-{type}-{n}`" + a 1-2 line summary of what this topic contributes. 3. The **topic-specific cross-ref** body is one sentence: "See canonical entry at `../XCUT/XCUT_ISSUES.md#accore-xcut-{type}-{xxxx}`" + a 1-2 line summary of what this topic contributes.
## How `XCUT-*-N` IDs are numbered ## How `ACCORE-XCUT-*` IDs are formed
Per the `TOPIC_CODES.md` registry: Per the `REPO_PREFIXES.md` and `TOPIC_CODES.md` registries:
- Counter is `XCUT` + type (`I` / `T` / `B` / `C`). - Format: `ACCORE-XCUT-<TYPE>-<RAND>` where `<TYPE>` is `I` (issue), `T` (TODO), `B` (bug), or `C` (critical), and `<RAND>` is a random 4-character `[A-Z0-9]` suffix.
- `XCUT-I-1`, `XCUT-I-2`, ... share one counter across all cross-cutting issues regardless of which topics they span. - All ACCORE cross-cutting issues live in the `ACCORE-XCUT-I-*` namespace, regardless of which topics they span. Same for TODOs (`ACCORE-XCUT-T-*`), bugs (`ACCORE-XCUT-B-*`), critical-severity (`ACCORE-XCUT-C-*`).
- `XCUT-T-1`, `XCUT-T-2`, ... share another counter for cross-cutting TODOs. - Append-only — once assigned, IDs never change. The random suffix avoids collisions across parallel branches; uniqueness is verified at creation per `REPO_PREFIXES.md` "Random suffix spec".
- Append-only — once assigned, IDs never change.
## Historical note ## Historical note

View File

@ -6,7 +6,7 @@ For planned cross-cutting work, see `XCUT_TODO.md`.
--- ---
## XCUT-I-1: JSON-in-Binary request parameters ## ACCORE-XCUT-I-X8Q1: JSON-in-Binary request parameters
**Status:** Closed (2026-04-26, landed in commits `cdd54d3` 2026-04-05 + `3b70070` 2026-04-06) **Status:** Closed (2026-04-26, landed in commits `cdd54d3` 2026-04-05 + `3b70070` 2026-04-06)
**Affects:** BINARY serializer (wire format) ↔ SIGNALR transport (envelope) ↔ all consuming projects (caller code) **Affects:** BINARY serializer (wire format) ↔ SIGNALR transport (envelope) ↔ all consuming projects (caller code)
@ -31,10 +31,10 @@ Client→server request parameters previously travelled as JSON inside a Binary
- **Wire impact:** No JSON round-trip on the wire for request params. **Breaking change** vs. previous JSON-in-Binary clients/servers — version bump required. - **Wire impact:** No JSON round-trip on the wire for request params. **Breaking change** vs. previous JSON-in-Binary clients/servers — version bump required.
- **Equivalent, not literal:** `SignalParams` (object[]-style packed payload) replaced `SignalPostJsonDataMessage<T>` instead of introducing a parallel `SignalPostBinaryDataMessage<T>`. Single unified envelope, fewer hot-path indirections. - **Equivalent, not literal:** `SignalParams` (object[]-style packed payload) replaced `SignalPostJsonDataMessage<T>` instead of introducing a parallel `SignalPostBinaryDataMessage<T>`. Single unified envelope, fewer hot-path indirections.
- **Coordinated change:** client + server + consumer projects updated together in the two commits (BREAKING). All five steps from the original "Do NOT attempt as a side-effect" list completed. - **Coordinated change:** client + server + consumer projects updated together in the two commits (BREAKING). All five steps from the original "Do NOT attempt as a side-effect" list completed.
- **Legacy cleanup outstanding (not blocking closure):** `[Obsolete]` wrappers can be deleted once external consumer projects stop referencing them — tracked in `../../../AyCode.Services/docs/SIGNALR/SIGNALR_TODO.md#sig-t-6` (separate from this canonical issue). - **Legacy cleanup outstanding (not blocking closure):** `[Obsolete]` wrappers can be deleted once external consumer projects stop referencing them — tracked in `../../../AyCode.Services/docs/SIGNALR/SIGNALR_TODO.md#accore-sig-t-s3n8` (separate from this canonical issue).
### Cross-references ### Cross-references
- **BINARY_ISSUES.md** (`../BINARY/BINARY_ISSUES.md#xcut-i-1`): cross-ref pointing here - **BINARY_ISSUES.md** (`../BINARY/BINARY_ISSUES.md#accore-xcut-i-x8q1`): cross-ref pointing here
- **SIGNALR_ISSUES.md** (`../../../AyCode.Services/docs/SIGNALR/SIGNALR_ISSUES.md#xcut-i-1`): cross-ref pointing here - **SIGNALR_ISSUES.md** (`../../../AyCode.Services/docs/SIGNALR/SIGNALR_ISSUES.md#accore-xcut-i-x8q1`): cross-ref pointing here
- **BINARY_TODO.md#bin-t-1**: migration plan (also marked Closed) - **BINARY_TODO.md#accore-bin-t-s8p4**: migration plan (also marked Closed)

View File

@ -152,7 +152,7 @@ GetParameterValues(ParameterInfo[]):
Type-guided deserialization — each parameter is individually serialized/deserialized with its concrete type, avoiding the `object[]` → dictionary problem of untyped binary deserialization. Type-guided deserialization — each parameter is individually serialized/deserialized with its concrete type, avoiding the `object[]` → dictionary problem of untyped binary deserialization.
> Known concerns and limitations on parameter serialization (per-parameter overhead, AcBinary-only) are tracked in `SIGNALR_ISSUES.md` under `SIG-I-2` and `SIG-I-3`. > Known concerns and limitations on parameter serialization (per-parameter overhead, AcBinary-only) are tracked in `SIGNALR_ISSUES.md` under `ACCORE-SIG-I-L5K3` and `ACCORE-SIG-I-H8D6`.
## Response Patterns ## Response Patterns

File diff suppressed because one or more lines are too long

View File

@ -5,12 +5,12 @@
--- ---
## SIG-T-1: Diagnose first-call null in PostDataAsync<T> ## ACCORE-SIG-T-J2P5: Diagnose first-call null in PostDataAsync<T>
**Priority:** P2 · **Type:** Investigation · **Related:** `SIGNALR_ISSUES.md#issue-02` **Priority:** P2 · **Type:** Investigation · **Related:** `SIGNALR_ISSUES.md#accore-sig-i-l5k3`
Reproduce the `GetProductDtos_80`-style first-call null. Add trace logs to `PostDataAsync<T>` awaiter path and `OnReceiveMessage → pending request` dictionary lookup. Verify `requestId → Task<T>` correlation on the very first chunked response of a fresh connection. Reproduce the `GetProductDtos_80`-style first-call null. Add trace logs to `PostDataAsync<T>` awaiter path and `OnReceiveMessage → pending request` dictionary lookup. Verify `requestId → Task<T>` correlation on the very first chunked response of a fresh connection.
## SIG-T-2: Document asymmetric send/receive capability ## ACCORE-SIG-T-W8R3: Document asymmetric send/receive capability
**Priority:** P1 · **Type:** Docs **Priority:** P1 · **Type:** Docs
Current behaviour: sender selects `BinaryProtocolMode` independently; receiver detects the wire format from the first byte (`CHUNK_START=200` → chunked path; else non-chunked). This means client and server can run DIFFERENT `ProtocolMode` settings independently — a core feature amplifying interoperability. Current behaviour: sender selects `BinaryProtocolMode` independently; receiver detects the wire format from the first byte (`CHUNK_START=200` → chunked path; else non-chunked). This means client and server can run DIFFERENT `ProtocolMode` settings independently — a core feature amplifying interoperability.
@ -20,20 +20,20 @@ Document in `../SIGNALR_BINARY_PROTOCOL/README.md` as a dedicated "Asymmetric se
- Third-party client on NuGet can pick any mode → server doesn't care - Third-party client on NuGet can pick any mode → server doesn't care
- Gradual mode rollouts possible (no synchronized deploy) - Gradual mode rollouts possible (no synchronized deploy)
## SIG-T-3: Code-level guard for FlushTimeout < ClientTimeoutInterval ## ACCORE-SIG-T-D7Q4: Code-level guard for FlushTimeout < ClientTimeoutInterval
**Priority:** P2 · **Type:** Feature **Priority:** P2 · **Type:** Feature
`AcBinaryHubProtocolOptions.Validate()` currently documents (in XML doc) that `FlushTimeout` should be less than the SignalR `HubOptions.ClientTimeoutInterval`, but there is no code-level check. Add validation at protocol registration time — if both options are resolvable from the DI scope, verify the constraint and emit a startup warning (or throw, pending decision). `AcBinaryHubProtocolOptions.Validate()` currently documents (in XML doc) that `FlushTimeout` should be less than the SignalR `HubOptions.ClientTimeoutInterval`, but there is no code-level check. Add validation at protocol registration time — if both options are resolvable from the DI scope, verify the constraint and emit a startup warning (or throw, pending decision).
## SIG-T-4: `BinaryProtocolMode.Auto` — adaptive send-mode ## ACCORE-SIG-T-V9H1: `BinaryProtocolMode.Auto` — adaptive send-mode
**Priority:** P3 · **Type:** Feature · **Related:** `../SIGNALR_BINARY_PROTOCOL/SIGNALR_BINARY_PROTOCOL_TODO.md#sbp-t-3` **Priority:** P3 · **Type:** Feature · **Related:** `../SIGNALR_BINARY_PROTOCOL/SIGNALR_BINARY_PROTOCOL_TODO.md#accore-sbp-t-l1v4`
Design: on first received message, inspect first byte to determine peer's send format. On subsequent sends, match it (subject to local-platform constraints, e.g. WASM never actually sends AsyncSegment). Per-`HubConnection` state. Optional upfront handshake-extension negotiation as an alternative — see wire-level TODO. Design: on first received message, inspect first byte to determine peer's send format. On subsequent sends, match it (subject to local-platform constraints, e.g. WASM never actually sends AsyncSegment). Per-`HubConnection` state. Optional upfront handshake-extension negotiation as an alternative — see wire-level TODO.
## SIG-T-5: Server-side NopCommerce plugin — expose `AcBinaryHubProtocolOptions` via appsettings ## ACCORE-SIG-T-M5L6: Server-side NopCommerce plugin — expose `AcBinaryHubProtocolOptions` via appsettings
**Priority:** P2 · **Type:** Consumer refactor · **Related:** `SIGNALR_ISSUES.md#sig-i-7`, `../../../AyCode.Core/docs/LOGGING/LOGGING_TODO.md#log-t-11` **Priority:** P2 · **Type:** Consumer refactor · **Related:** `SIGNALR_ISSUES.md#accore-sig-i-b5g9`, `../../../AyCode.Core/docs/LOGGING/LOGGING_TODO.md#accore-log-t-w4h9`
Bind `AcBinaryHubProtocolOptions` from `appsettings.json` instead of hardcoding `ProtocolMode` and constructing a manual `Logger` instance in `PluginNopStartup.cs`. This sibling task is paired with LOGGING_TODO.md#log-t-11 (same plugin, logger-setup migration) — best landed in one commit. Bind `AcBinaryHubProtocolOptions` from `appsettings.json` instead of hardcoding `ProtocolMode` and constructing a manual `Logger` instance in `PluginNopStartup.cs`. This sibling task is paired with LOGGING_TODO.md#accore-log-t-w4h9 (same plugin, logger-setup migration) — best landed in one commit.
### Target diff ### Target diff
```csharp ```csharp
@ -65,7 +65,7 @@ services.AddSignalR(hubOptions => { /* unchanged */ })
``` ```
### Consequences / checklist ### Consequences / checklist
- [ ] `new Logger(...)` line removed from SignalR registration → server-side logger now goes through the DI factory (see LOGGING_TODO.md#log-t-11). - [ ] `new Logger(...)` line removed from SignalR registration → server-side logger now goes through the DI factory (see LOGGING_TODO.md#accore-log-t-w4h9).
- [ ] Per-deploy tuning possible without recompile: switching `ProtocolMode` for diagnostics, extending `FlushTimeout` for slow links, adjusting `BufferSize` for different Kestrel slab sizes. - [ ] Per-deploy tuning possible without recompile: switching `ProtocolMode` for diagnostics, extending `FlushTimeout` for slow links, adjusting `BufferSize` for different Kestrel slab sizes.
- [ ] `Name` stays at `"acbinary"` default — changing it would break wire-level compat with existing clients. - [ ] `Name` stays at `"acbinary"` default — changing it would break wire-level compat with existing clients.
- [ ] `AcBinaryHubProtocolOptions.Validate()` still runs — invalid config (e.g. `ProtocolMode=AsyncSegment` on a WASM server, which is impossible here but hypothetically) throws at startup. - [ ] `AcBinaryHubProtocolOptions.Validate()` still runs — invalid config (e.g. `ProtocolMode=AsyncSegment` on a WASM server, which is impossible here but hypothetically) throws at startup.
@ -74,10 +74,10 @@ services.AddSignalR(hubOptions => { /* unchanged */ })
### Why this belongs in AyCode.Services (framework layer) docs ### Why this belongs in AyCode.Services (framework layer) docs
The gap is consumer-level, but the canonical "server-side registration recipe" is a FRAMEWORK responsibility — LOGGING.md already shows it for the logger side. Adding a matching recipe to `../SIGNALR_BINARY_PROTOCOL/README.md#Registration in Program.cs → Server` would prevent the next consumer from making the same mistake. That doc update is part of this TODO's acceptance criteria. The gap is consumer-level, but the canonical "server-side registration recipe" is a FRAMEWORK responsibility — LOGGING.md already shows it for the logger side. Adding a matching recipe to `../SIGNALR_BINARY_PROTOCOL/README.md#Registration in Program.cs → Server` would prevent the next consumer from making the same mistake. That doc update is part of this TODO's acceptance criteria.
## SIG-T-6: Delete legacy `[Obsolete]` JSON-in-Binary wrappers ## ACCORE-SIG-T-S3N8: Delete legacy `[Obsolete]` JSON-in-Binary wrappers
**Priority:** P3 · **Type:** Cleanup · **Related:** `../../../AyCode.Core/docs/XCUT/XCUT_ISSUES.md#xcut-i-1` (closed canonical), `../../../AyCode.Core/docs/BINARY/BINARY_TODO.md#bin-t-1` (closed) **Priority:** P3 · **Type:** Cleanup · **Related:** `../../../AyCode.Core/docs/XCUT/XCUT_ISSUES.md#accore-xcut-i-x8q1` (closed canonical), `../../../AyCode.Core/docs/BINARY/BINARY_TODO.md#accore-bin-t-s8p4` (closed)
Now that XCUT-I-1 / BIN-T-1 have landed and the wire format no longer uses JSON-in-Binary, the `[Obsolete]` wrapper types in `IAcSignalRHubClient.cs:60-111` can be deleted: Now that ACCORE-XCUT-I-X8Q1 / ACCORE-BIN-T-S8P4 have landed and the wire format no longer uses JSON-in-Binary, the `[Obsolete]` wrapper types in `IAcSignalRHubClient.cs:60-111` can be deleted:
- `SignalPostJsonMessage` - `SignalPostJsonMessage`
- `SignalPostJsonDataMessage<T>` - `SignalPostJsonDataMessage<T>`
- `SignalPostMessage<T>` - `SignalPostMessage<T>`

View File

@ -3,7 +3,7 @@
For planned/actionable work see `SIGNALR_BINARY_PROTOCOL_TODO.md`. For planned/actionable work see `SIGNALR_BINARY_PROTOCOL_TODO.md`.
For higher-level SignalR abstractions see `../SIGNALR/SIGNALR_ISSUES.md`. For higher-level SignalR abstractions see `../SIGNALR/SIGNALR_ISSUES.md`.
## SBP-I-1: AsyncSegment send-path unsupported on WebAssembly ## ACCORE-SBP-I-F6T2: AsyncSegment send-path unsupported on WebAssembly
**Severity:** Major (on WASM) · **Status:** Open · **Area:** `AsyncPipeWriterOutput` / WASM runtime **Severity:** Major (on WASM) · **Status:** Open · **Area:** `AsyncPipeWriterOutput` / WASM runtime
@ -18,7 +18,7 @@ For higher-level SignalR abstractions see `../SIGNALR/SIGNALR_ISSUES.md`.
### Related TODO ### Related TODO
None — architectural constraint of browser WASM threading model. None — architectural constraint of browser WASM threading model.
## SBP-I-2: StaticWebAssets SDK "Illegal characters" noise (consumer build) ## ACCORE-SBP-I-G4B5: StaticWebAssets SDK "Illegal characters" noise (consumer build)
**Severity:** Cosmetic (non-blocking) · **Status:** Open · **Area:** SDK, not our code **Severity:** Cosmetic (non-blocking) · **Status:** Open · **Area:** SDK, not our code

View File

@ -5,7 +5,7 @@
--- ---
## SBP-T-1: SegmentBufferReader isolated unit tests ## ACCORE-SBP-T-P8X9: SegmentBufferReader isolated unit tests
**Priority:** P1 · **Type:** Test coverage **Priority:** P1 · **Type:** Test coverage
Original `vast-brewing-moonbeam` refactor plan (chunked receive-path) listed these tests in its verification section; they were never written. Needed: Original `vast-brewing-moonbeam` refactor plan (chunked receive-path) listed these tests in its verification section; they were never written. Needed:
@ -14,7 +14,7 @@ Original `vast-brewing-moonbeam` refactor plan (chunked receive-path) listed the
- Missed-signal double-check pattern under `ManualResetEventSlim` reset - Missed-signal double-check pattern under `ManualResetEventSlim` reset
- `Dispose` lifecycle (buffer pool return, old-buffer cleanup) - `Dispose` lifecycle (buffer pool return, old-buffer cleanup)
## SBP-T-2: Chunked protocol integration test ## ACCORE-SBP-T-K3J7: Chunked protocol integration test
**Priority:** P1 · **Type:** Test coverage **Priority:** P1 · **Type:** Test coverage
End-to-end round-trip: End-to-end round-trip:
@ -27,8 +27,8 @@ Cover asymmetric cases:
- Bytes sender, AsyncSegment receiver - Bytes sender, AsyncSegment receiver
- WASM-downgraded sender (Segment), server AsyncSegment receiver - WASM-downgraded sender (Segment), server AsyncSegment receiver
## SBP-T-3: `BinaryProtocolMode.Auto` wire-detection implementation ## ACCORE-SBP-T-L1V4: `BinaryProtocolMode.Auto` wire-detection implementation
**Priority:** P3 · **Type:** Feature · **Related:** `../SIGNALR/SIGNALR_TODO.md#sig-t-4` **Priority:** P3 · **Type:** Feature · **Related:** `../SIGNALR/SIGNALR_TODO.md#accore-sig-t-v9h1`
Client-side adaptive mode: on first received message, inspect first payload byte: Client-side adaptive mode: on first received message, inspect first payload byte:
- `CHUNK_START (200)` → peer uses AsyncSegment → match it on subsequent sends (subject to local-platform constraint — WASM safety-net overrides to Segment) - `CHUNK_START (200)` → peer uses AsyncSegment → match it on subsequent sends (subject to local-platform constraint — WASM safety-net overrides to Segment)
@ -36,8 +36,8 @@ Client-side adaptive mode: on first received message, inspect first payload byte
Per-`HubConnection` state. Requires changes to `BinaryProtocolMode` enum + detection wiring in `TryParseMessage`. Per-`HubConnection` state. Requires changes to `BinaryProtocolMode` enum + detection wiring in `TryParseMessage`.
## SBP-T-4: SignalR handshake-extension for upfront mode negotiation ## ACCORE-SBP-T-R6D2: SignalR handshake-extension for upfront mode negotiation
**Priority:** P3 · **Type:** Feature · **Related:** SBP-T-3 **Priority:** P3 · **Type:** Feature · **Related:** ACCORE-SBP-T-L1V4
Alternative to wire-detection: use SignalR handshake message's `extensions` JSON field to carry protocol-capability info — e.g. Alternative to wire-detection: use SignalR handshake message's `extensions` JSON field to carry protocol-capability info — e.g.
```json ```json
@ -57,7 +57,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. 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.
## SBP-T-5: Optional payload encryption (`AcEncryptionOptions`) ## 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
### Niches where TLS alone is insufficient ### Niches where TLS alone is insufficient
@ -83,7 +83,7 @@ These ideas were captured because the **wider binary-protocol market is currentl
- Benchmark plan: zero-copy loss percentage, AsyncSegment chunk overhead. - Benchmark plan: zero-copy loss percentage, AsyncSegment chunk overhead.
- Decorator API sketch reviewed. - Decorator API sketch reviewed.
## SBP-T-6: Optional message compression with `MinSize` threshold ## 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
### Niches ### Niches
@ -134,7 +134,7 @@ In `AsyncSegment` mode the total message size is unknown until `CHUNK_END`, so a
**Default decision: (C)** — `AlwaysCompressInAsyncSegment = true`. Override possible per-options. Document the trade-off explicitly so users picking `AsyncSegment + compression + small messages` know what they're choosing. **Default decision: (C)** — `AlwaysCompressInAsyncSegment = true`. Override possible per-options. Document the trade-off explicitly so users picking `AsyncSegment + compression + small messages` know what they're choosing.
### Other design constraints ### Other design constraints
- **Decorator pattern**, same as SBP-T-5. - **Decorator pattern**, same as ACCORE-SBP-T-H7M5.
- **Algorithm-pluggable** — at minimum LZ4 (fastest), Brotli (best ratio for text), Zstd (modern balanced). Default **none**. - **Algorithm-pluggable** — at minimum LZ4 (fastest), Brotli (best ratio for text), Zstd (modern balanced). Default **none**.
- **Order with encryption matters**: compress FIRST, encrypt AFTER (compressing ciphertext is futile). - **Order with encryption matters**: compress FIRST, encrypt AFTER (compressing ciphertext is futile).
- **Wire marker byte** — 1-byte algorithm ID prefix so decoder knows what to expand. `0x00` = uncompressed (the small-message path), other values = algorithm IDs. - **Wire marker byte** — 1-byte algorithm ID prefix so decoder knows what to expand. `0x00` = uncompressed (the small-message path), other values = algorithm IDs.
@ -147,7 +147,7 @@ In `AsyncSegment` mode the total message size is unknown until `CHUNK_END`, so a
- AsyncSegment interaction documented — the (C) default with override path; sample log output showing `MinSize`-skipped vs. compressed messages for diagnostics. - AsyncSegment interaction documented — the (C) default with override path; sample log output showing `MinSize`-skipped vs. compressed messages for diagnostics.
- 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.) - 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.)
## SBP-T-7: OpenTelemetry tracing integration ## 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
### Niches ### Niches
@ -165,7 +165,7 @@ In `AsyncSegment` mode the total message size is unknown until `CHUNK_END`, so a
- API sketch with sample exporter wiring (Jaeger / OTel Collector). - API sketch with sample exporter wiring (Jaeger / OTel Collector).
- Hot-path branch verification — when `ActivitySource == null`, JIT must eliminate the tracing code completely. - Hot-path branch verification — when `ActivitySource == null`, JIT must eliminate the tracing code completely.
## SBP-T-8: Optional HMAC signing (without encryption) ## 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
### Niches ### Niches

View File

@ -8,7 +8,7 @@ Proposed (2026-04-25)
`AcBinaryHubProtocol` (`AyCode.Services/SignalRs/AcBinaryHubProtocol.cs`) is the AyCode binary `IHubProtocol` implementation for SignalR — the canonical wire format for binary hub messaging. The base implementation handles AcBinary serialization, three protocol modes (`Bytes` / `Segment` / `AsyncSegment`), chunked AsyncSegment streaming, and the base layer of features every consumer needs. `AcBinaryHubProtocol` (`AyCode.Services/SignalRs/AcBinaryHubProtocol.cs`) is the AyCode binary `IHubProtocol` implementation for SignalR — the canonical wire format for binary hub messaging. The base implementation handles AcBinary serialization, three protocol modes (`Bytes` / `Segment` / `AsyncSegment`), chunked AsyncSegment streaming, and the base layer of features every consumer needs.
A class of optional features is planned for NuGet competitiveness (currently P3 idea status, see `../SIGNALR_BINARY_PROTOCOL/SIGNALR_BINARY_PROTOCOL_TODO.md` SBP-T-5..8): **encryption**, **compression with MinSize**, **OpenTelemetry tracing**, **HMAC signing-only**. These features: A class of optional features is planned for NuGet competitiveness (currently P3 idea status, see `../SIGNALR_BINARY_PROTOCOL/SIGNALR_BINARY_PROTOCOL_TODO.md` ACCORE-SBP-T-H7M5..8): **encryption**, **compression with MinSize**, **OpenTelemetry tracing**, **HMAC signing-only**. These features:
- Are NOT current-priority — speculative additions for public NuGet positioning. - Are NOT current-priority — speculative additions for public NuGet positioning.
- Must be **opt-in** (default-off, zero overhead when not used). - Must be **opt-in** (default-off, zero overhead when not used).
@ -24,7 +24,7 @@ Adopt the **decorator-chain pattern** for composing optional features onto `AcBi
### `AcHubProtocolDecoratorBase` abstract base ### `AcHubProtocolDecoratorBase` abstract base
A new abstract class in `AyCode.Services/SignalRs/` (concrete API spec is impl-level, tracked as `SBP-T-9` in `SIGNALR_BINARY_PROTOCOL_TODO.md`): 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):
- Delegates passthrough `IHubProtocol` members (`Name`, `Version`, `TransferFormat`, `IsVersionSupported`, `GetMessageBytes`) to the wrapped inner protocol. - Delegates passthrough `IHubProtocol` members (`Name`, `Version`, `TransferFormat`, `IsVersionSupported`, `GetMessageBytes`) to the wrapped inner protocol.
- Exposes `protected IHubProtocol Inner { get; }` to derived classes. - 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. **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 `SBP-T-9`. 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.
### Per-message `IsCompressed` flag — semantic (A): standalone byte ### 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 ### Follow-ups required
1. **`AcHubProtocolDecoratorBase` implementation** — abstract class added to `AyCode.Services/SignalRs/`; tracked as new TODO entry `SBP-T-9` in `SIGNALR_BINARY_PROTOCOL_TODO.md`. Implementation deferred until at least one leaf ADR (0002-0005) reaches `Status: Accepted`. 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 `SBP-T-9`). 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 `SBP-T-9`. 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.
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. 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`. 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
- Related TODOs: `../SIGNALR_BINARY_PROTOCOL/SIGNALR_BINARY_PROTOCOL_TODO.md` SBP-T-5 (encryption), SBP-T-6 (compression+MinSize), SBP-T-7 (tracing), SBP-T-8 (signing); SBP-T-9 (`AcHubProtocolDecoratorBase` impl + handshake mechanism — 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 forthcoming `AcHubProtocolDecoratorBase` impl + handshake-mechanism TODO (to be added in Step 8 cross-reference round).
- Forthcoming ADRs: 0002 (payload encryption), 0003 (message compression with MinSize), 0004 (OpenTelemetry tracing), 0005 (HMAC signing-only) — all will declare `Depends on: ADR-0001`. - 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. - Topic-folder cross-ref: `../SIGNALR_BINARY_PROTOCOL/README.md` to add `## Related ADRs` section in Step 8.

View File

@ -7,7 +7,7 @@ For the architectural decision that scoped this topic, see [`../adr/0001-user-be
## Active entries ## Active entries
*(No `AUTH-I-N` entries yet — topic just created. Currently-relevant security entries live as `LOG-I-9` and `LOG-I-10` in [`LOGGING_ISSUES.md`](../../AyCode.Core/docs/LOGGING/LOGGING_ISSUES.md), pre-migration. ADR 0001's "Status migration on AUTH topic creation" follow-up is the planned home transfer — separate user-approved task.)* *(No `AUTH-I-N` entries yet — topic just created. Currently-relevant security entries live as `ACCORE-LOG-I-P5W3` and `ACCORE-LOG-I-K1Z7` in [`LOGGING_ISSUES.md`](../../AyCode.Core/docs/LOGGING/LOGGING_ISSUES.md), pre-migration. ADR 0001's "Status migration on AUTH topic creation" follow-up is the planned home transfer — separate user-approved task.)*
## Entry format ## Entry format

View File

@ -45,10 +45,10 @@ Sections to come (mirrors ADR 0001 Follow-up "Implementation" + "Consumer wiring
JWT signing keys, access tokens, refresh tokens, password hashes, and OAuth client secrets MUST NEVER appear in log output. Logged secrets leak via the same channels as the logs themselves (file system, retention archives, screenshots, bug reports). For diagnostics, log only metadata (user ID, expiry, issuer) or hash prefixes — never the raw value. JWT signing keys, access tokens, refresh tokens, password hashes, and OAuth client secrets MUST NEVER appear in log output. Logged secrets leak via the same channels as the logs themselves (file system, retention archives, screenshots, bug reports). For diagnostics, log only metadata (user ID, expiry, issuer) or hash prefixes — never the raw value.
This guideline emerged from `LOG-I-9` and `LOG-I-10` (both `Closed (2026-04-25)` via `#if DEBUG` gating per ADR 0001 pre-flight). Pending `LOG-T-12` proposes adding a generalized framework-level guideline section to [`LOGGING/README.md`](../../AyCode.Core/docs/LOGGING/README.md); when that lands, it becomes the canonical home and this section trims to a cross-ref. This guideline emerged from `ACCORE-LOG-I-P5W3` and `ACCORE-LOG-I-K1Z7` (both `Closed (2026-04-25)` via `#if DEBUG` gating per ADR 0001 pre-flight). A pending TODO (forthcoming entry in `LOGGING_TODO.md`, no ID assigned yet) proposes adding a generalized framework-level guideline section to [`LOGGING/README.md`](../../AyCode.Core/docs/LOGGING/README.md); when that lands, it becomes the canonical home and this section trims to a cross-ref.
## Cross-references ## Cross-references
- **ADR**: [`../adr/0001-user-bearer-token-flow.md`](../adr/0001-user-bearer-token-flow.md) — architectural decision. - **ADR**: [`../adr/0001-user-bearer-token-flow.md`](../adr/0001-user-bearer-token-flow.md) — architectural decision.
- **Topic registry**: `AUTH` row in [`TOPIC_CODES.md`](../../.github/skills/docs-check/references/TOPIC_CODES.md) (added in `LLMP-DEC-49`). - **Topic registry**: `AUTH` row in [`TOPIC_CODES.md`](../../.github/skills/docs-check/references/TOPIC_CODES.md) (added in `LLMP-DEC-49`).
- **Sibling pre-migration**: `LOG-I-9` / `LOG-I-10` in [`LOGGING_ISSUES.md`](../../AyCode.Core/docs/LOGGING/LOGGING_ISSUES.md) — currently homed in LOGGING; ADR 0001 Follow-up "Status migration on AUTH topic creation" lists these as candidates for relocation here as `AUTH-I-N` with `Superseded by` cross-refs in LOGGING. Migration NOT yet performed — separate user-approved follow-up. - **Sibling pre-migration**: `ACCORE-LOG-I-P5W3` / `ACCORE-LOG-I-K1Z7` in [`LOGGING_ISSUES.md`](../../AyCode.Core/docs/LOGGING/LOGGING_ISSUES.md) — currently homed in LOGGING; ADR 0001 Follow-up "Status migration on AUTH topic creation" lists these as candidates for relocation here as `AUTH-I-N` with `Superseded by` cross-refs in LOGGING. Migration NOT yet performed — separate user-approved follow-up.

File diff suppressed because one or more lines are too long

View File

@ -13,7 +13,7 @@ The framework has partial JWT infrastructure on the server side (`AcLoginService
- SignalR transport has no access-token provider wired into the hub-connection builder. - SignalR transport has no access-token provider wired into the hub-connection builder.
- Server has no `AddJwtBearer` registration, no `[Authorize]` plumbing, and no per-tag authorization for the single-method `OnReceiveMessage(int tag, ...)` SignalR dispatch. - Server has no `AddJwtBearer` registration, no `[Authorize]` plumbing, and no per-tag authorization for the single-method `OnReceiveMessage(int tag, ...)` SignalR dispatch.
Two security defects in `AcLoginServiceServer.GenerateAccessToken` (JWT signing key + issued access token written to logger) were addressed pre-flight as `LOG-I-9` and `LOG-I-10` `Closed`. Their existence is the proximate trigger for this ADR — they exposed the absence of an end-to-end auth contract. Two security defects in `AcLoginServiceServer.GenerateAccessToken` (JWT signing key + issued access token written to logger) were addressed pre-flight as `ACCORE-LOG-I-P5W3` and `ACCORE-LOG-I-K1Z7` `Closed`. Their existence is the proximate trigger for this ADR — they exposed the absence of an end-to-end auth contract.
Consumer apps (MAUI + WASM) cannot adopt the framework safely until this ADR's decisions are implemented. Consumer apps (MAUI + WASM) cannot adopt the framework safely until this ADR's decisions are implemented.
@ -52,7 +52,7 @@ Adopt a layered bearer-token authentication architecture across all client and s
**Positive:** **Positive:**
- Consumers get bearer-auth via three DI calls (one for JWT bundle, one for HTTP-handler registration, one for hub-builder extension) — minimum-boilerplate target met. - Consumers get bearer-auth via three DI calls (one for JWT bundle, one for HTTP-handler registration, one for hub-builder extension) — minimum-boilerplate target met.
- Per-tag authorization scales with the existing tag registry — no parallel registry to maintain. - Per-tag authorization scales with the existing tag registry — no parallel registry to maintain.
- Two confirmed-critical security leaks (`LOG-I-9`, `LOG-I-10`) are closed; future similar mistakes prevented by the framework guideline doc (`docs/AUTH/README.md`'s "Never log secrets" section). - Two confirmed-critical security leaks (`ACCORE-LOG-I-P5W3`, `ACCORE-LOG-I-K1Z7`) are closed; future similar mistakes prevented by the framework guideline doc (`docs/AUTH/README.md`'s "Never log secrets" section).
- WASM and MAUI use one shared token-store abstraction; consumer code is platform-aware only at registration time. - WASM and MAUI use one shared token-store abstraction; consumer code is platform-aware only at registration time.
- Framework-first preserved: all architectural pieces live in AyCode.Services / AyCode.Services.Server / AyCode.Interfaces; consumers supply only platform-storage and config values. - Framework-first preserved: all architectural pieces live in AyCode.Services / AyCode.Services.Server / AyCode.Interfaces; consumers supply only platform-storage and config values.
@ -64,8 +64,8 @@ Adopt a layered bearer-token authentication architecture across all client and s
**Follow-ups required:** **Follow-ups required:**
*Pre-flight (already landed in this commit series):* *Pre-flight (already landed in this commit series):*
- ✅ `LOG-I-9` closed: JWT signing key log-write DEBUG-gated. - ✅ `ACCORE-LOG-I-P5W3` closed: JWT signing key log-write DEBUG-gated.
- ✅ `LOG-I-10` closed: access token log-write DEBUG-gated; `AccesToken` typo fixed at the same line. - ✅ `ACCORE-LOG-I-K1Z7` closed: access token log-write DEBUG-gated; `AccesToken` typo fixed at the same line.
*Infrastructure (one-time, before implementation):* *Infrastructure (one-time, before implementation):*
- Create `docs/AUTH/` topic folder with `README.md` (consumer recipe: class names, method signatures, wire formats, diagrams, code examples), `AUTH_ISSUES.md`, `AUTH_TODO.md`. - Create `docs/AUTH/` topic folder with `README.md` (consumer recipe: class names, method signatures, wire formats, diagrams, code examples), `AUTH_ISSUES.md`, `AUTH_TODO.md`.
@ -86,7 +86,7 @@ Adopt a layered bearer-token authentication architecture across all client and s
- Per-consumer hub: tag allowlist for login + ping (at minimum). - Per-consumer hub: tag allowlist for login + ping (at minimum).
*Status migration on AUTH topic creation:* *Status migration on AUTH topic creation:*
- Move `LOG-I-9` and `LOG-I-10` to `AUTH_ISSUES.md` per their existing "Discovery context" notes; in `LOGGING_ISSUES.md` replace bodies with cross-ref `Superseded by AUTH-I-N`. - Move `ACCORE-LOG-I-P5W3` and `ACCORE-LOG-I-K1Z7` to `AUTH_ISSUES.md` per their existing "Discovery context" notes; in `LOGGING_ISSUES.md` replace bodies with cross-ref `Superseded by AUTH-I-N`.
*Future ADRs:* *Future ADRs:*
- `0002` — Refresh-token flow + sliding-vs-fixed lifetime decision. - `0002` — Refresh-token flow + sliding-vs-fixed lifetime decision.
@ -104,7 +104,7 @@ Adopt a layered bearer-token authentication architecture across all client and s
## Related ## Related
- Pre-flight fix entries: [`LOGGING_ISSUES.md#log-i-9`](../../AyCode.Core/docs/LOGGING/LOGGING_ISSUES.md), [`LOGGING_ISSUES.md#log-i-10`](../../AyCode.Core/docs/LOGGING/LOGGING_ISSUES.md) (both `Closed (2026-04-25)`). - Pre-flight fix entries: [`LOGGING_ISSUES.md#accore-log-i-p5w3`](../../AyCode.Core/docs/LOGGING/LOGGING_ISSUES.md), [`LOGGING_ISSUES.md#accore-log-i-k1z7`](../../AyCode.Core/docs/LOGGING/LOGGING_ISSUES.md) (both `Closed (2026-04-25)`).
- Tentative TODO surfaced post-fix: `LOG-T-12` ("Never log secrets" guideline section in `LOGGING/README.md`). - Tentative TODO surfaced post-fix: a "Never log secrets" framework-guideline section in `LOGGING/README.md` — forthcoming entry in `LOGGING_TODO.md` (no ID assigned yet).
- External: RFC 6750 (Bearer Token Usage), RFC 7519 (JWT), Microsoft.AspNetCore.SignalR auth docs (query-string token pattern). - External: RFC 6750 (Bearer Token Usage), RFC 7519 (JWT), Microsoft.AspNetCore.SignalR auth docs (query-string token pattern).
- Future: ADR `0002` (refresh-flow design). - Future: ADR `0002` (refresh-flow design).