feat(workflows): inline sub-agent definitions on DAG nodes#1276
Conversation
Add `agents:` node field letting workflow YAML define Claude Agent SDK sub-agents inline, keyed by kebab-case ID. The main agent can spawn them via the Task tool — useful for map-reduce patterns where a cheap model briefs items and a stronger model reduces. Authors no longer need standalone `.claude/agents/*.md` files for workflow-scoped helpers; the definitions live with the workflow. Claude only. Codex and community providers without the capability emit a capability warning and ignore the field. Merges with the internal `dag-node-skills` wrapper when `skills:` is also set — user-defined agents win on ID collision.
📝 WalkthroughWalkthroughAdds a new per-node Changes
Sequence DiagramsequenceDiagram
participant Parser as Workflow Parser
participant Validator as Validator
participant Executor as DAG Executor
participant Provider as Claude Provider
participant SDK as Claude SDK
Parser->>Validator: parse node (includes agents?)
Validator->>Validator: validate agents schema & provider caps
alt provider lacks agents
Validator->>Parser: emit warning (field: 'agents', hint: 'claude')
end
Parser->>Executor: pass normalized node (agents)
Executor->>Provider: sendQuery(nodeConfig { agents, skills, ... })
Provider->>Provider: merge nodeConfig.agents with skills-wrapper agents
Provider->>Provider: log warning if 'dag-node-skills' overridden
Provider->>SDK: call with options.agents (inline sub-agents)
SDK->>SDK: register inline sub-agents
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/web/src/lib/api.generated.d.ts (1)
2519-2532:⚠️ Potential issue | 🟡 MinorRegenerate OpenAPI schema to include the new
agentscapability flag.The
agents: booleanfield is already in the source schema (packages/providers/src/types.ts:179) with documentation, and the workflow validator actively checkscaps.agents(line 409 ofpackages/workflows/src/validator.ts). However, the auto-generated OpenAPI schema inpackages/web/src/lib/api.generated.d.ts(lines 2519–2532) is missing this field entirely, meaning web clients consuming/api/providerswon't know which providers support inline agents.Regenerate the OpenAPI schema from the backend source definition to sync the generated type.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/web/src/lib/api.generated.d.ts` around lines 2519 - 2532, The generated API types are missing the new ProviderCapabilities.agents boolean flag; regenerate the OpenAPI/type generation so packages/web/src/lib/api.generated.d.ts includes "agents: boolean" to match the source definition (packages/providers/src/types.ts) and the runtime validator (packages/workflows/src/validator.ts which checks caps.agents). Re-run the OpenAPI/type generation step (or the script that produces api.generated.d.ts) from the backend schema, verify ProviderCapabilities now contains agents, and commit the updated generated file.
🧹 Nitpick comments (4)
packages/providers/src/types.ts (1)
126-142: Structural shape is justified by the contract-module no-SDK rule.Normally the guideline would be to import
AgentDefinitionfrom@anthropic-ai/claude-agent-sdk, but this file is the SDK-free contract layer (see the HARD RULE at the top), and the JSDoc explicitly notes the shape mirrors the SDK'sAgentDefinition. One thing to watch: if Claude SDK'sAgentDefinitiondrifts (e.g. adds a required field or renames one), this structural type won't catch it — consider a type-compat assertion inpackages/providers/src/claude/provider.tswhere SDK types are available, e.g.:// in claude/provider.ts, near where nodeConfig.agents is consumed const _compat: AgentDefinition = {} as NonNullable<NodeConfig['agents']>[string];to fail the build if the shapes diverge.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/providers/src/types.ts` around lines 126 - 142, The agents structural type in packages/providers/src/types.ts may drift from the SDK's AgentDefinition; to detect divergences at build time, add a type-compat assertion in packages/providers/src/claude/provider.ts where nodeConfig.agents is consumed: create a compile-time assignment forcing an alias like AgentDefinition = NonNullable<NodeConfig['agents']>[string] (using the SDK's AgentDefinition type from `@anthropic-ai/claude-agent-sdk`) so the TypeScript compiler fails the build if the shapes diverge; reference AgentDefinition, NodeConfig, and the agents property when adding this assertion.packages/workflows/src/dag-executor.ts (1)
288-288: Minor: empty-map check differs slightly from validator.
node.agents !== undefined && Object.keys(node.agents).length > 0means anagents: {}node will not emit the runtime capability warning, whereas the validator (seevalidator.tsL409) warns on any truthy object. In practiceagents: {}is a no-op so this difference is benign — flagging only for consistency. If you want them aligned, drop theObject.keys(...).length > 0predicate here (mirroring the other cap checks likehooks/mcpwhich only test for!== undefined).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/workflows/src/dag-executor.ts` at line 288, The runtime capability check for agents in the array entry inside dag-executor.ts currently uses node.agents !== undefined && Object.keys(node.agents).length > 0 which differs from the validator behavior; change this predicate to mirror the other capability checks by removing the Object.keys(...).length > 0 part so it simply tests node.agents !== undefined (update the array entry that contains ['agents', 'agents', ...] accordingly) to keep consistency with validator.ts and the hooks/mcp checks.packages/docs-web/src/content/docs/guides/authoring-workflows.md (1)
1160-1160: Numbering nit:12b.breaks the ordered list.Consider renumbering the trailing items (13–19 become 14–20) so the list stays monotonic, or fold agents into item 12 alongside skills since they are closely related.
Proposed fix
-12. **`skills:`** — preload skills into Claude nodes for domain expertise -12b. **`agents:`** — inline Claude sub-agent definitions invokable via the `Task` tool -13. **`effort` / `thinking`** — control reasoning depth and thinking mode per node or workflow (Claude only) +12. **`skills:`** — preload skills into Claude nodes for domain expertise +13. **`agents:`** — inline Claude sub-agent definitions invokable via the `Task` tool (Claude only) +14. **`effort` / `thinking`** — control reasoning depth and thinking mode per node or workflow (Claude only)(and bump the remaining items 14–19 → 15–20)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/docs-web/src/content/docs/guides/authoring-workflows.md` at line 1160, Replace the non-monotonic list item labeled "12b." with a properly numbered entry: either merge the "agents" description into item "12" next to "skills" or renumber it as "13." and increment all following list items (change 13–19 to 14–20); locate the paragraph containing the "12b. **`agents:`**" token in the authoring-workflows.md content and apply the chosen renumbering so the ordered list remains sequential.packages/providers/src/claude/provider.ts (1)
453-464: Duplicateclaude.inline_agents_registeredlog per sendQuery.
applyNodeConfigis invoked twice: once on a throwawaytempOptionsat Line 911–912 to pre-compute warnings, and then again per attempt at Line 953–954. That meansclaude.inline_agents_registered(andclaude.skills_agent_created, which is pre-existing) fires at least twice for every workflow node that uses inline agents, and N+1 times with N retries. It also redundantly materialises the merged map ontempOptionsthat is then discarded.Consider splitting warning computation from option mutation, or gating the info log to only fire on the real options path (e.g. skip when
optionsis the throwaway). Non-blocking since the only effect is log noise, but worth cleaning up since this path will hit every inline-agent node.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/providers/src/claude/provider.ts` around lines 453 - 464, applyNodeConfig is mutating options and unconditionally emitting getLog().info('claude.inline_agents_registered'), which causes duplicate logs because applyNodeConfig is invoked once with a throwaway tempOptions and again with the real options; change applyNodeConfig to accept a small flag (e.g. dryRun or emitLogs:boolean) or detect a sentinel on tempOptions and skip emitting the info log when dryRun/emitLogs is false (or when options._isTemp is true), and only merge nodeConfig.agents into the real options.agents and call getLog().info({ agentIds: ... }, 'claude.inline_agents_registered') when emitting is enabled; reference applyNodeConfig, the options.agents merge, tempOptions, and getLog().info('claude.inline_agents_registered') when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/workflows/src/schemas/dag-node.ts`:
- Line 127: AGENT_ID_REGEX currently allows trailing and consecutive hyphens;
replace its pattern so IDs must start with one or more lowercase letters/digits
and then may have zero or more groups of a single hyphen followed by one or more
lowercase letters/digits (this disallows leading/trailing hyphens and
consecutive hyphens). Update the AGENT_ID_REGEX constant accordingly and
run/adjust any validation tests that rely on agent ID shape so invalid values
like "foo-", "a--b", or "brief-gen-" are rejected at schema validation time
rather than downstream in the Claude Agent SDK.
---
Outside diff comments:
In `@packages/web/src/lib/api.generated.d.ts`:
- Around line 2519-2532: The generated API types are missing the new
ProviderCapabilities.agents boolean flag; regenerate the OpenAPI/type generation
so packages/web/src/lib/api.generated.d.ts includes "agents: boolean" to match
the source definition (packages/providers/src/types.ts) and the runtime
validator (packages/workflows/src/validator.ts which checks caps.agents). Re-run
the OpenAPI/type generation step (or the script that produces
api.generated.d.ts) from the backend schema, verify ProviderCapabilities now
contains agents, and commit the updated generated file.
---
Nitpick comments:
In `@packages/docs-web/src/content/docs/guides/authoring-workflows.md`:
- Line 1160: Replace the non-monotonic list item labeled "12b." with a properly
numbered entry: either merge the "agents" description into item "12" next to
"skills" or renumber it as "13." and increment all following list items (change
13–19 to 14–20); locate the paragraph containing the "12b. **`agents:`**" token
in the authoring-workflows.md content and apply the chosen renumbering so the
ordered list remains sequential.
In `@packages/providers/src/claude/provider.ts`:
- Around line 453-464: applyNodeConfig is mutating options and unconditionally
emitting getLog().info('claude.inline_agents_registered'), which causes
duplicate logs because applyNodeConfig is invoked once with a throwaway
tempOptions and again with the real options; change applyNodeConfig to accept a
small flag (e.g. dryRun or emitLogs:boolean) or detect a sentinel on tempOptions
and skip emitting the info log when dryRun/emitLogs is false (or when
options._isTemp is true), and only merge nodeConfig.agents into the real
options.agents and call getLog().info({ agentIds: ... },
'claude.inline_agents_registered') when emitting is enabled; reference
applyNodeConfig, the options.agents merge, tempOptions, and
getLog().info('claude.inline_agents_registered') when making the change.
In `@packages/providers/src/types.ts`:
- Around line 126-142: The agents structural type in
packages/providers/src/types.ts may drift from the SDK's AgentDefinition; to
detect divergences at build time, add a type-compat assertion in
packages/providers/src/claude/provider.ts where nodeConfig.agents is consumed:
create a compile-time assignment forcing an alias like AgentDefinition =
NonNullable<NodeConfig['agents']>[string] (using the SDK's AgentDefinition type
from `@anthropic-ai/claude-agent-sdk`) so the TypeScript compiler fails the build
if the shapes diverge; reference AgentDefinition, NodeConfig, and the agents
property when adding this assertion.
In `@packages/workflows/src/dag-executor.ts`:
- Line 288: The runtime capability check for agents in the array entry inside
dag-executor.ts currently uses node.agents !== undefined &&
Object.keys(node.agents).length > 0 which differs from the validator behavior;
change this predicate to mirror the other capability checks by removing the
Object.keys(...).length > 0 part so it simply tests node.agents !== undefined
(update the array entry that contains ['agents', 'agents', ...] accordingly) to
keep consistency with validator.ts and the hooks/mcp checks.
🪄 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: 2045a604-6ecf-4183-a743-7c80451dfc6e
📒 Files selected for processing (15)
CLAUDE.mdpackages/docs-web/src/content/docs/guides/authoring-workflows.mdpackages/providers/src/claude/capabilities.tspackages/providers/src/claude/provider.test.tspackages/providers/src/claude/provider.tspackages/providers/src/codex/capabilities.tspackages/providers/src/codex/provider.test.tspackages/providers/src/community/pi/capabilities.tspackages/providers/src/registry.test.tspackages/providers/src/types.tspackages/web/src/lib/api.generated.d.tspackages/workflows/src/dag-executor.test.tspackages/workflows/src/dag-executor.tspackages/workflows/src/schemas/dag-node.tspackages/workflows/src/validator.ts
|
Related to #1118 — overlapping area or partial fix. |
PR Review Summary — 7 agents, findings belowCritical Issues (1)
Important Issues (3)
Suggestions (6)
Strengths
Documentation Gaps
VerdictNEEDS FIXES before merge. Recommended Actions
|
Critical: - Re-export agentDefinitionSchema + AgentDefinition from schemas/index.ts (matches the "schemas/index.ts re-exports all" convention). Important: - Surface user-override of internal 'dag-node-skills' wrapper: warn-level provider log + platform message to the user when agents: redefines the reserved ID alongside skills:. User-wins behavior preserved (by design) but silent capability removal is now observable. - Add validator test coverage for the agents-capability warning (codex node with agents: → warning; claude node → no warning; no-agents field → no warning). - Strengthen NodeConfig.agents duplicate-type comment explaining the intentional circular-dep avoidance and pointing at the Zod schema as authoritative source. Actual extraction is follow-up work. Simplifications: - Drop redundant typeof check in validator (schema already enforces). - Drop unreachable Object.keys(...).length > 0 check in dag-executor. - Drop rot-prone "(out of v1 scope)" parenthetical. - Drop WHAT-only comment on AGENT_ID_REGEX. - Tighten AGENT_ID_REGEX to reject trailing/double hyphens (/^[a-z0-9]+(-[a-z0-9]+)*$/). Tests: - parseWorkflow strips agents on script: and loop: nodes (parallel to the existing bash: coverage). - provider emits warn log on dag-node-skills collision; no warn on non-colliding inline agents. Docs: - Renumber authoring-workflows Summary section (12b → 13; bump 13-19). - Add Pi capability-table row for inline agents (❌, Claude-only). - Add when-to-use guidance (agents: vs .claude/agents/*.md) in the new "Inline sub-agents" section. - Cross-link skills.md Related → inline-sub-agents. - CHANGELOG [Unreleased] Added entry for #1276.
There was a problem hiding this comment.
🧹 Nitpick comments (2)
packages/providers/src/types.ts (1)
126-151: Consider a CI guard to detect drift againstagentDefinitionSchema.The hand-written duplicate is justified (circular-dep contract layer), and the docstring acknowledges the drift risk. To reduce the chance of silent divergence before the follow-up extraction lands, consider a small CI check that type-asserts shape-compatibility, e.g. in a test file that imports both:
import type { z } from '@hono/zod-openapi'; import type { agentDefinitionSchema } from '@archon/workflows/schemas'; import type { NodeConfig } from '@archon/providers/types'; type SchemaAgent = z.infer<typeof agentDefinitionSchema>; type InlineAgent = NonNullable<NodeConfig['agents']>[string]; // Bidirectional assignability — fails to compile if either side drifts const _a: SchemaAgent = {} as InlineAgent; const _b: InlineAgent = {} as SchemaAgent;This keeps the contract layer SDK-free while failing fast on drift.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/providers/src/types.ts` around lines 126 - 151, Add a compile-time CI guard that asserts the hand-written agents shape in NodeConfig stays type-compatible with the authoritative agentDefinitionSchema: create a test module that imports agentDefinitionSchema from '@archon/workflows/schemas' and NodeConfig from '@archon/providers/types', define SchemaAgent = z.infer<typeof agentDefinitionSchema> and InlineAgent = NonNullable<NodeConfig['agents']>[string], then add bidirectional assignability checks (assign InlineAgent to SchemaAgent and vice versa) so the TypeScript build fails if either side drifts; ensure the test is included in CI so any divergence between agentDefinitionSchema and the agents field is detected.packages/workflows/src/dag-executor.test.ts (1)
2472-2515: Codex-agents warning assertion is too loose.
messages.find(m => m.includes('agents') && m.includes('codex'))will also match the skills warning text (e.g. "skills … translated to agents for codex") if it were ever emitted here. Since the node setsagents(notskills), the current test passes, but the assertion doesn't pin the warning to the agents-capability path. Consider tightening to the exact warning phrase used by the validator/executor (e.g.'inline agents'or"not supported by provider 'codex'") to avoid false positives if skills/agents warnings ever co-occur.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/workflows/src/dag-executor.test.ts` around lines 2472 - 2515, The test's warning lookup is too loose: instead of using messages.find(m => m.includes('agents') && m.includes('codex')), tighten the assertion to match the exact warning emitted by the executor/validator (for example the phrase "inline agents" or "not supported by provider 'codex'"); update the predicate used to build `warning` (in the test case that calls executeDagWorkflow and inspects platform.sendMessage calls) to search for that exact phrase or a precise regex so the test pins the agents-capability warning path rather than accidentally matching a skills-related message.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@packages/providers/src/types.ts`:
- Around line 126-151: Add a compile-time CI guard that asserts the hand-written
agents shape in NodeConfig stays type-compatible with the authoritative
agentDefinitionSchema: create a test module that imports agentDefinitionSchema
from '@archon/workflows/schemas' and NodeConfig from '@archon/providers/types',
define SchemaAgent = z.infer<typeof agentDefinitionSchema> and InlineAgent =
NonNullable<NodeConfig['agents']>[string], then add bidirectional assignability
checks (assign InlineAgent to SchemaAgent and vice versa) so the TypeScript
build fails if either side drifts; ensure the test is included in CI so any
divergence between agentDefinitionSchema and the agents field is detected.
In `@packages/workflows/src/dag-executor.test.ts`:
- Around line 2472-2515: The test's warning lookup is too loose: instead of
using messages.find(m => m.includes('agents') && m.includes('codex')), tighten
the assertion to match the exact warning emitted by the executor/validator (for
example the phrase "inline agents" or "not supported by provider 'codex'");
update the predicate used to build `warning` (in the test case that calls
executeDagWorkflow and inspects platform.sendMessage calls) to search for that
exact phrase or a precise regex so the test pins the agents-capability warning
path rather than accidentally matching a skills-related message.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 8a2c3648-eb2c-432f-a56c-62497177807c
📒 Files selected for processing (13)
CHANGELOG.mdpackages/docs-web/src/content/docs/getting-started/ai-assistants.mdpackages/docs-web/src/content/docs/guides/authoring-workflows.mdpackages/docs-web/src/content/docs/guides/skills.mdpackages/providers/src/claude/provider.test.tspackages/providers/src/claude/provider.tspackages/providers/src/types.tspackages/workflows/src/dag-executor.test.tspackages/workflows/src/dag-executor.tspackages/workflows/src/schemas/dag-node.tspackages/workflows/src/schemas/index.tspackages/workflows/src/validator.test.tspackages/workflows/src/validator.ts
✅ Files skipped from review due to trivial changes (4)
- packages/docs-web/src/content/docs/guides/authoring-workflows.md
- packages/workflows/src/validator.ts
- CHANGELOG.md
- packages/docs-web/src/content/docs/guides/skills.md
🚧 Files skipped from review as they are similar to previous changes (4)
- packages/workflows/src/dag-executor.ts
- packages/providers/src/claude/provider.ts
- packages/providers/src/claude/provider.test.ts
- packages/workflows/src/schemas/dag-node.ts
Adds .archon/workflows/repo-triage.yaml: a self-contained periodic maintenance workflow that uses inline sub-agents (Claude SDK agents: field introduced in #1276) for map-reduce across open issues and PRs. Six DAG nodes, three-layer topology: - Layer 1 (parallel): triage-issues, link-prs, closed-pr-dedup-check, stale-nudge - Layer 2: closed-dedup-check (reads triage-issues state) - Layer 3: digest (synthesises all prior nodes + writes markdown) Capabilities per node: - triage-issues: delegates labeling to on-disk triage-agent; inline brief-gen Haiku for duplicate detection; 3-day auto-close clock for unanswered duplicate warnings - link-prs: conservative PR ↔ issue cross-refs via inline pr-issue- matcher Haiku, Sonnet re-verifies fully-addresses claims before suggesting Closes #X; auto-nudges on low-quality PR template fill with first-run grandfather guard (snapshot-only, no nudge spam) - closed-dedup-check: cross-matches open issues against recently- closed ones via inline closed-brief-gen Haiku; same 3-day clock - closed-pr-dedup-check: flags open PRs duplicating recently-closed PRs via inline pr-brief-gen Haiku; comment-only, never closes PRs - stale-nudge: 60-day inactivity pings (configurable); no auto-close - digest: synthesises per-node outputs + reads state files to emit $ARTIFACTS_DIR/digest.md with clickable GitHub comment links Env-gated rollout knobs: - DRY_RUN=1 (read-only; prints [DRY] lines, no gh/state mutations) - SKIP_PR_LINK=1, SKIP_CLOSED_DEDUP=1, SKIP_CLOSED_PR_DEDUP=1, SKIP_STALE_NUDGE=1 - STALE_DAYS=N (stale-nudge window; default 60) Cross-run state under .archon/state/ (gitignored): - triage-state.json briefs + pendingDedupComments - closed-dedup-state.json closedBriefs + closedMatchComments - closed-pr-dedup-state.json openBriefs + closedBriefs + matches - pr-state.json linkedPrs + commentIds + templateAdherence - stale-nudge-state.json nudged (with updatedAtAtNudge for re-nudge) Every bot comment: - @-tags the target human (reporter for issues, author for PRs) - Tracks comment ID in state for traceability - Is idempotent — re-runs skip existing comments Intended use: invoke periodically (`archon workflow run repo-triage --no-worktree`) once a scheduler lands; live state persists across runs so previously-flagged items reconcile correctly. .gitignore: adds .archon/state/ for cross-run memory files.
…iku sub-agents (#1293) * feat(workflows): add repo-triage — 6-node periodic maintenance workflow Adds .archon/workflows/repo-triage.yaml: a self-contained periodic maintenance workflow that uses inline sub-agents (Claude SDK agents: field introduced in #1276) for map-reduce across open issues and PRs. Six DAG nodes, three-layer topology: - Layer 1 (parallel): triage-issues, link-prs, closed-pr-dedup-check, stale-nudge - Layer 2: closed-dedup-check (reads triage-issues state) - Layer 3: digest (synthesises all prior nodes + writes markdown) Capabilities per node: - triage-issues: delegates labeling to on-disk triage-agent; inline brief-gen Haiku for duplicate detection; 3-day auto-close clock for unanswered duplicate warnings - link-prs: conservative PR ↔ issue cross-refs via inline pr-issue- matcher Haiku, Sonnet re-verifies fully-addresses claims before suggesting Closes #X; auto-nudges on low-quality PR template fill with first-run grandfather guard (snapshot-only, no nudge spam) - closed-dedup-check: cross-matches open issues against recently- closed ones via inline closed-brief-gen Haiku; same 3-day clock - closed-pr-dedup-check: flags open PRs duplicating recently-closed PRs via inline pr-brief-gen Haiku; comment-only, never closes PRs - stale-nudge: 60-day inactivity pings (configurable); no auto-close - digest: synthesises per-node outputs + reads state files to emit $ARTIFACTS_DIR/digest.md with clickable GitHub comment links Env-gated rollout knobs: - DRY_RUN=1 (read-only; prints [DRY] lines, no gh/state mutations) - SKIP_PR_LINK=1, SKIP_CLOSED_DEDUP=1, SKIP_CLOSED_PR_DEDUP=1, SKIP_STALE_NUDGE=1 - STALE_DAYS=N (stale-nudge window; default 60) Cross-run state under .archon/state/ (gitignored): - triage-state.json briefs + pendingDedupComments - closed-dedup-state.json closedBriefs + closedMatchComments - closed-pr-dedup-state.json openBriefs + closedBriefs + matches - pr-state.json linkedPrs + commentIds + templateAdherence - stale-nudge-state.json nudged (with updatedAtAtNudge for re-nudge) Every bot comment: - @-tags the target human (reporter for issues, author for PRs) - Tracks comment ID in state for traceability - Is idempotent — re-runs skip existing comments Intended use: invoke periodically (`archon workflow run repo-triage --no-worktree`) once a scheduler lands; live state persists across runs so previously-flagged items reconcile correctly. .gitignore: adds .archon/state/ for cross-run memory files. * feat(workflows/repo-triage): post digest to Slack when SLACK_WEBHOOK is set Extends the digest node with an optional Slack-post step after the canonical digest.md artifact is written. Uses Slack incoming webhook (no bot token required beyond the incoming-webhook scope). Behavior: - SLACK_WEBHOOK unset → skipped silently with a one-line note - DRY_RUN=1 → prints full payload, does not curl - Otherwise → POSTs a compact (<3500 char) mrkdwn-formatted summary containing headline numbers, this-run comment index (clickable GitHub URLs), pending items, and a path reference to digest.md - curl failure or non-ok Slack response is logged but does not fail the node — digest.md on disk remains authoritative - Intermediate Slack text written to $ARTIFACTS_DIR/digest-slack.txt for traceability; payload JSON assembled via jq and written to $ARTIFACTS_DIR/slack-payload.json before curl posts it Slack mrkdwn conversion rules baked into the prompt (no tables, link shape <url|text>, single-asterisk bold) so Sonnet emits a variant that renders cleanly in Slack rather than being sent raw. The webhook URL is read from the operator's environment (Archon auto-loads ~/.archon/.env on CLI startup — put SLACK_WEBHOOK=... there). * fix(workflows/repo-triage): address PR #1293 review feedback Critical (3): - `gh issue close --reason "not planned"` (space, not underscore) — the CLI expects lowercase with a space; `not_planned` fails at runtime. Fixed in both auto-close paths (triage-issues step 8, closed-dedup- check step 7). - link-prs step 7 state save was sparse `{ sha, processedAt, related, fullyAddresses }`, overwriting `commentIds` / `templateNudgedAt` / `templateAdherence`. Changed to explicit merge that spreads existing entry first so per-run captured fields survive. - Corrupt-JSON state files previously treated as first-run default (silent `pendingDedupComments` reset → 3-day clock restarts forever). All five state-load sites now abort loudly on JSON.parse throw; ENOENT/empty continue to default-shape. Important (7): - Sub-agents (`brief-gen`, `closed-brief-gen`, `pr-brief-gen`, `pr-issue-matcher`) emit `ERROR: <reason>` on gh failures rather than partial/fabricated JSON. Orchestrator detects the sentinel, logs the failed ID + first 200 chars of raw response, tracks in a failed-list, and aborts the cluster/match pass if ≥50% of items failed (avoids acting on bad data). - `pr-brief-gen` now sets `diffTruncated: true` when the 30k-char diff cap hits; link-prs verify pass downgrades any `fully-addresses` claim to `related` when either side's brief was truncated. - 3-day auto-close validates `postedAt` parses as ISO-8601 before the elapsed-time comparison; corrupt timestamps are logged and skipped, never acted on. - `gh issue close` failure path no longer drops state — sets `closeAttemptFailed: true` on the entry for next-run retry. Only drops on exit 0. - `closed-pr-dedup-check` idempotency check (`gh pr view --json comments`) now aborts the post on fetch failure rather than falling through — prevents double-posts on gh hiccups. - `triage-agent` label pass has preflight `test -f` check for `.claude/agents/triage-agent.md`; skips the pass with a clear log if the file is missing rather than firing Task calls that fail obscurely. - `brief-gen` template-adherence wording flipped from "Ignore … as 'filled'" (ambiguous, read as affirmative) to explicit "A section counts as MISSING when …", matching the `pr-issue-matcher` phrasing. Minor: - `stale-nudge` idempotency check uses substring "has been quiet for" instead of a prefix check that never matched (posted body starts with @<author>). - `closed-dedup-check` distinguishes "upstream crashed" (missing/corrupt triage-state.json, or `lastRunAt == null`) from "legitimately quiet day" (state present, briefs empty) — different log lines. - Slack curl adds `-w "\nHTTP_STATUS:%{http_code}"` + `2>&1` so TLS / 4xx / 5xx errors are visible in captured output. - `stateReason` values from `gh issue view --json stateReason` are UPPERCASE (`COMPLETED`, `NOT_PLANNED`); documented and instruct sub-agent to normalize to lowercase for consistency. Docs: - CLAUDE.md repo-level `.archon/` tree now lists `state/`. - archon-directories.md tree adds `state/` + `scripts/` (both were missing) with purpose descriptions. Deferred (worth doing as a follow-up, not blocking): - DRY/SKIP preamble duplication (~30-50 lines across 5 nodes). - Explicit `BASELINE_IS_EMPTY` capture in link-prs (current derived check works but is a load-bearing model instruction). - Digest `WARNING` prefix block when upstream nodes are missing outputs — today's "(output unavailable)" sub-line is functional. - Pre-existing README workflow-count (17 → 20) and table gaps — not caused by this PR.
Summary
Taskmust author loose.claude/agents/*.mdfiles on disk. Definitions live outside the workflow YAML — hurts self-containment, portability, and discoverability..claude/agents/. Inline agents let the entire workflow travel in one YAML file.agents:— map of{agentId: {description, prompt, model?, tools?, disallowedTools?, skills?, maxTurns?}}passed straight through to Claude Agent SDK'soptions.agents. Schema validation, provider-capability gate, validator warnings for non-Claude providers.agents:(deferred to v2), per-agentmcpServers,criticalSystemReminder_EXPERIMENTAL, and web UI editor — YAML-only authoring in v1; the existing YAML code view round-trips the field without data loss.UX Journey
Before
```
author workflow file tree sub-agent loading
────────────── ───────── ─────────────────
write workflow ──────▶ .archon/workflows/my.yaml
want Haiku fan-out:
create agent file ──▶ .claude/agents/ loose file on disk,
brief-gen.md ships separately
reference in prompt: from the workflow
Task(subagent_type='brief-gen', ...)
```
After
```
author workflow file tree sub-agent loading
────────────── ───────── ─────────────────
write workflow ──────▶ .archon/workflows/my.yaml [+] inline in same
agents: YAML as workflow
brief-gen:
model: haiku
prompt: ...
reference in prompt:
Task(subagent_type='brief-gen', ...)
```
Architecture Diagram
Before
```
YAML packages/workflows packages/providers
──── ────────────────── ──────────────────
node.skills ──▶ dagNodeSchema ──────────▶ NodeConfig.skills
├─ ClaudeProvider:
│ options.agents[dag-node-skills]
├─ Codex: ignored + cap warning
└─ Pi: ignored + cap warning
```
After
```
YAML packages/workflows packages/providers
──── ────────────────── ──────────────────
node.skills ────────▶ dagNodeSchema ──────────────▶ NodeConfig.skills
│ │
│ [
] │]:▼ ▼
node.agents [+] ════▶ agentDefinitionSchema [+] ═══▶ NodeConfig.agents [+]
(kebab-case keys) │
├─ ClaudeProvider [
│ options.agents
│ MERGE: skills wrapper
│ + inline agents, user
│ wins on ID collision
├─ Codex: ignored + cap warn [+]
└─ Pi: ignored + cap warn [+]
Validator [
]: new capability-mismatch warning mirroring the skills pattern.]: new capCheck entry surfaces warning to user via platform.dag-executor [
```
Connection inventory:
Label Snapshot
Change Metadata
Linked Issue
(No existing issue — this is an ad-hoc enhancement uncovered while building a workflow that wanted inline Haiku sub-agents.)
Validation Evidence (required)
```bash
bun run validate # check:bundled, type-check, lint, format:check, test — all pass
```
Manual end-to-end smoke: authored a workflow (`repo-triage`) with an inline Haiku `brief-gen` sub-agent, ran it live against the repo. Observed:
New tests added:
Security Impact (required)
Compatibility / Migration
Human Verification (required)
Verified scenarios:
Edge cases checked:
What was not verified:
Side Effects / Blast Radius (required)
Rollback Plan (required)
Risks and Mitigations
Summary by CodeRabbit
New Features
agents:field in workflow configurations for Claude, enabling parallel sub-agent invocation via the Task tool. User-supplied agents take precedence over internal wrappers on ID collisions.Documentation
Provider Support