Skip to content

feat: add skills/ directory with preserved history + AGENTS.md#1385

Open
rohitg00 wants to merge 41 commits intomainfrom
feat/add-skills-v2
Open

feat: add skills/ directory with preserved history + AGENTS.md#1385
rohitg00 wants to merge 41 commits intomainfrom
feat/add-skills-v2

Conversation

@rohitg00
Copy link
Copy Markdown
Contributor

@rohitg00 rohitg00 commented Mar 31, 2026

Summary

Brings iii-hq/skills into the monorepo at `skills/` using `git subtree add`, preserving the full commit history so diffs are reviewable.

What's in the subtree (from iii-hq/skills)

  • 24 iii-prefixed skills with full commit history
  • PR feat: introduce docs boilerplate #12 fixes (Anthony's review: leading slashes, `expression` config, "When to Use" sections, iii- rename)
  • 49 reference implementations (TypeScript, Python, Rust)

Additional changes on top

  • README rewritten for monorepo context (`npx skills add iii-hq/iii`)
  • CodeRabbit fixes: createStream positional args, TriggerAction import, order_id clone
  • iii-node-sdk: added missing "When to Use" / "Boundaries" sections
  • iii-getting-started: new skill — install engine, first worker, next steps
  • Slash-separated function IDs: expanded `state::set/get/update` → `state::set`, `state::get`, `state::update` (Mike + Anthony feedback)
  • AGENTS.md: commands-first structure, always/ask first/never boundaries, code examples

Install

npx skills add iii-hq/iii          # 25 skills, auto-discovered
npx skills add https://iii.dev/docs # Mintlify auto-generated skill

Test plan

  • `npx skills add iii-hq/iii --list` discovers all 25 `iii-*` skills
  • Diffs are reviewable (history preserved from iii-hq/skills)
  • All `api_path` values have leading slashes
  • All cron config fields use `expression`
  • No slash-separated function IDs in SKILL.md files

Summary by CodeRabbit

  • Documentation

    • Added comprehensive guides for core architectural patterns including agentic backends, channels, cron scheduling, custom triggers, dead-letter queues, event-driven CQRS, HTTP endpoints, queue processing, reactive backends, real-time streams, state management, and workflow orchestration.
    • Added skill documentation for all supported trigger types and SDKs (Node.js, Python, Rust).
    • Added repository operating procedures and project structure guidelines.
  • Examples

    • Added working reference implementations across all patterns in JavaScript, Python, and Rust.
    • Added engine configuration examples and best practices.

rohitg00 and others added 30 commits March 6, 2026 19:58
SDK reference (Python, Rust), core patterns (REST API, background jobs,
cron tasks, workflows, state management, real-time streaming, pub/sub,
channels), advanced (multi-trigger, custom triggers, AI agents, testing),
and operations (deployment, observability).
Add explicit boundary rules and a data-driven endpoint-map example so prompts about `{ path, id }` looped registerFunction calls are steered to the http-invoked-functions pattern.
Documents all Motia framework patterns that will be removed from
iii-prefixed skills, including per-skill details and the iii-sdk
equivalents for each Motia concept.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove 16 iii-prefixed skills that contained outdated Motia framework
patterns not matching the actual iii documentation. These will be
rebuilt from scratch using the iii docs HOWTOs as the source of truth.

Removed skills: iii-rest-api, iii-cron-tasks, iii-background-jobs,
iii-state-management, iii-realtime-streaming, iii-custom-triggers,
iii-pubsub, iii-workflows, iii-multi-trigger, iii-deployment,
iii-observability, iii-python-sdk, iii-rust-sdk, iii-ai-agents,
iii-channels, iii-testing

See MOTIA_REMOVAL_SUMMARY.md for details on what was removed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Per the Agent Skills standard, reference files belong in a references/
subdirectory. Moved reference.js for all 7 pattern skills and updated
SKILL.md paths accordingly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Created SKILL.md files for effect-system, event-driven-cqrs, and
low-code-automation which previously only had reference.js files.
Follows the same structure as the other pattern skills.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Deleted all REFERENCE.md files from HOWTO skill references/
- Created reference.js files for all 12 HOWTO skills (except engine-config)
  following the same structure as existing pattern skills
- Rewrote all 13 SKILL.md files to remove fenced code blocks and config
  sections, matching the agentic-backend/SKILL.md template
- engine-config/SKILL.md references ../references/iii-config.yaml (root)
- All other skills reference references/reference.js

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Created references/iii-config.yaml with the full annotated engine
  config reference covering all modules and adapters
- Created .github/workflows/sync-config.yml to auto-sync the config
  from https://iii.dev/docs/how-to/configure-engine on merge to main
  and weekly on Mondays
- engine-config/SKILL.md already points to ../references/iii-config.yaml

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Minimal skills for node-sdk, python-sdk, and rust-sdk that point to
the official SDK API reference docs at iii.dev/docs/api-reference/.
Each lists key exports and notes specific to that language.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Lists all 23 skills grouped by category: HOWTO skills (13),
architecture pattern skills (7), and SDK reference skills (3).
Also references the shared iii-config.yaml.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Describes a plan for automatically adding, removing, and updating
skills when HOWTOs change in the iii docs. Recommends a GitHub Action
with AI agent approach that fetches llms.txt, diffs against current
skills, generates content, and opens PRs for review.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Moved all per-skill references/reference.js files into a single root
references/ directory, named by skill (e.g., references/http-endpoints.js).
Updated all SKILL.md reference links to ../references/{skill-name}.js.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace outdated iii-prefixed skill references with the current 23
skills grouped by category: HOWTO skills, architecture patterns, and
SDK references. Update install examples and format section to reflect
the root references/ directory structure.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Expanded descriptions for queue-processing, state-management,
trigger-actions, trigger-conditions, state-reactions, and
cron-scheduling to include more concrete actions and natural trigger
terms as flagged by tessl skill review.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Added "Engine Configuration" sections to 9 skills that reference
modules, adapters, or queue_configs, pointing to the shared
references/iii-config.yaml for the full annotated config reference.

Skills updated: cron-scheduling, queue-processing, state-management,
state-reactions, realtime-streams, observability, dead-letter-queues,
agentic-backend, workflow-orchestration

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add explicit iii-config references to relevant skills and improve some descriptions
chore: make compatible with skillkit; fix README commands
update config to better baseline/defaults
Replace init() with registerWorker()/register_worker() across all skills
(node-sdk, python-sdk, rust-sdk, functions-and-triggers, observability) to
match the actual SDK exports. Update Rust SDK skill to use the new
RegisterFunction builder API and pin version to 0.10. Add Python and Rust
reference implementations for functions-and-triggers.

Closes #8 items 1-4

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…CLI timeout

- Add channels skill with SKILL.md and reference implementations (JS, Python, Rust)
  for binary streaming between workers
- Add request/response format auto-registration docs to functions-and-triggers
  (schemars in Rust, type hints in Python, manual in Node.js)
- Add --timeout-ms CLI flag mentions to dead-letter-queues and trigger-actions
- Note CLI trigger is now part of the engine binary (not a separate tool)

Closes #8 item 5

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Document the external worker module system: iii.toml manifest, iii worker
CLI commands (add, remove, list, info), workers section in config.yaml with
marker-delimited config blocks, and binary resolution from iii_workers/.

Closes #8 remaining item (worker module system)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
anthonyiscoding and others added 11 commits March 27, 2026 16:25
- Clarify channel consumer behavior in channels/SKILL.md
- Standardize on `iii-config.yaml` in engine-config/SKILL.md
- Document synchronous `trigger()` usage in python-sdk/SKILL.md
- Handle empty streams gracefully in JS and Python channel references
- Prevent deadlock by using non-blocking trigger in Rust channel reference
- Use `.get()` to prevent KeyErrors in Python functions-and-triggers reference
Fixes from Anthony's feedback on PR #11:
- Restore leading slashes on api_path (their standard)
- Revert cron config field from 'cron' back to 'expression'
- Restore "When to Use" / "Boundaries" sections in all SKILL.md files
- Remove incorrect per-language matrix from README (features at parity)

Rename all skill folders with iii- prefix for marketplace indexing:
- All 24 skill directories renamed (e.g. channels/ -> iii-channels/)
- SKILL.md name fields updated to match directory names
- All cross-references updated in README.md, SKILLS.md, and SKILL.md files
- Python/Rust reference files included with fixes applied
fix: address Anthony's review + rename skills with iii- prefix
git-subtree-dir: skills
git-subtree-mainline: b56a528
git-subtree-split: d7cb246
- README install commands now point to iii-hq/iii (not iii-hq/skills)
- Added Getting Started section to README
- Added iii-channels entry to SKILLS.md
- realtime-streams.js: fix createStream to use positional args (SDK signature)
- channels.rs: add missing TriggerAction import
- observability.rs: clone order_id before async move (borrow checker)
Installation, first worker, and next steps — walks through engine install,
project creation, SDK setup, shows first worker in TS/Python/Rust, and
points to npx skills add iii-hq/iii for agent skills.
'state::set/get/update' reads as a literal function ID with slashes.
Expanded to separate names so agents don't confuse shorthand with real
function IDs. Fixes feedback from Mike and Anthony.
Commands first, three-tier boundaries (always/ask first/never), code
style examples in Rust/TS/Python. Follows GitHub's 2,500-repo analysis
best practices.
@vercel
Copy link
Copy Markdown
Contributor

vercel bot commented Mar 31, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
iii-website Ready Ready Preview, Comment Mar 31, 2026 9:26pm
motia-docs Ready Ready Preview, Comment Mar 31, 2026 9:26pm

Request Review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 31, 2026

📝 Walkthrough

Walkthrough

This PR introduces comprehensive documentation for the iii engine framework, including repository operating procedures (AGENTS.md with setup, build, and deployment guidelines), a structured skills catalog system with 25+ skill guides (each documenting a specific pattern, use case, and boundary rules), and 100+ runnable reference implementations across JavaScript/TypeScript, Python, and Rust demonstrating practical application of each skill.

Changes

Cohort / File(s) Summary
Root repository documentation
AGENTS.md
Defines repository operating procedures, project map, workspace tooling, explicit boundaries/rules (conventions, ask-first topics, prohibited actions), and code-style examples for function registration, HTTP triggers, and cron triggers across Rust/TypeScript/Python with licensing details.
Skills directory root
skills/.gitignore, skills/LICENSE, skills/README.md, skills/SKILLS.md
Adds Apache 2.0 license, expands .gitignore for IDE directories, provides skills catalog overview with installation/sync instructions and skill groupings (getting started, HOWTO, architecture patterns, SDK references, shared references), and creates SKILLS.md entry point linking to individual skill documentation.
Getting started and fundamentals
skills/iii-getting-started/SKILL.md, skills/iii-functions-and-triggers/SKILL.md
Documents end-to-end onboarding with engine installation, project setup, first-worker examples in three languages, and HTTP testing; defines core function/trigger registration, cross-language invocation patterns, and request/response handling.
HTTP and REST patterns
skills/iii-http-endpoints/SKILL.md, skills/iii-http-invoked-functions/SKILL.md
Describes REST API endpoint exposure with routing, request/response handling, path params, and status codes; documents outbound HTTP function invocation with auth configuration (bearer, API key, HMAC).
Queue and asynchronous processing
skills/iii-queue-processing/SKILL.md, skills/iii-dead-letter-queues/SKILL.md, skills/iii-trigger-actions/SKILL.md
Covers named queue configuration, enqueue vs fire-and-forget vs sync invocation modes, retry/backoff semantics, DLQ routing after max retries, and redrive/recovery workflows with CLI and SDK examples.
State and data persistence
skills/iii-state-management/SKILL.md, skills/iii-state-reactions/SKILL.md
Defines scoped key-value state store operations (set/get/list/delete/update), automatic trigger-on-change reactions with optional conditional gating, and state-driven reactive side effects.
Scheduling and time-based triggers
skills/iii-cron-scheduling/SKILL.md
Documents 7-field cron expression format, lightweight handler behavior, enqueue heavy work pattern, and per-job logging with multiple language examples.
Streaming and real-time
skills/iii-realtime-streams/SKILL.md, skills/iii-channels/SKILL.md
Describes WebSocket-backed stream CRUD (set/get/list/delete/send) for live client updates and binary streaming channels with reader/writer pairs, buffering, and cross-worker handoff via serializable references.
Custom extensibility
skills/iii-custom-triggers/SKILL.md, skills/iii-trigger-conditions/SKILL.md
Documents custom trigger type registration for external event sources, per-trigger lifecycle management, and boolean condition functions for gating handler execution before invocation.
Higher-level patterns
skills/iii-agentic-backend/SKILL.md, skills/iii-effect-system/SKILL.md, skills/iii-event-driven-cqrs/SKILL.md, skills/iii-reactive-backend/SKILL.md, skills/iii-low-code-automation/SKILL.md, skills/iii-workflow-orchestration/SKILL.md
Covers multi-agent orchestration with queue handoffs and approval gates; composable functional chains with tracing; event sourcing with projections and pub/sub fan-out; reactive backends with state-driven updates; queue-chained automation flows; and durable multi-step workflows with step tracking and DLQ handling.
SDK reference and observability
skills/iii-node-sdk/SKILL.md, skills/iii-python-sdk/SKILL.md, skills/iii-rust-sdk/SKILL.md, skills/iii-observability/SKILL.md
Provides SDK API tables (exports, installation, key types/functions) for each language; documents auto-instrumented OpenTelemetry tracing, custom spans/metrics, log subscription, OTLP export, Prometheus metrics, and W3C trace-context propagation.
Engine configuration
skills/iii-engine-config/SKILL.md
Documents iii-config.yaml structure, module registry (HTTP REST, streaming, state, queue, pub/sub, cron, OTEL), worker module system with iii.toml manifests, operational/deployment commands, and port/healthcheck conventions.
JavaScript reference implementations
skills/references/agentic-backend.js, skills/references/channels.js, skills/references/cron-scheduling.js, skills/references/custom-triggers.js, skills/references/dead-letter-queues.js, skills/references/effect-system.js, skills/references/event-driven-cqrs.js, skills/references/functions-and-triggers.js, skills/references/http-endpoints.js, skills/references/http-invoked-functions.js, skills/references/low-code-automation.js, skills/references/observability.js, skills/references/queue-processing.js, skills/references/reactive-backend.js, skills/references/realtime-streams.js, skills/references/state-management.js, skills/references/state-reactions.js, skills/references/trigger-actions.js, skills/references/trigger-conditions.js, skills/references/workflow-orchestration.js
Working examples demonstrating skill patterns with worker registration, function handlers, trigger binding, state/queue/stream operations, error handling, and cross-function invocation across 20+ patterns.
Python reference implementations
skills/references/channels.py, skills/references/cron-scheduling.py, skills/references/custom-triggers.py, skills/references/dead-letter-queues.py, skills/references/functions-and-triggers.py, skills/references/http-endpoints.py, skills/references/http-invoked-functions.py, skills/references/observability.py, skills/references/queue-processing.py, skills/references/realtime-streams.py, skills/references/state-management.py, skills/references/state-reactions.py, skills/references/trigger-actions.py, skills/references/trigger-conditions.py
Async-based examples using register_worker, InitOptions, iii.trigger_async, TriggerAction variants, with state/queue/stream payload handling, structured logging, and OTEL integration.
Rust reference implementations
skills/references/channels.rs, skills/references/cron-scheduling.rs, skills/references/custom-triggers.rs, skills/references/dead-letter-queues.rs, skills/references/functions-and-triggers.rs, skills/references/http-endpoints.rs, skills/references/http-invoked-functions.rs, skills/references/observability.rs, skills/references/queue-processing.rs, skills/references/realtime-streams.rs, skills/references/state-management.rs, skills/references/state-reactions.rs, skills/references/trigger-actions.rs, skills/references/trigger-conditions.rs
Tokio-based async implementations with serde JSON deserialization, RegisterFunction builders, trigger registration, with error handling, custom tracing/metrics via OpenTelemetry, and Ctrl-C shutdown.
Engine configuration reference
skills/references/iii-config.yaml
Complete YAML configuration template documenting HTTP REST module, streaming module, state persistence, queue retry/concurrency, pub/sub, cron scheduling, OpenTelemetry observability (sampling, retention, export), and HTTP-functions security controls.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

The changes consist of homogeneous documentation and reference implementations following consistent patterns across 25+ skill guides and 100+ runnable examples. While large in scope, each file exhibits predictable structure (YAML frontmatter + markdown for skills; boilerplate worker/function registration for implementations), minimal logic density, and repetitive patterns across the three language SDKs. No complex architectural changes, API modifications, or intricate logic require deep analysis—primarily additive content demonstrating established framework patterns.

Possibly related issues

  • SDK audit: cross-language consistency, symmetry & ergonomics (v0.10.0) skills#10 — The SDK audit reported missing Python/Rust references and cross-language inconsistencies (e.g., api_path formatting, cron expression field counts). This PR directly addresses those gaps by adding comprehensive Python and Rust reference implementations alongside JavaScript, standardizing API path conventions (leading slashes), and documenting explicit boundaries and naming rules in AGENTS.md.

Possibly related PRs

Suggested reviewers

  • sergiofilhowz

🐰 Hop! Hop! A thousand docs now bloom so bright,
From state to streams, from dawn to night,
Each skill a skill, each ref a guide,
The engine's heart laid open wide!
✨ 🎯 iii leaps ahead, no more to hide!

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/add-skills-v2

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 13

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

🟡 Minor comments (12)
skills/references/custom-triggers.py-85-111 (1)

85-111: ⚠️ Potential issue | 🟡 Minor

Handle 304 Not Modified responses via HTTPError exception.

urllib.request.urlopen() raises urllib.error.HTTPError for 304 responses instead of returning a response object. The resp.status == 304 check at line 92 is unreachable, causing 304 responses to be caught by the generic exception handler and logged as failures instead of being handled normally.

Wrap the urlopen() call in a try-except block and check the exception code:

Suggested fix
async def _poll_loop(trigger_id, function_id, url, interval_ms):
    import urllib.request
+   import urllib.error
    import json

    last_etag = None
    interval_s = interval_ms / 1000

    while True:
        try:
            req = urllib.request.Request(url, method="GET")
            if last_etag:
                req.add_header("If-None-Match", last_etag)

-           resp = await asyncio.to_thread(urllib.request.urlopen, req)
-
-           if resp.status == 304:
-               await asyncio.sleep(interval_s)
-               continue
+           try:
+               resp = await asyncio.to_thread(urllib.request.urlopen, req)
+           except urllib.error.HTTPError as err:
+               if err.code == 304:
+                   await asyncio.sleep(interval_s)
+                   continue
+               raise

            etag = resp.headers.get("ETag")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/references/custom-triggers.py` around lines 85 - 111, The polling loop
currently expects resp.status == 304 but urllib.request.urlopen raises
urllib.error.HTTPError for 304, so wrap the call to
asyncio.to_thread(urllib.request.urlopen, req) in a try/except that catches
urllib.error.HTTPError (importing urllib.error if needed), and if the caught
exception has .code == 304 perform the same await asyncio.sleep(interval_s) and
continue; for other HTTPError codes re-raise or log as before so they hit the
existing generic exception handler. Update the logic around last_etag/etag
handling in the try block (the resp.status check is unreachable) so successful
responses continue to parse resp and call iii.trigger_async as before.
skills/references/workflow-orchestration.js-219-223 (1)

219-223: ⚠️ Potential issue | 🟡 Minor

Let the generated order_id win in the enqueue payload.

Line 221 spreads data after order_id, so a caller-supplied data.order_id overrides the ID you just persisted and return. That can make the queued workflow mutate a different record than the one created.

🛠️ Suggested fix
-    payload: { order_id, ...data, force_payment_failure, fail_until_payment_attempt },
+    payload: { ...data, order_id, force_payment_failure, fail_until_payment_attempt },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/references/workflow-orchestration.js` around lines 219 - 223, The
payload currently spreads data after order_id so a caller-provided data.order_id
can override the persisted ID; update the enqueue payload in the iii.trigger
call (function iii.trigger, variable enqueueResult) so the authoritative
order_id wins — either spread data first and then set order_id last, or
delete/order_id from data before spreading — ensuring the payload contains the
generated order_id value passed to the queued workflow.
skills/references/http-endpoints.js-92-100 (1)

92-100: ⚠️ Potential issue | 🟡 Minor

Update response omits the actual persisted shape.

Line 99 returns { id, ...updates }, but Line 92 also writes updated_at and unchanged fields remain in state. Returning the stored object would keep the example behavior consistent for clients.

💡 Proposed fix
   await iii.trigger({
     function_id: 'state::update',
     payload: { scope: 'users', key: id, ops },
   })

-  return { status_code: 200, body: { id, ...updates } }
+  const updated = await iii.trigger({
+    function_id: 'state::get',
+    payload: { scope: 'users', key: id },
+  })
+
+  return { status_code: 200, body: updated }
 })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/references/http-endpoints.js` around lines 92 - 100, The response
currently returns { id, ...updates } which omits persisted fields (like
updated_at) and unchanged data; after calling iii.trigger with function_id:
'state::update' (using ops, id, updates), fetch the stored record via
iii.trigger with function_id: 'state::get' (scope: 'users', key: id) and return
that full persisted object in the response body so clients receive the actual
saved shape.
skills/iii-observability/SKILL.md-44-49 (1)

44-49: ⚠️ Potential issue | 🟡 Minor

Fix awkward sentence split in reference implementation section.

The sentence starting on line 44 is interrupted by the Python and Rust reference links (lines 46-48) and then completed on line 49, making it difficult to read. Restructure so the sentence completes before listing the alternative language implementations.

📝 Suggested restructure
-See [../references/observability.js](../references/observability.js) for the full working example — a worker with custom spans,
-
-Also available in **Python**: [../references/observability.py](../references/observability.py)
-
-Also available in **Rust**: [../references/observability.rs](../references/observability.rs)
-metrics counters, trace propagation, and log subscriptions connected to an OTel collector.
+See [../references/observability.js](../references/observability.js) for the full working example — a worker with custom spans, metrics counters, trace propagation, and log subscriptions connected to an OTel collector.
+
+Also available in **Python**: [../references/observability.py](../references/observability.py)
+
+Also available in **Rust**: [../references/observability.rs](../references/observability.rs)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/iii-observability/SKILL.md` around lines 44 - 49, The README text in
SKILL.md splits one sentence across the JS reference and the Python/Rust links
causing awkward flow; update the paragraph so the complete sentence describing
the example ("a worker with custom spans, metrics counters, trace propagation,
and log subscriptions connected to an OTel collector") follows the JS reference
link (../references/observability.js) and then list the alternate
implementations ("Also available in Python: ../references/observability.py" and
"Also available in Rust: ../references/observability.rs") on their own lines,
ensuring the description is one coherent sentence before the language links.
skills/iii-dead-letter-queues/SKILL.md-40-45 (1)

40-45: ⚠️ Potential issue | 🟡 Minor

Fix awkward sentence split in reference implementation section.

Similar to the observability skill, the sentence starting on line 40 is interrupted by the Python and Rust reference links and completed on line 45, disrupting readability.

📝 Suggested restructure
-See [../references/dead-letter-queues.js](../references/dead-letter-queues.js) for the full working example — inspecting DLQ status,
-
-Also available in **Python**: [../references/dead-letter-queues.py](../references/dead-letter-queues.py)
-
-Also available in **Rust**: [../references/dead-letter-queues.rs](../references/dead-letter-queues.rs)
-redriving failed jobs via SDK and CLI, and configuring retry behavior.
+See [../references/dead-letter-queues.js](../references/dead-letter-queues.js) for the full working example — inspecting DLQ status, redriving failed jobs via SDK and CLI, and configuring retry behavior.
+
+Also available in **Python**: [../references/dead-letter-queues.py](../references/dead-letter-queues.py)
+
+Also available in **Rust**: [../references/dead-letter-queues.rs](../references/dead-letter-queues.rs)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/iii-dead-letter-queues/SKILL.md` around lines 40 - 45, The sentence in
SKILL.md is split by the Python and Rust links, breaking readability; rephrase
so the main sentence about the full working example in
../references/dead-letter-queues.js is continuous and then list
language-specific links after it. Concretely, combine the clause "redriving
failed jobs via SDK and CLI, and configuring retry behavior" with the earlier
"inspecting DLQ status" phrase in the ../references/dead-letter-queues.js
sentence (the line referencing references/dead-letter-queues.js) and then put
"Also available in Python: ../references/dead-letter-queues.py" and "Also
available in Rust: ../references/dead-letter-queues.rs" as separate trailing
sentences or items.
skills/references/trigger-conditions.rs-220-223 (1)

220-223: ⚠️ Potential issue | 🟡 Minor

Update to use shutdown_async().await for proper telemetry flush.

shutdown() is deprecated in the Rust SDK. Use shutdown_async().await instead to guarantee telemetry is flushed before process exit. Move it inside the async block where it can be awaited:

Change
-    tokio::runtime::Runtime::new().unwrap().block_on(async {
-        tokio::signal::ctrl_c().await.ok();
-    });
-    iii.shutdown();
+    tokio::runtime::Runtime::new().unwrap().block_on(async {
+        tokio::signal::ctrl_c().await.ok();
+        iii.shutdown_async().await;
+    });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/references/trigger-conditions.rs` around lines 220 - 223, The shutdown
call currently uses iii.shutdown() after blocking on an async block, which is
deprecated and can drop telemetry; move the shutdown into the async context and
await the async variant: inside the
tokio::runtime::Runtime::new().unwrap().block_on(async { ... }) block, after
awaiting tokio::signal::ctrl_c().await.ok(), call and await
iii.shutdown_async().await instead of calling iii.shutdown() outside the async
block so telemetry is flushed before exit.
skills/references/http-endpoints.rs-34-52 (1)

34-52: ⚠️ Potential issue | 🟡 Minor

Variable id used after move in json! macro.

The id variable is moved into the json! macro at line 45, then used again at line 52 in logger.info. Since String doesn't implement Copy, this would cause a compile error.

🔧 Proposed fix: Clone `id` before use in json! macro
-                let id = format!("usr-{:x}", std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_nanos());
+                let id = format!("usr-{:x}", std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_nanos());
+                let id_clone = id.clone();

                 let user = json!({
-                    "id": id,
+                    "id": id_clone,
                     "name": req.body.name,
                     "email": req.body.email,
                     "created_at": chrono::Utc::now().to_rfc3339(),
                 });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/references/http-endpoints.rs` around lines 34 - 52, The variable id is
being moved into the json! macro when building user and then reused in
logger.info, causing a move error; fix by ensuring id is cloned when inserted
into the json! payload (or otherwise use a borrowed &id) so the original String
remains available for subsequent use by logger.info and in the TriggerRequest
payload (look for the id variable, the json! call that constructs user, the
TriggerRequest instantiation, and the logger.info call).
skills/iii-getting-started/SKILL.md-141-147 (1)

141-147: ⚠️ Potential issue | 🟡 Minor

Update the skills count to 25.

Line 143 still says 24 skills, but this PR’s own discovery target is 25 iii-* skills, so the copy is already stale.

💡 Suggested fix
-Get all 24 iii skills for your AI coding agent:
+Get all 25 iii skills for your AI coding agent:
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/iii-getting-started/SKILL.md` around lines 141 - 147, Update the text
under the "Install Agent Skills" section: change the phrase "Get all 24 iii
skills for your AI coding agent:" to "Get all 25 iii skills for your AI coding
agent:" so the count matches the repository's current discovery target; this is
the copy near the "Install Agent Skills" heading and the `npx skills add
iii-hq/iii` example.
skills/references/channels.rs-138-140 (1)

138-140: ⚠️ Potential issue | 🟡 Minor

Use tokio::signal::ctrl_c() and update to the non-deprecated shutdown_async() API.

The infinite sleep on line 139 prevents graceful shutdown, and shutdown() is deprecated in the iii Rust SDK. Align with the pattern used in other reference files and production code by listening for a shutdown signal and calling the async shutdown method.

Suggested fix
-    tokio::time::sleep(Duration::from_secs(u64::MAX)).await;
-    iii.shutdown();
+    tokio::signal::ctrl_c().await.ok();
+    iii.shutdown_async().await;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/references/channels.rs` around lines 138 - 140, Replace the infinite
sleep and deprecated synchronous shutdown with a Ctrl-C signal handler and the
async shutdown API: remove
tokio::time::sleep(Duration::from_secs(u64::MAX)).await and instead await
tokio::signal::ctrl_c(), then call iii.shutdown_async().await (or the async
method name provided by the iii SDK) to perform graceful shutdown; ensure any
errors from shutdown_async() are handled or logged.
skills/references/cron-scheduling.rs-255-258 (1)

255-258: ⚠️ Potential issue | 🟡 Minor

Use shutdown_async().await in the Rust exit path.

Line 258 calls the deprecated shutdown() method. Since lines 255–257 already hold a Tokio runtime for Ctrl-C handling, call shutdown_async().await within that async block to ensure telemetry is flushed before process exit.

Suggested fix
-    tokio::runtime::Runtime::new().unwrap().block_on(async {
-        tokio::signal::ctrl_c().await.ok();
-    });
-    iii.shutdown();
+    tokio::runtime::Runtime::new().unwrap().block_on(async move {
+        tokio::signal::ctrl_c().await.ok();
+        iii.shutdown_async().await;
+    });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/references/cron-scheduling.rs` around lines 255 - 258, The exit path
currently creates a Tokio runtime to await Ctrl-C then calls the deprecated
synchronous iii.shutdown(); move the shutdown to the async context and call the
async flush method by invoking iii.shutdown_async().await inside the same
tokio::runtime::Runtime::new().unwrap().block_on(async { ... }) block (after
tokio::signal::ctrl_c().await.ok()) so telemetry is flushed before process exit;
replace the iii.shutdown() call with that async call and remove the synchronous
shutdown usage.
skills/references/state-reactions.rs-108-109 (1)

108-109: ⚠️ Potential issue | 🟡 Minor

Type error: and_then closure doesn't return Option

This line will not compile. v["customer"].clone() returns serde_json::Value, and calling .into() on it doesn't convert to Option<_>. The and_then combinator expects the closure to return Option<T>.

🐛 Proposed fix
-                let customer = event.new_value.as_ref().and_then(|v| v["customer"].clone().into());
+                let customer = event.new_value.as_ref().and_then(|v| v.get("customer").cloned());
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/references/state-reactions.rs` around lines 108 - 109, The second line
uses and_then but the closure returns a serde_json::Value (via
v["customer"].clone().into()) instead of an Option, causing a type error; change
the combinator to map or use v.get("customer").cloned() (which returns
Option<Value>) and then convert that Value to the desired Rust type (e.g., use
as_str().map(|s| s.to_string()) or as_i64()/as_f64() as appropriate) so that
customer is an Option of the correct type; update the expression that assigns
customer (which reads event.new_value) accordingly.
skills/references/http-invoked-functions.js-189-193 (1)

189-193: ⚠️ Potential issue | 🟡 Minor

Fix cron expression format inconsistency across reference files.

The codebase mixes two cron expression formats:

  • 7-field (used in http-invoked-functions.js): '0 0 * * * * *' (second, minute, hour, day, month, weekday, year)
  • 5-field (used in dead-letter-queues.js): '0 * * * *' (minute, hour, day, month, weekday)

The cron-scheduling.py documentation explicitly defines the iii engine as using 7-field cron expressions. Align all reference files to use the 7-field format consistently, including dead-letter-queues.js and dead-letter-queues.py.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/references/http-invoked-functions.js` around lines 189 - 193, The
project mixes 5-field and 7-field cron formats; update any iii.registerTrigger
cron expressions (e.g., the config.expression used with type: 'cron' and
function_id like 'integrations::hourly-heartbeat') to the engine's 7-field
format (second minute hour day month weekday year). For each 5-field string
(minute hour day month weekday) in dead-letter-queues.js and
dead-letter-queues.py, convert it by prefixing a "0" for seconds and appending "
*" for the year (e.g., "0 * * * *" -> "0 0 * * * * *") so all references use the
7-field format consistently.
🧹 Nitpick comments (15)
skills/references/realtime-streams.rs (1)

364-367: Prefer shutdown_async().await in the Rust reference example.

The current example uses the deprecated synchronous API. Moving shutdown_async() into the existing Tokio block keeps the reference aligned with the SDK's recommended pattern and guarantees telemetry flush before process exit.

🛠️ Suggested update
-    tokio::runtime::Runtime::new().unwrap().block_on(async {
-        tokio::signal::ctrl_c().await.ok();
-    });
-    iii.shutdown();
+    let iii = iii.clone();
+    tokio::runtime::Runtime::new().unwrap().block_on(async move {
+        tokio::signal::ctrl_c().await.ok();
+        iii.shutdown_async().await;
+    });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/references/realtime-streams.rs` around lines 364 - 367, Replace the
synchronous shutdown call with the async shutdown helper: inside the existing
Tokio block (the tokio::runtime::Runtime::new().unwrap().block_on(async { ...
})), await the SDK's async shutdown method instead of calling iii.shutdown();
specifically call iii.shutdown_async().await from within that async block so the
shutdown runs on the runtime and guarantees telemetry flush before exit.
skills/iii-trigger-actions/SKILL.md (1)

39-44: Reference paragraph is hard to parse.

Like the other skill docs, this reads better as a compact list (JS, Python, Rust) instead of one sentence interrupted by language links.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/iii-trigger-actions/SKILL.md` around lines 39 - 44, Replace the
one-line reference paragraph with a compact list of language-specific links to
improve readability: convert the sentence that mixes links into three short
items (JS: ../references/trigger-actions.js, Python:
../references/trigger-actions.py, Rust: ../references/trigger-actions.rs) and
append a brief clarifying phrase like "invocation modes showing sync, void, and
enqueue patterns" so the SKILL.md paragraph reads as a concise list referencing
those files.
skills/iii-trigger-conditions/SKILL.md (1)

39-45: Reference section reads as a broken sentence.

The sentence started on Line 39 is interrupted by the “Also available…” lines and resumes on Line 44. Consider converting these into a short bullet list for cleaner scanability.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/iii-trigger-conditions/SKILL.md` around lines 39 - 45, The reference
paragraph in SKILL.md is broken by inline “Also available…” lines; change it to
one clear sentence describing the example (e.g., "See
../references/trigger-conditions.js for the full working example where a
business rule function filters events before the handler processes them.")
followed by a short bullet list of language variants referencing
trigger-conditions.js, trigger-conditions.py, and trigger-conditions.rs; ensure
the clause "where a business rule function filters events before the handler
processes them" remains attached to the main sentence rather than split across
the list.
AGENTS.md (1)

39-50: Specify language for fenced code block.

The project map code block should have a language identifier for clarity and to satisfy markdown linters. Use text or plaintext for non-code content.

📝 Proposed fix
 ## Project Map
 
-```
+```text
 engine/                   Rust engine — runtime, modules, protocol, CLI
 sdk/packages/node/iii/    TypeScript SDK (npm: iii-sdk)
 sdk/packages/python/iii/  Python SDK (PyPI: iii-sdk)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@AGENTS.md` around lines 39 - 50, Add a language identifier to the fenced code
block in the AGENTS.md snippet so markdown linters and renderers treat it as
plain text; change the opening ``` to ```text (or ```plaintext) for the project
map block so the block contains ` ```text` followed by the same lines shown in
the diff and the closing ``` remains unchanged.
skills/references/observability.rs (1)

272-278: Use the async shutdown API in the cleanup path.

This section explicitly teaches clean teardown. Ending with deprecated iii.shutdown() points readers at the obsolete lifecycle API.

Suggested edit
     tokio::runtime::Runtime::new().unwrap().block_on(async {
         tokio::signal::ctrl_c().await.ok();
 
         #[cfg(feature = "otel")]
         shutdown_otel().await;
-    });
-    iii.shutdown();
+
+        iii.shutdown_async().await;
+    });

The Rust SDK has deprecated shutdown() in favor of shutdown_async().await to guarantee telemetry flush before the process exits. Since this example covers observability specifically, showing the correct shutdown pattern is important.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/references/observability.rs` around lines 272 - 278, The code
currently calls the deprecated synchronous API iii.shutdown() after the
tokio::runtime::Runtime::new().unwrap().block_on(...) block; replace this with
the async shutdown API by moving the SDK shutdown into the async block and
calling iii.shutdown_async().await (alongside the existing #[cfg(feature =
"otel")] shutdown_otel().await) so the shutdown is awaited and telemetry is
flushed before exit; remove the deprecated iii.shutdown() call and ensure the
call site is inside the block_on async context where
tokio::signal::ctrl_c().await is handled.
skills/references/state-management.rs (1)

7-10: Unused import: TriggerAction.

TriggerAction is imported but never used in this file. All trigger calls use action: None.

🧹 Remove unused import
 use iii_sdk::{
-    register_worker, InitOptions, RegisterFunction, TriggerRequest, TriggerAction,
+    register_worker, InitOptions, RegisterFunction, TriggerRequest,
     builtin_triggers::*, IIITrigger, Logger,
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/references/state-management.rs` around lines 7 - 10, Remove the unused
import TriggerAction from the use list in this module; locate the import
statement that declares TriggerAction alongside register_worker, InitOptions,
RegisterFunction, TriggerRequest, builtin_triggers::*, IIITrigger, and Logger
and delete TriggerAction since all trigger calls use action: None and the symbol
is never referenced (ensure no other code in this file relies on TriggerAction
before removing).
skills/iii-agentic-backend/SKILL.md (1)

41-41: Consider consistent backtick formatting for primitives.

The table cell trigger \state::set`, `state::get`, `state::update`` doesn't wrap "trigger" in backticks, while line 58 has the same inconsistency. For consistency with other primitive references in the table, consider:

📝 Suggested formatting fix
-| trigger `state::set`, `state::get`, `state::update`                 | Shared context between agents                |
+| `trigger` `state::set`, `state::get`, `state::update`               | Shared context between agents                |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/iii-agentic-backend/SKILL.md` at line 41, Wrap the word "trigger" in
backticks to match the existing primitive formatting for the entries that
reference state operations: change the table cell that currently reads trigger
`state::set`, `state::get`, `state::update` to use backticks around "trigger"
(i.e., `trigger` `state::set`, `state::get`, `state::update`), and make the same
change for the similar inconsistent occurrence later (the other table cell on
line ~58) so all primitive references are consistently backticked.
skills/references/dead-letter-queues.rs (1)

230-233: Add an optional inline comment about the deprecated shutdown pattern instead of requiring code changes.

Line 233 uses the deprecated shutdown() method; for educational clarity, consider adding a comment that shutdown_async().await is preferred in production code but omitted here for reference simplicity.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/references/dead-letter-queues.rs` around lines 230 - 233, The call to
iii.shutdown() uses the deprecated synchronous shutdown pattern; instead of
changing behavior, add a brief inline comment next to the iii.shutdown()
invocation explaining that this synchronous shutdown is used here for brevity
and that production code should call the async variant (e.g.,
shutdown_async().await) after awaiting tokio::signal::ctrl_c() — reference the
iii.shutdown() call and the preferred shutdown_async().await method so readers
know the recommended replacement.
skills/references/http-endpoints.py (1)

18-18: Unused import: TriggerAction

TriggerAction is imported but not used in this file. Consider removing it to keep the example focused.

🔧 Suggested fix
-from iii import InitOptions, Logger, TriggerAction, register_worker
+from iii import InitOptions, Logger, register_worker
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/references/http-endpoints.py` at line 18, The import list in this
module includes an unused symbol TriggerAction; remove TriggerAction from the
import statement (leaving InitOptions, Logger, register_worker) to eliminate the
unused import and keep the example focused—update the import line that currently
names TriggerAction so it no longer references that symbol.
skills/references/queue-processing.rs (1)

23-23: Unused import: Duration

std::time::Duration is imported but not used in this file.

🔧 Suggested fix
-use std::time::Duration;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/references/queue-processing.rs` at line 23, The import
std::time::Duration in skills/references/queue-processing.rs is unused; either
remove the unused use std::time::Duration; line or actually use Duration where
intended (for example in functions like process_queue or backoff logic) — locate
the use statement and delete it if no timing functionality is needed, or
replace/add references to Duration in the relevant function (e.g.,
process_queue, handle_item, or retry/backoff helpers) so the import is utilized.
skills/references/state-management.js (1)

12-12: Unused import: TriggerAction

TriggerAction is imported but not used in this file. Consider removing it.

🔧 Suggested fix
-import { registerWorker, Logger, TriggerAction } from 'iii-sdk'
+import { registerWorker, Logger } from 'iii-sdk'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/references/state-management.js` at line 12, The import list in
skills/references/state-management.js includes an unused symbol TriggerAction;
remove TriggerAction from the import statement (leaving registerWorker and
Logger) to eliminate the unused import and any linter warnings, i.e., update the
import line that currently references TriggerAction so it only imports the
actually used identifiers.
skills/references/http-invoked-functions.rs (1)

20-20: Unused import: Duration

std::time::Duration is imported but not used in this file.

🔧 Suggested fix
-use std::time::Duration;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/references/http-invoked-functions.rs` at line 20, The file currently
imports std::time::Duration but never uses it; remove the unused import
statement to eliminate the warning. Locate the top-level use declaration
referencing Duration in http-invoked-functions.rs and delete the "use
std::time::Duration;" line (or if you actually intended to use Duration, add the
proper usage where needed in the function that requires timing). Ensure no other
references to Duration remain so the file compiles cleanly.
skills/references/state-reactions.rs (1)

13-13: Unused import: Duration

std::time::Duration is imported but not used in this file.

🔧 Suggested fix
-use std::time::Duration;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/references/state-reactions.rs` at line 13, Remove the unused import of
std::time::Duration (the Duration symbol) from the top of the file (the use
std::time::Duration; line) or, if you intended to use it, actually reference
Duration in the relevant code path; otherwise simply delete the import to
resolve the unused-import warning.
skills/references/observability.py (1)

154-156: Consider using asyncio.new_event_loop() for Python 3.10+ compatibility.

asyncio.get_event_loop() is deprecated when called without a running event loop (Python 3.10+). In a signal handler context, this may raise warnings or fail. A safer pattern:

♻️ Suggested fix
 def _shutdown(signum, frame):
-    asyncio.get_event_loop().run_until_complete(iii.shutdown_otel())
+    loop = asyncio.new_event_loop()
+    loop.run_until_complete(iii.shutdown_otel())
+    loop.close()
     raise SystemExit(0)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/references/observability.py` around lines 154 - 156, The signal
handler _shutdown currently calls asyncio.get_event_loop(), which is deprecated
without a running loop; change it to create a fresh event loop, set it as
current, run the coroutine iii.shutdown_otel() on it, then close the loop:
create a new loop via asyncio.new_event_loop(), call
asyncio.set_event_loop(loop) (or temporarily use loop.run_until_complete without
setting if you prefer), await/ run_until_complete iii.shutdown_otel(), then
loop.close() and finally raise SystemExit(0); this ensures compatibility on
Python 3.10+ and that iii.shutdown_otel() (a coroutine) runs on a proper event
loop.
skills/references/cron-scheduling.py (1)

61-65: 7-field cron expressions used consistently here — but inconsistent with JS references.

This file correctly uses 7-field cron expressions throughout (second minute hour day month weekday year). However, dead-letter-queues.js uses 5-field expressions. Consider aligning all reference files to the same format for consistency.

Per the file's docstring (lines 5-6), the 7-field format is documented, so this file is internally consistent.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/references/cron-scheduling.py` around lines 61 - 65, This cron trigger
uses a 7-field cron expression via iii.register_trigger (function_id
"cron::hourly-cleanup") but other references (notably dead-letter-queues.js) use
5-field expressions; align formats across the repo by updating the 5-field
expressions in dead-letter-queues.js to the documented 7-field format (second
minute hour day month weekday year) so all cron expressions follow the docstring
convention — locate cron expressions in dead-letter-queues.js and expand them to
include seconds (and year if used elsewhere) matching the pattern used by
iii.register_trigger.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@skills/iii-custom-triggers/SKILL.md`:
- Around line 19-20: The SKILL.md uses the deprecated positional call
iii.trigger(function_id, event); update every usage (lines referencing
iii.trigger) to the structured request form iii.trigger({ function_id, payload
}) so the SDK accepts the call; search for all occurrences of iii.trigger and
replace the positional signature with the object form using keys function_id and
payload, and verify any examples/reference text mention the request-object shape
rather than the old positional parameters; keep mention of unregisterTriggerType
as-is.

In `@skills/iii-effect-system/SKILL.md`:
- Around line 3-6: The activation description in SKILL.md currently mentions
"retry", which biases selection for retry/DLQ scenarios; remove the word "retry"
from the description paragraph (the description: "Builds composable, pipeable
function chains... retry.") and replace or rephrase to clarify synchronous
trigger chaining (e.g., mention failures stop the pipeline) and, if needed, add
a note directing retry-heavy flows to use TriggerAction.Enqueue({ queue }) or
the iii-workflow-orchestration skill instead; update the description string in
SKILL.md accordingly.

In `@skills/iii-getting-started/SKILL.md`:
- Around line 47-50: Replace the npm install instruction in the code block in
SKILL.md (the TypeScript / Node.js example) with the pnpm equivalent: change the
command that installs iii-sdk to use pnpm install and update any surrounding
text if it references npm; ensure the example follows repo guideline "Use pnpm
(never npm) for JS/TS" so the TypeScript/Node.js install step consistently uses
pnpm across the getting-started skill.

In `@skills/iii-node-sdk/SKILL.md`:
- Around line 16-18: The Install snippet in SKILL.md currently uses "npm install
iii-sdk"; update the documentation to follow repo convention by replacing that
command with the pnpm equivalent ("pnpm add iii-sdk") in the Install section and
any other occurrences; ensure the Install code block under the "## Install"
heading and any related examples use pnpm (not npm) to comply with the "Use pnpm
(never npm) for JS/TS" guideline.
- Around line 20-35: Rename the section title from "Key Exports" to "Key APIs"
and update the table to reflect which functions are top-level vs instance
methods: keep registerWorker(...) as a top-level export but prefix the instance
SDK methods with "iii." — specifically change registerFunction({ id }, handler)
to iii.registerFunction(...), registerTrigger({ type, function_id, config }) to
iii.registerTrigger(...), createStream(name, adapter) to iii.createStream(...),
and registerTriggerType(id, handler) to iii.registerTriggerType(...); leave
other symbols (trigger, TriggerAction.Void/Enqueue, Logger, withSpan, getTracer,
getMeter, createChannel) as-is but verify their ownership is correct in the
table.

In `@skills/references/channels.js`:
- Around line 58-84: The consumer registered in pipeline::consume incorrectly
uses the raw serializable ref (data.readerRef) instead of constructing a
ChannelReader; change the assignment for reader to instantiate the SDK reader
(e.g. new ChannelReader(iii.address, data.readerRef)) so subsequent calls to
reader.onMessage and reader.readAll work; keep the Logger usage and rest of the
processing logic the same but replace the direct ref usage with the
ChannelReader constructor to properly reconstruct the reader.

In `@skills/references/dead-letter-queues.py`:
- Around line 180-184: The cron expression passed to iii.register_trigger for
function_id "admin::auto-redrive" uses a 5-field format ("0 * * * *") but the
engine requires a 7-field expression under the config key "expression". Update
the config.expression to a 7-field cron string (sec min hour dom month dow year)
that matches the original intent (e.g., run at second 0, minute 0 of every hour)
so the trigger registration in iii.register_trigger uses the correct 7-field
format.

In `@skills/references/functions-and-triggers.rs`:
- Around line 167-203: The code currently moves input.order_id and input.items
into the first json! call, causing later uses to fail; fix by extracting owned
copies or references before the first trigger (e.g., let order_id =
input.order_id.clone(); let items = input.items.clone(); or
&input.order_id/&input.items if lifetimes allow) and then use those variables in
all TriggerRequest payloads (used with iii.trigger and TriggerRequest for
"orders::validate", "notifications::on-order-complete", and "orders::fulfill")
so no field is moved more than once.

In `@skills/references/http-invoked-functions.py`:
- Around line 158-162: The state trigger registered via iii.register_trigger for
"integrations::order-webhook" is filtering on the wrong key ("status") so it
never matches the state writes that use data["orderId"]; update the trigger's
config to use the same key used when writing state (i.e., the orderId key)
instead of "status" — replace "key": "status" with the same
expression/identifier used at the state write sites (data["orderId"] or its
evaluated value) so register_trigger(...) will match the state updates sent to
integrations::order-webhook.

In `@skills/references/queue-processing.js`:
- Around line 85-90: The example fires a notification to function_id
'notifications::send' but that function isn't registered in this standalone
reference, breaking end-to-end execution; fix by registering a local handler
named 'notifications::send' (or replace the trigger target with an inline/no-op
handler) in this file so the iii.trigger call has a reachable function: add a
registration using the same runtime API used elsewhere in the file (e.g.,
iii.registerFunction or iii.handle) with an async handler that accepts the
payload ({ type, orderId, chargeId }) and performs the illustrative action
(log/send) and ensure the handler's signature and behavior match the
fire-and-forget TriggerAction.Void() usage.

In `@skills/references/trigger-conditions.js`:
- Around line 140-146: The cron expression in the iii.registerTrigger call for
function_id 'reports::weekday-digest' uses a 5-field format ("0 8 * * *");
update the config.expression to the required 7-field format "0 0 8 * * * *" (sec
min hour dom month dow year) so the trigger conforms to the iii-cron-scheduling
skill; keep the rest of the registerTrigger block (type, function_id,
condition_function_id) unchanged.
- Around line 27-35: The condition and handler are using the wrong payload field
(data.value) — state-trigger events use { new_value, old_value, key, event_type
} — so update the functions registered as 'conditions::is-high-value' and
'orders::flag-high-value' to read totals from data.new_value (not data.value)
and use optional chaining (e.g., data.new_value?.total) to avoid crashes when
new_value is missing; ensure the logger uses data.key and data.new_value?.total
for safe logging.

In `@skills/references/trigger-conditions.rs`:
- Around line 211-215: The cron expression passed to CronTriggerConfig::new in
the iii.register_trigger call uses a 5-field format ("0 8 * * *"); update it to
the engine's 7-field format (second minute hour day month weekday year) used by
CronTriggerConfig, e.g. change the string to include seconds and year
placeholders like "0 0 8 * * * *" in the IIITrigger::Cron /
CronTriggerConfig::new invocation where .condition("conditions::is-weekday") is
set.

---

Minor comments:
In `@skills/iii-dead-letter-queues/SKILL.md`:
- Around line 40-45: The sentence in SKILL.md is split by the Python and Rust
links, breaking readability; rephrase so the main sentence about the full
working example in ../references/dead-letter-queues.js is continuous and then
list language-specific links after it. Concretely, combine the clause "redriving
failed jobs via SDK and CLI, and configuring retry behavior" with the earlier
"inspecting DLQ status" phrase in the ../references/dead-letter-queues.js
sentence (the line referencing references/dead-letter-queues.js) and then put
"Also available in Python: ../references/dead-letter-queues.py" and "Also
available in Rust: ../references/dead-letter-queues.rs" as separate trailing
sentences or items.

In `@skills/iii-getting-started/SKILL.md`:
- Around line 141-147: Update the text under the "Install Agent Skills" section:
change the phrase "Get all 24 iii skills for your AI coding agent:" to "Get all
25 iii skills for your AI coding agent:" so the count matches the repository's
current discovery target; this is the copy near the "Install Agent Skills"
heading and the `npx skills add iii-hq/iii` example.

In `@skills/iii-observability/SKILL.md`:
- Around line 44-49: The README text in SKILL.md splits one sentence across the
JS reference and the Python/Rust links causing awkward flow; update the
paragraph so the complete sentence describing the example ("a worker with custom
spans, metrics counters, trace propagation, and log subscriptions connected to
an OTel collector") follows the JS reference link
(../references/observability.js) and then list the alternate implementations
("Also available in Python: ../references/observability.py" and "Also available
in Rust: ../references/observability.rs") on their own lines, ensuring the
description is one coherent sentence before the language links.

In `@skills/references/channels.rs`:
- Around line 138-140: Replace the infinite sleep and deprecated synchronous
shutdown with a Ctrl-C signal handler and the async shutdown API: remove
tokio::time::sleep(Duration::from_secs(u64::MAX)).await and instead await
tokio::signal::ctrl_c(), then call iii.shutdown_async().await (or the async
method name provided by the iii SDK) to perform graceful shutdown; ensure any
errors from shutdown_async() are handled or logged.

In `@skills/references/cron-scheduling.rs`:
- Around line 255-258: The exit path currently creates a Tokio runtime to await
Ctrl-C then calls the deprecated synchronous iii.shutdown(); move the shutdown
to the async context and call the async flush method by invoking
iii.shutdown_async().await inside the same
tokio::runtime::Runtime::new().unwrap().block_on(async { ... }) block (after
tokio::signal::ctrl_c().await.ok()) so telemetry is flushed before process exit;
replace the iii.shutdown() call with that async call and remove the synchronous
shutdown usage.

In `@skills/references/custom-triggers.py`:
- Around line 85-111: The polling loop currently expects resp.status == 304 but
urllib.request.urlopen raises urllib.error.HTTPError for 304, so wrap the call
to asyncio.to_thread(urllib.request.urlopen, req) in a try/except that catches
urllib.error.HTTPError (importing urllib.error if needed), and if the caught
exception has .code == 304 perform the same await asyncio.sleep(interval_s) and
continue; for other HTTPError codes re-raise or log as before so they hit the
existing generic exception handler. Update the logic around last_etag/etag
handling in the try block (the resp.status check is unreachable) so successful
responses continue to parse resp and call iii.trigger_async as before.

In `@skills/references/http-endpoints.js`:
- Around line 92-100: The response currently returns { id, ...updates } which
omits persisted fields (like updated_at) and unchanged data; after calling
iii.trigger with function_id: 'state::update' (using ops, id, updates), fetch
the stored record via iii.trigger with function_id: 'state::get' (scope:
'users', key: id) and return that full persisted object in the response body so
clients receive the actual saved shape.

In `@skills/references/http-endpoints.rs`:
- Around line 34-52: The variable id is being moved into the json! macro when
building user and then reused in logger.info, causing a move error; fix by
ensuring id is cloned when inserted into the json! payload (or otherwise use a
borrowed &id) so the original String remains available for subsequent use by
logger.info and in the TriggerRequest payload (look for the id variable, the
json! call that constructs user, the TriggerRequest instantiation, and the
logger.info call).

In `@skills/references/http-invoked-functions.js`:
- Around line 189-193: The project mixes 5-field and 7-field cron formats;
update any iii.registerTrigger cron expressions (e.g., the config.expression
used with type: 'cron' and function_id like 'integrations::hourly-heartbeat') to
the engine's 7-field format (second minute hour day month weekday year). For
each 5-field string (minute hour day month weekday) in dead-letter-queues.js and
dead-letter-queues.py, convert it by prefixing a "0" for seconds and appending "
*" for the year (e.g., "0 * * * *" -> "0 0 * * * * *") so all references use the
7-field format consistently.

In `@skills/references/state-reactions.rs`:
- Around line 108-109: The second line uses and_then but the closure returns a
serde_json::Value (via v["customer"].clone().into()) instead of an Option,
causing a type error; change the combinator to map or use
v.get("customer").cloned() (which returns Option<Value>) and then convert that
Value to the desired Rust type (e.g., use as_str().map(|s| s.to_string()) or
as_i64()/as_f64() as appropriate) so that customer is an Option of the correct
type; update the expression that assigns customer (which reads event.new_value)
accordingly.

In `@skills/references/trigger-conditions.rs`:
- Around line 220-223: The shutdown call currently uses iii.shutdown() after
blocking on an async block, which is deprecated and can drop telemetry; move the
shutdown into the async context and await the async variant: inside the
tokio::runtime::Runtime::new().unwrap().block_on(async { ... }) block, after
awaiting tokio::signal::ctrl_c().await.ok(), call and await
iii.shutdown_async().await instead of calling iii.shutdown() outside the async
block so telemetry is flushed before exit.

In `@skills/references/workflow-orchestration.js`:
- Around line 219-223: The payload currently spreads data after order_id so a
caller-provided data.order_id can override the persisted ID; update the enqueue
payload in the iii.trigger call (function iii.trigger, variable enqueueResult)
so the authoritative order_id wins — either spread data first and then set
order_id last, or delete/order_id from data before spreading — ensuring the
payload contains the generated order_id value passed to the queued workflow.

---

Nitpick comments:
In `@AGENTS.md`:
- Around line 39-50: Add a language identifier to the fenced code block in the
AGENTS.md snippet so markdown linters and renderers treat it as plain text;
change the opening ``` to ```text (or ```plaintext) for the project map block so
the block contains ` ```text` followed by the same lines shown in the diff and
the closing ``` remains unchanged.

In `@skills/iii-agentic-backend/SKILL.md`:
- Line 41: Wrap the word "trigger" in backticks to match the existing primitive
formatting for the entries that reference state operations: change the table
cell that currently reads trigger `state::set`, `state::get`, `state::update` to
use backticks around "trigger" (i.e., `trigger` `state::set`, `state::get`,
`state::update`), and make the same change for the similar inconsistent
occurrence later (the other table cell on line ~58) so all primitive references
are consistently backticked.

In `@skills/iii-trigger-actions/SKILL.md`:
- Around line 39-44: Replace the one-line reference paragraph with a compact
list of language-specific links to improve readability: convert the sentence
that mixes links into three short items (JS: ../references/trigger-actions.js,
Python: ../references/trigger-actions.py, Rust:
../references/trigger-actions.rs) and append a brief clarifying phrase like
"invocation modes showing sync, void, and enqueue patterns" so the SKILL.md
paragraph reads as a concise list referencing those files.

In `@skills/iii-trigger-conditions/SKILL.md`:
- Around line 39-45: The reference paragraph in SKILL.md is broken by inline
“Also available…” lines; change it to one clear sentence describing the example
(e.g., "See ../references/trigger-conditions.js for the full working example
where a business rule function filters events before the handler processes
them.") followed by a short bullet list of language variants referencing
trigger-conditions.js, trigger-conditions.py, and trigger-conditions.rs; ensure
the clause "where a business rule function filters events before the handler
processes them" remains attached to the main sentence rather than split across
the list.

In `@skills/references/cron-scheduling.py`:
- Around line 61-65: This cron trigger uses a 7-field cron expression via
iii.register_trigger (function_id "cron::hourly-cleanup") but other references
(notably dead-letter-queues.js) use 5-field expressions; align formats across
the repo by updating the 5-field expressions in dead-letter-queues.js to the
documented 7-field format (second minute hour day month weekday year) so all
cron expressions follow the docstring convention — locate cron expressions in
dead-letter-queues.js and expand them to include seconds (and year if used
elsewhere) matching the pattern used by iii.register_trigger.

In `@skills/references/dead-letter-queues.rs`:
- Around line 230-233: The call to iii.shutdown() uses the deprecated
synchronous shutdown pattern; instead of changing behavior, add a brief inline
comment next to the iii.shutdown() invocation explaining that this synchronous
shutdown is used here for brevity and that production code should call the async
variant (e.g., shutdown_async().await) after awaiting tokio::signal::ctrl_c() —
reference the iii.shutdown() call and the preferred shutdown_async().await
method so readers know the recommended replacement.

In `@skills/references/http-endpoints.py`:
- Line 18: The import list in this module includes an unused symbol
TriggerAction; remove TriggerAction from the import statement (leaving
InitOptions, Logger, register_worker) to eliminate the unused import and keep
the example focused—update the import line that currently names TriggerAction so
it no longer references that symbol.

In `@skills/references/http-invoked-functions.rs`:
- Line 20: The file currently imports std::time::Duration but never uses it;
remove the unused import statement to eliminate the warning. Locate the
top-level use declaration referencing Duration in http-invoked-functions.rs and
delete the "use std::time::Duration;" line (or if you actually intended to use
Duration, add the proper usage where needed in the function that requires
timing). Ensure no other references to Duration remain so the file compiles
cleanly.

In `@skills/references/observability.py`:
- Around line 154-156: The signal handler _shutdown currently calls
asyncio.get_event_loop(), which is deprecated without a running loop; change it
to create a fresh event loop, set it as current, run the coroutine
iii.shutdown_otel() on it, then close the loop: create a new loop via
asyncio.new_event_loop(), call asyncio.set_event_loop(loop) (or temporarily use
loop.run_until_complete without setting if you prefer), await/
run_until_complete iii.shutdown_otel(), then loop.close() and finally raise
SystemExit(0); this ensures compatibility on Python 3.10+ and that
iii.shutdown_otel() (a coroutine) runs on a proper event loop.

In `@skills/references/observability.rs`:
- Around line 272-278: The code currently calls the deprecated synchronous API
iii.shutdown() after the tokio::runtime::Runtime::new().unwrap().block_on(...)
block; replace this with the async shutdown API by moving the SDK shutdown into
the async block and calling iii.shutdown_async().await (alongside the existing
#[cfg(feature = "otel")] shutdown_otel().await) so the shutdown is awaited and
telemetry is flushed before exit; remove the deprecated iii.shutdown() call and
ensure the call site is inside the block_on async context where
tokio::signal::ctrl_c().await is handled.

In `@skills/references/queue-processing.rs`:
- Line 23: The import std::time::Duration in
skills/references/queue-processing.rs is unused; either remove the unused use
std::time::Duration; line or actually use Duration where intended (for example
in functions like process_queue or backoff logic) — locate the use statement and
delete it if no timing functionality is needed, or replace/add references to
Duration in the relevant function (e.g., process_queue, handle_item, or
retry/backoff helpers) so the import is utilized.

In `@skills/references/realtime-streams.rs`:
- Around line 364-367: Replace the synchronous shutdown call with the async
shutdown helper: inside the existing Tokio block (the
tokio::runtime::Runtime::new().unwrap().block_on(async { ... })), await the
SDK's async shutdown method instead of calling iii.shutdown(); specifically call
iii.shutdown_async().await from within that async block so the shutdown runs on
the runtime and guarantees telemetry flush before exit.

In `@skills/references/state-management.js`:
- Line 12: The import list in skills/references/state-management.js includes an
unused symbol TriggerAction; remove TriggerAction from the import statement
(leaving registerWorker and Logger) to eliminate the unused import and any
linter warnings, i.e., update the import line that currently references
TriggerAction so it only imports the actually used identifiers.

In `@skills/references/state-management.rs`:
- Around line 7-10: Remove the unused import TriggerAction from the use list in
this module; locate the import statement that declares TriggerAction alongside
register_worker, InitOptions, RegisterFunction, TriggerRequest,
builtin_triggers::*, IIITrigger, and Logger and delete TriggerAction since all
trigger calls use action: None and the symbol is never referenced (ensure no
other code in this file relies on TriggerAction before removing).

In `@skills/references/state-reactions.rs`:
- Line 13: Remove the unused import of std::time::Duration (the Duration symbol)
from the top of the file (the use std::time::Duration; line) or, if you intended
to use it, actually reference Duration in the relevant code path; otherwise
simply delete the import to resolve the unused-import warning.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: b08fc6c8-3b6c-4cf1-b00c-cc5ff2a4ff97

📥 Commits

Reviewing files that changed from the base of the PR and between b56a528 and a93fb6d.

📒 Files selected for processing (79)
  • AGENTS.md
  • skills/.gitignore
  • skills/LICENSE
  • skills/README.md
  • skills/SKILLS.md
  • skills/iii-agentic-backend/SKILL.md
  • skills/iii-channels/SKILL.md
  • skills/iii-cron-scheduling/SKILL.md
  • skills/iii-custom-triggers/SKILL.md
  • skills/iii-dead-letter-queues/SKILL.md
  • skills/iii-effect-system/SKILL.md
  • skills/iii-engine-config/SKILL.md
  • skills/iii-event-driven-cqrs/SKILL.md
  • skills/iii-functions-and-triggers/SKILL.md
  • skills/iii-getting-started/SKILL.md
  • skills/iii-http-endpoints/SKILL.md
  • skills/iii-http-invoked-functions/SKILL.md
  • skills/iii-low-code-automation/SKILL.md
  • skills/iii-node-sdk/SKILL.md
  • skills/iii-observability/SKILL.md
  • skills/iii-python-sdk/SKILL.md
  • skills/iii-queue-processing/SKILL.md
  • skills/iii-reactive-backend/SKILL.md
  • skills/iii-realtime-streams/SKILL.md
  • skills/iii-rust-sdk/SKILL.md
  • skills/iii-state-management/SKILL.md
  • skills/iii-state-reactions/SKILL.md
  • skills/iii-trigger-actions/SKILL.md
  • skills/iii-trigger-conditions/SKILL.md
  • skills/iii-workflow-orchestration/SKILL.md
  • skills/references/agentic-backend.js
  • skills/references/channels.js
  • skills/references/channels.py
  • skills/references/channels.rs
  • skills/references/cron-scheduling.js
  • skills/references/cron-scheduling.py
  • skills/references/cron-scheduling.rs
  • skills/references/custom-triggers.js
  • skills/references/custom-triggers.py
  • skills/references/custom-triggers.rs
  • skills/references/dead-letter-queues.js
  • skills/references/dead-letter-queues.py
  • skills/references/dead-letter-queues.rs
  • skills/references/effect-system.js
  • skills/references/event-driven-cqrs.js
  • skills/references/functions-and-triggers.js
  • skills/references/functions-and-triggers.py
  • skills/references/functions-and-triggers.rs
  • skills/references/http-endpoints.js
  • skills/references/http-endpoints.py
  • skills/references/http-endpoints.rs
  • skills/references/http-invoked-functions.js
  • skills/references/http-invoked-functions.py
  • skills/references/http-invoked-functions.rs
  • skills/references/iii-config.yaml
  • skills/references/low-code-automation.js
  • skills/references/observability.js
  • skills/references/observability.py
  • skills/references/observability.rs
  • skills/references/queue-processing.js
  • skills/references/queue-processing.py
  • skills/references/queue-processing.rs
  • skills/references/reactive-backend.js
  • skills/references/realtime-streams.js
  • skills/references/realtime-streams.py
  • skills/references/realtime-streams.rs
  • skills/references/state-management.js
  • skills/references/state-management.py
  • skills/references/state-management.rs
  • skills/references/state-reactions.js
  • skills/references/state-reactions.py
  • skills/references/state-reactions.rs
  • skills/references/trigger-actions.js
  • skills/references/trigger-actions.py
  • skills/references/trigger-actions.rs
  • skills/references/trigger-conditions.js
  • skills/references/trigger-conditions.py
  • skills/references/trigger-conditions.rs
  • skills/references/workflow-orchestration.js

Comment on lines +19 to +20
- When the external event fires, call `iii.trigger(function_id, event)` to invoke the registered function
- **unregisterTriggerType** cleans up when the trigger type is no longer needed
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.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -e
# Expect the SDK types/examples to describe a request-object trigger API.
rg -n -C3 '\btrigger\s*\(' sdk/packages/node/iii/src --glob '*.ts'
rg -n -C1 'iii\.trigger\(' skills/iii-custom-triggers/SKILL.md skills/iii-node-sdk/SKILL.md skills/references

Repository: iii-hq/iii

Length of output: 50366


Replace all iii.trigger(function_id, event) calls with the request-object form iii.trigger({ function_id, payload }).

The SDK and all reference examples use the structured request form. This SKILL.md currently shows a deprecated positional signature that will cause runtime errors. Applies to lines 19, 27, 37, 55, and 67.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/iii-custom-triggers/SKILL.md` around lines 19 - 20, The SKILL.md uses
the deprecated positional call iii.trigger(function_id, event); update every
usage (lines referencing iii.trigger) to the structured request form
iii.trigger({ function_id, payload }) so the SDK accepts the call; search for
all occurrences of iii.trigger and replace the positional signature with the
object form using keys function_id and payload, and verify any
examples/reference text mention the request-object shape rather than the old
positional parameters; keep mention of unregisterTriggerType as-is.

Comment on lines +3 to +6
description: >-
Builds composable, pipeable function chains on the iii engine. Use when
building functional pipelines, effect systems, or typed composition layers
where each step is a pure function with distributed tracing and retry.
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.

⚠️ Potential issue | 🟠 Major

Remove retry from the activation description.

This skill is documented as synchronous trigger chaining where failures stop the pipeline. Keeping retry in the description will bias selection toward iii-effect-system for retry/DLQ requests that should land on queue or workflow patterns.

Suggested edit
 description: >-
   Builds composable, pipeable function chains on the iii engine. Use when
   building functional pipelines, effect systems, or typed composition layers
-  where each step is a pure function with distributed tracing and retry.
+  where each step is a pure function with distributed tracing.

As per coding guidelines, skills are activated via the description field, and iii-effect-system should use TriggerAction.Enqueue({ queue }) or iii-workflow-orchestration for retry-heavy flows.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/iii-effect-system/SKILL.md` around lines 3 - 6, The activation
description in SKILL.md currently mentions "retry", which biases selection for
retry/DLQ scenarios; remove the word "retry" from the description paragraph (the
description: "Builds composable, pipeable function chains... retry.") and
replace or rephrase to clarify synchronous trigger chaining (e.g., mention
failures stop the pipeline) and, if needed, add a note directing retry-heavy
flows to use TriggerAction.Enqueue({ queue }) or the iii-workflow-orchestration
skill instead; update the description string in SKILL.md accordingly.

Comment on lines +47 to +50
```bash
# TypeScript / Node.js
npm install iii-sdk

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.

⚠️ Potential issue | 🟠 Major

Document the JS install step with pnpm.

Line 49 teaches npm install iii-sdk, which conflicts with the repo-wide docs/skills rules. The getting-started path should use pnpm for JS/TS examples.

💡 Suggested fix
-# TypeScript / Node.js
-npm install iii-sdk
+# TypeScript / Node.js
+pnpm add iii-sdk

As per coding guidelines, "Use pnpm (never npm) for JS/TS."

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
```bash
# TypeScript / Node.js
npm install iii-sdk
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/iii-getting-started/SKILL.md` around lines 47 - 50, Replace the npm
install instruction in the code block in SKILL.md (the TypeScript / Node.js
example) with the pnpm equivalent: change the command that installs iii-sdk to
use pnpm install and update any surrounding text if it references npm; ensure
the example follows repo guideline "Use pnpm (never npm) for JS/TS" so the
TypeScript/Node.js install step consistently uses pnpm across the
getting-started skill.

Comment on lines +16 to +18
## Install

`npm install iii-sdk`
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.

⚠️ Potential issue | 🟠 Major

Use pnpm in the install snippet.

Line 18 uses npm install iii-sdk, which conflicts with the repo’s JS/TS command convention for skills/docs.

🛠️ Suggested fix
-`npm install iii-sdk`
+`pnpm add iii-sdk`

As per coding guidelines, "Use pnpm (never npm) for JS/TS."

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
## Install
`npm install iii-sdk`
## Install
`pnpm add iii-sdk`
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/iii-node-sdk/SKILL.md` around lines 16 - 18, The Install snippet in
SKILL.md currently uses "npm install iii-sdk"; update the documentation to
follow repo convention by replacing that command with the pnpm equivalent ("pnpm
add iii-sdk") in the Install section and any other occurrences; ensure the
Install code block under the "## Install" heading and any related examples use
pnpm (not npm) to comply with the "Use pnpm (never npm) for JS/TS" guideline.

Comment on lines +20 to +35
## Key Exports

| Export | Purpose |
| ------------------------------------------------ | ------------------------------------------- |
| `registerWorker(url, { workerName })` | Connect to the engine and return the client |
| `registerFunction({ id }, handler)` | Register an async function handler |
| `registerTrigger({ type, function_id, config })` | Bind a trigger to a function |
| `trigger({ function_id, payload, action? })` | Invoke a function |
| `TriggerAction.Void()` | Fire-and-forget invocation mode |
| `TriggerAction.Enqueue({ queue })` | Durable async invocation mode |
| `Logger` | Structured logging |
| `withSpan`, `getTracer`, `getMeter` | OpenTelemetry instrumentation |
| `createChannel()` | Binary streaming between workers |
| `createStream(name, adapter)` | Custom stream implementation |
| `registerTriggerType(id, handler)` | Custom trigger type 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.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -e
# Expect top-level exports and worker methods to be distinct in the Node SDK source.
rg -n -C2 'export .*registerWorker|export .*registerFunction|export .*registerTrigger|export .*createStream|export .*registerTriggerType|export .*createChannel' sdk/packages/node/iii/src --glob '*.ts'
rg -n -C2 '\.registerFunction\s*\(|\.registerTrigger\s*\(|\.createStream\s*\(|\.registerTriggerType\s*\(' skills/references sdk/packages/node/iii/src

Repository: iii-hq/iii

Length of output: 50368


🏁 Script executed:

#!/bin/bash
# Find the ISdk interface definition to confirm these are methods on the worker instance
rg -n "interface ISdk|class.*Sdk" sdk/packages/node/iii/src --glob '*.ts' -A 30 | head -100

Repository: iii-hq/iii

Length of output: 4899


🏁 Script executed:

#!/bin/bash
# Read the SKILL.md file to see the current table
cat -n skills/iii-node-sdk/SKILL.md | sed -n '20,35p'

Repository: iii-hq/iii

Length of output: 1469


🏁 Script executed:

#!/bin/bash
# Get the complete export list from index.ts
cat -n sdk/packages/node/iii/src/index.ts

Repository: iii-hq/iii

Length of output: 1408


🏁 Script executed:

#!/bin/bash
# Check ISdk for createStream and registerTriggerType methods
rg -n "createStream|registerTriggerType" sdk/packages/node/iii/src/types.ts -A 5 | head -50

Repository: iii-hq/iii

Length of output: 1882


Change section title from "Key Exports" to "Key APIs" and prefix instance methods with iii.

The table mixes a top-level export (registerWorker) with instance methods on the SDK object. Lines 25, 26, 32, and 34 list registerFunction, registerTrigger, createStream, and registerTriggerType without the iii. prefix, incorrectly suggesting they are bare imports. All skill examples use iii.registerFunction(...), iii.registerTrigger(...), etc., so the table should reflect this.

Suggested update
-## Key Exports
+## Key APIs

-| `registerFunction({ id }, handler)`              | Register an async function handler          |
-| `registerTrigger({ type, function_id, config })` | Bind a trigger to a function                |
+| `iii.registerFunction({ id }, handler)`          | Register an async function handler          |
+| `iii.registerTrigger({ type, function_id, config })` | Bind a trigger to a function           |
-| `createChannel()`                                | Binary streaming between workers            |
-| `createStream(name, adapter)`                    | Custom stream implementation                |
-| `registerTriggerType(id, handler)`               | Custom trigger type registration            |
+| `iii.createChannel()`                            | Binary streaming between workers            |
+| `iii.createStream(name, adapter)`                | Custom stream implementation                |
+| `iii.registerTriggerType(id, handler)`           | Custom trigger type registration            |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/iii-node-sdk/SKILL.md` around lines 20 - 35, Rename the section title
from "Key Exports" to "Key APIs" and update the table to reflect which functions
are top-level vs instance methods: keep registerWorker(...) as a top-level
export but prefix the instance SDK methods with "iii." — specifically change
registerFunction({ id }, handler) to iii.registerFunction(...),
registerTrigger({ type, function_id, config }) to iii.registerTrigger(...),
createStream(name, adapter) to iii.createStream(...), and
registerTriggerType(id, handler) to iii.registerTriggerType(...); leave other
symbols (trigger, TriggerAction.Void/Enqueue, Logger, withSpan, getTracer,
getMeter, createChannel) as-is but verify their ownership is correct in the
table.

Comment on lines +158 to +162
iii.register_trigger({
"type": "state",
"function_id": "integrations::order-webhook",
"config": {"scope": "orders", "key": "status"},
})
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.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n skills/references/http-invoked-functions.py | sed -n '115,195p'

Repository: iii-hq/iii

Length of output: 3029


🏁 Script executed:

rg "register_trigger.*cron" -A 3

Repository: iii-hq/iii

Length of output: 7626


🏁 Script executed:

find skills/references -name "*.py" -type f | head -20

Repository: iii-hq/iii

Length of output: 584


🏁 Script executed:

rg "register_trigger.*cron" -A 2 skills/references/

Repository: iii-hq/iii

Length of output: 36


🏁 Script executed:

grep -r "expression.*\*" skills/references/ | grep -i cron

Repository: iii-hq/iii

Length of output: 549


🏁 Script executed:

cat -n skills/references/functions-and-triggers.py | grep -A 5 "cron"

Repository: iii-hq/iii

Length of output: 621


🏁 Script executed:

cat -n skills/references/functions-and-triggers.py | sed -n '65,80p'

Repository: iii-hq/iii

Length of output: 749


🏁 Script executed:

cat -n skills/references/cron-scheduling.py

Repository: iii-hq/iii

Length of output: 6307


This state trigger never matches the keys written above.

Lines 123 and 135 write state with key data["orderId"], but line 161 filters on the literal key "status". As written, integrations::order-webhook will never receive those updates.

🩹 Minimal fix
 iii.register_trigger({
     "type": "state",
     "function_id": "integrations::order-webhook",
-    "config": {"scope": "orders", "key": "status"},
+    "config": {"scope": "orders"},
 })
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
iii.register_trigger({
"type": "state",
"function_id": "integrations::order-webhook",
"config": {"scope": "orders", "key": "status"},
})
iii.register_trigger({
"type": "state",
"function_id": "integrations::order-webhook",
"config": {"scope": "orders"},
})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/references/http-invoked-functions.py` around lines 158 - 162, The
state trigger registered via iii.register_trigger for
"integrations::order-webhook" is filtering on the wrong key ("status") so it
never matches the state writes that use data["orderId"]; update the trigger's
config to use the same key used when writing state (i.e., the orderId key)
instead of "status" — replace "key": "status" with the same
expression/identifier used at the state write sites (data["orderId"] or its
evaluated value) so register_trigger(...) will match the state updates sent to
integrations::order-webhook.

Comment on lines +85 to +90
// Fire-and-forget notification
iii.trigger({
function_id: 'notifications::send',
payload: { type: 'payment_captured', orderId: data.orderId, chargeId },
action: TriggerAction.Void(),
})
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.

⚠️ Potential issue | 🟠 Major

Register or replace notifications::send in this example.

Line 86 dispatches notifications::send, but this file never registers that function ID. In a standalone reference, payments::process now depends on some other worker being present, so the example no longer runs end-to-end by itself.

Based on learnings, files under skills/references/ are standalone pattern-demonstration reference files, not production code and not part of any build.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/references/queue-processing.js` around lines 85 - 90, The example
fires a notification to function_id 'notifications::send' but that function
isn't registered in this standalone reference, breaking end-to-end execution;
fix by registering a local handler named 'notifications::send' (or replace the
trigger target with an inline/no-op handler) in this file so the iii.trigger
call has a reachable function: add a registration using the same runtime API
used elsewhere in the file (e.g., iii.registerFunction or iii.handle) with an
async handler that accepts the payload ({ type, orderId, chargeId }) and
performs the illustrative action (log/send) and ensure the handler's signature
and behavior match the fire-and-forget TriggerAction.Void() usage.

Comment on lines +27 to +35
iii.registerFunction({ id: 'conditions::is-high-value' }, async (data) => {
// data is the same event the handler would receive
return data.value?.total > 500
})

// Handler function — only runs when the condition passes
iii.registerFunction({ id: 'orders::flag-high-value' }, async (data) => {
const logger = new Logger()
logger.info('High-value order detected', { key: data.key, total: data.value.total })
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.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify the state-trigger payload shape used in sibling docs/examples.
# Expected: state-trigger docs and sibling references use `new_value`, not `value`.
rg -n -C2 'new_value|old_value|event_type' skills/iii-state-reactions/SKILL.md skills/references/state-reactions.js skills/references/trigger-conditions.rs skills/references/trigger-conditions.js

Repository: iii-hq/iii

Length of output: 11287


Use the state event's new_value field here.

The state-trigger payload is { new_value, old_value, key, event_type }. Accessing data.value causes the condition to fail silently and the handler to crash when accessing the field.

Suggested edit
 iii.registerFunction({ id: 'conditions::is-high-value' }, async (data) => {
   // data is the same event the handler would receive
-  return data.value?.total > 500
+  return data.new_value?.total > 500
 })
 
 // Handler function — only runs when the condition passes
 iii.registerFunction({ id: 'orders::flag-high-value' }, async (data) => {
   const logger = new Logger()
-  logger.info('High-value order detected', { key: data.key, total: data.value.total })
+  logger.info('High-value order detected', { key: data.key, total: data.new_value.total })
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
iii.registerFunction({ id: 'conditions::is-high-value' }, async (data) => {
// data is the same event the handler would receive
return data.value?.total > 500
})
// Handler function — only runs when the condition passes
iii.registerFunction({ id: 'orders::flag-high-value' }, async (data) => {
const logger = new Logger()
logger.info('High-value order detected', { key: data.key, total: data.value.total })
iii.registerFunction({ id: 'conditions::is-high-value' }, async (data) => {
// data is the same event the handler would receive
return data.new_value?.total > 500
})
// Handler function — only runs when the condition passes
iii.registerFunction({ id: 'orders::flag-high-value' }, async (data) => {
const logger = new Logger()
logger.info('High-value order detected', { key: data.key, total: data.new_value.total })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/references/trigger-conditions.js` around lines 27 - 35, The condition
and handler are using the wrong payload field (data.value) — state-trigger
events use { new_value, old_value, key, event_type } — so update the functions
registered as 'conditions::is-high-value' and 'orders::flag-high-value' to read
totals from data.new_value (not data.value) and use optional chaining (e.g.,
data.new_value?.total) to avoid crashes when new_value is missing; ensure the
logger uses data.key and data.new_value?.total for safe logging.

Comment on lines +140 to +146
iii.registerTrigger({
type: 'cron',
function_id: 'reports::weekday-digest',
config: {
expression: '0 8 * * *', // runs daily at 08:00 but condition limits to weekdays
condition_function_id: 'conditions::is-weekday',
},
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.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify the repo's cron guidance and inspect this example.
# Expected: AGENTS.md / iii-cron-scheduling say 7-field cron, while this file still shows 5 fields.
rg -n -C2 '7-field format|second minute hour day month weekday year|expression' AGENTS.md skills/iii-cron-scheduling/SKILL.md
rg -n -C2 "expression: '.*'" skills/references/trigger-conditions.js

Repository: iii-hq/iii

Length of output: 5569


Update cron expression to 7-field format.

The expression at line 144 is 5-field (0 8 * * *), but the codebase requires 7-field format per the iii-cron-scheduling skill documentation. Change to 0 0 8 * * * * (sec min hour dom month dow year).

Suggested edit
   config: {
-    expression: '0 8 * * *', // runs daily at 08:00 but condition limits to weekdays
+    expression: '0 0 8 * * * *', // runs daily at 08:00 but condition limits to weekdays
     condition_function_id: 'conditions::is-weekday',
   },
 })
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
iii.registerTrigger({
type: 'cron',
function_id: 'reports::weekday-digest',
config: {
expression: '0 8 * * *', // runs daily at 08:00 but condition limits to weekdays
condition_function_id: 'conditions::is-weekday',
},
iii.registerTrigger({
type: 'cron',
function_id: 'reports::weekday-digest',
config: {
expression: '0 0 8 * * * *', // runs daily at 08:00 but condition limits to weekdays
condition_function_id: 'conditions::is-weekday',
},
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/references/trigger-conditions.js` around lines 140 - 146, The cron
expression in the iii.registerTrigger call for function_id
'reports::weekday-digest' uses a 5-field format ("0 8 * * *"); update the
config.expression to the required 7-field format "0 0 8 * * * *" (sec min hour
dom month dow year) so the trigger conforms to the iii-cron-scheduling skill;
keep the rest of the registerTrigger block (type, function_id,
condition_function_id) unchanged.

Comment on lines +211 to +215
iii.register_trigger(
IIITrigger::Cron(
CronTriggerConfig::new("0 8 * * *")
.condition("conditions::is-weekday"),
)
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.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify the repo's cron guidance and inspect this example.
# Expected: AGENTS.md / iii-cron-scheduling say 7-field cron, while this file shows a 5-field string.
rg -n -C2 '7-field format|second minute hour day month weekday year|expression' AGENTS.md skills/iii-cron-scheduling/SKILL.md
rg -n -C2 'CronTriggerConfig::new\(".*"\)' skills/references/trigger-conditions.rs

Repository: iii-hq/iii

Length of output: 5536


Switch this cron string to the engine's 7-field format.

This is a 5-field schedule; the repo standard is 7-field format (second minute hour day month weekday year).

Suggested edit
     iii.register_trigger(
         IIITrigger::Cron(
-            CronTriggerConfig::new("0 8 * * *")
+            CronTriggerConfig::new("0 0 8 * * * *")
                 .condition("conditions::is-weekday"),
         )
         .for_function("reports::weekday-digest"),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
iii.register_trigger(
IIITrigger::Cron(
CronTriggerConfig::new("0 8 * * *")
.condition("conditions::is-weekday"),
)
iii.register_trigger(
IIITrigger::Cron(
CronTriggerConfig::new("0 0 8 * * * *")
.condition("conditions::is-weekday"),
)
.for_function("reports::weekday-digest"),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/references/trigger-conditions.rs` around lines 211 - 215, The cron
expression passed to CronTriggerConfig::new in the iii.register_trigger call
uses a 5-field format ("0 8 * * *"); update it to the engine's 7-field format
(second minute hour day month weekday year) used by CronTriggerConfig, e.g.
change the string to include seconds and year placeholders like "0 0 8 * * * *"
in the IIITrigger::Cron / CronTriggerConfig::new invocation where
.condition("conditions::is-weekday") is set.

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.

2 participants