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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
<PackageVersion Include="SlackNet" Version="$(SlackNetVersion)" />
<PackageVersion Include="SlackNet.Extensions.DependencyInjection" Version="$(SlackNetVersion)" />
<PackageVersion Include="Cronos" Version="0.13.0" />
<PackageVersion Include="Netclaw.SkillClient" Version="0.3.1" />
<PackageVersion Include="Netclaw.SkillClient" Version="0.4.0-beta.1" />
<PackageVersion Include="ShellSyntaxTree" Version="0.1.5" />
<PackageVersion Include="Termina" Version="0.15.0" />
</ItemGroup>
Expand Down
1 change: 1 addition & 0 deletions Netclaw.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
<Project Path="src/Netclaw.Configuration.Tests/Netclaw.Configuration.Tests.csproj" />
<Project Path="src/Netclaw.Configuration/Netclaw.Configuration.csproj" />
<Project Path="src/Netclaw.Daemon.Tests/Netclaw.Daemon.Tests.csproj" />
<Project Path="src/Netclaw.Daemon.IntegrationTests/Netclaw.Daemon.IntegrationTests.csproj" />
<Project Path="src/Netclaw.Daemon/Netclaw.Daemon.csproj" />
<Project Path="src/Netclaw.Media.Tests/Netclaw.Media.Tests.csproj" />
<Project Path="src/Netclaw.Media/Netclaw.Media.csproj" />
Expand Down
17 changes: 12 additions & 5 deletions docs/runbooks/subagents.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,13 @@ skill system uses and the de facto format used by Claude Code and OpenCode.
One file per agent. No JSON sidecar. The filename is a convenience for humans;
the authoritative agent name comes from the `name` field in the frontmatter.

SkillServer feed sync can also install managed subagent definitions under
`~/.netclaw/agents/.server-feeds/<feed-name>/<agent-name>.md`. Those files are
owned by the server-feed sync process: edit local user-authored agents in the
top-level `~/.netclaw/agents/*.md` namespace instead. If a top-level local agent
and a managed feed agent declare the same `name`, the local definition wins and
the managed one is skipped with a warning.

### Frontmatter fields

```markdown
Expand Down Expand Up @@ -194,15 +201,15 @@ written.
### Loader behavior (fail loud)

On the next turn or subagent lookup, `FileSubAgentDefinitionLoader` rescans
`~/.netclaw/agents/*.md` and logs a specific warning for every file it rejects.
A rejection does not stop the scan — other valid files in the same directory
still load. Rejection
reasons:
top-level `~/.netclaw/agents/*.md` files first, then managed server-feed files
under `~/.netclaw/agents/.server-feeds/*/*.md`. It logs a specific warning for
every file it rejects. A rejection does not stop the scan — other valid files in
the same directory still load. Rejection reasons:

- Missing or unparseable YAML frontmatter
- Missing required field (`name` or `description`)
- Empty body (system prompt)
- Duplicate `name` across files (the alphabetically-first file wins)
- Duplicate `name` across files (top-level local files win over managed feed files; managed feed duplicates use configured feed order)

Non-`.md` files in the agents directory (`stray.json`, `README.txt`, etc.) are
ignored at the glob layer and never logged.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-06-30
141 changes: 141 additions & 0 deletions openspec/changes/skillserver-native-sidecar-sync/design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
## Context

NetClaw already syncs private SkillServer feeds through `ServerFeedSkillSyncService` using the Cloudflare Agent Skills RFC index. That path is intentionally skill-only and writes managed skills under `~/.netclaw/skills/.server-feeds/<feed-name>/` before rebuilding the skill registry.

SkillServer now exposes a native manifest sidecar at `/manifest.json` with native resource traversal and artifact download APIs. The native sidecar is needed for sub-agents because the RFC skill feed cannot represent them. NetClaw's current sub-agent loader only scans top-level `~/.netclaw/agents/*.md`, so a managed server-feed namespace must be introduced without letting feeds overwrite operator-authored local agents.

Actor boundary note: this change stays in daemon background sync and configuration loading. It updates files on disk and the in-memory sub-agent registry, but it does not add new actor messages, session journal events, or persistence schema. Sub-agent execution behavior remains under the existing `SubAgentActor` contract after definitions are loaded.

## Goals / Non-Goals

**Goals:**

- Preserve RFC skill sync as the primary and authoritative skill path.
- Feature-detect native `/manifest.json` per configured SkillServer feed.
- Sync native `agent-md` sub-agent artifacts into a NetClaw-owned managed namespace.
- Verify SHA-256 digests before writing managed sub-agent files.
- Keep local user-authored sub-agents authoritative on name conflicts.
- Prune only managed server-feed sub-agents after a confirmed successful native sync.
- Keep previous managed files during native sidecar outages, malformed responses, timeouts, or verification failures.

**Non-Goals:**

- Replace RFC skill sync with native skill sync.
- Add native sync for non-sub-agent resources in this MVP.
- Add new feed configuration knobs unless implementation proves they are required.
- Let SkillServer manifest data prescribe local filesystem paths.
- Add manifest signature verification.

## Decisions

### D1. RFC index fetch remains the feed reachability gate

For each enabled feed, NetClaw first fetches the RFC skill index with the existing client path. If that fetch times out or fails, the service skips both skill updates and native sidecar sync for that feed. If the RFC index fetch succeeds, even with zero skills, NetClaw may attempt optional native sidecar detection.

Rationale:

- Preserves the current RFC-first mental model and failure behavior.
- Avoids treating native manifest success as a replacement for RFC skill sync.
- Still allows sub-agent-only feeds when the server is reachable and the RFC endpoint responds with an empty index.

Alternative considered:

- Fetch native sidecar even when RFC fetch fails. Rejected because it creates two competing feed reachability models and makes pruning safety harder to reason about during partial outages.

### D2. Native sidecar sync is fail-soft and optional

Missing `/manifest.json`, 404s, malformed native manifests, unsupported native manifest shapes, and native traversal failures are logged and treated as sidecar sync failures only. The existing RFC skill sync result remains valid, and existing managed sub-agent files are left untouched.

Rationale:

- Existing SkillServer feeds and non-SkillServer RFC feeds should continue to work unchanged.
- Native sidecar deployment can roll out independently from NetClaw client support.

Alternative considered:

- Fail the entire feed sync when native sidecar sync fails. Rejected because sub-agent distribution is additive and should not break skill updates.

### D3. NetClaw owns all managed local paths

Server-synced sub-agents are written under `~/.netclaw/agents/.server-feeds/<feed-name>/<agent-name>.md`. NetClaw derives the filename from the logical sub-agent name after validating it is a safe file segment. Manifest-provided paths are ignored for local storage.

Rationale:

- Prevents path traversal and server-controlled writes outside the managed namespace.
- Mirrors the existing managed server-feed skill namespace.
- Makes pruning scope precise.

Alternative considered:

- Allow the native manifest to carry local target paths. Rejected because it gives remote feed content too much authority over the operator's filesystem.

### D4. Downloaded sub-agent files must verify and self-identify

NetClaw downloads the native `agent-md` artifact for each advertised sub-agent, verifies the expected SHA-256 digest, parses the markdown frontmatter, and requires the frontmatter `name` to match the advertised manifest name before replacing the managed file.

Rationale:

- Digest verification protects against corrupted or wrong artifacts.
- Frontmatter identity validation prevents a feed from advertising one agent name while delivering another.
- Reusing the existing markdown parser keeps format behavior aligned with local sub-agent authoring.

Alternative considered:

- Trust manifest metadata without parsing the downloaded file before write. Rejected because the runtime loads the markdown file, so the file's own frontmatter is the authoritative execution input.

### D5. Local user-authored agents take precedence over managed feed agents

The loader scans top-level `~/.netclaw/agents/*.md` as user-owned definitions first, then scans managed server-feed directories. If a managed feed agent has the same logical name as a local user-owned definition, the local definition is registered and the managed one is skipped with a diagnostic. If multiple managed feeds publish the same name, the configured feed order determines the winner and later duplicates are skipped with diagnostics.

Rationale:

- Protects operator intent and local customization.
- Keeps conflict behavior deterministic.
- Keeps managed feed files available on disk for audit and future conflict resolution without exposing the shadowed definition at runtime.

Alternative considered:

- Let the most recently synced managed feed override local files. Rejected because it would make remote feeds capable of changing local operator behavior unexpectedly.

### D6. Pruning is a post-success managed-only operation

Each feed tracks its managed sub-agent sync state separately from user-authored files. After native sidecar traversal and all advertised sub-agent artifact operations for that feed complete successfully, NetClaw prunes managed files and state entries no longer advertised by that feed. If any native sub-agent download, verification, parse, or write fails, the sync is partial and no managed sub-agent pruning occurs for that feed.

Rationale:

- Prevents transient partial failures from deleting still-useful managed agents.
- Keeps destructive behavior confined to the feed-owned managed namespace.

Alternative considered:

- Prune based on whatever subset was successfully downloaded. Rejected because one failed artifact could incorrectly remove other managed agents during an outage or server bug.

## Risks / Trade-offs

- [Risk] The existing sub-agent loader fingerprint only covers top-level files. Mitigation: include managed feed files in the fingerprint so runtime refresh sees server-synced changes.
- [Risk] Managed feed conflicts can be confusing when local definitions win. Mitigation: emit explicit diagnostics with local path, feed name, and shadowed managed path.
- [Risk] Feed names may contain unsafe path characters. Mitigation: reuse existing feed directory behavior only if safe; otherwise add a shared safe-segment helper before writing managed agent paths.
- [Risk] Native sidecar sync adds network calls to startup feed sync. Mitigation: reuse per-feed timeout bounds and keep sidecar failures fail-soft.
- [Risk] Partial native sync may write some updated agents while retaining stale ones and skipping prune. Mitigation: log partial sync status and retry on the next scheduled sync.

## Migration Plan

1. Upgrade NetClaw's `Netclaw.SkillClient` dependency to the published prerelease containing native manifest APIs.
2. Add NetClaw path helpers for managed server-feed sub-agent directories and sync-state path.
3. Extend `ServerFeedSkillSyncService` with optional native sidecar discovery after successful RFC index fetch.
4. Implement verified native sub-agent download, validation, atomic managed-file replacement, state updates, and safe pruning.
5. Extend `FileSubAgentDefinitionLoader` to scan local top-level agents first and managed feed agents second with deterministic conflict diagnostics.
6. Add targeted tests for sidecar absence, sidecar success, digest failure, partial sync no-prune, local precedence, and managed prune behavior.
7. Update docs if operator-facing feed/sub-agent sync behavior needs to be documented.

Rollback:

- Disable or remove the native sidecar branch from `ServerFeedSkillSyncService`; existing RFC skill sync remains intact.
- Managed sub-agent files under `~/.netclaw/agents/.server-feeds/` can remain on disk but will no longer be refreshed or loaded if the loader change is also reverted.
- No journal or database rollback is required.

## Open Questions

- Should shadowed managed sub-agents appear in diagnostic tooling beyond daemon logs?
- Should stale managed sub-agent files be kept for audit instead of deleted when pruned?
- Should `SkillSync.Enabled = false` also disable managed sub-agent server-feed loading, even if files already exist on disk?
69 changes: 69 additions & 0 deletions openspec/changes/skillserver-native-sidecar-sync/proposal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
## Why

SkillServer now exposes native manifest endpoints for resources that the Cloudflare Agent Skills RFC feed cannot represent, especially sub-agents. NetClaw currently consumes only the RFC skill index, so private SkillServer feeds can distribute skills but cannot safely distribute companion sub-agent definitions needed by `metadata.subagent` routing.

## What Changes

- Keep RFC skill sync as the authoritative primary path for skills.
- Feature-detect each configured SkillServer feed's optional native `/manifest.json` sidecar after RFC sync remains available.
- Sync native sub-agent definitions from the sidecar into a feed-owned managed namespace under `~/.netclaw/agents/.server-feeds/<feed-name>/`.
- Verify downloaded sub-agent artifacts by SHA-256 before writing them to disk.
- Preserve existing local user-authored sub-agent files and give them precedence on name conflicts.
- Prune only managed server-synced sub-agent files, and only after a confirmed successful native sidecar sync.
- Keep previous managed sub-agent files when the native sidecar is unavailable, malformed, times out, or fails artifact verification.

## Capabilities

### New Capabilities

- `skillserver-native-sidecar-sync`: Defines optional native manifest sidecar discovery, native-only resource sync, managed storage, digest verification, and safe pruning semantics for SkillServer feeds.

### Modified Capabilities

- `netclaw-subagents`: Add managed server-feed sub-agent discovery, local-user precedence, and conflict diagnostics to the sub-agent loading contract.

## Impact

### Affected code and systems

- `ServerFeedSkillSyncService` will continue using the RFC index for skills and add optional native manifest traversal for sub-agents.
- `Netclaw.SkillClient` package consumption will move to the prerelease client version that contains native manifest and sub-agent artifact APIs.
- `NetclawPaths` will need a managed sub-agent feed namespace alongside the existing user-authored `AgentsDirectory`.
- `FileSubAgentDefinitionLoader` and `SubAgentDefinitionRegistry` will need deterministic loading and conflict handling across user-authored and managed feed files.
- Daemon tests will need coverage for native sidecar success, unavailable sidecar fallback, digest failure, user precedence, and prune safety.

### APIs and behavior

- No public user-facing CLI or config breaking change is intended.
- Existing SkillServer feeds without `/manifest.json` continue to sync skills exactly as today.
- Existing local sub-agent files remain user-owned and are never overwritten or deleted by server-feed sync.

### Security and operational impact

- Server manifest metadata SHALL NOT prescribe local filesystem paths; NetClaw maps names to its own managed namespace.
- Artifact digests are verified before writes; failed verification keeps the previous managed file, if any.
- Sub-agent sync is fail-soft relative to skill sync so a native sidecar outage cannot remove existing skills or agents.
- Conflict diagnostics must make it clear when a server-managed sub-agent is shadowed by a local user-authored definition.

### In scope for MVP

- Optional `/manifest.json` feature detection.
- Native sub-agent traversal and `agent-md` artifact download.
- SHA-256 verification and atomic managed-file replacement.
- Managed sub-agent load support and local precedence.
- Safe managed sub-agent pruning after successful sync.
- Unit tests and targeted daemon/configuration tests for the new sync behavior.

### Out of scope for MVP

- Replacing RFC skill sync with native skill sync.
- Syncing non-sub-agent native resources.
- Letting SkillServer choose NetClaw local paths.
- Overwriting or deleting user-authored local sub-agents.
- Signature verification for native manifests beyond the existing digest verification requirement.

### Source PRDs

- `PRD-001` (MVP runtime determinism and reliability)
- `PRD-002` (security envelope and fail-closed/default-deny posture)
- `PRD-004` (operator configuration and local filesystem ownership)
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
## ADDED Requirements

### Requirement: Managed server-feed sub-agent discovery

The system SHALL load sub-agent definitions from user-authored top-level files under `~/.netclaw/agents/*.md` and from managed server-feed files under `~/.netclaw/agents/.server-feeds/<feed-name>/*.md`. User-authored top-level sub-agents SHALL take precedence over managed server-feed sub-agents with the same logical name. Shadowed managed sub-agents SHALL NOT be exposed through sub-agent discovery, `spawn_agent`, or routed skill execution.

#### Scenario: Managed sub-agent is loaded when no local conflict exists

- **GIVEN** `~/.netclaw/agents/.server-feeds/team/code-reviewer.md` declares `name: code-reviewer`
- **AND** no top-level local sub-agent declares `name: code-reviewer`
- **WHEN** the sub-agent loader refreshes definitions
- **THEN** `code-reviewer` is registered as an available sub-agent according to its frontmatter visibility

#### Scenario: Local sub-agent shadows managed sub-agent

- **GIVEN** `~/.netclaw/agents/code-reviewer.md` declares `name: code-reviewer`
- **AND** `~/.netclaw/agents/.server-feeds/team/code-reviewer.md` also declares `name: code-reviewer`
- **WHEN** the sub-agent loader refreshes definitions
- **THEN** the top-level local `code-reviewer` definition is registered
- **AND** the managed feed `code-reviewer` definition is skipped
- **AND** NetClaw emits a diagnostic identifying the shadowed managed definition

#### Scenario: Shadowed managed sub-agent cannot be spawned by routed skill

- **GIVEN** a top-level local sub-agent shadows a managed server-feed sub-agent with the same name
- **WHEN** a skill routes execution through `metadata.subagent` using that name
- **THEN** routed execution resolves to the registered local sub-agent definition
- **AND** the shadowed managed definition is not used

#### Scenario: Managed feed conflicts are deterministic

- **GIVEN** two configured server feeds both provide a managed sub-agent named `reviewer`
- **WHEN** the sub-agent loader refreshes definitions
- **THEN** NetClaw registers only one `reviewer` definition using deterministic configured feed order
- **AND** skips later managed duplicates with diagnostics

#### Scenario: Managed file changes refresh the registry

- **GIVEN** a managed sub-agent file changes under `~/.netclaw/agents/.server-feeds/team/`
- **WHEN** the sub-agent loader checks for changes
- **THEN** the loader detects the managed file change
- **AND** refreshes the sub-agent registry snapshot

#### Scenario: Missing managed namespace does not block local loading

- **GIVEN** `~/.netclaw/agents/.server-feeds/` does not exist
- **AND** top-level local sub-agent files exist under `~/.netclaw/agents/`
- **WHEN** the sub-agent loader refreshes definitions
- **THEN** local sub-agent loading continues without requiring the managed namespace to exist
Loading
Loading