Sync coding agent knowledge base

This commit is contained in:
vczh
2026-07-08 02:13:27 -07:00
parent 9fa7178f84
commit 35c3cbc6ef
6 changed files with 34 additions and 16 deletions
@@ -322,7 +322,7 @@ The core-side stack uses:
The renderer-side stack uses:
1. `GuiRemoteProtocolAsyncJsonChannelRenderer` when network packages can arrive before the native GacUI window is ready. It queues received packages until `SetInvokeInMainThread(...)` installs an `IGuiRemoteProtocolAsyncRendererInvoker`, then drains them on the renderer UI thread. Its message version prevents callbacks captured by an old reader from running after the reader is replaced.
2. `GuiRemoteProtocolRendererChannel` to bridge the renderer JSON channel to a concrete renderer `IGuiRemoteProtocol` implementation and to serialize renderer events/responses back to the channel.
2. `GuiRemoteProtocolRendererChannel` to bridge the renderer JSON channel to a concrete renderer `IGuiRemoteProtocol` implementation and to serialize renderer events/responses back to `GacUIRemoteProtocolCoreClientId`. Construct it with the renderer-side `IJsonChannel` and the `IGuiRemoteProtocol`.
## Image Service
@@ -85,7 +85,7 @@ The channel system converts typed protocol calls to Parser2 JSON node packages a
Two channel adapters bridge typed protocol calls and JSON packages:
- `GuiRemoteProtocolCoreChannel`: Implements `IGuiRemoteProtocol` and reads from an `IJsonChannel`. `RequestNAME(...)` serializes arguments with `ConvertCustomTypeToJson()`, packs an envelope with `JsonChannelPack()`, and sends it to the current renderer client. Incoming packages are unpacked with `JsonChannelUnpack()`, dispatched by name, deserialized with `ConvertJsonToCustomType()`, and delivered as `events->OnNAME(...)` or `events->RespondNAME(...)`. It also queues outgoing packages before a renderer is known and exposes `DetachRenderer(clientId)` for renderer replacement.
- `GuiRemoteProtocolRendererChannel`: Reads renderer-side JSON packages and calls the wrapped `IGuiRemoteProtocol`. It also implements `IGuiRemoteProtocolEvents` so renderer events and responses are serialized back to JSON packages and flushed through the renderer channel.
- `GuiRemoteProtocolRendererChannel`: Reads renderer-side JSON packages and calls the wrapped `IGuiRemoteProtocol`. Its constructor takes the renderer-side `IJsonChannel` and the `IGuiRemoteProtocol`; the channel sends renderer events and responses back to `GacUIRemoteProtocolCoreClientId`.
### Transport Layer
+6 -1
View File
@@ -11,6 +11,7 @@
- Use `WString::IndexOf` with `wchar_t` (not `const wchar_t*`) [4]
- Use `collections::BinarySearchLambda` on contiguous buffers (guard empty) [4]
- Use `vl::Exception` for expected semantic failures and `CHECK_ERROR` for invariants [3]
- Keep design documentation aligned with code after refactoring [3]
- Capture dependent lambdas explicitly [2]
- Don't assume observable changes are batched [2]
- Do not assume async callback owners are heap allocated [2]
@@ -19,8 +20,8 @@
- Prefer simple calls before interface casts [2]
- Validate expectations against implementation and existing tests [2]
- Treat Debug memory leak dumps as required failures [2]
- Keep design documentation aligned with code after refactoring [3]
- Prefer well-defined tests over ambiguous edge cases [1]
- Fix behavior at the owning state instead of patching symptoms [1]
- Prefer `operator<=> = default` for lexicographic key structs [1]
- Prefer two-pointer merge for sorted range maps [1]
- Use named sentinel constants instead of raw values [1]
@@ -253,3 +254,7 @@ For application refactors, remove helper wrappers that only duplicate an already
## Keep design documentation aligned with code after refactoring
When a refactoring changes architecture or behavior, update the corresponding design documents in the same task rather than deferring it. After a structural change, re-read the related documents and reconcile anything that became misaligned (for example, descriptions of a transport path that no longer exists). Treat documentation drift left by a previous refactoring as part of the current cleanup.
## Fix behavior at the owning state instead of patching symptoms
When a bug is caused by state attached to a temporary or overly broad owner, move the state to the object or region that semantically owns it. Avoid compensating fixes such as forcing override values on every affected descendant, duplicating a template/resource just to mask inherited state, or adding side-channel code that only makes the symptom disappear. If a proposed fix looks like a patch, revisit the ownership boundary and preserve naturally correct inherited/default behavior for unaffected parts.
@@ -29,7 +29,7 @@ The window id is a path segment after `IO`, not a query parameter. All other met
## Starting The Service
Call `StartWindowsHttpAutomationService` from `GuiMain`, after the setup function has installed the current native controller and before entering the application event loop. Call `StopWindowsHttpAutomationService` during shutdown when the service lifetime is not already stopped through `INativeAutomationService::Stop`.
Call `StartWindowsHttpAutomationService` from `GuiMain`, after the setup function has installed the current native controller and before entering the application event loop. Every code path that calls `StartWindowsHttpAutomationService` must later call `StopWindowsHttpAutomationService` before the native controller or substituted automation service is torn down. Skipping the stop leaks the process-wide HTTP service. Use a local guard or equivalent try/catch so the stop runs after the start on normal returns and exceptions.
A normal Windows application can start the service before `GetApplication()->Run`:
```c++
@@ -39,17 +39,30 @@ using namespace vl;
using namespace vl::presentation;
using namespace vl::presentation::controls;
class WindowsHttpAutomationServiceScope
{
public:
WindowsHttpAutomationServiceScope(const WString& applicationName, vint port)
{
windows::StartWindowsHttpAutomationService(applicationName, port);
}
~WindowsHttpAutomationServiceScope()
{
windows::StopWindowsHttpAutomationService();
}
};
void GuiMain()
{
demo::MainWindow window;
window.ForceCalculateSizeImmediately();
window.MoveToScreenCenter();
windows::StartWindowsHttpAutomationService(
WindowsHttpAutomationServiceScope httpAutomationService(
WString::Unmanaged(L"Automation/MyApp"),
8888);
GetApplication()->Run(&window);
windows::StopWindowsHttpAutomationService();
}
int CALLBACK WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
@@ -58,7 +71,7 @@ int CALLBACK WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
}
```
Repeated calls do not create multiple listeners. The Windows implementation keeps one process-wide HTTP service until `StopWindowsHttpAutomationService` or `INativeAutomationService::Stop` stops it.
Repeated calls do not create multiple listeners. The Windows implementation keeps one process-wide HTTP service until `StopWindowsHttpAutomationService` stops it.
## Setup Function Cases
@@ -79,19 +92,20 @@ The setup function decides which controller and service are active while `GuiMai
Use `GetNativeServiceSubstitution()->Substitute(service, false)` before the automation service is first requested. The substitution layer rejects a late substitution after a service has already been used. Keep the substituted object alive until it is unsubstituted.
A remote protocol core can expose the core-side automation surface like this:
A remote protocol core can expose the core-side automation surface like this. The sample uses the same `WindowsHttpAutomationServiceScope` guard from the normal Windows application example.
```c++
void GuiMain()
{
RemoteProtocolAutomationService automationService;
GetNativeServiceSubstitution()->Substitute(&automationService, false);
windows::StartWindowsHttpAutomationService(
WString::Unmanaged(L"Automation/RemoteCore"),
8888);
GetApplication()->Run(mainWindow);
{
WindowsHttpAutomationServiceScope httpAutomationService(
WString::Unmanaged(L"Automation/RemoteCore"),
8888);
GetApplication()->Run(mainWindow);
}
windows::StopWindowsHttpAutomationService();
GetNativeServiceSubstitution()->Unsubstitute(&automationService);
}
```
@@ -48,7 +48,6 @@ int StartNamedPipeRemoteRenderer()
GuiRemoteProtocolAsyncJsonChannelRenderer asyncRendererChannel(channelClient.GetProtocolChannel());
GuiRemoteRendererSingle renderer(true);
GuiRemoteProtocolRendererChannel rendererChannel(
&channelClient,
&asyncRendererChannel,
&renderer);
@@ -5,7 +5,7 @@ A remote protocol core application is a normal GacUI application that calls Setu
The standard C++ path is small:
- Start a GuiRemoteProtocolNetworkChannelServer\<TServerBase\> over an INetworkProtocolServer implementation such as NamedPipeServer or HttpServer.
- Connect the core to that server with GuiRemoteProtocolLocalChannelClient.
- Wrap the core channel with GuiRemoteProtocolJsonChannelRenderer_Async and GuiRemoteProtocolCoreChannel.
- Wrap the core channel with GuiRemoteProtocolAsyncJsonChannel and GuiRemoteProtocolCoreChannel.
- Pass the protocol, usually after GuiRemoteProtocolFilter and GuiRemoteProtocolDomDiffConverter, to SetupRemoteNativeController.
The core client is expected to be registered as GacUIRemoteProtocolCoreClientId. A renderer created by GuiRemoteProtocolChannelClient advertises GacUIRemoteProtocolChannelName. GuiRemoteProtocolCoreChannel learns the renderer client id from the renderer's ControllerConnect event, so user code does not need to route individual remote protocol messages.
@@ -69,7 +69,7 @@ void StartNamedPipeRemoteCore()
server.WaitForRenderer();
GuiRemoteProtocolJsonChannelRenderer_Async asyncChannel(coreClient->GetProtocolChannel());
GuiRemoteProtocolAsyncJsonChannel asyncChannel(coreClient->GetProtocolChannel());
GuiRemoteProtocolCoreChannel coreProtocol(
coreClient.Obj(),
&asyncChannel,