NE

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.zip

Installs 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.
163 chars✓ has a “when” trigger
Advanced

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

You give it
Object type, module name, class name, properties, base class
You get back
Dlang boilerplate code for a new Collection-managed object

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:

  1. Object type: Stream, Interface, Protocol client/server, or other
  2. Module name: e.g., "mqtt", "modbus", "tcp", "serial"
  3. Class name: e.g., "MQTTBroker", "HTTPServer", "CANInterface"
  4. Properties: List of properties with types and descriptions
  5. 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 - Success
  • CompletionStatus.Continue - Still initializing, call again next frame
  • CompletionStatus.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-client
  • StringLit!"serial"/stream/serial
  • StringLit!"modbus"/interface/modbus
  • StringLit!"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.

SkillInstallsUpdatedSafetyDifficulty
new-collection-object (this skill)03moNo flagsAdvanced
architecture-patterns552moNo flagsAdvanced
kotlin-multiplatform323moReviewAdvanced
nodejs-best-practices286moNo flagsAdvanced

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.

55214

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.

32156

nodejs-best-practices

davila7

Node.js development principles and decision-making. Framework selection, async patterns, security, and architecture. Teaches thinking, not copying.

28120

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.

10117

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.

3492

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.

2975

Search skills

Search the agent skills registry