new-collection-object
Provides patterns and boilerplate for OpenWatt runtime objects.
Install
mkdir -p .claude/skills/new-collection-object && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/14741" && unzip -o skill.zip -d .claude/skills/new-collection-object && rm skill.zipInstalls to .claude/skills/new-collection-object
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Boilerplate and patterns for creating new Collection-managed runtime objects (Streams, Interfaces, Protocol components). Use when adding a new managed object type.Key capabilities
- →Generate boilerplate for Collection-managed runtime objects.
- →Define object type, module name, class name, and properties.
- →Implement property declaration patterns.
- →Handle mutually exclusive properties.
- →Implement property getter/setter patterns for various types.
- →Implement state machine methods like `validate()`, `startup()`, `shutdown()`, and `update()`.
How it works
This skill generates Dlang boilerplate code for new Collection-managed runtime objects in the OpenWatt codebase, including file structure, property declarations, getter/setter patterns, and state machine methods. It guides the user through defining object specifics like type, name, and properties.
Inputs & outputs
When to use new-collection-object
- →Create new stream object
- →Implement new protocol interface
- →Generate runtime object boilerplate
About this skill
new-collection-object
Create a new Collection-managed runtime object in the OpenWatt codebase. This skill generates the boilerplate for objects managed by the Collection system (Streams, Interfaces, Protocol components, etc.).
Usage
When the user asks to create a new Collection-managed object, use this skill to guide the implementation. Ask the user for:
- Object type: Stream, Interface, Protocol client/server, or other
- Module name: e.g., "mqtt", "modbus", "tcp", "serial"
- Class name: e.g., "MQTTBroker", "HTTPServer", "CANInterface"
- Properties: List of properties with types and descriptions
- Base class: Usually Stream, BaseInterface, or BaseObject
Boilerplate Patterns
1. File Structure
All Collection-managed objects follow this structure:
module [layer].[category].[module];
// Standard imports
import urt.lifetime;
import urt.log;
import urt.string;
import urt.time;
// ... other urt imports as needed
import manager;
import manager.base;
import manager.collection;
import manager.console;
import manager.plugin;
// Layer-specific imports
public import [layer].[category]; // e.g., router.stream, router.iface
nothrow @nogc:
// Enums/structs as needed
enum YourEnum : ubyte
{
Value1,
Value2,
}
// Main class
class YourObject : BaseClass
{
__gshared Property[N] Properties = [ Property.create!("prop1", prop1)(),
Property.create!("prop2", prop2)(),
/* ... */ ];
nothrow @nogc:
alias TypeName = StringLit!"type-name";
this(String name, ObjectFlags flags = ObjectFlags.None)
{
super(collection_type_info!YourObject, name.move, flags);
}
// Properties...
[property implementations]
// API...
override bool validate() const pure
{
// Validate configuration
return true;
}
override CompletionStatus startup()
{
// Initialize resources
return CompletionStatus.Complete;
}
override CompletionStatus shutdown()
{
// Clean up resources
return CompletionStatus.Complete;
}
override void update()
{
// Per-frame processing
}
private:
// Member variables
}
// Module class
class YourModule : Module
{
mixin DeclareModule!"[layer].[module]";
nothrow @nogc:
Collection!YourObject objects;
override void init()
{
g_app.console.registerCollection("/[category]/[type]", objects);
}
override void update()
{
objects.update_all();
}
}
2. Property Declaration Patterns
The Properties Array (REQUIRED):
__gshared Property[N] Properties = [ Property.create!("prop-name", prop_name)(),
Property.create!("other-prop", other_prop)(),
/* exactly N properties */ ];
Mutually Exclusive Properties:
When properties represent different modes/options where only one should be active, track which is set internally rather than validating mutual exclusion:
void mode_a(TypeA value)
{
if (_mode_a == value)
return;
_mode_a = value;
_active_mode = Mode.A; // Track which mode is active
restart();
}
void mode_b(TypeB value)
{
if (_mode_b == value)
return;
_mode_b = value;
_active_mode = Mode.B; // Overwrite previous mode
restart();
}
// In validate(), check that one mode is set
override bool validate() const
{
return _active_mode != Mode.None;
}
Property Getter/Setter Patterns:
Simple value type:
// Getter
ushort port() const pure
=> _port;
// Setter
void port(ushort value)
{
if (_port == value)
return; // early out is there is an advantage
_port = value;
restart(); // Trigger re-initialization if required
}
String/complex type:
// Getter
ref const(String) device() const pure
=> _device;
// Setter with validation (returns StringResult)
StringResult device(String value)
{
if (!value)
return StringResult("device cannot be empty");
if (_device == value)
return StringResult.success;
_device = value.move;
restart();
return StringResult.success;
}
Enum type:
Parity parity() const pure
=> _parity;
void parity(Parity value)
{
if (_parity == value)
return;
_parity = value;
restart();
}
Multiple overloads (type conversion):
void port(WellKnownPort value)
=> port(cast(ushort)value);
void port(ushort value)
{
if (_port == value)
return;
_port = value;
restart();
}
ObjectRef property (reference to another Collection object):
// Getter
inout(Stream) stream() inout pure
=> _stream;
// Setter
StringResult stream(Stream value)
{
if (!value)
return StringResult("stream cannot be null");
if (_stream is value)
return StringResult.success;
_stream = value;
restart();
return StringResult.success;
}
private:
ObjectRef!Stream _stream;
3. State Machine Methods
validate() - Configuration Validation
Return true if configuration is valid:
override bool validate() const pure
{
// Check required properties are set
if (_device.empty)
return false;
// Check valid ranges/combinations
if (_baud_rate < 300 || _baud_rate > 115200)
return false;
// Check dependencies
if (_stream.get() is null)
return false;
return true;
}
startup() - Resource Initialization
Initialize resources. Return:
CompletionStatus.Complete- SuccessCompletionStatus.Continue- Still initializing, call again next frameCompletionStatus.Error- Failed, will retry with exponential backoff
override CompletionStatus startup()
{
// Async resolution example
if (_host)
{
AddressInfo addrInfo;
addrInfo.family = AddressFamily.ipv4;
AddressInfoResolver results;
get_address_info(_host, null, &addrInfo, results);
if (!results.next_address(addrInfo))
return CompletionStatus.Continue; // Not ready yet
_address = addrInfo.address;
}
// Resource creation with error handling
Result r = create_resource(_handle);
if (!r)
{
writeError(type, " '", name, "' - failed to create resource: ", r);
return CompletionStatus.Error; // Will retry with backoff
}
// Initialize state
_initialized = true;
writeInfo(type, " '", name, "' started successfully");
return CompletionStatus.Complete;
}
shutdown() - Resource Cleanup
Clean up all resources. Must not fail. Called repeatedly while in State.Stopping (NOT update()).
Return CompletionStatus.Continue to continue shutdown asynchronously:
override CompletionStatus shutdown()
{
// For async/cancellable resources, request cancellation and wait
if (has_pending_operations())
{
request_cancellation();
poll_for_completion();
if (still_waiting())
return CompletionStatus.Continue; // Will be called again
}
// Close handles/sockets
if (_handle != invalid_handle)
{
close_handle(_handle);
_handle = invalid_handle;
}
// Clear state
_initialized = false;
// Destroy if temporary
if (_flags & ObjectFlags.Temporary)
destroy();
return CompletionStatus.Complete;
}
update() - Per-Frame Processing
Called every frame while Running:
override void update()
{
// Check connection status
if (!is_connected())
{
restart();
return;
}
// Process incoming data
ubyte[4096] buffer;
ptrdiff_t received = read(buffer);
if (received > 0)
process_data(buffer[0..received]);
// Update internal state
_last_activity = getTime();
}
4. Constructor Pattern
For Streams (with StreamOptions):
this(String name, ObjectFlags flags = ObjectFlags.None, StreamOptions options = StreamOptions.None)
{
super(collection_type_info!YourStream, name.move, flags, options);
}
For Interfaces (with InterfaceOptions):
this(String name, ObjectFlags flags = ObjectFlags.None, InterfaceOptions options = InterfaceOptions.None)
{
super(collection_type_info!YourInterface, name.move, flags, options);
}
For other BaseObject subclasses:
this(String name, ObjectFlags flags = ObjectFlags.None)
{
super(collection_type_info!YourObject, name.move, flags);
}
5. TypeName Requirement
Every Collection-managed class needs:
alias TypeName = StringLit!"type-name";
This determines:
- Console command path:
/stream/type-name,/interface/type-name - Default object naming
- Type identification in logs
Examples:
StringLit!"tcp"→/stream/tcp-clientStringLit!"serial"→/stream/serialStringLit!"modbus"→/interface/modbusStringLit!"mqtt-broker"→/protocol/mqtt/broker
6. Module Class Pattern
class YourModule : Module
{
mixin DeclareModule!"layer.module";
nothrow @nogc:
// Collections
Collection!YourStream streams;
Collection!YourInterface interfaces;
// Non-collection objects (if any)
Map!(const(char)[], YourClient) clients;
override void init()
{
// Register collections with console
g_app.console.registerCollection("/stream/your-type", streams);
g_app.console.registerCollection("/interface/your-type", interfaces);
}
override void pre_update()
{
// Update all collection objects
streams.update_all();
interfaces.update_all();
}
override void update()
{
// Update non-collection objects
foreach(client; clients.values)
client.update();
}
override void post_update()
{
// Post-processing if needed
}
}
7. Module Registration
Add to `src/ma
Content truncated.
When not to use it
- →When creating objects not managed by the Collection system.
- →When not working within the OpenWatt codebase.
- →When not adding a new managed object type.
Limitations
- →The skill is specific to the OpenWatt codebase.
- →It generates Dlang code.
- →It requires objects to be managed by the Collection system.
How it compares
This skill automates the creation of structured boilerplate for specific Collection-managed objects in OpenWatt, ensuring adherence to predefined patterns and reducing manual setup compared to creating objects from scratch.
Compared to similar skills
new-collection-object side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| new-collection-object (this skill) | 0 | 3mo | No flags | Advanced |
| architecture-patterns | 55 | 2mo | No flags | Advanced |
| kotlin-multiplatform | 32 | 3mo | Review | Advanced |
| nodejs-best-practices | 28 | 6mo | No flags | Advanced |
Try saying
Example prompts that trigger this skill in your AI assistant.
You might also like
architecture-patterns
wshobson
Implement proven backend architecture patterns including Clean Architecture, Hexagonal Architecture, and Domain-Driven Design. Use when architecting complex backend systems or refactoring existing applications for better maintainability.
kotlin-multiplatform
vitorpamplona
Platform abstraction decision-making for Amethyst KMP project. Guides when to abstract vs keep platform-specific, source set placement (commonMain, jvmAndroid, platform-specific), expect/actual patterns. Covers primary targets (Android, JVM/Desktop, iOS) with web/wasm future considerations. Integrates with gradle-expert for dependency issues. Triggers on: abstraction decisions ("should I share this?"), source set placement questions, expect/actual creation, build.gradle.kts work, incorrect placement detection, KMP dependency suggestions.
nodejs-best-practices
davila7
Node.js development principles and decision-making. Framework selection, async patterns, security, and architecture. Teaches thinking, not copying.
workflow-orchestration-patterns
wshobson
Design durable workflows with Temporal for distributed systems. Covers workflow vs activity separation, saga patterns, state management, and determinism constraints. Use when building long-running processes, distributed transactions, or microservice orchestration.
java-pro
sickn33
Master Java 21+ with modern features like virtual threads, pattern matching, and Spring Boot 3.x. Expert in the latest Java ecosystem including GraalVM, Project Loom, and cloud-native patterns. Use PROACTIVELY for Java development, microservices architecture, or performance optimization.
arm-cortex-expert
sickn33
Senior embedded software engineer specializing in firmware and driver development for ARM Cortex-M microcontrollers (Teensy, STM32, nRF52, SAMD). Decades of experience writing reliable, optimized, and maintainable embedded code with deep expertise in memory barriers, DMA/cache coherency, interrupt-driven I/O, and peripheral drivers.