Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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 IMPLEMENTATION_PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ Done when:

- [x] Stage 1 lands required run scopes, per-call isolation, and non-null
security/authority dependencies without compatibility shims.
- [ ] Stage 2 lands the composed pipeline without changing existing background,
- [x] Stage 2 lands the composed pipeline without changing existing background,
fallback, authorization, approval, MCP, or model-visible behavior.
- [ ] Stage 3 lands child fork/delta semantics and Git inspection only for
non-Public runs with a declared Git project.
Expand Down
18 changes: 18 additions & 0 deletions docs/spec/SPEC-002-session-lifecycle-and-protocol.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,24 @@ retains synchronous execution behavior. This internal composition does not
change MCP schemas, persisted actor messages, approval outcomes, or model-facing
tool results.

### Working Context and Child Runs

For Team and Personal turns with a declared project directory, the session
captures Git working context asynchronously before invoking the model. Git
inspection has one aggregate deadline and produces an explicit available,
not-repository, or unavailable result. Public turns and turns without a project
directory do not launch Git. Continuations carry a generation number so a late
inspection from a cancelled or superseded call cannot mutate the active turn.

Each admitted subagent receives a `ChildRunScope`: a fork of immutable tool
authority plus the parent's working-context snapshot. The child owns fresh
activity tracking and mutable tool-call state; neither is shared with the
parent or sibling runs. Terminal results use typed completion variants.
Completed and partial runs carry a `WorkingContextDelta`; failed and cancelled
runs cannot carry one. The parent merges only files the child confirms it
changed through first-party tools. Git-observed dirty files remain diagnostic
context and are never attributed to the child.

## Subscriber Model

Subscribers join via `JoinSession` with an `OutputFilter` bitmask controlling
Expand Down
9 changes: 6 additions & 3 deletions feeds/skills/.system/files/netclaw-operations/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: netclaw-operations
description: "REQUIRED when the user asks about scheduling, reminders, cron jobs, timers, background jobs, diagnostics, troubleshooting, MCP tools, daemon health, identity updates, or Netclaw capabilities and self-maintenance."
metadata:
author: netclaw
version: "2.27.0"
version: "2.28.0"
---

# Netclaw Operations
Expand Down Expand Up @@ -47,8 +47,11 @@ For Team and Personal sessions, `[working-context]` is refreshed at the start
of each new turn. In a Git project it includes the active worktree, branch,
HEAD, upstream divergence, and dirty counts. Treat this as turn-start
grounding: a checkout or commit performed during the current tool loop appears
in the next turn's snapshot. Subagents receive a read-only project/recent-file
snapshot and return confirmed file edits to the parent when they complete.
in the next turn's snapshot. If Git is unavailable, the turn continues with an
explicit unavailable status rather than invented repository state. Subagents
receive a read-only project/recent-file snapshot. Successful and partial runs
return only file edits confirmed through their own tools; failed or cancelled
runs contribute no parent working-context changes.

## Scheduling & Background Jobs

Expand Down
8 changes: 7 additions & 1 deletion feeds/skills/.system/files/netclaw-projects/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: netclaw-projects
description: "How to create, find, and work with project workspaces. Load when the user references a project, asks to organize work, or you need a sustained workspace."
metadata:
author: netclaw
version: "1.1.0"
version: "1.2.0"
license: MIT
compatibility: "netclaw >= 0.10.0"
disable-model-invocation: false
Expand Down Expand Up @@ -65,6 +65,12 @@ re-read it manually.
Use the project directory as your working directory for all project-related
file operations. Commit meaningful changes so the project has history.

Team and Personal sessions refresh Git working context at the beginning of a
turn. That snapshot is grounding, not a live view: changes made during the turn
appear in the next turn. A spawned subagent receives its own read-only snapshot
and reports only changes confirmed through its tools. Do not infer that every
dirty file in the shared worktree was changed by that subagent.

## When NOT to Create a Project

One-off tasks don't need a project. If the user asks a quick question, runs a
Expand Down
19 changes: 10 additions & 9 deletions openspec/changes/simplify-tool-execution-context/tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,22 @@
- [x] 2.3 Preserve existing `_background` behavior for shell, non-shell, missing-manager, and dispatch-failure paths while removing redundant parameter plumbing.
- [x] 2.4 Add characterization tests for audit/logging/approval/background infrastructure, malformed metadata, ACL and approval denial, supported background routing, missing-manager fallback, dispatch failure, and non-shell fallback.
- [x] 2.5 Verify MCP request/response schemas and persisted actor contracts remain compatible; update affected engineering docs and specs, and review the versioned `netclaw-operations` system skill without changing model-visible guidance for an internal behavior-preserving refactor.
- [ ] 2.6 Run targeted tests, the tool-definition eval suite, `dotnet test`, Slopwatch, file-header verification, and `git diff --check`; open and babysit Stage 2 through review, CI, merge, and post-merge `dev` verification.
- [x] 2.6 Run targeted tests, the tool-definition eval suite, `dotnet test`, Slopwatch, file-header verification, and `git diff --check`; open and babysit Stage 2 through review, CI, merge, and post-merge `dev` verification.

## 3. Stage 3 — Child Context and Async Git

- [ ] 3.1 Introduce a framework-owned child-run scope that forks immutable parent authority and a read-only working-context snapshot while allocating independent child activity state.
- [ ] 3.2 Introduce typed child outcomes and working-context deltas; merge only confirmed changed files from successful children and keep Git-observed changes non-attributed.
- [ ] 3.3 Split Git process execution, snapshot composition, and rendering; make inspection asynchronous with explicit available, not-repository, and unavailable outcomes.
- [ ] 3.4 Gate Git inspection before process launch so it occurs only for Team/Personal runs with a declared project directory that Git recognizes as a worktree.
- [ ] 3.5 Correlate session-actor Git continuations with turn generations and discard stale results; await bounded spawn/completion snapshots at subagent async boundaries.
- [ ] 3.6 Add deterministic tests for Public no-inspection, missing/non-Git project handling, sanitized failures, stale continuation rejection, child isolation, successful confirmed merge, and failed/cancelled no-merge without sleeps.
- [ ] 3.7 Update engineering docs and the versioned `netclaw-operations` and `netclaw-projects` system skills for working-context and subagent behavior.
- [ ] 3.8 Run targeted tests, prompt/tool eval suites, `dotnet test`, Slopwatch, file-header verification, and `git diff --check`; open and babysit Stage 3 through review, CI, merge, and post-merge `dev` verification.
- [x] 3.1 Introduce a framework-owned child-run scope that forks immutable parent authority and a read-only working-context snapshot while allocating independent child activity state.
- [x] 3.2 Introduce typed child outcomes and working-context deltas; merge only confirmed changed files from successful children and keep Git-observed changes non-attributed.
- [x] 3.3 Split Git process execution, snapshot composition, and rendering; make inspection asynchronous with explicit available, not-repository, and unavailable outcomes.
- [x] 3.4 Gate Git inspection before process launch so it occurs only for Team/Personal runs with a declared project directory that Git recognizes as a worktree.
- [x] 3.5 Correlate session-actor Git continuations with turn generations and discard stale results; await bounded spawn/completion snapshots at subagent async boundaries.
- [x] 3.6 Add deterministic tests for Public no-inspection, missing/non-Git project handling, sanitized failures, stale continuation rejection, child isolation, successful confirmed merge, and failed/cancelled no-merge without sleeps.
- [x] 3.7 Update engineering docs and the versioned `netclaw-operations` and `netclaw-projects` system skills for working-context and subagent behavior.
- [ ] 3.8 Run targeted tests, prompt/tool eval suites, `dotnet test`, Slopwatch, file-header verification, and `git diff --check`; open and babysit Stage 3 through review, CI, merge, and post-merge `dev` verification, then close GitHub issue #1633 with the acceptance evidence.

## 4. Closeout

- [ ] 4.1 Verify all three merged stages against the OpenSpec scenarios and PRD traceability from current `dev`, including serialization/recovery and MCP compatibility evidence.
- [ ] 4.2 Sync the delta specs to main specs with `/opsx-sync`, run `/opsx-verify`, and archive the completed change with `/opsx-archive`.
- [ ] 4.3 Run the RALPH adversarial output review, diagnostics, and after-action workflow; capture durable follow-ups without leaving undocumented behavior drift.
- [ ] 4.4 Decide whether `IToolAuditLogger` should adapt structured tool events into the canonical `SessionLogActor` transcript or be removed; Stage 2 deliberately retained the existing disabled production diagnostics and null audit sink to avoid behavior drift.
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,15 @@ internal static class LlmSessionTestExtensions
{
public static IServiceCollection AddLlmSessionCompositeRecords(this IServiceCollection services)
{
services.TryAddSingleton(TimeProvider.System);
services.TryAddSingleton<IGitWorkingContextInspector, GitWorkingContextInspector>();
services.TryAddSingleton<IWorkingContextSnapshotProvider, WorkingContextSnapshotProvider>();
services.TryAddSingleton(sp => new SessionServices(
sp.GetRequiredService<IChatClientProvider>(),
sp.GetRequiredService<ISystemPromptProvider>(),
sp.GetService<IReadOnlyList<IContextLayerProvider>>() ?? Array.Empty<IContextLayerProvider>(),
sp.GetRequiredService<IWorkingContextSnapshotProvider>(),
sp.GetService<TimeProvider>() ?? TimeProvider.System,
sp.GetRequiredService<TimeProvider>(),
sp.GetRequiredService<NetclawPaths>()));

services.TryAddSingleton(sp => new SessionMemoryServices(
Expand Down
23 changes: 19 additions & 4 deletions src/Netclaw.Actors.Tests/Sessions/SessionStateTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -634,15 +634,16 @@ public void ProcessedReminderIds_is_not_persisted_in_snapshot()
[Fact]
public void Successful_subagent_merge_adds_only_confirmed_changed_files()
{
var child = new SubAgentWorkingContextInfo
var child = new WorkingContextDelta
{
ReadFiles = ["src/ReadOnly.cs"],
ConfirmedChangedFiles = ["src/Changed.cs"],
ObservedChangedFiles = ["src/ObservedOnly.cs"]
};

var merged = LlmSessionActor.MergeSuccessfulSubAgentWorkingContext(
WorkingContext.Empty, true, child);
WorkingContext.Empty,
new ChildRunCompletion.Completed(child));

Assert.Equal(["src/Changed.cs"], merged.RecentFiles);
}
Expand All @@ -651,12 +652,26 @@ public void Successful_subagent_merge_adds_only_confirmed_changed_files()
public void Failed_subagent_merge_does_not_change_parent_working_context()
{
var current = WorkingContext.Empty.AddRecentFile("src/Existing.cs");
var child = new SubAgentWorkingContextInfo
var child = new WorkingContextDelta
{
ConfirmedChangedFiles = ["src/Denied.cs"]
};

var merged = LlmSessionActor.MergeSuccessfulSubAgentWorkingContext(current, false, child);
var merged = LlmSessionActor.MergeSuccessfulSubAgentWorkingContext(
current,
new ChildRunCompletion.Failed(SubAgentOutcomeReason.ToolExecutionFailed));

Assert.Same(current, merged);
}

[Fact]
public void Cancelled_subagent_cannot_supply_parent_working_context_changes()
{
var current = WorkingContext.Empty.AddRecentFile("src/Existing.cs");

var merged = LlmSessionActor.MergeSuccessfulSubAgentWorkingContext(
current,
new ChildRunCompletion.Cancelled(SubAgentOutcomeReason.CancelledByParent));

Assert.Same(current, merged);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,9 @@ You specialize in daemon health checks.
toolAccessPolicy,
approvalService: null,
promptProvider,
new WorkingContextSnapshotProvider(Microsoft.Extensions.Logging.Abstractions.NullLogger<WorkingContextSnapshotProvider>.Instance),
new WorkingContextSnapshotProvider(
new GitWorkingContextInspector(TimeProvider.System),
Microsoft.Extensions.Logging.Abstractions.NullLogger<WorkingContextSnapshotProvider>.Instance),
Microsoft.Extensions.Logging.Abstractions.NullLogger<SubAgentSpawner>.Instance);

registry.Register(new SpawnAgentTool(subAgentRegistry, spawner, subAgentPaths));
Expand Down
Loading
Loading