Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .github/prompts/openspec-apply.prompt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
description: Implement an approved OpenSpec change and keep tasks in sync.
---

$ARGUMENTS
<!-- OPENSPEC:START -->
**Guardrails**
- Favor straightforward, minimal implementations first and add complexity only when it is requested or clearly required.
- Keep changes tightly scoped to the requested outcome.
- Refer to `openspec/AGENTS.md` (located inside the `openspec/` directory—run `ls openspec` or `openspec update` if you don't see it) if you need additional OpenSpec conventions or clarifications.

**Steps**
Track these steps as TODOs and complete them one by one.
1. Read `changes/<id>/proposal.md`, `design.md` (if present), and `tasks.md` to confirm scope and acceptance criteria.
2. Work through tasks sequentially, keeping edits minimal and focused on the requested change.
3. Confirm completion before updating statuses—make sure every item in `tasks.md` is finished.
4. Update the checklist after all work is done so each task is marked `- [x]` and reflects reality.
5. Reference `openspec list` or `openspec show <item>` when additional context is required.

**Reference**
- Use `openspec show <id> --json --deltas-only` if you need additional context from the proposal while implementing.
<!-- OPENSPEC:END -->
26 changes: 26 additions & 0 deletions .github/prompts/openspec-archive.prompt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
description: Archive a deployed OpenSpec change and update specs.
---

$ARGUMENTS
<!-- OPENSPEC:START -->
**Guardrails**
- Favor straightforward, minimal implementations first and add complexity only when it is requested or clearly required.
- Keep changes tightly scoped to the requested outcome.
- Refer to `openspec/AGENTS.md` (located inside the `openspec/` directory—run `ls openspec` or `openspec update` if you don't see it) if you need additional OpenSpec conventions or clarifications.

**Steps**
1. Determine the change ID to archive:
- If this prompt already includes a specific change ID (for example inside a `<ChangeId>` block populated by slash-command arguments), use that value after trimming whitespace.
- If the conversation references a change loosely (for example by title or summary), run `openspec list` to surface likely IDs, share the relevant candidates, and confirm which one the user intends.
- Otherwise, review the conversation, run `openspec list`, and ask the user which change to archive; wait for a confirmed change ID before proceeding.
- If you still cannot identify a single change ID, stop and tell the user you cannot archive anything yet.
2. Validate the change ID by running `openspec list` (or `openspec show <id>`) and stop if the change is missing, already archived, or otherwise not ready to archive.
3. Run `openspec archive <id> --yes` so the CLI moves the change and applies spec updates without prompts (use `--skip-specs` only for tooling-only work).
4. Review the command output to confirm the target specs were updated and the change landed in `changes/archive/`.
5. Validate with `openspec validate --strict --no-interactive` and inspect with `openspec show <id>` if anything looks off.

**Reference**
- Use `openspec list` to confirm change IDs before archiving.
- Inspect refreshed specs with `openspec list --specs` and address any validation issues before handing off.
<!-- OPENSPEC:END -->
27 changes: 27 additions & 0 deletions .github/prompts/openspec-proposal.prompt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
description: Scaffold a new OpenSpec change and validate strictly.
---

$ARGUMENTS
<!-- OPENSPEC:START -->
**Guardrails**
- Favor straightforward, minimal implementations first and add complexity only when it is requested or clearly required.
- Keep changes tightly scoped to the requested outcome.
- Refer to `openspec/AGENTS.md` (located inside the `openspec/` directory—run `ls openspec` or `openspec update` if you don't see it) if you need additional OpenSpec conventions or clarifications.
- Identify any vague or ambiguous details and ask the necessary follow-up questions before editing files.
- Do not write any code during the proposal stage. Only create design documents (proposal.md, tasks.md, design.md, and spec deltas). Implementation happens in the apply stage after approval.

**Steps**
1. Review `openspec/project.md`, run `openspec list` and `openspec list --specs`, and inspect related code or docs (e.g., via `rg`/`ls`) to ground the proposal in current behaviour; note any gaps that require clarification.
2. Choose a unique verb-led `change-id` and scaffold `proposal.md`, `tasks.md`, and `design.md` (when needed) under `openspec/changes/<id>/`.
3. Map the change into concrete capabilities or requirements, breaking multi-scope efforts into distinct spec deltas with clear relationships and sequencing.
4. Capture architectural reasoning in `design.md` when the solution spans multiple systems, introduces new patterns, or demands trade-off discussion before committing to specs.
5. Draft spec deltas in `changes/<id>/specs/<capability>/spec.md` (one folder per capability) using `## ADDED|MODIFIED|REMOVED Requirements` with at least one `#### Scenario:` per requirement and cross-reference related capabilities when relevant.
6. Draft `tasks.md` as an ordered list of small, verifiable work items that deliver user-visible progress, include validation (tests, tooling), and highlight dependencies or parallelizable work.
7. Validate with `openspec validate <id> --strict --no-interactive` and resolve every issue before sharing the proposal.

**Reference**
- Use `openspec show <id> --json --deltas-only` or `openspec show <spec> --type spec` to inspect details when validation fails.
- Search existing requirements with `rg -n "Requirement:|Scenario:" openspec/specs` before writing new ones.
- Explore the codebase with `rg <keyword>`, `ls`, or direct file reads so proposals align with current implementation realities.
<!-- OPENSPEC:END -->
39 changes: 39 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: CI

on:
pull_request:
push:
branches:
- develop
- main

jobs:
test:
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Dart
uses: dart-lang/setup-dart@v1

- name: Install workspace deps
run: dart pub get

- name: Activate Melos
run: |
dart pub global activate melos 7.3.0
echo "$HOME/.pub-cache/bin" >> "$GITHUB_PATH"

- name: Bootstrap
run: melos bootstrap

- name: Analyze
run: melos run analyze --no-select

- name: Format Check
run: melos run format:check --no-select

- name: Test
run: melos run test --no-select
60 changes: 60 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
name: Publish

on:
workflow_dispatch:
push:
tags:
- "v*.*.*"

concurrency:
group: publish-${{ github.ref }}
cancel-in-progress: false

jobs:
publish:
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Setup Dart
uses: dart-lang/setup-dart@v1

- name: Install workspace deps
run: dart pub get

- name: Activate Melos
run: |
dart pub global activate melos 7.3.0
echo "$HOME/.pub-cache/bin" >> "$GITHUB_PATH"

- name: Bootstrap
run: melos bootstrap

- name: Analyze
run: melos run analyze --no-select

- name: Test
run: melos run test --no-select

- name: Configure pub.dev credentials
env:
PUB_CREDENTIALS: ${{ secrets.PUB_CREDENTIALS }}
run: |
test -n "$PUB_CREDENTIALS"
mkdir -p "$HOME/.config/dart"
printf '%s' "$PUB_CREDENTIALS" > "$HOME/.config/dart/pub-credentials.json"

- name: Copy root CHANGELOG into all packages
run: |
for dir in packages/*; do
if [ -d "$dir" ] && [ -f "$dir/pubspec.yaml" ]; then
cp CHANGELOG.md "$dir/CHANGELOG.md"
fi
done

- name: Publish packages
run: melos publish --no-private --no-dry-run --yes
28 changes: 28 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Dart/Flutter
.dart_tool/
.packages
build/
pubspec.lock

# Coverage
coverage/

# IDE
.idea/
*.iml
.vscode/

# OS
.DS_Store
Thumbs.db

# Melos
.melos_tool/

# Generated
*.g.dart
*.freezed.dart

# Local environment
.env
.env.local
18 changes: 18 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<!-- OPENSPEC:START -->
# OpenSpec Instructions

These instructions are for AI assistants working in this project.

Always open `@/openspec/AGENTS.md` when the request:
- Mentions planning or proposals (words like proposal, spec, change, plan)
- Introduces new capabilities, breaking changes, architecture shifts, or big performance/security work
- Sounds ambiguous and you need the authoritative spec before coding

Use `@/openspec/AGENTS.md` to learn:
- How to create and apply change proposals
- Spec format and conventions
- Project structure and guidelines

Keep this managed block so 'openspec update' can refresh the instructions.

<!-- OPENSPEC:END -->
27 changes: 27 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added

- Initial release of the loom worker pool framework
- `WorkerPool` with configurable worker count and execution modes
- `Task<I, O>` for defining typed, reusable work units
- Priority-based job scheduling (`Priority.low`, `normal`, `high`, `critical`)
- Retry policies: `none`, `fixed`, `exponentialBackoff`, `linear`
- Cancellation support via `CancellationToken` and `CancellationTokenSource`
- Progress reporting through `TaskContext.reportProgress`
- Lifecycle hooks: `onJobStart`, `onJobSuccess`, `onJobFailure`, `onRetry`, `onPoolIdle`, `onPoolShutdown`
- Multiple execution backends: `MainIsolateBackend`, `IsolatePoolBackend`, `TestBackend`
- `JobResult<O>` sealed class with `JobSuccess` and `JobFailure` variants
- `JobHandle<O>` for tracking job status, progress, and cancellation
- `Loom` singleton for global default pool access
- Queue overflow strategies: `reject`, `dropOldest`, `dropNewest`
- Graceful and force shutdown support
- Comprehensive test suite (200 tests)
- Performance benchmarks
102 changes: 101 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,101 @@
# loom
# Loom

A lightweight, flexible worker pool framework for Dart applications.

## Overview

Loom provides a robust foundation for managing concurrent task execution with:

- **Priority-based scheduling** - Critical tasks run first
- **Automatic retry** - Configurable retry policies with backoff
- **Graceful cancellation** - Cancel queued or running jobs
- **Progress reporting** - Real-time progress streams
- **Multiple execution modes** - Main isolate, background isolates, or test mode
- **Lifecycle hooks** - Monitor pool activity

## Packages

| Package | Description |
|---------|-------------|
| [loom](packages/loom/) | Core worker pool framework |

## Getting Started

### Prerequisites

- Dart SDK ^3.10.7
- [Melos](https://melos.invertase.dev/) for monorepo management

### Setup

```bash
# Install melos globally
dart pub global activate melos

# Bootstrap the workspace
melos bootstrap
```

### Common Commands

```bash
# Run all tests
melos run test

# Run analyzer
melos run analyze

# Format code
melos run format

# Run all checks (analyze + format + test)
melos run check

# Run performance tests
melos run test:perf

# Run example
melos run run:example
```

## Quick Example

```dart
import 'package:loom/loom.dart';

void main() async {
// Define a task
final task = Task<String, int>.simple(
name: 'parseNumber',
executor: (input, ctx) async => int.parse(input),
);

// Create a pool
final pool = WorkerPool.io('my-pool');

// Submit work
final handle = pool.submit(task, '42');
final result = await handle.result;

print('Parsed: ${result.valueOrThrow}'); // 42

await pool.shutdown();
}
```

## Documentation

- [Loom Package README](packages/loom/README.md)
- [API Documentation](packages/loom/doc/)

## Contributing

1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Run `melos run check` to verify
5. Submit a pull request

## License

See [LICENSE](LICENSE) for details.
Loading