Skip to content

Setting up the initial copilot instructions#2069

Open
sramani-metro wants to merge 7 commits intomasterfrom
dev/initial-copilot-instructions
Open

Setting up the initial copilot instructions#2069
sramani-metro wants to merge 7 commits intomasterfrom
dev/initial-copilot-instructions

Conversation

@sramani-metro
Copy link
Copy Markdown
Contributor

No description provided.

@sramani-metro sramani-metro self-assigned this Mar 3, 2026
Copilot AI review requested due to automatic review settings March 3, 2026 11:24
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds initial GitHub Copilot instruction documents for the Thunder framework, including a repo-wide overview and layer-scoped guidance that applies automatically to Source/** subtrees.

Changes:

  • Added repo-wide .github/copilot-instructions.md covering architecture, workflows, and cross-cutting C++/Thunder conventions.
  • Added scoped instruction files under .github/instructions/ for core/, com/, plugins/, and Source/Thunder/.
  • Documented macOS-specific portability pitfalls (notably cross-dylib dynamic_cast issues with template typeinfo).

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
.github/instructions/thunder-runtime.instructions.md Adds runtime/daemon-layer guidance for Source/Thunder/** (Server/ServiceMap/Controller/config/lifecycle).
.github/instructions/plugins.instructions.md Adds plugin framework API rules for Source/plugins/** (IPlugin, IShell lifetime, JSONRPC patterns).
.github/instructions/core.instructions.md Adds Source/core/** rules for dependencies, threading, sync, JSON, IPC, portability.
.github/instructions/com.instructions.md Adds Source/com/** rules for COM-RPC interfaces, proxy/stub patterns, and macOS IPC casting caveats.
.github/copilot-instructions.md Adds top-level repository + style conventions and links to scoped instruction files.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +319 to +326
### Include Guards
Use the `__UPPER_CASE_H__` double-underscore pattern — **not** `#pragma once`:
```cpp
#ifndef __MYPLUGIN_H__
#define __MYPLUGIN_H__
...
#endif // __MYPLUGIN_H__
```
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file mandates include guards and forbids #pragma once, but the codebase uses both patterns today (e.g. Source/com/com.h:20 uses #pragma once, while Source/plugins/IPlugin.h:20 uses #ifndef __IPLUGIN_H__). To avoid confusing contributors/Copilot, consider either allowing both or documenting where each is preferred, rather than an absolute prohibition.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot open a new pull request to apply changes based on this feedback

Comment on lines +98 to +101
## `IPlugin::IPluginExtended` and `IPlugin::ICompositPlugin`
- `IPluginExtended`: adds `Inbound(Web::Request&)` and `Process(const Web::Request&)` for HTTP handling. Also known as the `IWeb` pattern (prefer `IWeb` from the exchange interfaces if available).
- `ICompositPlugin`: for plugins that aggregate sub-plugins — rare, check existing examples before implementing.

Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IPluginExtended/ICompositPlugin are not nested under IPlugin (they’re PluginHost::IPluginExtended and PluginHost::ICompositPlugin), and IPluginExtended does not add Inbound()/Process() for HTTP handling. In Source/plugins/IPlugin.h, HTTP handling is provided by PluginHost::IWeb (Inbound(Web::Request&), Process(const Web::Request&)), while IPluginExtended only adds Attach()/Detach() for channel lifecycle. Please update this section header and bullets to match the actual interfaces to avoid misleading plugin authors.

Suggested change
## `IPlugin::IPluginExtended` and `IPlugin::ICompositPlugin`
- `IPluginExtended`: adds `Inbound(Web::Request&)` and `Process(const Web::Request&)` for HTTP handling. Also known as the `IWeb` pattern (prefer `IWeb` from the exchange interfaces if available).
- `ICompositPlugin`: for plugins that aggregate sub-plugins — rare, check existing examples before implementing.
## `PluginHost::IPluginExtended` and `PluginHost::ICompositPlugin`
- `PluginHost::IPluginExtended`: extends `IPlugin` with `Attach()` / `Detach()` for channel lifecycle handling (called when a channel connects/disconnects).
- HTTP handling is provided by `PluginHost::IWeb`, which adds `Inbound(Web::Request&)` and `Process(const Web::Request&)`. Prefer `PluginHost::IWeb` (or the corresponding exchange interface) when exposing HTTP endpoints.
- `PluginHost::ICompositPlugin`: for plugins that aggregate sub-plugins — rare, check existing examples before implementing.

Copilot uses AI. Check for mistakes.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings March 3, 2026 12:20
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copy link
Copy Markdown

Copilot AI commented Mar 3, 2026

@sramani-metro I've opened a new pull request, #2070, to work on those changes. Once the pull request is ready, I'll request review from you.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

- `Service` wraps the loaded plugin library and its `IPlugin` implementation.
- Activation path: `Service::Activate()` → loads `.so` → calls `Initialize()` → transitions to `ACTIVATED`.
- Deactivation path: `Service::Deactivate()` → calls `Deinitialize()` → unloads `.so`.
- Reason codes for deactivation (`REQUESTED`, `AUTOMATIC`, `FAILURE`, `SHUTDOWN`, `CONDITIONS`, `WATCHDOG`) are passed to `IPlugin::INotification::Deactivated()` — use the correct reason when triggering deactivation.
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This says deactivation reason codes are passed to IPlugin::INotification::Deactivated(), but that callback’s signature is only (callsign, IShell*) (no reason parameter). The reason is available via IShell::Reason() (e.g. used in Source/Thunder/Controller.h). Please update this guidance, and also align the reason names with the actual PluginHost::IShell::reason enum (e.g. WATCHDOG_EXPIRED, INITIALIZATION_FAILED, etc.).

Suggested change
- Reason codes for deactivation (`REQUESTED`, `AUTOMATIC`, `FAILURE`, `SHUTDOWN`, `CONDITIONS`, `WATCHDOG`) are passed to `IPlugin::INotification::Deactivated()` — use the correct reason when triggering deactivation.
- Deactivation reasons are represented by `PluginHost::IShell::reason` (e.g. `WATCHDOG_EXPIRED`, `INITIALIZATION_FAILED`, `SHUTDOWN`, `REQUESTED`) and are available inside `IPlugin::INotification::Deactivated(const string& callsign, PluginHost::IShell* shell)` via `shell->Reason()` — always set and interpret the correct reason when triggering or handling deactivation.

Copilot uses AI. Check for mistakes.
Comment on lines +30 to +31
- Activation path: `Service::Activate()` → loads `.so` → calls `Initialize()` → transitions to `ACTIVATED`.
- Deactivation path: `Service::Deactivate()` → calls `Deinitialize()` → unloads `.so`.
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The activation/deactivation paths mention loading/unloading a .so, but Thunder also targets macOS where the runtime library extension is .dylib. Consider wording this as “shared library” (or “.so/.dylib”) to avoid encoding a Linux-only assumption.

Suggested change
- Activation path: `Service::Activate()` → loads `.so` → calls `Initialize()` → transitions to `ACTIVATED`.
- Deactivation path: `Service::Deactivate()` → calls `Deinitialize()` → unloads `.so`.
- Activation path: `Service::Activate()` → loads the plugin shared library (`.so`/`.dylib`) → calls `Initialize()` → transitions to `ACTIVATED`.
- Deactivation path: `Service::Deactivate()` → calls `Deinitialize()` → unloads the plugin shared library (`.so`/`.dylib`).

Copilot uses AI. Check for mistakes.
### Execution Modes (plugin `configuration.root.mode`)
| Mode | Meaning |
|------|---------|
| `Off` | In-process (default if no `root` block) |
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The execution mode table describes Off as “In-process”, but in-process is the default when there is no root block. In code, RootConfig::ModeType::OFF is treated as an invalid/unexpected value (ASSERT(false) in Source/plugins/Shell.cpp), so documenting it as an in-process mode is incorrect.

Suggested change
| `Off` | In-process (default if no `root` block) |
| `Off` | Invalid / reserved — do not use; in-process is the implicit default when no `root` block is present |

Copilot uses AI. Check for mistakes.
- **Acquire `_adminLock` as briefly as possible** — never call plugin code (`Initialize`, `Deinitialize`, JSON-RPC handlers) while holding it.
- `_pluginHandling` (per-`Service`) guards per-plugin interface pointers — separate from `_adminLock`. Never hold both simultaneously.
- Lookup by callsign: `ServiceMap::FromLocator()` resolves a URL segment to a `Service*`.
- Plugin states are `UNAVAILABLE → DEACTIVATED → PRECONDITION → ACTIVATED` (plus `SUSPENDED`/`HIBERNATED`). Only the framework transitions states — never force-set `_state` directly.
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The plugin state list is incomplete/misleading. In this repo PluginHost::IShell::state includes additional lifecycle states like ACTIVATION, DEACTIVATION, and DESTROYED (see Source/plugins/IShell.h), so describing the states as only UNAVAILABLE → DEACTIVATED → PRECONDITION → ACTIVATED is inaccurate.

Suggested change
- Plugin states are `UNAVAILABLE → DEACTIVATEDPRECONDITIONACTIVATED` (plus `SUSPENDED`/`HIBERNATED`). Only the framework transitions states — never force-set `_state` directly.
- Plugin states follow `PluginHost::IShell::state` (see `Source/plugins/IShell.h`), including `UNAVAILABLE`, `DEACTIVATED`, `ACTIVATION`, `PRECONDITION`, `ACTIVATED`, `DEACTIVATION`, `SUSPENDED`, `HIBERNATED`, and `DESTROYED`. Only the framework transitions states — never force-set `_state` directly.

Copilot uses AI. Check for mistakes.

Thunder is a **plugin-based device abstraction framework** for embedded platforms (primarily ARM/MIPS running Linux). It is developed by Metrological, a Comcast company, and is the backbone of RDK (Reference Design Kit) deployments worldwide.

Thunder provides:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perhaps mention something that it also provides general abstrtactions to plugins, e.g. OS things like threads, time, low level network stuff, files etc.?

| `Thunder/` | Core daemon + libraries (`core/`, `com/`, `plugins/`, `Source/Thunder/`) | 2nd |
| `ThunderInterfaces/` | Shared COM-RPC interface headers (`interfaces/I*.h`) + generated JSON-RPC wrappers (`jsonrpc/`) | 3rd |
| `ThunderNanoServices/` | Metrological-maintained plugins (not used in RDK deployments) | 4th |
| `rdkservices/` | RDK-specific plugins consuming ThunderInterfaces | 4th (parallel with NanoServices) |
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't know if it is important but that is old RDKV. Newer code is in all the enterservices- repo's


| Repo | Role | Build Order |
|------|------|-------------|
| `ThunderTools/` | Code generators: `ProxyStubGenerator`, `JsonGenerator`, `ConfigGenerator`, `LuaGenerator` (all Python) | 1st — must be installed before any other build |
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ThunderExtensions repo is missinf(should we explain inhere what that is)
Also ThunderClient libraries, ThunderNanoServicesRDK, ThunderTools,


**The build order is mandatory.** Each repo's installed artifacts are required by the next. Never attempt to build out of order.

## Key Binary and Library Outputs
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As it mentions key gues does not have to be complete

┌──────────────────────────────────────────────┐
│ rdkservices / ThunderNanoServices │ Plugin implementations
├──────────────────────────────────────────────┤
│ Source/Thunder/ │ Daemon runtime: Server, ServiceMap, Controller
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't know if it is important, but Thunder is more next to plugins

## Required Elements in Every Interface Header

```cpp
#ifndef __IMYFEATURE_H__
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prefer #pragma once?

| Primitive | Class | When to use |
|-----------|-------|------------|
| Mutual exclusion | `Core::CriticalSection` | Protecting shared mutable state |
| RAII lock guard | `Core::SafeSyncType<CriticalSection>` | Preferred over manual Lock/Unlock |
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not preferred over manual (okay now stating Pierre's opinion :) )
Only for very small functions we use RAII over manual. But we can discuss in the CCB. But for now this should be the rule to be consistent with existing code

@@ -0,0 +1,621 @@
---
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isnn't that the same as this PR: #2072 ?

See my remark there, can't we point to the PSG somehow ? It has all options ad is reviewed

@@ -0,0 +1,245 @@
---
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This whole section is hugely incorrect. Just remove it?


## `Core::IUnknown` — The Base of All COM Interfaces
- Every COM interface must inherit **`virtual public Core::IUnknown`** — virtual inheritance is mandatory to avoid diamond-problem reference count collisions.
- `Core::IUnknown` provides `AddRef()`, `Release()`, and `QueryInterface(id)`.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mhmm that is strange, that is not even completely correct (addref and release come in via IReferenceCounted :)

Copy link
Copy Markdown
Contributor

@sebaszm sebaszm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for putting this together! That was a lot to go through!

Thus I have the impression this is too verbose for what it's supposed to address. I expected a list of brief guidelines, rather than such detailed description, whch is hard to review and maintain being correct. I even think some sections like how to build, run and test are redundant completely from a pull-request reviewer perspective.

Many do's and don'ts are repeated multiple times (like not using a vector, disregarding it being incorrect), some are not applicable (like don't edit generated files - this can't happen in a PR), some should be already obvious for AI (like what asserts are for).

And there seems to be much focus towards macOS (it hardly being a main target platform).

I left some comments on statements that I believe are straight incorrect. I also hope with so much text it's not a TL;DR sort of thing for Copilot. :)

| `libThunderCOM.so/.dylib` | `install/lib/` | COM-RPC infrastructure — proxy/stub registration, communicator |
| `libThunderPlugins.so/.dylib` | `install/lib/` | Plugin framework API — `IPlugin`, `IShell`, `JSONRPC` |
| `libThunderCOMProcessStubs.so/.dylib` | `install/lib/` | Core COM proxy/stub library |
| Plugin `.so`/`.dylib` files | `install/lib/thunder/plugins/` | Individual plugin implementations |
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing extensions.


These rules apply to **every file** in every repo. No exceptions.

1. **All COM interface methods must return `Core::hresult`** — `Core::ERROR_NONE` (0) on success.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would be true for JSON-RPC. For COM-RPC, while this would be preferred, I don't think this is a concrete rule. Certainly it's supported that the return value is not hresult.


1. **All COM interface methods must return `Core::hresult`** — `Core::ERROR_NONE` (0) on success.
2. **COM interfaces are immutable once published** — methods must never be changed or removed; only append new methods at the end.
3. **No STL containers across COM boundaries** — use `RPC::IIteratorType<T, ID>` iterators. Never pass `std::vector`, `std::list`, or `std::map` across an interface.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not completely true. We do support std::vecotor in COM-RPC (with mandatory (at)restrict limitation).


## Include Guards

Thunder does **not** use `#pragma once`. Every header must use the double-underscore guard pattern:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's the opposite? We endorese #pragma once, but old code still uses header guards, no point in changing it now.

## Virtual Methods and `override`

- Every method that overrides a virtual base **must** have the `override` specifier. Never rely on implicit override.
- Interface destructors must be declared `= default` with `override` — never define a body:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why they are expected to be declared? the interface already inherits a virtual destructor from IUnknown, and re-declaring it in interface has zero functional purpose and only clutters the contract :) (the header).

```

**Rules**:
- Never pass `std::vector`, `std::list`, `std::map`, or any STL container across a COM interface.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Neither list or map will even work. But vector will.


## `Core::IUnknown` — The Reference Counting Contract

All COM objects in Thunder inherit from `Core::IUnknown`, which provides:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

technically Iunknown only provides queryInterface :)

Core::ProxyType<InvokeMessage> invoke(base); // dynamic_cast fails on macOS (see below)
```

## macOS `dynamic_cast` Failure for Template Types
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this section will be romoved once the issue described here is resolved.

- `enum { ID = RPC::ID_XXX }` — unique registered ID; see `06-comrpc-fundamentals.md` for ID allocation
- `~IMyFeature() override = default` — destructor must be `= default`, never defined with a body
- `EXTERNAL` on the struct — required for cross-dylib visibility
- All methods return `Core::hresult` — no exceptions to this rule
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

again, I don't think this is a no-exception rule

namespace { static Plugin::Metadata<Plugin::MyPlugin> metadata(1, 0, 0, {}, {}, {}); }
```

## JSON-RPC Method Registration
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is typically autogenerated, no need for calling these manually.

Copilot AI review requested due to automatic review settings March 12, 2026 07:30
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 10 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

- `Core::ERROR_UNAVAILABLE`, `Core::ERROR_ILLEGAL_STATE`, `Core::ERROR_BAD_REQUEST` for common failure modes.
- Use `Core::ErrorToString(code)` when logging errors.
- Check for transport vs plugin errors: `if (result & COM_ERROR) { /* transport failure */ }`.
- `Core::ERROR_TIMEDOUT` (== `Core::ERROR_ASYNC_ABORTED` in some contexts) indicates a COM-RPC call exceeded `CommunicationTimeOut`.
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error-code note says Core::ERROR_TIMEDOUT is equal to Core::ERROR_ASYNC_ABORTED “in some contexts”, but these are distinct values in Source/core/Errors.h (TIMEDOUT=11, ASYNC_ABORTED=4). This equivalence statement is likely incorrect and could cause people to write the wrong comparisons/handling; suggest removing the equality claim or describing the actual scenario being referred to (e.g. specific APIs translating one to the other).

Suggested change
- `Core::ERROR_TIMEDOUT` (== `Core::ERROR_ASYNC_ABORTED` in some contexts) indicates a COM-RPC call exceeded `CommunicationTimeOut`.
- `Core::ERROR_TIMEDOUT` indicates a COM-RPC call exceeded `CommunicationTimeOut`. Do not assume it is equal to `Core::ERROR_ASYNC_ABORTED`; handle them as distinct error codes.

Copilot uses AI. Check for mistakes.

## Hot-Reload Details

- **Plugin config hot-reload**: `FileObserver` watches the `configs` directory. On change, `ConfigObserver` calls `Service::ConfigUpdated()` → plugin is deactivated, config updated, re-activated (if autostart). This is non-atomic — there is a brief window where the plugin is inactive.
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The “Plugin config hot-reload” description doesn’t match the current runtime behavior in Source/Thunder/PluginServer.h: ConfigObserver::Updated() calls ServiceMap::ConfigReload(), which iterates *.json files and calls Insert(...); Insert ignores duplicate callsigns rather than deactivating/reactivating an existing plugin. Consider updating this section to reflect what actually happens today (and/or documenting the current limitations) so this file doesn’t teach a hot-reload mechanism that isn’t implemented.

Suggested change
- **Plugin config hot-reload**: `FileObserver` watches the `configs` directory. On change, `ConfigObserver` calls `Service::ConfigUpdated()` → plugin is deactivated, config updated, re-activated (if autostart). This is non-atomic — there is a brief window where the plugin is inactive.
- **Plugin config hot-reload” (current behavior is limited)**: `FileObserver` watches the `configs` directory. On change, `ConfigObserver::Updated()` calls `ServiceMap::ConfigReload()`, which rescans `*.json` files and calls `Insert(...)` for each. `Insert` **ignores duplicate callsigns**, so existing plugins are *not* deactivated, reconfigured, or re-activated — only newly added configs for previously unknown callsigns are picked up. To apply config changes for an existing plugin, you must currently deactivate/activate it (for example via `Controller`) or restart the daemon. Treat this as **additive-only discovery**, not full hot-reload.

Copilot uses AI. Check for mistakes.

### Comments

Keep comments minimal — code must be self-explanatory. A comment must add context that cannot be read from the code itself. Write comments in correct English. Place comments on their own line immediately before or after the relevant code — never on the same line as a statement.
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rule "Place comments ... never on the same line as a statement" contradicts existing Thunder code style (there are many inline trailing comments in Source/, e.g. Source/Thunder/PluginHost.cpp:84 and Source/com/Administrator.h:37). Consider relaxing this to a preference (e.g. avoid trailing comments when possible) rather than a hard prohibition, so contributors/Copilot aren’t pushed into a style that the repo doesn’t follow.

Suggested change
Keep comments minimal — code must be self-explanatory. A comment must add context that cannot be read from the code itself. Write comments in correct English. Place comments on their own line immediately before or after the relevant code — never on the same line as a statement.
Keep comments minimal — code must be self-explanatory. A comment must add context that cannot be read from the code itself. Write comments in correct English. Prefer placing comments on their own line immediately before or after the relevant code; avoid trailing comments where possible, but they are acceptable when they significantly improve clarity or match the surrounding file’s style.

Copilot uses AI. Check for mistakes.
Comment on lines +66 to +70
- **Files**: PascalCase — `MyModule.h`, `MyModule.cpp`.
- **Namespaces and classes**: PascalCase — `MyNameSpace`, `MyClass`.
- **Class member methods**: PascalCase — `GetValue()`, `SetActive()`.
- **Class member variables**: `_` prefix + camelCase — `_myVariable`, `_adminLock`.

Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section states that all files should be PascalCase, but the repository contains many established lowercase headers (e.g. Source/core/core.h, Source/plugins/plugins.h, Source/com/com.h). To avoid generating renames or inconsistent guidance, consider scoping this to “new feature/module files” or documenting exceptions/legacy naming patterns.

Copilot uses AI. Check for mistakes.

## COM-RPC Debugging
- Switch `communicator` to TCP (`"127.0.0.1:62000"`) to capture traffic.
- Run `Thunder/GenerateLua.sh Thunder/ ThunderInterfaces/` to produce the Wireshark dissector data file.
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section references Thunder/GenerateLua.sh, but that script does not exist in this repository (no GenerateLua.sh found). If the intent is cross-repo documentation, consider either linking to the correct script/path (if it lives elsewhere) or removing this step to avoid a dead-end for contributors.

Suggested change
- Run `Thunder/GenerateLua.sh Thunder/ ThunderInterfaces/` to produce the Wireshark dissector data file.

Copilot uses AI. Check for mistakes.

# Thunder — Navigation

Use this file to find where detailed documentation or canonical examples live. No rules are defined here — rules are in `copilot-instructions.md` and `constraints.md`. For anti-patterns, see `common-mistakes.md`.
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file says “No rules are defined here — rules are in copilot-instructions.md and constraints.md”, but the repo also contains several other rule-bearing instruction files (e.g. the scoped core.instructions.md, com.instructions.md, plugins.instructions.md, thunder-runtime.instructions.md). Consider rewording to avoid implying that only those two files contain rules.

Suggested change
Use this file to find where detailed documentation or canonical examples live. No rules are defined here — rules are in `copilot-instructions.md` and `constraints.md`. For anti-patterns, see `common-mistakes.md`.
Use this file to find where detailed documentation or canonical examples live. This file does not define new rules — consult `copilot-instructions.md`, `constraints.md`, and other `*.instructions.md` files for rules. For anti-patterns, see `common-mistakes.md`.

Copilot uses AI. Check for mistakes.

1. **Plugin isolation** — the daemon is infrastructure only. All domain logic lives in plugins behind COM interfaces. Never add business logic to `Source/Thunder/` or `Source/core/`.
2. **COM-RPC over everything** — inter-plugin calls go through COM interface pointers. The framework short-circuits in-process calls to direct vtable calls automatically. Never use JSON-RPC, raw sockets, or function pointers to call between plugins.
3. **No RTTI, no exceptions, no STL allocations across ABI boundaries** — Thunder targets embedded Linux (`-fno-exceptions`, `-fno-rtti`). COM ref-counting (`AddRef`/`Release`/`Core::ProxyType`) replaces smart pointers at interface boundaries.
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sentence states Thunder targets -fno-rtti, but the build configuration in this repo does not appear to disable RTTI (e.g. cmake/common/CompileSettings.cmake sets -fno-exceptions but not -fno-rtti), and the codebase uses dynamic_cast in multiple places. Consider removing the -fno-rtti claim or rephrasing it to the actual constraint being enforced (e.g. avoid RTTI-dependent patterns across dylib boundaries).

Suggested change
3. **No RTTI, no exceptions, no STL allocations across ABI boundaries** — Thunder targets embedded Linux (`-fno-exceptions`, `-fno-rtti`). COM ref-counting (`AddRef`/`Release`/`Core::ProxyType`) replaces smart pointers at interface boundaries.
3. **No RTTI, no exceptions, no STL allocations across ABI boundaries** — Thunder targets embedded Linux and is built with `-fno-exceptions`. Treat RTTI-dependent patterns (`dynamic_cast`, `typeid`, etc.) as forbidden across COM/ABI boundaries so deployments can safely opt into `-fno-rtti`. COM ref-counting (`AddRef`/`Release`/`Core::ProxyType`) replaces smart pointers at interface boundaries.

Copilot uses AI. Check for mistakes.

### Include Guards

Prefer `#pragma once` for new header files. Existing files that use classic `#ifndef` guards may keep them — there is no need to convert. Never mix both styles in the same file.
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file says to prefer #pragma once for new headers, but core.instructions.md later says “use classic guards … (double-underscore prefix/suffix)” for new headers. These two instruction sources conflict and will cause inconsistent output; consider making them agree (either prefer #pragma once everywhere for new headers, or scope classic guards to specific subtrees / legacy files).

Suggested change
Prefer `#pragma once` for new header files. Existing files that use classic `#ifndef` guards may keep them — there is no need to convert. Never mix both styles in the same file.
For new header files, use classic `#ifndef` / `#define` / `#endif` include guards, with a double-underscore prefix and suffix in the macro name (for example, `__MYMODULE_H__`). Existing files that already use `#pragma once` or classic guards may keep them — there is no need to convert. Never mix both styles in the same file.

Copilot uses AI. Check for mistakes.
Comment on lines +120 to +123
`Source/core/` is a **provider of abstractions** — it must never depend on upper layers. Upper layers (`com/`, `plugins/`, `Thunder/`) implement the abstractions. Concretely:
- `core/` defines `IWorkerPool`, `IResource`, `IDispatch` — it does not implement a `WorkerPool` that knows about plugins.
- `com/` and `Thunder/` provide the concrete `WorkerPool` and `ResourceMonitor` implementations that are injected at runtime.
- If a new abstraction in `core/` needs a concrete implementation, define an interface in `core/` and provide the implementation in `Thunder/` or `com/`.
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The “Dependency Inversion Rule” claims com/ or Thunder/ provide the concrete WorkerPool / ResourceMonitor implementations, but in this repo they are implemented in Source/core/ (e.g. Source/core/WorkerPool.h defines Core::WorkerPool). This is likely to mislead contributors about where to add/modify threading/event-loop behavior; please update this section to reflect the actual layering used here.

Suggested change
`Source/core/` is a **provider of abstractions** — it must never depend on upper layers. Upper layers (`com/`, `plugins/`, `Thunder/`) implement the abstractions. Concretely:
- `core/` defines `IWorkerPool`, `IResource`, `IDispatch` — it does not implement a `WorkerPool` that knows about plugins.
- `com/` and `Thunder/` provide the concrete `WorkerPool` and `ResourceMonitor` implementations that are injected at runtime.
- If a new abstraction in `core/` needs a concrete implementation, define an interface in `core/` and provide the implementation in `Thunder/` or `com/`.
`Source/core/` is a **provider of abstractions and platform primitives** — it must never depend on upper layers. Upper layers (`com/`, `plugins/`, `Thunder/`) configure, own, and wrap these primitives, but do not introduce dependencies back into `core/`. Concretely:
- `core/` defines interfaces such as `IWorkerPool`, `IResource`, and `IDispatch`, and provides their generic implementations (for example, `Core::WorkerPool` in `Source/core/WorkerPool.h` and `Core::ResourceMonitor` in `Source/core/ResourceMonitor.h`). These implementations must remain independent of plugins and upper-layer concepts.
- Threading, job-scheduling, and file-descriptor readiness behavior (event loop, `poll`/`epoll` integration, etc.) should be added or modified in `Source/core/` (typically in the `WorkerPool*` and `ResourceMonitor*` files), not in `com/` or `Thunder/`.
- Upper layers (`com/`, `Thunder/`, `plugins/`) may own instances of `Core::WorkerPool` / `Core::ResourceMonitor`, wrap them, and register their own `IResource`/`IDispatch`/job types, but must not create new dependencies from `Source/core/` back into these layers.
- If a new abstraction in `core/` needs a concrete implementation that depends on plugins, IPC, or other upper-layer types, define an interface in `core/` and provide the implementation in `Thunder/` or `com/`, accessed via dependency injection or callbacks.

Copilot uses AI. Check for mistakes.
- **Unregister timing**: `Unregister()` may be called from the `Handle()` callback itself (re-entrant safe), but the resource must not be destroyed until `Unregister()` returns.

## New File Checklist
- Include guard for new headers: use classic guards `#ifndef __MYFILE_H__` / `#define __MYFILE_H__` / `#endif` (double-underscore prefix and suffix); existing headers that already use `#pragma once` may remain unchanged.
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This “New File Checklist” says new headers should use classic include guards with double-underscore identifiers, but (a) constraints.md instructs to prefer #pragma once for new headers, and (b) many core headers already use #pragma once (e.g. Source/core/WorkerPool.h, Source/core/Errors.h). Consider aligning this checklist with the repo’s current practice and the global constraints file to avoid conflicting guidance.

Suggested change
- Include guard for new headers: use classic guards `#ifndef __MYFILE_H__` / `#define __MYFILE_H__` / `#endif` (double-underscore prefix and suffix); existing headers that already use `#pragma once` may remain unchanged.
- Include guard for new headers: prefer `#pragma once` (as per `constraints.md`). Existing headers that already use classic include guards may remain unchanged, but do not introduce new double-underscore identifiers in include guards.

Copilot uses AI. Check for mistakes.
- `ERROR_COMPOSIT_OBJECT` returned from `AddRef()` signals a composit (delegate-lifetime) object — `Administrator::RecoverySet` handles this correctly; do not special-case it elsewhere.

## Collections Across COM Boundaries
- **Never** pass `std::vector`, `std::list`, `std::map`, or any STL container across a COM interface.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This no longer holds for std::vector ;)

## `Core::ProxyType<T>` — Dynamic Cast Internals
- `Core::ProxyType<T>` uses `dynamic_cast` internally when converting between types (e.g. `ProxyType<IIPC>` → `ProxyType<InvokeMessage>`).
- **Never** rely on `ProxyType<T>(base_proxy)` conversion for `IPCMessageType<>` specialisations — `dynamic_cast` on template types is not reliable across dylib boundaries. Use `IIPC::Label()` + `static_cast` instead.
- This does NOT affect `ProxyType` conversions for non-template types (e.g. `ProxyType<IPCChannel>`) — those work correctly.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or inside the same library

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps we should be careful with the rather detailed part. I do not want AI start generating code everywhere using labes or adding own RTTI info and static cast.


## `SmartInterfaceType<T>` — COM-RPC Client Helper
For external tools/clients connecting to Thunder via COM-RPC, `SmartInterfaceType<T>` provides automatic connection management:
```cpp
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps also mention the other ones, like for Controller and Plugin?

@@ -0,0 +1,157 @@
---
name: Thunder-Core-Library
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we perhaps mention generated code should use code form core over what is available in the C++ standard library?

- `ProcessShutdown` (background timer) manages graceful shutdown of multiple OOP processes.
- `RemoteConnectionMap::Closed()` is called when a channel disconnects — notifies `IRemoteConnection::INotification` observers, then calls `Terminate()`.

### CLI Arguments for ThunderPlugin
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should not be needed at all.
This process should always be spawned from Thunder itself

- Modifying the signature, return type, or parameter list of an existing published COM interface method — always extend via a new versioned interface
- Process-global mutable state outside of `Core::Singleton<T>`

---
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scanning through this I think I also would have expected a section on coding guidelines etc.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants