From afe084fc6097f605c857b336b06743fae796d00e Mon Sep 17 00:00:00 2001 From: vczh Date: Wed, 11 Feb 2026 00:20:57 -0800 Subject: [PATCH] ... --- .github/KnowledgeBase/Learning.md | 100 +++++++++++++++- .github/Scripts/copilotPrepare.ps1 | 23 +++- .github/TaskLogs/.gitignore | 1 + .github/copilot-instructions.md | 1 + .github/prompts/refine.prompt.md | 68 +++++++++++ .github/prompts/review.prompt.md | 183 +++++++++++++++++++++++++++++ AGENTS.md | 11 +- CLAUDE.md | 11 +- 8 files changed, 393 insertions(+), 5 deletions(-) create mode 100644 .github/prompts/refine.prompt.md create mode 100644 .github/prompts/review.prompt.md diff --git a/.github/KnowledgeBase/Learning.md b/.github/KnowledgeBase/Learning.md index b50af892..7368d27c 100644 --- a/.github/KnowledgeBase/Learning.md +++ b/.github/KnowledgeBase/Learning.md @@ -1 +1,99 @@ -# Learning +# !!!LEARNING!!! + +# Orders + +- Use `WString::IndexOf` with `wchar_t` (not `const wchar_t*`) [4] +- Capture dependent lambdas explicitly [2] +- Don't assume observable changes are batched [2] +- Prefer simple calls before interface casts [2] +- Use `collections::BinarySearchLambda` on contiguous buffers (guard empty) [2] +- Validate expectations against implementation and existing tests [2] +- Prefer well-defined tests over ambiguous edge cases [1] +- Prefer `operator<=> = default` for lexicographic key structs [1] +- Prefer two-pointer merge for sorted range maps [1] +- Use `Variant::Index()` to check active alternative [1] +- Avoid references into containers when mutating them [1] +- Prefer designated initializers for aggregate-like structs [1] +- Construct `Nullable` explicitly in function calls [1] +- `collections::Dictionary` copy assignment is deleted (use move/swap) [1] +- Dereference `Ptr` via `.Obj()` (not `*ptr`) [1] +- `vl::regex` separator regex: `L"[\\/\\\\]+"` [1] +- Use 2-space indentation in embedded XML/JSON literals [1] + +# Refinements + +## Capture dependent lambdas explicitly + +When a C++ lambda uses another local lambda (or any local variable), capture it explicitly (e.g. `[&]` or `[CreateBindableTree]`). Lambdas do not implicitly capture other lambdas, and missing captures can show up as confusing compile errors. + +## Don't assume observable changes are batched + +When verifying callbacks from an observable collection, do not assume multiple operations collapse into a single notification. For example, for `vl::collections::ObservableList`, `Clear()` followed by multiple `Add()` calls triggers one callback pair per operation; test expectations should match the actual per-operation granularity. + +## Prefer simple calls before interface casts + +Do not add explicit interface casting (e.g. `dynamic_cast(...)`) just because a similar file does it. Start with the simplest direct call, and only introduce interface casts when the compiler or access rules actually require it; unnecessary casts make tests noisier and can obscure which API surface is being exercised. + +## Prefer well-defined tests over ambiguous edge cases + +When a scenario’s expected behavior is unclear or undocumented (e.g. calling an operation while an object is in an “invisible”/non-effective state), avoid turning it into a test requirement. Prefer fewer tests that validate the public contract and real-world usage; if an edge case is important, first clarify the intended semantics from implementation and/or documentation. + +## Validate expectations against implementation and existing tests + +Before encoding expectations (especially for return value conventions and error semantics), read the relevant implementation and check existing tests for established patterns. This reduces churn from mismatched assumptions (e.g. public API returning a normalized error value even if internals use different sentinel codes). + +This also applies to enums and API surface: verify that enum values and method names/signatures actually exist before using them. + +## Prefer `operator<=> = default` for lexicographic key structs + +When a struct is a pure lexicographic key (e.g. `{begin, end}`), prefer `auto operator<=>(const T&) const = default;` to generate a correct, consistent ordering and equality set automatically, instead of hand-writing `<`, `==`, etc. + +## Use `collections::BinarySearchLambda` on contiguous buffers (guard empty) + +`collections::BinarySearchLambda` expects a contiguous buffer pointer and count (e.g. `&keys[0]`, `keys.Count()`), plus a search item, an out `index`, and an orderer that returns `std::strong_ordering`. When using it on `Dictionary::Keys()`, guard the empty-map case before taking `&Keys()[0]`. + +If you are searching for “overlap” rather than exact ordering, provide a custom orderer that defines “before / after / overlap” semantics for your ranges. + +If multiple entries can satisfy “overlap”, binary search can return any matching entry. Scan backward (and/or forward) from the returned index to locate the first overlapping entry you intend to process. + +## Prefer two-pointer merge for sorted range maps + +When combining or diffing two maps that are already sorted by range keys, iterate both in one pass using a two-pointer “merge sort merge phase” approach. This avoids nested scans and keeps merge/diff logic linear in the number of runs. + +## Use `Variant::Index()` to check active alternative + +For `Variant`, use `Index()` (active alternative index) to branch on the stored type; do not assume helper methods like `GetType()` exist. + +## Avoid references into containers when mutating them + +When iterating a container and performing mutations like `Remove()`/`Add()`, avoid holding references (`auto&&`) to elements across mutations, because internal storage can be reallocated or reordered. Copy keys/values you still need to local variables before modifying the container. + +## Prefer designated initializers for aggregate-like structs + +For small structs used as value objects (especially those with default member initializers), prefer designated initializers like `{ .field = value }` for clarity and to avoid compile-time issues from positional aggregate initialization. + +## Construct `Nullable` explicitly in function calls + +When passing string literals to a function parameter typed as `Nullable`, wrap them in `WString(...)` (or otherwise construct a `WString`) to make the conversion explicit. Direct assignment to a `Nullable` field may compile via an assignment operator, but function-call argument conversion can require explicit construction. + +## `collections::Dictionary` copy assignment is deleted (use move/swap) + +`collections::Dictionary` does not support copy assignment. When you need to replace one dictionary with another, use move semantics (when appropriate), or rebuild/swap explicitly instead of `a = b`. + +## Dereference `Ptr` via `.Obj()` (not `*ptr`) + +`Ptr` is a smart pointer wrapper; to dereference it, use `ptr.Obj()` to get a raw pointer first (e.g. `*ptr.Obj()`). The `*ptr` syntax is not supported. + +## Use `WString::IndexOf` with `wchar_t` (not `const wchar_t*`) + +`vl::WString::IndexOf` searches for a single character when given a character parameter; pass character literals like `L'\r'` / `L'\n'`, not string literals like `L"\r"` / `L"\n"` (which are `const wchar_t*`). + +## Use 2-space indentation in embedded XML/JSON literals + +When writing XML or JSON inside a C++ string literal (e.g. `LR"GacUISrc(... )GacUISrc"` resources), indent the XML/JSON with 2 spaces (not tabs) to match the repo’s formatting rules for embedded structured text. + +## `vl::regex` separator regex: `L"[\\/\\\\]+"` + +In `vl::regex::Regex`, both `/` and `\\` are escaping characters, and incorrect escaping inside `[]` can throw errors like `Illegal character set definition.` + +To split paths by either `/` or `\\`, a verified pattern is `L"[\\/\\\\]+"`, and using `Regex::Split(..., keepEmptyMatch=false, ...)` conveniently drops empty components (so `//` behaves like `/`). diff --git a/.github/Scripts/copilotPrepare.ps1 b/.github/Scripts/copilotPrepare.ps1 index c11ef885..aac6bcf1 100644 --- a/.github/Scripts/copilotPrepare.ps1 +++ b/.github/Scripts/copilotPrepare.ps1 @@ -1,9 +1,28 @@ # Prepare Copilot workspace files param( - [switch]$Backup + [switch]$Backup, + [switch]$Earliest ) +if (@($Backup, $Earliest).Where({ $_ }).Count -gt 1) { + throw "At most one switch can be true: -Backup, -Earliest." +} + +if ($Earliest) { + $learningRoot = Resolve-Path -LiteralPath "$PSScriptRoot\..\Learning" + $earliestFolder = Get-ChildItem -LiteralPath $learningRoot -Directory | + Sort-Object -Property Name | + Select-Object -First 1 + + if ($null -eq $earliestFolder) { + throw "No folder is found in '$learningRoot'." + } + + Write-Output $earliestFolder.FullName + exit 0 +} + # Create or override the markdown files with the specified content $filesToOverride = @{ "Copilot_Planning.md" = "# !!!PLANNING!!!" @@ -84,4 +103,4 @@ if ($Backup) { } } -Write-Host "Copilot preparation completed." \ No newline at end of file +Write-Host "Copilot preparation completed." diff --git a/.github/TaskLogs/.gitignore b/.github/TaskLogs/.gitignore index b222c103..cec65793 100644 --- a/.github/TaskLogs/.gitignore +++ b/.github/TaskLogs/.gitignore @@ -1,3 +1,4 @@ Copilot_Execution.md Copilot_Planning.md Copilot_Task.md +Copilot_Review*.md diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 5f0c73e9..d53f988b 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -14,6 +14,7 @@ - DO NOT call `msbuild` or other executable files directly. - DO NOT create or delete any file unless explicitly directed. - MUST run any PowerShell script in this format: `& absolute-path.ps1 parameters...`. + - Multiple powershell commands are concatenated with `;` to be executed in one line. - If you are on Linux, offered powershell script files won't work and here are replacements: - You still need to maintain `*.sln`, `*.slnx`, `*.vcxitems`, `*.vcxproj`, `*.vcxproj.filters`. diff --git a/.github/prompts/refine.prompt.md b/.github/prompts/refine.prompt.md new file mode 100644 index 00000000..bf8cf52b --- /dev/null +++ b/.github/prompts/refine.prompt.md @@ -0,0 +1,68 @@ +# Refine + +- Check out `Accessing Script Files` for context about mentioned `*.ps1` files. +- All `*.md` and `*.ps1` files should exist; you should not create any new files unless explicitly instructed. +- Following `Leveraging the Knowledge Base` in `REPO-ROOT/.github/copilot-instructions.md`, find knowledge and documents for this project in `REPO-ROOT/.github/KnowledgeBase/Index.md`. + +## Goal and Constraints + +- Your goal is to extract learnings from completed task logs and write them to learning files. +- The `KnowledgeBase` and `Learning` folders mentioned in this document are in `REPO-ROOT/.github/`. +- You are not allowed to modify any source code. +- Write learnings to these files, including not only best practices but the user's preferences: + - `KnowledgeBase/Learning.md`: Learnings that apply across projects, including C++, library usage, and general best practices. + - `Learning/Learning_Coding.md`: Learnings specific to this project's source code. + - `Learning/Learning_Testing.md`: Learnings specific to this project's test code. +- Put learnings in `Learning/` instead of `KnowledgeBase/` when the knowledge is specific to this project. + +## Document Structure (Learning.md, Learning_Coding.md, Learning_Testing.md) + +- `# !!!LEARNING!!!`: This file always begins with this title. +- `# Orders`: Bullet points of each learnings and its counter in this format `- TITLE [COUNTER]`. +- `# Refinements`: + - `## Title`: Learning and its actual content. + +## Step 1. Find the Earliest Backup Folder + +- Find and execute `copilotPrepare.ps1 -Earliest` to get the absolute path to the earliest backup folder in `Learning`. +- If the script fails, it means there is no material to learn from, stops. Otherwise continue to process this folder. + +## Step 2. Read All Documents + +- Read all files in the earliest backup folder. These may include: + - `Copilot_Task.md` + - `Copilot_Planning.md` + - `Copilot_Execution.md` + - `Copilot_Execution_Finding.md` + +## Step 3. Extract Findings + +- Focus on the following sections across all documents: + - All `## UPDATE` sections in each document. + - `# Comparing to User Edit` from `Copilot_Execution_Finding.md`. +- From these sections, identify learnings about: + - Best practices and coding preferences. + - Mistakes made and corrections applied. + - Patterns the user prefers or dislikes. + - Any insight into the user's philosophy about code quality, style, or approach. + +## Step 4. Write Learnings + +- For each finding, determine the appropriate learning file based on the categorization in `Goal and Constraints`. +- Each finding must have a short title that includes the key idea. + - This document will be read by you in the future. + - Even when I would like to see a short title and concentrated content, you should still ensure both title and content: + - Include enough constraints so that you know clearly what it actually covers. + - For example, when mentioning a function name, if the naming is too general, including the its class name or namespace is always a good idea. +- You must determine if the finding is new or matches an existing learning: +- If the finding is new, add `- TITLE [1]` to `# Orders` and add a new `## Title` section under `# Refinements` with the detailed description. +- If the finding matches an existing entry in `# Orders`, increase its counter. + - When the finding does not conflict with the existing content, you can modify the content. + - Otherwise, keep the counter, update the content + - It happens when I improved and have a different idea with what I used to agree. +- Keep `# Orders` sorted by counter in descending order. + +## Step 5. Delete the Processed Folder + +- After all learnings from the earliest backup folder have been written, delete the earliest backup folder that was processed. + - No continuation for the second round required. diff --git a/.github/prompts/review.prompt.md b/.github/prompts/review.prompt.md new file mode 100644 index 00000000..e5efcd35 --- /dev/null +++ b/.github/prompts/review.prompt.md @@ -0,0 +1,183 @@ +# Review + +- Check out `Accessing Task Documents` for context about mentioned `*.md` files. +- All `*.md` and `*.ps1` files should exist; you should not create any new files unless explicitly instructed. +- Following `Leveraging the Knowledge Base` in `REPO-ROOT/.github/copilot-instructions.md`, find knowledge and documents for this project in `REPO-ROOT/.github/KnowledgeBase/Index.md`. + +## Goal and Constraints + +- Your goal is to review a document as one member of a 4-model review panel. +- The `KnowledgeBase` and `Learning` folders mentioned in this document are in `REPO-ROOT/.github/`. +- The mentioned `Copilot_Review.md` and `Copilot_Review_*_*.md` files are in `REPO-ROOT/.github/TaskLogs/`. +- Each model writes its review to a separate file. +- When you are asked to create a `Copilot_Review_*_*.md`, You are only allowed to create your own review file. +- Each round of review should consider knowledges from the knowledge base. +- Each round of review should consider learnings from `KnowledgeBase/Learning.md`, `KnowledgeBase/Learning.md`, `Learning/Learning_Coding.md`, and `Learning/Learning_Testing.md` if they exist. + +## Identify Yourself + +- You are one of the 4 models in the review panel. Identify yourself: + - If you are GPT, your file name fragment is `GPT`. + - If you are Claude (Opus), your file name fragment is `Opus`. + - If you are Grok, your file name fragment is `Grok`. + - If you are Gemini, your file name fragment is `Gemini`. +- Use your file name fragment in all file operations below. + +## Step 1. Identify the Target Document to Review + +- Find the title in the LATEST chat message: + - `# Scrum`: review `Copilot_Scrum.md`, begins from `# TASKS` until the end, focus only on unfinished tasks (those marked `- [ ]` instead of `- [*]`). + - `# Design`: review `Copilot_Task.md`, begins from `# INSIGHTS AND REASONING` until the end. + - `# Plan`: review `Copilot_Planning.md`, begins from `# EXECUTION PLAN` until the end. + - `# Execution`: review `Copilot_Execution.md`, begins from `# EXECUTION PLAN` until the end. + - `# Final`: skip all remaining steps and go to the `Final Review` section. + - `# Apply`: skip all remaining steps and go to the `Apply Review` section. +- If there is nothing: it means you are accidentally stopped. Please continue your work. + +## Step 2. Determine the Current Round Index + +- Look for existing `Copilot_Review_*_*.md` files. The file name stands for `Copilot_Review_{RoundIndex}_{FileNameFragment}.md`. +- If no review files exist, the current round index is `1`. +- Otherwise find the highest `RoundIndex`: + - Here are a list of review files from the previous round: + - `Copilot_Review_*_GPT.md` + - `Copilot_Review_*_Opus.md` + - `Copilot_Review_*_Grok.md` + - `Copilot_Review_*_Gemini.md` + - If all files exist, the current round index is `RoundIndex + 1`. + - Otherwise, the current round index is that `RoundIndex`. +- If your file for the current round already exists, report that you have already completed this round and stop. +- You must be aware of that, some model may already started the current round, so this is a way to determine the round index without race condition. + +## Step 3. Read Context + +- Read the target document identified in Step 1. + - For `Copilot_Scrum.md`, focus only on unfinished tasks. +- If the current round index is greater than `1`, read all review files from the previous round to collect other models' opinions. + - Their opinion of the review. + - Their replies to you. + +## Step 4. Write Your Review + +- Create file: `Copilot_Review_{RoundIndex}_{YourFileNameFragment}.md` +- You need to consolidate all information from Step 3. +- Find what inspires you, what you agree with, and what you disagree with. +- Complete the document following the format: + - `# Review Target: {TargetDocumentName}`: the name of the document you are reviewing. + - `## Opinion`: + - Your complete summarized feedback and suggestions for the target document. + - You should not omit anything what is in any documents in the previous round, this is what complete means. + - `## Replies`: this section exists only when the current round index is greater than `1`. + - `### Reply to {ModelName}`: Reply to the `## Opinion` of `Copilot_Review_{RoundIndex-1}_{ModelName}.md`. + - If you totally agree with their opinion, the title should be `### AGREE with {ModelName}` with no content. If you have anything to add, put them in your own `## Opinion`. +- The following sections are about what you need to pay attention to when reviewing the target document. +- After finishing the review document, stops. + +### Review the Architecture + +- This applies when the document talks about architecture and interface design. +- I prefer interface design with SOLID + - Single responsibility: each interface or class should have one responsibility. + - Open-closed principle: software entities should be open for extension but closed for modification. + - Liskov substitution: objects of a superclass should be replaceable with objects of a subclass without affecting the correctness of the program. + - Interface segregation: many client-specific interfaces are better than one general-purpose interface. + - Dependency inversion: depend on abstractions, not on concretions. +- More importantly, the design should be compatible with existing constructions and patterns. + +### Review the Data Structure and Algorithm + +- This applies when the document talks about selecting or adding data structures and algorithms. +- When possible, use existing types and algorithms from libraries in the `Import` folder, especially important primitive types and collection types. + - Only when the mission is performance sensitive, low level constructions are allowed. +- Prefer algorithms with lower time and space complexity. + +### Review the Code Quality + +- This applies when the document tasks about actual code implementation. +- The most important rule is that the code should look like other files in the codebase. +- Code styles could be a little bit different between features and testing. +- TRY YOUR BEST to prevent from code duplication. + +### Code in Feature + +- Feature code usually refer to files in the `Source` folder. +- Header files should follow the style of existing header files, ensuring that no cyclic dependencies and guarded to prevent from being included multiple times. +- When a header file `Something.h` matches with multiple cpp files, names of these cpp files usually named after `Something_Category.cpp`. +- Naming convention follow .NET framework guidelines, although these are C++ code. + +### Code in Testing + +- Testing code usually refer to files in the `Test` folder. +- Header files in testing code are important, as they usually contain shared constructions. Reuse them as much as possible. +- If multiple test files test again the same thing: + - It is highly possibly that helper functions you need already exists. Reuse them as much as possible. + - When test patterns are obvious, you should follow the pattern. + +### Review with Learnings + +- Learnings from the past are important, they are written in: + - `KnowledgeBase/Learning.md` + - `Learning/Learning_Coding.md` + - `Learning/Learning_Testing.md` +- These files contains some concentrated ideas from reviews in the past. +- Each item has a `[SCORE]` with their title in the `# Orders` section. +- Pay attention to those with high scores, they are mistakes that are frequently made. +- Apply all learnings to the document and find out what could be improved. + +### Review with the Knowledge Base + +- None should be conflict with the knowledge base. +- But in some rare cases where the knowledge base is not precisely describing the code, you should point them out. + +## Final Review (only when `# Final` appears in the LATEST chat message) + +Ignore this section if there is no `# Final` in the LATEST chat message. + +### Step F1. Verify Convergence + +- Find the latest round of review files. +- Check that in the latest round, every models agree with each other. + - If any reply is not agree, report that the review has not converged and stop. + +### Step F2. Identify the Target Document + +- Identify all review files from the last round. Read their `# Review Target`, they should be the same. + +### Step F3. Create the Summary + +- Read the `## Optnion` section from each review file from the last round. +- Consolidate all options into a single review opinion. +- Write the review opinion to `Copilot_Review.md` as a cohesive set of actionable feedback. + - The only title in this file should be `# Review Target: {TargetDocumentName}`. + - The content should not contain any title. + - DO NOT mention which model offers which opinion, the review opinion should be a cohesive whole, not a collection of separate opinions. + +### Step F4. Clean Up + +- Delete all `Copilot_Review_*_*.md` files. +- Stops. + +## Apply Review (only when `# Apply` appears in the LATEST chat message) + +Ignore this section if there is no `# Apply` in the LATEST chat message. + +### Step A1. Identify the Target Document + +- The title of `Copilot_Review.md` is `# Review Target: {TargetDocumentName}`. This is the target document to apply the review opinion. +- According to the target document, follow one of the instruction files: + - For `Copilot_Scrum.md`, follow `REPO-ROOT/.github/0-scrum.prompt.md`. + - For `Copilot_Task.md`, follow `REPO-ROOT/.github/1-design.prompt.md`. + - For `Copilot_Planning.md`, follow `REPO-ROOT/.github/2-planning.prompt.md`. + - For `Copilot_Execution.md`, follow `REPO-ROOT/.github/4-execution.prompt.md`. + +### Step A2. Apply the Review + +- Treat the LATEST chat message as `# Update` followed by the content of `Copilot_Review.md`. + - Do not include the title of `Copilot_Review.md` in the content. +- Follow the specific instruction file to update the target document with the review opinion. + - Skip the part that adding a new `# Update` section to the target document. + +### Step A3. Clean Up + +- Delete `Copilot_Review.md`. +- Stops. \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md index ea353d4e..437c1157 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -15,6 +15,8 @@ Read the first word of the request, and read an additional instruction file when - "verify": REPO-ROOT/.github/prompts/5-verifying.prompt.md - "ask": REPO-ROOT/.github/prompts/ask.prompt.md - "code": REPO-ROOT/.github/prompts/code.prompt.md +- "refine": REPO-ROOT/.github/prompts/refine.prompt.md +- "review": REPO-ROOT/.github/prompts/review.prompt.md ### Exceptions @@ -25,7 +27,14 @@ Read the first word of the request, and read an additional instruction file when ## Step 2 -Read the second word if it exists, convert it to a title `# THE-WORD`. +- Only applies when the first word is: + - "scrum" + - "design" + - "plan" + - "summary" + - "execute" + - "review" +- Read the second word if it exists, convert it to a title `# THE-WORD`. ## Step 3 diff --git a/CLAUDE.md b/CLAUDE.md index ea353d4e..437c1157 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -15,6 +15,8 @@ Read the first word of the request, and read an additional instruction file when - "verify": REPO-ROOT/.github/prompts/5-verifying.prompt.md - "ask": REPO-ROOT/.github/prompts/ask.prompt.md - "code": REPO-ROOT/.github/prompts/code.prompt.md +- "refine": REPO-ROOT/.github/prompts/refine.prompt.md +- "review": REPO-ROOT/.github/prompts/review.prompt.md ### Exceptions @@ -25,7 +27,14 @@ Read the first word of the request, and read an additional instruction file when ## Step 2 -Read the second word if it exists, convert it to a title `# THE-WORD`. +- Only applies when the first word is: + - "scrum" + - "design" + - "plan" + - "summary" + - "execute" + - "review" +- Read the second word if it exists, convert it to a title `# THE-WORD`. ## Step 3