[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:
parent
b59b42d381
commit
c062ded9a4
File diff suppressed because one or more lines are too long
|
|
@ -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.
|
||||
|
|
@ -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
|
|
@ -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.`
|
||||
|
||||
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
|
||||
|
||||
|
|
|
|||
|
|
@ -78,5 +78,5 @@ Proposed (YYYY-MM-DD)
|
|||
- Supersedes: <ADR-XXXX, if applicable>
|
||||
- Superseded by: <ADR-YYYY, if this ADR was later overturned>
|
||||
- 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>
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ User confirmation required. Never auto-invoke.
|
|||
|
||||
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**:
|
||||
- `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
|
|
@ -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)
|
||||
|
||||
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
|
||||
- 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
|
||||
|
|
@ -157,6 +157,20 @@ On-demand archive lookup:
|
|||
- `**/docs/{TOKEN}/{TOKEN}_TODO_*.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
|
||||
|
||||
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
|
|
@ -141,18 +141,18 @@ Two-phase:
|
|||
- `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
|
||||
|
||||
## 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).
|
||||
- **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).
|
||||
- **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).
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
## Deserialization
|
||||
|
||||
### BIN-I-1: Non-array-backed memory — per-segment copy
|
||||
### ACCORE-BIN-I-D2J5: Non-array-backed memory — per-segment copy
|
||||
|
||||
**Status:** Open
|
||||
**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.
|
||||
|
||||
### 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
|
||||
**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.
|
||||
|
||||
### BIN-I-3: ReadBytes always copies
|
||||
### ACCORE-BIN-I-S1F8: ReadBytes always copies
|
||||
|
||||
**Status:** Open
|
||||
**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.
|
||||
|
||||
### BIN-I-4: ReadStringUtf8 requires contiguous buffer
|
||||
### ACCORE-BIN-I-V5L2: ReadStringUtf8 requires contiguous buffer
|
||||
|
||||
**Status:** Open
|
||||
**Affects:** `BinaryDeserializationContext.ReadStringUtf8(int length)`
|
||||
|
|
@ -41,14 +41,14 @@ The scratch buffer is `ArrayPool.Rent`-ed on first cross-boundary read and reuse
|
|||
|
||||
## Serialization
|
||||
|
||||
### BIN-I-5: BufferWriterBinaryOutput fallback path allocates per-chunk
|
||||
### ACCORE-BIN-I-K8R4: BufferWriterBinaryOutput fallback path allocates per-chunk
|
||||
|
||||
**Status:** Open
|
||||
**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
|
||||
**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()`.
|
||||
|
||||
### 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
|
||||
**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)
|
||||
|
||||
### BIN-I-8: PipeReaderBinaryInput uses sync ReadAsync().GetResult()
|
||||
### ACCORE-BIN-I-B4Y7: PipeReaderBinaryInput uses sync ReadAsync().GetResult()
|
||||
|
||||
**Status:** Open
|
||||
**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)
|
||||
|
||||
### BIN-I-9: CS8625 warnings for non-nullable reference types
|
||||
### ACCORE-BIN-I-H2C5: CS8625 warnings for non-nullable reference types
|
||||
|
||||
**Status:** Open
|
||||
**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.
|
||||
|
||||
### BIN-I-10: First-run cold-start overhead
|
||||
### ACCORE-BIN-I-N6Q3: First-run cold-start overhead
|
||||
|
||||
**Status:** Open
|
||||
**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
|
||||
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).
|
||||
|
||||
**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.
|
||||
|
||||
### 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
|
||||
**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.
|
||||
|
||||
**Related TODO:** `BINARY_TODO.md#bin-t-2`
|
||||
**Related TODO:** `BINARY_TODO.md#accore-bin-t-q2n7`
|
||||
|
||||
## Buffer Writer (BWO)
|
||||
|
||||
### BIN-I-12: Struct copy semantics
|
||||
### ACCORE-BIN-I-J4D2: Struct copy semantics
|
||||
|
||||
**Status:** Open
|
||||
**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.
|
||||
|
||||
### BIN-I-13: Initialize resets tracking
|
||||
### ACCORE-BIN-I-R5V9: Initialize resets tracking
|
||||
|
||||
**Status:** Open
|
||||
**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.
|
||||
|
||||
### BIN-I-14: Constructor acquires chunk
|
||||
### ACCORE-BIN-I-L7G3: Constructor acquires chunk
|
||||
|
||||
**Status:** Open
|
||||
**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.
|
||||
|
||||
### BIN-I-15: No mode mixing
|
||||
### ACCORE-BIN-I-M3K6: No mode mixing
|
||||
|
||||
**Status:** Open
|
||||
**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/`)
|
||||
|
||||
### 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).
|
||||
|
||||
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
|
|
@ -5,8 +5,8 @@
|
|||
|
||||
---
|
||||
|
||||
## BIN-T-1: 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`
|
||||
## 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#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.
|
||||
|
||||
|
|
@ -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).
|
||||
- **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).
|
||||
- **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
|
||||
**Priority:** P3 · **Type:** Investigation · **Related:** `BINARY_ISSUES.md#bin-i-11`
|
||||
## ACCORE-BIN-T-Q2N7: Re-evaluate DiscountProductMapping SGen exclusion
|
||||
**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.
|
||||
|
||||
## BIN-T-3: Generate `BinarySerializeTypeMetadata` / `BinaryDeserializeTypeMetadata` at compile time
|
||||
**Priority:** P1 · **Type:** Performance · **Related:** `BINARY_ISSUES.md#bin-i-10`
|
||||
## ACCORE-BIN-T-W9F1: Generate `BinarySerializeTypeMetadata` / `BinaryDeserializeTypeMetadata` at compile time
|
||||
**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.
|
||||
|
||||
|
|
@ -48,10 +48,10 @@ Eliminate the dominant first-call cost (reflection + `Expression.Compile` in met
|
|||
- Deserialize side has parity (same approach for `BinaryDeserializeTypeMetadata`).
|
||||
- Existing tests pass; wire format unchanged.
|
||||
|
||||
## BIN-T-4: JIT Tier 1 warmup for generated hot methods
|
||||
**Priority:** P2 · **Type:** Performance · **Related:** `BINARY_ISSUES.md#bin-i-10`
|
||||
## ACCORE-BIN-T-T5J8: JIT Tier 1 warmup for generated hot methods
|
||||
**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.
|
||||
- **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.
|
||||
|
||||
**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.
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
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)
|
||||
|
||||
|
|
@ -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[]`.
|
||||
|
||||
### 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`
|
||||
|
||||
|
|
@ -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).
|
||||
|
||||
### 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
|
||||
|
||||
|
|
@ -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`
|
||||
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
|
||||
`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`
|
||||
|
||||
### 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`
|
||||
- `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.
|
||||
|
||||
### 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
|
||||
|
||||
|
|
@ -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).
|
||||
|
||||
### 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`
|
||||
|
||||
|
|
@ -125,9 +125,9 @@ Either:
|
|||
- **(b)** Change the comparison to strict `<` with `Disabled` as sentinel (larger refactor — affects semantic of `Disabled` elsewhere)
|
||||
|
||||
### 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`
|
||||
|
||||
|
|
@ -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`.
|
||||
|
||||
### 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
|
||||
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`.
|
||||
|
||||
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)
|
||||
- Is inconsistent with the client-side pattern → two mental models for the same framework
|
||||
|
||||
### Fix direction
|
||||
See `LOGGING_TODO.md#log-t-11`.
|
||||
See `LOGGING_TODO.md#accore-log-t-w4h9`.
|
||||
|
||||
### Related
|
||||
- `LOG-I-1` (trigger — NopLogWriter ctor mismatch, currently causing Console.Error noise)
|
||||
- `LOG-I-3` (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)
|
||||
- Sibling gap: `../SIGNALR/SIGNALR_ISSUES.md#sig-i-7` (same server-side plugin, protocol-options adoption gap)
|
||||
- `ACCORE-LOG-I-K7M2` (trigger — NopLogWriter ctor mismatch, currently causing Console.Error noise)
|
||||
- `ACCORE-LOG-I-L4N8` (root cause — two coexisting setup patterns)
|
||||
- `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#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.
|
||||
|
||||
## 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`
|
||||
|
||||
|
|
@ -206,11 +206,11 @@ Remove the `GlobalLogger.Detail($"Key: ...")` line entirely. The signing key mus
|
|||
|
||||
### 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.
|
||||
- `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)
|
||||
- **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`
|
||||
|
||||
|
|
@ -223,7 +223,7 @@ GlobalLogger.Detail($"AccesToken: {writtenToken}");
|
|||
|
||||
(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
|
||||
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).
|
||||
|
||||
**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
|
||||
- **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)
|
||||
- Same `LOGGING/README.md` "Never log secrets" guideline gap as LOG-I-9
|
||||
- Same discovery-context note as LOG-I-9 (bearer token ADR session, candidate for AUTH topic relocation if/when that emerges)
|
||||
- `ACCORE-LOG-I-P5W3` (sibling — JWT signing key also leaked, same file)
|
||||
- Same `LOGGING/README.md` "Never log secrets" guideline gap as ACCORE-LOG-I-P5W3
|
||||
- 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)
|
||||
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
|
||||
---
|
||||
|
||||
## LOG-T-1: Fix the writer-ctor mismatch for DI-injected writers
|
||||
**Priority:** P2 · **Type:** Bug fix · **Related:** `LOGGING_ISSUES.md#log-i-1`
|
||||
## ACCORE-LOG-T-H6Y4: Fix the writer-ctor mismatch for DI-injected writers
|
||||
**Priority:** P2 · **Type:** Bug fix · **Related:** `LOGGING_ISSUES.md#accore-log-i-k7m2`
|
||||
|
||||
Options:
|
||||
- **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.
|
||||
|
||||
## LOG-T-2: Expose `AcEnv.AppConfiguration` setter for consumer init
|
||||
**Priority:** P2 · **Type:** Feature · **Related:** `LOGGING_ISSUES.md#log-i-2`
|
||||
## ACCORE-LOG-T-N2D8: Expose `AcEnv.AppConfiguration` setter for consumer init
|
||||
**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.
|
||||
|
||||
## LOG-T-3: Unify or clearly separate config-reading and DI-based patterns
|
||||
**Priority:** P2 · **Type:** Docs / Refactor · **Related:** `LOGGING_ISSUES.md#log-i-3`
|
||||
## ACCORE-LOG-T-R7L3: Unify or clearly separate config-reading and DI-based patterns
|
||||
**Priority:** P2 · **Type:** Docs / Refactor · **Related:** `LOGGING_ISSUES.md#accore-log-i-l4n8`
|
||||
|
||||
Decide the canonical direction:
|
||||
- **(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)
|
||||
- **(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
|
||||
|
||||
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.
|
||||
|
||||
## LOG-T-5: Unify default LogLevel across setup paths
|
||||
**Priority:** P1 · **Type:** Behaviour decision + cleanup · **Related:** `LOGGING_ISSUES.md#log-i-4`
|
||||
## ACCORE-LOG-T-J9G2: Unify default LogLevel across setup paths
|
||||
**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.
|
||||
|
||||
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
|
||||
**Priority:** P3 · **Type:** Bug fix · **Related:** `LOGGING_ISSUES.md#log-i-5`
|
||||
## ACCORE-LOG-T-B8K5: Fix `AcConsoleLogWriter.Initialize()` double-run
|
||||
**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.
|
||||
|
||||
## LOG-T-7: Fix `ILogger.IsEnabled(MsLogLevel.None)` wrongly reporting enabled
|
||||
**Priority:** P2 · **Type:** Bug fix · **Related:** `LOGGING_ISSUES.md#log-i-6`
|
||||
## ACCORE-LOG-T-X1V4: Fix `ILogger.IsEnabled(MsLogLevel.None)` wrongly reporting enabled
|
||||
**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.
|
||||
|
||||
## LOG-T-8: Cleanup batch (low-risk micro-refactor, one commit)
|
||||
**Priority:** P3 · **Type:** Cleanup · **Related:** `LOGGING_ISSUES.md#log-i-7`
|
||||
## ACCORE-LOG-T-M7P2: Cleanup batch (low-risk micro-refactor, one commit)
|
||||
**Priority:** P3 · **Type:** Cleanup · **Related:** `LOGGING_ISSUES.md#accore-log-i-t8f2`
|
||||
|
||||
Single commit covering:
|
||||
- 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.
|
||||
- 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
|
||||
|
||||
`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).
|
||||
|
||||
## LOG-T-10: Writer exception isolation
|
||||
## ACCORE-LOG-T-Q6Z1: Writer exception isolation
|
||||
**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.
|
||||
|
|
@ -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).
|
||||
|
||||
## LOG-T-11: 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`
|
||||
## ACCORE-LOG-T-W4H9: Migrate server-side NopCommerce plugin logger setup to DI-factory
|
||||
**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.
|
||||
|
||||
|
|
@ -114,10 +114,10 @@ services.AddAcLoggerFactory<Logger>();
|
|||
|
||||
### Consequences / checklist
|
||||
- [ ] 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".
|
||||
- [ ] 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`).
|
||||
|
||||
### Why this is a separate TODO from LOG-T-3
|
||||
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.
|
||||
### Why this is a separate TODO from ACCORE-LOG-T-R7L3
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@ Each writer's constructor signature must accept `(AppType, LogLevel, string?)`.
|
|||
|
||||
## 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
|
||||
|
||||
|
|
@ -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 |
|
||||
| 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
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# 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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
- **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.
|
||||
- **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`.
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ Useful extra info because LLMs can misjudge narrow integer type sizes:
|
|||
| read-only property (no public setter) | `readonly` |
|
||||
| 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
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
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`
|
||||
|
||||
|
|
@ -48,7 +48,7 @@ var trulyCompact = new AcToonSerializerOptions {
|
|||
### Related TODO
|
||||
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`
|
||||
|
||||
|
|
@ -83,7 +83,7 @@ Explicit `[ToonDescription(Constraints = "...")]` on the affected property (repl
|
|||
### Related TODO
|
||||
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.
|
||||
|
||||
|
|
@ -118,7 +118,7 @@ None — the schema is wrong as-is. Downstream consumers must tolerate the dupli
|
|||
### Related TODO
|
||||
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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
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.
|
||||
|
||||
## 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
|
||||
|
||||
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
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
|
||||
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.
|
||||
|
||||
## TOON-T-5: Schema hash in `@meta`
|
||||
**Priority:** P3 · **Type:** Feature (defense-in-depth for TOON-T-2, TOON-T-3) · **Origin:** 2026-04-24
|
||||
## ACCORE-TOON-T-S6B9: Schema hash in `@meta`
|
||||
**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`:
|
||||
|
||||
|
|
@ -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.
|
||||
|
||||
## TOON-T-6: Deserialization support
|
||||
## ACCORE-TOON-T-F3X1: Deserialization support
|
||||
**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.
|
||||
|
|
@ -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.)
|
||||
- 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
|
||||
|
||||
The original `ToonExtendedInfo.txt` (deleted during migration) claimed:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
- **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`).
|
||||
- **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.
|
||||
- **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 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
|
||||
|
||||
- **`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.
|
||||
- *(hypothetical)* `XCUT-T-N` 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.
|
||||
- **`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)* `ACCORE-XCUT-T-XXXX` for a shared infrastructure refactor that touches Logger, SignalR, and SourceGenerator simultaneously.
|
||||
- *(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
|
||||
|
||||
|
|
@ -21,16 +21,15 @@ Canonical home for issues, TODOs, bugs, and critical items that span **two or mo
|
|||
## Cross-reference convention
|
||||
|
||||
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.
|
||||
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.
|
||||
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#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:
|
||||
- Counter is `XCUT` + type (`I` / `T` / `B` / `C`).
|
||||
- `XCUT-I-1`, `XCUT-I-2`, ... share one counter across all cross-cutting issues regardless of which topics they span.
|
||||
- `XCUT-T-1`, `XCUT-T-2`, ... share another counter for cross-cutting TODOs.
|
||||
- Append-only — once assigned, IDs never change.
|
||||
Per the `REPO_PREFIXES.md` and `TOPIC_CODES.md` registries:
|
||||
- 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.
|
||||
- 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-*`).
|
||||
- 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".
|
||||
|
||||
## Historical note
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
**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.
|
||||
- **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.
|
||||
- **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
|
||||
|
||||
- **BINARY_ISSUES.md** (`../BINARY/BINARY_ISSUES.md#xcut-i-1`): cross-ref pointing here
|
||||
- **SIGNALR_ISSUES.md** (`../../../AyCode.Services/docs/SIGNALR/SIGNALR_ISSUES.md#xcut-i-1`): cross-ref pointing here
|
||||
- **BINARY_TODO.md#bin-t-1**: migration plan (also marked Closed)
|
||||
- **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#accore-xcut-i-x8q1`): cross-ref pointing here
|
||||
- **BINARY_TODO.md#accore-bin-t-s8p4**: migration plan (also marked Closed)
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
> 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
|
||||
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -5,12 +5,12 @@
|
|||
|
||||
---
|
||||
|
||||
## SIG-T-1: Diagnose first-call null in PostDataAsync<T>
|
||||
**Priority:** P2 · **Type:** Investigation · **Related:** `SIGNALR_ISSUES.md#issue-02`
|
||||
## ACCORE-SIG-T-J2P5: Diagnose first-call null in PostDataAsync<T>
|
||||
**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.
|
||||
|
||||
## SIG-T-2: Document asymmetric send/receive capability
|
||||
## ACCORE-SIG-T-W8R3: Document asymmetric send/receive capability
|
||||
**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.
|
||||
|
|
@ -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
|
||||
- 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
|
||||
|
||||
`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
|
||||
**Priority:** P3 · **Type:** Feature · **Related:** `../SIGNALR_BINARY_PROTOCOL/SIGNALR_BINARY_PROTOCOL_TODO.md#sbp-t-3`
|
||||
## ACCORE-SIG-T-V9H1: `BinaryProtocolMode.Auto` — adaptive send-mode
|
||||
**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.
|
||||
|
||||
## SIG-T-5: 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`
|
||||
## ACCORE-SIG-T-M5L6: Server-side NopCommerce plugin — expose `AcBinaryHubProtocolOptions` via appsettings
|
||||
**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
|
||||
```csharp
|
||||
|
|
@ -65,7 +65,7 @@ services.AddSignalR(hubOptions => { /* unchanged */ })
|
|||
```
|
||||
|
||||
### 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.
|
||||
- [ ] `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.
|
||||
|
|
@ -74,10 +74,10 @@ services.AddSignalR(hubOptions => { /* unchanged */ })
|
|||
### 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.
|
||||
|
||||
## SIG-T-6: 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)
|
||||
## ACCORE-SIG-T-S3N8: Delete legacy `[Obsolete]` JSON-in-Binary wrappers
|
||||
**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`
|
||||
- `SignalPostJsonDataMessage<T>`
|
||||
- `SignalPostMessage<T>`
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
For planned/actionable work see `SIGNALR_BINARY_PROTOCOL_TODO.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
|
||||
|
||||
|
|
@ -18,7 +18,7 @@ For higher-level SignalR abstractions see `../SIGNALR/SIGNALR_ISSUES.md`.
|
|||
### Related TODO
|
||||
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
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
---
|
||||
|
||||
## SBP-T-1: SegmentBufferReader isolated unit tests
|
||||
## ACCORE-SBP-T-P8X9: SegmentBufferReader isolated unit tests
|
||||
**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:
|
||||
|
|
@ -14,7 +14,7 @@ Original `vast-brewing-moonbeam` refactor plan (chunked receive-path) listed the
|
|||
- Missed-signal double-check pattern under `ManualResetEventSlim` reset
|
||||
- `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
|
||||
|
||||
End-to-end round-trip:
|
||||
|
|
@ -27,8 +27,8 @@ Cover asymmetric cases:
|
|||
- Bytes sender, AsyncSegment receiver
|
||||
- WASM-downgraded sender (Segment), server AsyncSegment receiver
|
||||
|
||||
## SBP-T-3: `BinaryProtocolMode.Auto` wire-detection implementation
|
||||
**Priority:** P3 · **Type:** Feature · **Related:** `../SIGNALR/SIGNALR_TODO.md#sig-t-4`
|
||||
## ACCORE-SBP-T-L1V4: `BinaryProtocolMode.Auto` wire-detection implementation
|
||||
**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:
|
||||
- `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`.
|
||||
|
||||
## SBP-T-4: SignalR handshake-extension for upfront mode negotiation
|
||||
**Priority:** P3 · **Type:** Feature · **Related:** SBP-T-3
|
||||
## ACCORE-SBP-T-R6D2: SignalR handshake-extension for upfront mode negotiation
|
||||
**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.
|
||||
```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.
|
||||
|
||||
## SBP-T-5: Optional payload encryption (`AcEncryptionOptions`)
|
||||
## ACCORE-SBP-T-H7M5: Optional payload encryption (`AcEncryptionOptions`)
|
||||
**Priority:** P3 — IDEA · **Type:** Feature (NuGet competitiveness) · **Status:** Open
|
||||
|
||||
### 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.
|
||||
- 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
|
||||
|
||||
### 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.
|
||||
|
||||
### 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**.
|
||||
- **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.
|
||||
|
|
@ -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.
|
||||
- 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
|
||||
|
||||
### 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).
|
||||
- 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
|
||||
|
||||
### Niches
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
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.
|
||||
- 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
|
||||
|
||||
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.
|
||||
- Exposes `protected IHubProtocol Inner { get; }` to derived classes.
|
||||
|
|
@ -72,7 +72,7 @@ At connection setup (1 application-level handshake roundtrip after SignalR's pro
|
|||
|
||||
**Mismatch handling**: asymmetric registration (one side has decorator, other doesn't) → handshake fails fast with a logged reason at `Error` level. Per-message asymmetry (handshake-skip artefacts) is handled by the decorator-failure unified protocol below.
|
||||
|
||||
The app-level handshake mechanism's concrete shape (Option A: a special `__ac_handshake` HubMessage as first message; Option B: wire-level `WriteHandshake` virtual on `AcHubProtocolDecoratorBase`) is **deferred to a follow-up** — the umbrella commits to "handshake-based" but does not pick the mechanism. Tracked as part of `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
|
||||
|
||||
|
|
@ -186,9 +186,9 @@ A v1 client (no decorators registered) connecting to a v2 server (decorators in
|
|||
|
||||
### Follow-ups required
|
||||
|
||||
1. **`AcHubProtocolDecoratorBase` implementation** — abstract class added to `AyCode.Services/SignalRs/`; tracked as 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`.
|
||||
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`).
|
||||
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`.
|
||||
1. **`AcHubProtocolDecoratorBase` implementation** — abstract class added to `AyCode.Services/SignalRs/`; tracked as a new forthcoming TODO entry in `SIGNALR_BINARY_PROTOCOL_TODO.md` (no ID assigned yet). Implementation deferred until at least one leaf ADR (0002-0005) reaches `Status: Accepted`.
|
||||
2. **Stacking-order validation in DI** — `AddAcEncryption` / `AddAcSigning` / `AddAcCompression` extension methods should warn or throw if registered in a non-canonical order (concrete impl part of the forthcoming TODO).
|
||||
3. **App-level handshake mechanism design** — Option A (`__ac_handshake` HubMessage as first message) vs Option B (`WriteHandshake` virtual on `AcHubProtocolDecoratorBase`). Tracked as part of the forthcoming TODO.
|
||||
4. **Benchmark scaffolding** — message-granularity vtable cost measurement for the "negligible" claim. Each leaf ADR's acceptance criteria includes verifying this in their feature's context.
|
||||
5. **Cross-references** — Step 8 of the `adr-author` skill: this ADR links to the 4 forthcoming leaf ADRs (0002-0005); `../SIGNALR_BINARY_PROTOCOL/README.md` gets a new `## Related ADRs` section pointing back to `0001-acbinary-decorator-feature-stack-design.md`.
|
||||
|
||||
|
|
@ -210,6 +210,6 @@ A v1 client (no decorators registered) connecting to a v2 server (decorators in
|
|||
|
||||
## Related
|
||||
|
||||
- Related TODOs: `../SIGNALR_BINARY_PROTOCOL/SIGNALR_BINARY_PROTOCOL_TODO.md` 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`.
|
||||
- Topic-folder cross-ref: `../SIGNALR_BINARY_PROTOCOL/README.md` to add `## Related ADRs` section in Step 8.
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ For the architectural decision that scoped this topic, see [`../adr/0001-user-be
|
|||
|
||||
## 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
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
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
|
||||
|
||||
- **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`).
|
||||
- **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
|
|
@ -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.
|
||||
- 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.
|
||||
|
||||
|
|
@ -52,7 +52,7 @@ Adopt a layered bearer-token authentication architecture across all client and s
|
|||
**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.
|
||||
- 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.
|
||||
- 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:**
|
||||
|
||||
*Pre-flight (already landed in this commit series):*
|
||||
- ✅ `LOG-I-9` 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-P5W3` closed: JWT signing key log-write DEBUG-gated.
|
||||
- ✅ `ACCORE-LOG-I-K1Z7` closed: access token log-write DEBUG-gated; `AccesToken` typo fixed at the same line.
|
||||
|
||||
*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`.
|
||||
|
|
@ -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).
|
||||
|
||||
*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:*
|
||||
- `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
|
||||
|
||||
- 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)`).
|
||||
- Tentative TODO surfaced post-fix: `LOG-T-12` ("Never log secrets" guideline section in `LOGGING/README.md`).
|
||||
- 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: 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).
|
||||
- Future: ADR `0002` (refresh-flow design).
|
||||
|
|
|
|||
Loading…
Reference in New Issue