IMPLEMENTATION_PLAN.md
Rama dev · réplica de lectura del repositorio original.
# Netclaw Implementation Plan
Execution plan last updated: 2026-08-28. Restart review proposal added: 2026-09-18.
This is the execution plan for Netclaw. Autonomous agents and RALPH-style loops
SHALL work from `NOW` by default. `NEXT` and `LATER` work belongs in
`BACKLOG_PARKING_LOT.md` unless the user explicitly reprioritizes it.
## Operating Principle: Swing Through The Ball
A task is not done when the local component accepts input, renders a screen, or
writes a file. A task is done when the downstream runtime path consumes the
produced artifact successfully, or bad input is rejected before it crosses the
boundary.
Examples:
- A config editor is done only when runtime startup/ACL/routing consumes the
saved shape it emits.
- A TUI flow is done only when typed input, paste input, persisted state,
re-entry, and semantic smoke assertions all agree.
- A tool or adapter is done only when policy denial, invalid credentials,
missing resources, and happy-path dispatch are all covered.
- A planning task is done only when PRD, spec, OpenSpec, tests, docs, and skill
guidance point at the same behavior.
## Verification Levels
Use the highest level required by the task. Higher levels include the lower
levels unless explicitly stated otherwise.
| Level | Name | Required proof |
|-------|------|----------------|
| L0 | Planning-only | PRD/spec/docs updated; no runtime behavior changed. |
| L1 | Unit/contract | Targeted unit or contract tests prove pure behavior, serialization, validation, mapping, or policy decisions. |
| L2 | Integration | Component integration tests prove real persistence, DI, actor lifecycle, config binding, or fake-provider boundaries. |
| L3 | Interactive/smoke | Native smoke tape, CLI/TUI smoke, or equivalent real binary exercise proves the user-visible path. |
| L4 | Live/demo/e2e | Aspire demo, live provider, Docker image, or full runtime flow proves external/runtime wiring. |
## Non-Negotiable Quality Gates
These gates apply to every `NOW` task unless the task explicitly says why a gate
does not apply.
- [ ] **Discovery gate:** Read the matching PRD, spec, OpenSpec capability, and
active change plan before coding.
- [ ] **Consumer gate:** Name the downstream consumer of any config, event,
actor message, persisted record, tool schema, or protocol payload the task
writes.
- [ ] **Canonical representation gate:** Prove the producer emits the exact
representation expected by the consumer, not merely a schema-valid value.
- [ ] **Negative-path gate:** Add at least one invalid/unresolved/denied test for
every security-relevant or routing-relevant input.
- [ ] **No silent fallback gate:** Misconfiguration fails visibly; fallback is
allowed only when partial failure is normal runtime behavior.
- [ ] **Schema gate:** Any `*Config` property change updates
`src/Netclaw.Configuration/Schemas/netclaw-config.v1.schema.json`.
- [ ] **TUI gate:** Termina/init/config changes run the native smoke harness and
include semantic assertions, not just screen text.
- [ ] **Runtime gate:** If config drives runtime behavior, verify startup,
runtime binding, ACL, routing, or tool execution consumes the saved config.
- [ ] **Docs/spec gate:** Behavior changes update the relevant docs/specs and
any mapped system skill.
- [ ] **Repository gates:** Run `dotnet test`, `dotnet slopwatch analyze`,
`pwsh ./scripts/Add-FileHeaders.ps1 -Verify`, and `git diff --check` unless
the task explicitly scopes to docs-only work.
## Automation-First QA Floor
The human bare minimum should be priority calls, secrets/credentials for live
checks, and occasional high-risk UX spot checks. Agents are responsible for the
automatable proof below.
| Recent bug class | Required automation | Human minimum |
|------------------|---------------------|---------------|
| Typed input does not reach a TUI field | Headless Termina test with `VirtualInputSource` covering typed characters, paste, Tab/focus movement, Enter/submit, Escape/back. Critical flows also need a native VHS smoke tape. | Run or review one live command only when a real terminal/TTY bug is suspected. |
| Dynamic validation does not run | Fake-provider failure test proving save is blocked, persistence is unchanged, and the visible error is shown. Tests must call the same public save path the UI uses. | Provide real provider credentials only for optional live probes. |
| Old config paradigm not ported to new editor | Load/round-trip tests from existing config and secrets into the new editor model, then back to disk. Tests must assert dormant values and secrets are preserved unless reset/delete is explicit. | Confirm whether stale fields should migrate, preserve, or fail. |
| Config shape accepted but runtime cannot consume it | Contract test between editor/init output and runtime options/ACL/routing/startup consumer. Assert canonical IDs/names/permissions, not just schema validity. | Decide behavior for ambiguous external API cases. |
| Smoke passes while semantic behavior is wrong | Smoke assertion script checks canonical persisted values, encrypted secrets, runtime-visible config, and error states. Screen text alone is not enough. | Review smoke artifact only if the assertion fails or UX changed substantially. |
| Async UI action fails silently | Public async method has direct tests; fire-and-forget handlers catch exceptions and surface status errors. Test fake exceptions from validation/save dependencies. | None by default. |
| Secret rotation/reset reintroduces old behavior | Tests cover blank-preserve, nonblank-replace, disable-preserve, reset-delete-immediate, and reopen-after-reset. | Confirm destructive copy in the UI. |
Minimum automation by surface area:
| Surface | Minimum gate |
|---------|--------------|
| Config editor | Static validation test, dynamic fake-failure test, existing-config round-trip test, config-to-runtime consumer test, native smoke for visible TUI paths. |
| Init wizard | Headless typed-input test for each prompt kind, native `init-wizard` smoke, existing-install path test, destructive-action double-confirm test. |
| Channel adapter | Options-binding test, ACL allow/deny tests, malformed/missing credential test, reply/routing integration or opt-in live smoke. |
| Tool/MCP | Schema generation test, schema coercion negative test, permission allow/deny/prompt tests, malformed metadata test. |
| Persistence/memory/session | Serialization round-trip, restart/recovery test, corrupt/missing state test, eval suite when prompt/memory behavior changes. |
| Packaging/demo | Install smoke, Docker image binary/version check, health endpoint check, opt-in demo smoke when runtime wiring changes. |
Manual-only acceptance criteria are not allowed for `NOW` implementation tasks.
If something truly cannot be automated, the task must say why and must provide
the smallest repeatable manual script plus expected output.
## Current Source Artifacts
- Product: `PROJECT_CONTEXT.md`, `docs/prd/README.md`, `docs/prd/PRD-001-netclaw-mvp.md`
- CLI/config: `docs/prd/PRD-004-cli-onboarding-and-config.md`, `docs/spec/SPEC-004-cli-contract.md`, `docs/spec/SPEC-007-guided-onboarding.md`, `openspec/specs/netclaw-config-command/spec.md`, `openspec/changes/netclaw-config-command/tasks.md`
- Security/gateway: `docs/prd/PRD-002-gateway-security-envelope.md`, `docs/spec/SPEC-001-runtime-boundaries.md`, `docs/architecture/tool-authorization.md`, `openspec/specs/tool-authorization/spec.md`
- Input adapters: `docs/prd/PRD-009-input-adapters-and-unified-input.md`, `openspec/specs/netclaw-input-adapters/spec.md`, `openspec/specs/netclaw-slack-socket/spec.md`, `openspec/specs/netclaw-discord-socket/spec.md`, `openspec/changes/add-mattermost-channel/tasks.md`
- Models/providers: `docs/prd/PRD-005-model-provider-strategy.md`, `docs/spec/SPEC-008-model-provider-abstraction.md`, `openspec/specs/netclaw-model-providers/spec.md`
- MCP/tools: `docs/prd/PRD-006-mcp-tool-integration.md`, `openspec/specs/netclaw-mcp/spec.md`, `openspec/specs/netclaw-tools/spec.md`, `openspec/specs/tool-authorization/spec.md`
- Memory/personality: `docs/prd/PRD-007-agent-personality-and-local-memory.md`, `openspec/specs/netclaw-agent-memory/spec.md`, `openspec/specs/project-instructions/spec.md`
- Scheduling: `docs/prd/PRD-008-scheduling-and-periodic-tasks.md`, `openspec/specs/netclaw-scheduling/spec.md`, `openspec/specs/reminder-execution-history/spec.md`
- Testing: `docs/spec/SPEC-010-testing-and-smoke-strategy.md`, `TOOLING.md`
## Review Proposal: Shorter Daemon Restarts
Status: Approved for implementation on 2026-09-18.
This plan does not authorize a live daemon restart.
Baseline revision: `a4b81b5c9184d1986bfa7d872e0932ac636d4b71`.
Source PRDs: [PRD-001 FR-016](docs/prd/PRD-001-netclaw-mvp.md#fr-016-config-change-restart-coordination)
and [PRD-008](docs/prd/PRD-008-scheduling-and-periodic-tasks.md).
Related contracts: [SPEC-011](docs/spec/SPEC-011-daemon-architecture.md),
[session resume](openspec/specs/session-resume/spec.md), and
[reminders](openspec/specs/netclaw-scheduling/spec.md).
Use the [engineering glossary](docs/spec/GLOSSARY.md) for shared terms.
### Objective
Reduce the stop-to-listener interval while Netclaw preserves accepted work.
Do not replay a tool after its effect can be uncertain.
Do not increase the original turn authority.
Do not add channel-specific recovery code.
This work excludes CLI display latency and recovery after an ungraceful crash.
Issue [#2199](https://github.com/netclaw-dev/netclaw/issues/2199) tracks CLI latency.
### Observed Delay
One local restart supplied this evidence:
| Phase | Observed time | Finding |
|---|---:|---|
| Session drain | 190 seconds | Two sessions waited on durable approvals until the deadline. |
| Later teardown | About 16 seconds | Browser MCP disposal used about 10 seconds. |
| Service gap | About 26 seconds | The available logs do not assign this delay. |
| Process start to listener | About 8.2 seconds | Startup work delayed listener readiness. |
The startup trace included these approximate costs:
- Process setup and model probes used 1.5 seconds.
- SQLite and memory startup used 1.4 seconds.
- Four MCP connections used 4.2 seconds.
- Final hosted services used 1.1 seconds.
PR #2203 now connects independent MCP servers concurrently.
A new trace must measure its actual startup gain.
The unexplained 26-second service gap remains a separate investigation.
### Approved Stop Policy
Any graceful stop can create a restart reminder for safely interrupted work.
This policy covers updates, manual restarts, service restarts, and pod replacements.
A forced process kill cannot create this guarantee.
A restart reminder expires ten minutes after the interruption.
The deadline does not reset after another process start.
An expired reminder logs one warning and does not start model work.
Netclaw creates no reminder for these states:
- The session completed its work and waits for user input.
- A tool can have an uncertain external effect.
- Partial assistant text reached the output stream.
- The stored turn has no channel type for current-session delivery.
- Pending inputs have different authority.
An open approval remains parked in the journal.
The existing approval path can re-drive it after user action.
This slice accepts temporary approval UI inconsistency after restart.
### Minimal Design
The design reuses these owners:
| Data or decision | Owner | Lifetime |
|---|---|---|
| Accepted input and original authority | Session journal | Durable |
| Selected input and current turn authority | `SessionInputFlow` | Actor-local |
| Active model and tool cancellation | `SessionExecutionLifetime` | Actor-local |
| Wakeup schedule and delivery | Reminder manager | Durable |
| Cross-process handoff | Restart manifest | Durable until startup reads it |
The session stores each accepted input before it sends an acknowledgment.
The record contains its ID, content, media, source ID, and `TurnContextRecord`.
Journal order supplies input order.
Terminal turn events close the consumed input IDs.
The session lifetime owner gives an active model call a two-second completion grace.
It then cancels the call and waits for that exact task to stop.
A durable approval wait uses the existing tool pipeline cancellation token.
The lifetime owner does not cancel a tool with an uncertain effect.
The actor returns one standard `ReminderDefinition` after a safe interruption.
The reminder uses `DeliveryKind.CurrentSession` and expires after ten minutes.
Its instruction is `Resume the work that was interrupted by the daemon restart.`
The restart manifest stores the definition.
Startup registers each fresh definition through `SaveReminderCommand`.
The reminder manager owns scheduling, retry, deduplication, and delivery.
Existing gateways deliver the reminder through their current path.
The session recognizes the internal reminder ID.
It restores pending input and its original `TurnContextRecord` from the journal.
The reminder supplies a wakeup signal only.
Its automation authority does not replace the stored authority.
No route binder is required.
No channel state is stored.
No resume candidate protocol is required.
No channel adapter changes are allowed.
### Ordered Flow
This flow is schematic.
It omits persistence callbacks and normal reminder settlement.
```text
input -> session: SendUserMessage
session -> journal: InputAdmitted
journal -> session: stored
session -> source: CommandAck
stop -> session: PrepareForDaemonRestart
session -> model: cancel and await exact task
session -> stop: standard ReminderDefinition or none
stop -> manifest: restart reminders
start -> reminder manager: SaveReminderCommand
gateway -> session: ordinary reminder turn
session -> journal: restore pending input and authority
session -> model: resume stored work
```
### Risk Controls
| Risk | Control | Required proof |
|---|---|---|
| Lost accepted input | Persist before acknowledgment. | Actor test with journal replay. |
| Duplicate input after a lost acknowledgment | Keep a bounded source ID ledger. | Retry test with one pending record. |
| Duplicate tool effect | Close input before tool execution. Do not cancel uncertain effects. | Actor test for a started tool batch. |
| Repeated partial reply | Track streamed text. Do not create a reminder. | Actor stream test. |
| Stale wakeup | Store an absolute ten-minute expiry. | Fake-time startup test. |
| Wider authority | Restore the journaled `TurnContextRecord`. | Actor test with low trust input. |
| Duplicate reminder registration | Use one stable reminder ID and `CreateOnly`. | Startup conflict test. |
| Missing delivery route | Require a stored channel type. Let the reminder system resolve the gateway. | Actor test with missing channel context. |
| Manifest truncation | Write the manifest atomically. | Store round-trip test. |
| Long cooperative cancellation | Keep the bounded daemon stop deadline. | Test a client that ignores cancellation. |
### Pull Request Stack
**PR #2207: Plan and contracts.**
- Record the measured delay and its limits.
- Record the ten-minute policy and safety boundaries.
- Keep all later pull requests traceable to this objective.
**PR #2208: Stop durable approval waits.**
- Cancel only a tool pipeline that waits only on journaled approvals.
- Wait for the exact pipeline task before drain acknowledgment.
- Keep the approval request usable after cold recovery.
**PR #2209: Persist accepted input.**
- Add `InputId`, `InputAdmitted`, and `InputClosed`.
- Persist input before acknowledgment.
- Restore pending input from the journal and snapshots.
- Deduplicate a retry by its stable source identity.
**PR #2210: Resume through the reminder manager.**
- Give the active model call a two-second grace.
- Cancel and await an eligible call.
- Put a standard restart reminder in the manifest.
- Register fresh reminders at startup.
- Restore original input and authority when the reminder arrives.
- Change no channel-specific file.
**PR #2211: Bound daemon stop time.**
- Reduce the final daemon stop budget to 30 seconds.
- Keep the drain deadline below the host and service deadlines.
- Preserve timeout diagnostics for sessions that cannot stop safely.
### Validation
Run focused actor tests for the state transitions and journal boundaries.
Run daemon tests for manifest production and reminder registration.
Run reminder tests for one-shot expiration.
Run the behavioral eval suite because the operations skill changes.
Use `https://spark2.testlab.petabridge.net/` for eval execution.
Run `dotnet slopwatch analyze` after code changes.
Run `./scripts/Add-FileHeaders.ps1 -Verify` after code changes.
Run OpenSpec validation for each changed contract.
A process check must use a disposable `NETCLAW_HOME`.
It must prove a fresh reminder resumes work once.
It must prove an expired reminder stays quiet.
It must prove a parked approval remains usable.
It must not replace the active daemon binary on this system.
### Completion Conditions
- Approval-only drain finishes after pipeline cancellation.
- Safe model interruption returns one standard reminder.
- Completed sessions create no restart reminder.
- Startup ignores reminders older than ten minutes.
- The resumed model call uses stored input and authority.
- No channel-specific code changes appear in the stack.
- The 30-second stop budget remains a fallback for unsafe states.
- Tests, OpenSpec validation, Slopwatch, headers, and evals pass.
## NOW
### Priority: Restore Restricted Session File Authority
Source PRDs: PRD-002 and PRD-007. Owner: `PathAccessPolicy`.
Use the [engineering glossary](docs/spec/GLOSSARY.md) for path and authority terms.
- [x] Deny implicit sibling access for Public and Team; preserve current roots and exact own legacy logs.
- [x] Verify parent/child authority and the approved legacy cross-run log exception.
- [x] Keep result conversion and attachment-destination investigation in separate review slices.
- [x] Complete deterministic boundary tests, system-skill evals, and repository quality gates.
Local actor tests: 3,883 passed; six platform-specific skips. Daemon tests: 1,098 passed.
Focused child-log and operations evals each passed five runs. Native Windows proof remains pending.
### Priority: Unify Session Storage And Temporary Files
- [x] New sessions bind one durable, versioned
[session storage envelope](docs/spec/GLOSSARY.md#session-storage-envelope).
- [x] Existing sessions keep their established workspace and log paths.
- [x] Parent and child processes receive separate managed temporary paths.
- [x] Successful subagent results return exact child log and artifact paths.
- [x] Use the current session and inherited trusted roots for ordinary file and
shell authority; remove special log, child, and foreign-session rules.
- [x] Remove `worktree_create`; expose `worktree_dir` and compose
`shell_execute` with `set_working_directory`.
- [x] Allow normal structured reads of ordinary `netclaw.json`
while keeping secret stores and control-plane state denied.
- [x] Fix collision-safe session roots, journal-only legacy discovery,
root-segment link checks, old background-job JSON, and per-log resolver locks.
- [x] Replace weak worktree and child-handoff eval assertions and complete the
repository gates.
- [x] Update the operations skill, runbooks, and release notes to the revised
path, authority, worktree, and configuration contracts.
- [ ] Harvest sanitized traffic after the binary swap.
### Priority: Secure Host Pairing and Recovery
**PRDs:** `docs/prd/PRD-002-gateway-security-envelope.md`, `docs/prd/PRD-004-cli-onboarding-and-config.md`
**Spec:** `openspec/changes/secure-host-pairing-proof/`
**Surface area:** daemon security, CLI, HTTP, device registry, operations
**Verification:** L3
The host must generate a pairing code in every exposure mode without granting
host authority to traffic that a tunnel or proxy forwards through loopback.
Done when:
- [x] A versioned Data Protection proof authorizes the host-only endpoint.
- [x] The SignalR hub no longer exposes pairing code generation.
- [x] Valid device records, tokens, and exposure settings survive the upgrade.
- [x] A duplicate device name or registry failure does not consume a valid code.
- [x] Tests prove host success and remote denial in every exposure mode.
- [x] Process smoke proves host recovery without a live tunnel dependency.
- [x] The operations skill and next `0.27` beta website task describe the new procedure.
- [x] The public draft PR receives normal CI before the fixed beta.
- [x] The host command keeps proofs away from remote client endpoints, proxies, redirects, and bearer headers.
- [x] One pairing actor owns the code and exchange transaction; actor tests prove mailbox order, cancellation, and recovery.
### Priority: Keep MCP HTTP Protocol Fallback Deterministic
**PRD:** `docs/prd/PRD-006-mcp-tool-integration.md`
**Spec:** `openspec/specs/netclaw-mcp/spec.md`
**Surface area:** MCP HTTP transport, daemon connections, CLI probes
**Verification:** L1 plus the existing HTTP MCP smoke tests
The MCP SDK can retain its discovery protocol version when probe cancellation
selects the initialize fallback. Netclaw must not send that stale version in an
initialize request.
Done when:
- [x] Daemon connections and CLI probes remove a retained protocol-version
header only from the initialize request.
- [x] Discovery and established-session requests keep their protocol-version
header.
- [x] Tests prove the header correction and preserve unrelated headers.
### Priority: Preserve The Daemon Working Directory
**PRD:** `docs/prd/PRD-001-netclaw-mvp.md`
**Specs:** `openspec/specs/netclaw-tools/spec.md`, `openspec/specs/tool-authorization/spec.md`
**Surface area:** daemon lifecycle, path normalization, shell authorization
**Verification:** L1 plus a live daemon restart
The daemon process directory must survive routine system temporary-directory
cleanup. Absolute path validation must not depend on that process directory.
Done when:
- [x] The daemon uses a durable runtime directory below the Netclaw home.
- [x] Absolute path normalization does not read the process working directory.
- [x] Focused tests and the full repository quality gates pass.
- [ ] An installed daemon restart confirms the live process uses the durable directory.
### Priority: Complete Tool Remediation
**PRD:** `docs/prd/PRD-006-mcp-tool-integration.md`
**Spec:** `openspec/changes/complete-tool-remediation/`
**Surface area:** internal tool receipts and model-facing tool results
**Verification:** L2
The receipt must drive the next action. The model must not depend on duplicated
handwritten guidance or an unchecked string code.
Done when:
- [x] Every `RecoverableCorrection` receipt carries one defined internal code.
- [x] Parent and child paths use one presenter before result delivery.
- [x] Hidden tools are not named by remediation.
- [x] Approval authority, scratch retry state, durable messages, and public APIs remain unchanged.
- [ ] The stacked follow-up handles `attach_file` exposure and native-tool shell mistakes separately.
### Priority: Stop Repeated Tool Cycles
**PRDs:** `docs/prd/PRD-001-netclaw-mvp.md`, `docs/prd/PRD-006-mcp-tool-integration.md`
**Spec:** `openspec/changes/stop-repeated-tool-cycles/`
**Surface area:** turn state, tool results, compaction, parent and child actors
**Verification:** standalone deterministic proof, private replay, L2, and observe-only evidence
Active tool loops can produce valid model, tool, and actor activity. The static
iteration limits stop productive work and stop exact cycles too late.
Done when:
- [ ] Successful normal compaction preserves loaded deferred schemas.
- [ ] LLM failure and context overflow evict loaded schemas.
- [ ] A six-entry detector blocks exact periods one through three before execution.
- [ ] The first block returns paired correction results without a side effect.
- [ ] A repeated blocked action forces a truthful text-only response.
- [ ] Parent and child actors produce equal decisions from equal histories.
- [ ] Replay and observe-only evidence contain no confirmed false execution block.
- [ ] The parent and child iteration limits are removed only after all gates pass.
- [ ] Logs contain decisions and counts, but no arguments, results, hashes, or identities.
### Priority: Prevent Native-Tool Shell Mistakes
**Stack parent:** PR #2046 (`fix/repair-tool-rollout-contracts`).
- [x] Specify `attach_file` as a policy-filtered Core tool that accepts the authorized source path directly.
- [x] Specify parser-owned exact executable matching without executable-private argument parsing.
- [x] Return a typed correction before shell approval or execution and expose one deferred schema actor-locally.
- [x] Prove parent/child, hard-deny, hidden-tool, and eventual-authorization boundaries.
- [x] Refresh deterministic and hosted PII-free behavioral evidence.
- [x] Complete Release, OpenSpec, header, formatting, Slopwatch, and adversarial-review gates before opening the stacked PR.
### Priority: Reduce Shell Approval Fatigue
**PRDs:** `docs/prd/PRD-002-gateway-security-envelope.md`, `docs/prd/PRD-006-mcp-tool-integration.md`
**Spec:** `openspec/specs/tool-authorization/spec.md`
**Surface area:** shell authorization, approval matching, security corpus
**Verification:** L2
#### Verified implementation: contained glob directory aliases
N1 keeps contained directory aliases within the existing shell authority model.
`openspec/changes/archive/2026-10-28-allow-contained-glob-directory-aliases/` defines target protection,
source and target scopes, incomplete reach, and the 4096-unit work bound.
Each intermediate directory visit and each link hop consumes one unit.
The implementation carries known targets even when another branch remains unresolved.
Only complete reach can support reusable approval.
The final proof includes independent authorizer cases, focused mutants, and the maintained corpus.
The coordinator owns the corpus audit and publication decisions.
The changed skill guidance passes its retained targeted case, five of five trials.
Broader model attempts remain incomplete or canceled at the user's request.
#### Active delivery: complete compound scopes
The `0.27.0-beta.4` session declared its project root. Later calls still used
inline `cd` with a pipeline and later statements. ShellSyntaxTree parsed the
source, but Netclaw lost reusable candidates after a directory join. The
source also contains `rm`, `python3`, and `dotnet new install` prompts with no
matching grant. Those prompts remain subject to approval.
The approved contract is in
`openspec/changes/reduce-complex-shell-approval-prompts/`. The goal is to
reuse existing grants only when every reachable verb, directory, and path
scope has proof. A parent folder grant covers descendants after each concrete
path passes symlink and protected-path checks. A deep glob cannot use that
rule without proof for every possible match.
**Delivery:** Five reviewable PRs are planned. The first Netclaw PR adds a
typed one-call directory correction and sanitized evidence. The second
Netclaw PR adds conservative coverage for complete static compounds. The
ShellSyntaxTree PR publishes bounded syntax facts, then tag
`0.4.0-beta.3` publishes the public package. The fourth PR pins that
package and consumes only proved facts. The user authorized automatic merge
after CI and independent security review.
The fifth PR adds an explicit repository grant for ordinary Git checkouts
and their registered linked worktrees. Existing folder grants retain their
path meaning. A main checkout with `--separate-git-dir` keeps folder scope.
**Delivery evidence:** ShellSyntaxTree PR #184 merged. Its `0.4.0-beta.3`
package passed the publish job and is available on NuGet. Netclaw PRs #2191,
#2193, and #2194 merged after their CI checks passed. PR #2194 pins the public
beta. PR #2195 adds the explicit repository grant and has passed local tests,
focused mutants, native approval smoke, and independent authority review.
Its final merge depends on CI for the branch after the PR #2194 merge.
The full hosted eval for PR #2195 passed 73 of 91 cases, so that suite remains
red. The isolated typed-directory case passed five of five runs. Several
failed worktree cases used successful read-only `git -C` calls, which the
typed-directory oracle rejects. The worktree creation case denied an
unapproved `git worktree add`, as required. The live daemon still runs
`0.27.0-beta.4`; these changes need a later Netclaw rollout.
**Gates:** Prove both outcomes of a directory change, every pipeline stage,
an ungranted verb, each possible path, redirects, hard denials, protected
paths, symbolic links, audience and session boundaries, and exact retries.
Run focused mutation tests at the authority boundary. Restore the public
beta from NuGet before the fourth PR. Keep unknown syntax, runtime
values, and deep globs under exact approval. Do not add executable-specific
argument parsers to Netclaw.
The user promoted this work into `NOW`. The work must reduce repeat prompts
without allowing an incomplete or unknown shell form.
Done when:
- [x] The sanitized v0.26.0-beta.3 approval window has 18 classified cases in
the linked ShellSyntaxTree and Netclaw OpenSpec changes.
- [x] `openspec/changes/archive/2026-08-15-structure-shell-approval-policy/` defines the typed
coordinator and actor protocol, per-candidate coverage, real and intent
scopes, token-boundary grants, reviewed policy catalog, bounded trace,
migration, and validation tasks.
- [x] The maintainer approves the ShellSyntaxTree 0.3.1 API names and the
use of separate authored-source facts for approval matching before
implementation.
- [x] The maintainer approves whether simple v2 grants gain token-prefix
authority during schema-3 migration or remain exact until re-approved.
- [x] Approval-store schema 3 uses closed shell token-prefix, shell legacy-exact,
and non-shell exact forms. Version-2 conversion keeps prior shell authority
exact, creates a byte-identical backup, and fails closed on invalid data or
storage errors.
- [x] New reusable shell grants use ShellSyntaxTree token facts with explicit
Bash or PowerShell identity. Distinct command occurrences remain visible
through actor checks, one-time keys, persisted events, and subagent prompts.
- [x] CLI and TUI list and revoke typed phrases with unambiguous labels. The
native approvals tape proves version-2 conversion, non-shell label display,
and revoke behavior against a published binary.
- [ ] The policy pipeline replaces the shell branches in `ToolAccessPolicy`
and `ShellApprovalMatcher`; any retained legacy scan is deny-only and cannot
authorize, create candidates, or widen scope.
- [x] `ToolAccessPolicy` no longer completes shell authorization synchronously.
It produces preflight facts only. `ShellPolicyCoordinator` is the sole path
that applies corrections, reviewed-safe coverage, grants, and a final shell
authorization result.
- [ ] Move the remaining parser-to-candidate projection out of the broad
`ShellApprovalMatcher` compatibility surface without changing its released
approval shapes.
The preliminary complete-footprint audit after PR #1947 found 1,484 added
production lines and 52 added control-flow lines. Later slices reduced the
post-corpus footprint from 10,085 lines and 663 control-flow lines to 9,693
and 634. Control-flow is now below the frozen 635-line baseline. The final
audit must justify the remaining 721-line delta through tested security or
compatibility obligations instead of deleting required distinctions.
- [x] Shell calls pass through one coordinator. It snapshots immutable parser
and run-scope facts, requests one typed actor batch, and composes grant and
reviewed-safe coverage per candidate before one final result.
- [x] The actor response preserves stable candidate IDs and typed persistent
store status. Duplicate IDs, mismatched facts, impossible grant states, and
internal stage faults deny without a prompt.
- [x] The shell coordinator returns a typed operator trace with at most 256
rows. Each row uses enum facts, a call-local candidate ID, a redacted
executable basename, grant scope, and grant time. The actor derives a match
or one near miss from one store snapshot pass. Raw paths, command text,
arguments, secrets, prompts, and session events never receive trace data.
- [x] The approval runbook documents the complete parser-fact-to-policy flow,
bounded status and finite-loop examples, partial candidate coverage, and a
trace-first operator diagnostic procedure. The source-generated fixture
replay pins current D02, D10, and D14 coordinator behavior.
- [x] The bundled reviewed-diagnostic catalog constrains shell-authored argv.
Parser-owned prefix order and lexical path-shape guards remain required.
Redirect, filesystem-value, provider, and unknown-expansion checks stay
separate.
- [x] The `git ls-tree` production special case is removed. Token-boundary
policy match handles longer parser phrases without private command grammar.
- [x] A synthetic workload corpus covers ordinary search, read, pipeline,
redirect, and file-change commands without production command text.
- [x] Sanitized post-swap evidence classifies 51 prompts across 202 shell calls.
Eleven executable live cases pin the intended allow and prompt boundaries.
- [x] Post-1952 live evidence froze 285 shell calls and 69 prompts.
Twenty-one sanitized cases sample expected, guidance, and ShellSyntaxTree gaps.
The coordinator executes one identity-free regression for each sampled case.
Each regression locks its evidence source, approval shape, and actor contact.
- [x] A safe pipeline stage can compose with a stored grant for each stage that
still requires approval.
- [x] The paired Windows PowerShell consumer pins official
ShellSyntaxTree `0.4.0-beta.1`.
Partial diagnostic syntax remains denial-only. Typed tree facts keep Windows
PowerShell 5.1 link-following recursion exact-only. Interactive Auto and
Approval offer only Once or Deny. Headless Auto denies the call. The exact
retry repeats hard-deny and protected-path checks. The explicit restore,
Release build, Security tests, Actors tests, Configuration tests, and 79
focused mutants passed against the official package. The Netclaw beta
remains unpublished.
- [x] Adopt ShellSyntaxTree `0.4.0-beta.2` with analyzer and approval-catalog
coverage for bounded PowerShell split/index/join projections on PowerShell 7
and Windows PowerShell 5.1. Dynamic projection operands stay strict. The
public package restore, Release build, full test suite, and all 79 focused
shell-policy mutants pass.
- [x] A prompt excludes a safe stage from the approval candidates that the user
can persist.
- [x] A prompt excludes candidates that existing session or persistent grants
already cover, while it preserves exact directory-scoped occurrences.
- [x] Reviewed-safe phrases grant no implicit authority to a headless, reminder,
or webhook run. Unattended candidates need explicit one-time or stored-grant
authority; approval-exempt shell side effects retain their bounded exception.
- [x] A one-time retry is bound to the exact prompted candidate set, including
each effective directory, across live, sub-agent, and redrive paths.
- [x] External paths, mismatched grants, dynamic syntax, and hard-deny rules
keep their strict behavior.
- [x] PowerShell 7 command-argument regions with no child commands can reuse an
explicit host grant when ShellSyntaxTree reports a complete known region.
Unknown receivers, unknown syntax nodes, methods, assignments, and executable
substitutions remain one-time-only. A body command needs separate authority.
- [x] Bash causal approval intent composes exact stored grants for an initial
exact ShellSyntaxTree 0.3.4 directory change with reviewed diagnostic tails.
Execution facts, folder grants, protected paths, headless authority, and
PowerShell remain strict. Each possible fallback passes the same symlink
check. POSIX `/tmp` aliases map through host path facts. The exact D03 fixture
must pass on Linux, macOS, and native Windows CI.
PR #1967 added exact PowerShell 7 and Windows PowerShell 5.1 strict-scope
rows. Both rows required approval despite matching stored grants. Workflow
`31859321334` passed Ubuntu, macOS, and Windows.
- [x] Directory operands preserve dotted directory names without weakening the
external-path or symlink checks.
- [x] Reviewed-safe policy matches canonical ShellSyntaxTree token prefixes.
It does not rewrite a variable `git ls-tree` operand or any other command.
- [x] Tool schemas and always-loaded guidance distinguish a persistent project
root from one-command `WorkingDirectory` scope, prevent redundant project
switches, and preserve `cd` when directory mutation is the requested shell
behavior.
- [x] Always-loaded guidance, shell metadata, and the operations skill prefer
first-party file tools for known reads, listings, and edits. Local repository
search, builds, tests, VCS, and process workflows remain shell use cases.
External discovery and retrieval remain routed through built-in `web_search`
and `web_fetch`. Five-run unaided evals passed 5/5 for read, listing, edit,
and local search. Web search passed 4/5 at the required 0.80 threshold.
- [x] Reviewed-safe shell work beneath an undeclared cwd returns the same
`set_working_directory` correction in parent sessions and subagents before
any user prompt. The original tool call remains unchanged. The registered
tool must accept the exact non-temp cwd; unsafe phrases, outside paths,
Public sessions, and unavailable scope tools retain normal approval behavior.
A successful child declaration updates only the child scope, reloads its
project instructions, and leaves the parent project unchanged. Headless
declarations prevent repeated corrections but do not grant execution
authority.
- [x] Bounded non-path `IntegerRange` and `Concatenation` data do not make a
complete shell command complex. Unknown values, identities, paths, and
redirects stay strict.
- [x] Sanitized behavioral eval cases cover early project declaration,
one-command typed scope, failed-path recovery, and deliberate inline `cd`.
- [x] Two naturalistic cases now test a child checkout beneath a declared
parent project. The prompts do not name a tool, `WorkingDirectory`, or `cd`.
Against the configured evaluation model, the worktree status baseline passed 1/5.
Three failures used `git -C`, and one failure used inline `cd`. The source
inspection baseline passed 0/5. Each run read the named file, then tried
shell search before `file_list`.
- [x] The shell schema and always-loaded rules now separate `Command` from
one-call directory selection. The rules do not name an executable syntax.
The unchanged worktree status case passed 4/5. Direct typed scope and the
deliberate inline-directory case each passed 5/5.
- [x] The unchanged source-inspection case passed 1/5. Four runs used shell
search after `file_read`. This result tracks a separate first-party recursive
file-search gap.
- [x] A sanitized subagent eval proves that a different user-named project is
declared before the child's first multi-command shell inspection. Absolute
path operands remain exact scopes, but do not create a trusted root. The
configured evaluation endpoint passed 4/5 runs. The assertion
orders declaration before two exact successful shell calls and verifies the
reported layout and build file. One run used one-shot scope without declaring
the project and failed as intended.
- [x] The session-scratch model-guidance eval passed 4/5 against the configured
configured evaluation endpoint. This measures headless path preference;
deterministic actor tests own interactive correction and approval proof.
- [x] Post-0.26.0 live evidence in
`openspec/changes/archive/2026-08-15-structure-shell-approval-policy/evidence/post-1952-live-approval-harvest.json`
classifies 69 prompts across 285 shell calls. The largest avoidable cluster
is 15 read-heavy prompts from one headless subagent working beneath shared
platform temp even though its execution scope already owns private session
scratch.
- [x] Personal and Team subagents now receive the exact bound `session_dir` in
their volatile working context before the first model call. Public context
remains redacted, and prompt-worthy headless shell work remains denied
without explicit authority. The first delegated eval result was invalidated:
all 5/5 runs omitted `WorkingDirectory` and passed through the existing shell
fallback. After the exact assertion and guidance were corrected, the fresh
`a1077feb-6bd7-413c-8a90-c651aa5a03df` run passed 4/5 against
the configured evaluation model. Four children passed the exact bound session
directory on both Git diagnostics; one omitted it and failed as intended.
- [x] Removing the prescribed answer from the existing parent-only disposable
output eval produced 3/5 path-aligned runs. All five completed through the
private session directory, and all recovered from denied shell writes with
first-party file tools, but two first attempted `/tmp` or `mktemp`. This is
retained as alignment evidence rather than reported as a passing gate.
- [x] Explicit `WorkingDirectory=/tmp` and deliberate inline `cd /tmp` evals
remain in the corpus so a platform-temp requirement is not rewritten.
- [x] Eligible interactive Personal shell work at the shared platform-temp root
receives a typed session-scratch correction before parent or subagent
approval. The original call remains in history. One exact later retry offers
only Once or Deny and creates no reusable temp authority. Headless behavior,
Team/Public denial, hard-deny rules, dynamic syntax, protected paths, and
native PowerShell causal scope stay strict.
- [x] Seven sanitized tool-friction cases replay through registration, policy,
dispatch, typed outcomes, and success-owned working context. Structured
search, batch read, JSON selection, image metadata, and spill continuation
need no shell approval. Representative interpreter fallbacks still prompt.
Parent and child diagnostics report counts and outcome categories only.
- [x] The hosted PR 7 comparison ran five trials per selected scenario. Search,
batch read, JSON selection, image metadata, and deferred discovery passed
25/25 with the intended structured tools and no shell fallback. Spill
continuation passed 0/5: all five runs used shell, none loaded or called
`tool_output_read`, and one run searched the catalog. Keep this failure as a
rollout finding; do not weaken its assertion.
- [x] Complete native platform CI, stack merge, binary swap, and a new
sanitized live traffic harvest.
- [ ] Define automated session-directory cleanup in a separate OpenSpec before
adding retention or deletion behavior.
- [ ] A constrained executable grammar proves any future safe `sed` form. The
`-n` option alone is not proof because a `sed` program can write files or
execute commands.
- [ ] Audited ShellSyntaxTree bindings prove bounded `grep` pattern operands,
including escaped BRE alternation, are non-filesystem data. File-bearing
options and unresolved patterns remain strict.
- [x] Netclaw consumes ShellSyntaxTree 0.3.0-alpha command occurrences and
explicit Bash redirect facts for the existing grammar.
- [x] Netclaw consumes ShellSyntaxTree `0.3.0` through its corrected
closed analysis API. The consumer uses joined arguments, value-domain type
patterns, redirect alternatives, and redirect-source alternatives. The
unchanged 225-test Bash, PowerShell 7, and Windows PowerShell 5.1 approval
matrix passes locally.
- [x] Netclaw resolves ShellSyntaxTree `0.3.2` for the separate authored-source
and path-shape facts introduced in 0.3.1. This store-v3 slice preserves those parser token facts
without executable-private command rules; later parent tasks consume the new
value-domain facts in the coordinator.
- [x] Netclaw consumes public ShellSyntaxTree `0.3.3` for the parser-owned
authored filesystem domain. Local code accepts only `Exact` and `FiniteSet`.
It checks each value through path policy and keeps unsafe transforms strict.
The Release build and all 7,138 runnable tests pass. The suite reports 15
expected platform or opt-in skips. Adversarial review and all required CI
checks passed before merge.
- [x] Netclaw consumes public ShellSyntaxTree `0.3.4` for parser-owned
working-directory effects. The causal policy does not recognize command
names or private executable grammar. Native platform CI remains required.
- [x] Netclaw consumes public ShellSyntaxTree `0.3.5` for positive authored
non-filesystem values. Only that argument's compatibility and lexical path
interpretations are omitted. Other arguments, redirects, effects, dynamic
values, and unknown commands remain strict. The sanitized live `tr -d '\\n'`
loop reuses its existing `gh run view` grant without creating a `/n` scope.
- [x] Parent, child, schemas, skills, and always-loaded rules share one
directory order and one shell-composition order. Approval-required shell
variants do not receive authority from that guidance.
- [x] ShellSyntaxTree 0.3.5 already publishes complete facts for the ten new
sanitized fresh-session samples. Sixteen focused tests and one corpus entry
pin current occurrence, cwd, redirect, argument, and dynamic boundaries.
- [x] A fixed leaf-glob root may contain an existing in-root final alias.
Broken, external, or uninspectable aliases remain strict. The exact
recursive-search pipeline now produces reusable candidates without private
executable parsing.
- [x] Five changed runs per eight cases used the baseline model and
configuration. Structured guardrails remained 15/15. Four alignment cases
stayed below threshold. Prompt equivalents fell from 32 to 25, while the
explicit directory-transition shell attempts fell from 24 to 12. These
variable results remain recorded, not converted into weaker assertions.
See `src/Netclaw.Security.Tests/Evidence/ApprovalEvidence/post-guidance-fresh-session-eval-results.json`.
- [x] PR #1982 merged as `9d02d19efd75fe871c8603e151e3e7169a9d9433`.
The live daemon preserved a rollback binary, swapped to that exact commit,
recovered its session catalog, and passed five natural fixed-pipeline runs
without an approval or complex marker.
- [x] Forty-five post-swap fresh sessions are frozen in
`src/Netclaw.Security.Tests/Evidence/ApprovalEvidence/post-9d02d19-binary-swap-eval-results.json`.
The original eight workloads improved from 17/40 to 23/40 behavior passes;
the added fixed-pipeline case passed 5/5. Observed prompt equivalents fell
from 25 to 2, but 446 historical grants make that prompt delta non-causal.
Fifteen retained trust-zone denials exposed redundant file verification,
disposable redirects, child absolute paths, and retry-after-denial debt.
- [x] PR #1983 merged as `7efa7fd0f711696343cd7d5e3d2abf75d20707d6`.
The exact binary was swapped into the live daemon with the prior binary kept
for rollback. Fifteen fresh affected sessions then improved from 2/15 to
14/15 behavior passes, and shell attempts fell from 25 to 6. Known-file edit
and disposable-output cases passed 10/10 without shell. All five requested
external directory transitions remained denied; one fallback-model run made
one additional shell call after denial. The initial controlled DeepSeek run
was blocked by provider billing, so the live comparison is operational
deployment evidence rather than a same-model causal estimate. See
`src/Netclaw.Security.Tests/Evidence/ApprovalEvidence/post-7efa7fd-followup-live-eval-results.json`.
- [x] After billing resumed, an isolated same-model rerun of the exact merged
image passed known-file and disposable-output cases 10/10, but the terminal
directory-denial case passed only 3/5. Both failures followed stale inherited
guidance by calling `set_working_directory` after `Tool access denied:`.
- [x] The pending correction removes that contradictory recovery rule and pins
denial versus deferred-correction behavior. On one DeepSeek image, all three
affected cases passed 15/15. Structured-file cases used no shell. All five
external directory transitions produced exactly one hard denial, no scope
correction, and no retry. See
`src/Netclaw.Security.Tests/Evidence/ApprovalEvidence/post-terminal-denial-guidance-eval-results.json`.
- [x] The terminal-denial guidance correction merged in PR #1985. The exact
merged binary was swapped into the live daemon. Five fresh live sessions
retained all five required denials. Three stopped without a substitute call;
one made a safe call before denial, and one made a safe call after denial.
- [x] Issue #1892 now has a seeded Git project and natural project-scope evals.
The final direct-provider image declared the project before project tools in
5/5 runs, avoided declarations for unrelated prompts in 5/5, and recovered
from a rejected named path in 5/5. The fix is
guidance-only: it declares the task's first path before probing, then uses a
user-provided fallback after rejection. It adds no shell authority.
- [x] A post-#1990 live sample found four approvals among ten shell calls. All
four calls joined independent diagnostics, and one also used inline `cd`.
The strict direct-provider baseline passed 3/5 and contained one prompted
compound run. Generic independent-operation guidance raised the exact
committed image to 5/5. It produced five successful single-operation shell
calls and no approval equivalent. See
`src/Netclaw.Security.Tests/Evidence/ApprovalEvidence/post-independent-operation-guidance-eval-results.json`.
- [x] The initial follow-up disposable eval was discarded. Its prompt requested
a diagnostic command but omitted the exact content required by its assertion.
The corrected case names the disposable file effect and exact content without
prescribing a tool.
- [x] Pre-delivery fresh-session evals passed the strengthened guidance boundary:
disposable file tools passed 5/5, terminal trust-zone denial passed 5/5
without a scope change or retry, and known file edit passed 4/5 at threshold.
The fifth edit run ended before model behavior with a provider 402 response.
An earlier denial run was discarded because its assertion expected a CLI log
shape that JSON mode does not emit; the corrected assertion binds the daemon's
exact authorization outcome.
- [x] The current live mixed-read chain is complex with inline `cd`. Supplying
the typed working directory makes the prompt reusable and exposes only the
unproved `sed` and escaped-`grep` pattern candidates.
- [x] The expanded 247-test matrix covers command-substitution and PowerShell
execution-region behavior. Known command-owned regions reuse independently
matched host and body grants after Netclaw accounts for the parsed body.
Unknown receivers and incomplete region facts remain prompt-only.
- [x] Unknown occurrences, cwd facts, wrappers, and redirects stay prompt-only.
Static descriptor redirects no longer appear dynamic.
- [x] The resolved POSIX `/dev/null` device does not create an approval
directory after host symlink checks. Other device paths and dynamic redirect
targets stay strict.
- [x] Netclaw consumes ShellSyntaxTree `0.3.0-alpha.1` and promotes Bash
command-resolution mutation and reserved execution forms into the strict
181-case review matrix.
- [x] ShellSyntaxTree `0.3.0-alpha.2` introduced one temporary POSIX PowerShell
child wrapper. Native-host activation removed that transitional consumer
behavior: Bash treats `pwsh` as an external command, and only a native
PowerShell host uses `PwshParser`.
- [x] The shell approval review table separates Bash, PowerShell 7, and Windows
PowerShell 5.1 rows. Cross-language payloads remain ordinary external-command
arguments; same-language static children use parser-returned occurrences.
- [x] A constrained stdin grammar allows a complete literal heredoc or bounded
here string only for argument-free `cat`. Unknown data, expanding heredocs,
arguments, wrappers, interpreters, and stored grants stay strict.
- [x] Netclaw interprets bounded non-path loop arguments through
ShellSyntaxTree 0.3.2's separate authored-source projection for approval
analysis. Effective runtime values retain priority, and path-bearing loop
arguments remain strict.
- [x] The approval matrix pins inherited and same-language child loops as
complex under the canonical unknown-state contract for Bash, PowerShell 7,
and Windows PowerShell 5.1. It also proves that a stored command grant
cannot cover an unproved loop-dependent argument.
- [x] Netclaw adopts bounded non-path `AuthoredValue`, status concatenation,
and the typed consumer boundary. Positive authored path evidence remains a
separate strict-policy task.
### Priority: Use Native PowerShell on Windows
**PRDs:** `docs/prd/PRD-001-netclaw-mvp.md`, `docs/prd/PRD-002-gateway-security-envelope.md`, `docs/prd/PRD-006-mcp-tool-integration.md`
**Specs:** `openspec/changes/archive/2026-08-10-native-windows-powershell-host/`
**Surface area:** shell execution, parsing, approval policy, model context
**Verification:** L2 plus native Windows L3
This work replaces `cmd.exe` with a native PowerShell host on Windows. Netclaw
prefers a compatible PowerShell 7.6 host and falls back to Windows PowerShell
5.1. It keeps Bash and PowerShell as separate host languages.
The additive foundation pins ShellSyntaxTree `0.3.0-alpha.5` and defines the
immutable environment, strict host probe, and process arguments. Runtime
activation now routes execution, policy, approval, background jobs, and model
context through the same resolved environment. PR #1848 auto-merged after
adversarial review and green native Windows CI. The canonical specifications
now contain the delivered contract. The OpenSpec change is archived.
Local validation on 2026-08-10 passed restore, the zero-warning Release build,
the full solution test suite, changed-file format verification, headers,
Slopwatch, `git diff --check`, and strict OpenSpec validation. The shell-platform
behavioral evaluation was unavailable because the required
`NETCLAW_EVAL_PROVIDER_TYPE`, `NETCLAW_EVAL_PROVIDER_ENDPOINT`, and
`NETCLAW_EVAL_MODEL_ID` settings were absent. This result is blocked evidence,
not an evaluation pass.
Native Windows workflow run `31381580072` passed the zero-warning Release build,
the complete security, actor, and daemon test suites, package staging, and CLI
smoke tests. The production resolver and deterministic dialect matrix cover
PowerShell 7.6 and Windows PowerShell 5.1. The Windows suite also executed the
explicit Windows PowerShell 5.1 host test.
The final OpenSpec verification mapped all 7 requirements and 24 scenarios to
runtime code and named tests. The focused verification passed 320 security
tests, 29 daemon tests, and 315 actor tests. One Windows-only actor test skipped
locally and passed in native Windows workflow run `31381580072`. Strict
validation passed all 78 OpenSpec items with no failures.
Done when:
- [x] One immutable shell environment selects the absolute executable path,
grammar, path style, process arguments, and PowerShell dialect for the daemon
lifetime.
- [x] Windows selects `pwsh.exe` only for versions from 7.6.4 through 7.6.x. It
falls back to `powershell.exe` 5.1 and fails clearly if neither host matches.
- [x] Execution, parsing, hard deny, approval matching, prompt display, and
model context use the same selected environment.
- [x] Bash treats `pwsh` as an external command. PowerShell treats `bash` as an
external command. Only same-language child hosts can recurse.
- [x] Unknown or incomplete facts cannot produce a stored approval candidate
or a safe-verb pass. Stored approval cannot bypass hard deny.
- [x] Personal sessions state the platform, executable, grammar, and dialect,
including sessions that have no project directory.
- [x] Native Windows tests cover PowerShell 7.6 and Windows PowerShell 5.1.
The security review table covers direct, child, retry, and background paths.
- [x] Consumer guidance and canonical specs match the delivered behavior. The
OpenSpec change passes verification, is synchronized, and is archived.
### Priority: Simplify Tool Execution Context Architecture
**PRDs:** `docs/prd/PRD-001-netclaw-mvp.md`, `docs/prd/PRD-002-gateway-security-envelope.md`, `docs/prd/PRD-006-mcp-tool-integration.md`, `docs/prd/PRD-007-agent-personality-and-local-memory.md`
**Specs:** `openspec/changes/archive/2026-07-15-simplify-tool-execution-context/`
**Surface area:** tool execution, session actors, subagents, working context
**Verification:** L2 plus behavioral evals
Deliver three sequential, independently reviewed PRs from the active OpenSpec
change. The series replaces nullable and context-free execution APIs with
required immutable scopes and semantic value objects, composes the session
pipeline with behavior-preserving dependency modeling, and gives child runs independent
working state with gated asynchronous Git enrichment.
Done when:
- [x] Stage 1 lands required run scopes, per-call isolation, and non-null
security/authority dependencies without compatibility shims.
- [x] Stage 2 lands the composed pipeline without changing existing background,
fallback, authorization, approval, MCP, or model-visible behavior.
- [x] Stage 3 lands child fork/delta semantics and Git inspection only for
non-Public runs with a declared Git project.
- [x] Each stage passes review, CI, post-merge fresh-worktree verification, and
repository quality gates. Stage 3 eval execution was attempted but explicitly
blocked because the required `NETCLAW_EVAL_*` provider environment was absent;
no model-facing tool schema or prompt behavior changed in that stage.
- [x] OpenSpec deltas are verified, synced, and archived after the final merge.
Durable execution details and checkbox state live in
`openspec/changes/archive/2026-07-15-simplify-tool-execution-context/tasks.md`. Per-run evidence
lives in `.ralph/runs/`; Git commits and PR state remain the recovery source of
truth across context compaction.
### Phase 0: Execution Governance
Purpose: prevent shallow local fixes from being mistaken for runtime-complete
work.
#### Task 0.1: Enforce the cross-boundary contract rule
**PRD:** `docs/prd/PRD-001-netclaw-mvp.md`
**Spec:** `docs/spec/SPEC-010-testing-and-smoke-strategy.md`
**Surface area:** cross-cutting
**Verification:** L0
Done when:
- [x] `AGENTS.md` references `IMPLEMENTATION_PLAN.md` as a read-first artifact.
- [x] `AGENTS.md` includes the Cross-Boundary Contract Rule.
- [x] This plan is the default routing artifact for build work.
- [x] `BACKLOG_PARKING_LOT.md` exists for non-now work.
#### Task 0.2: Add PRD/status traceability to the plan workflow
**PRD:** `docs/prd/README.md`
**Spec:** `docs/spec/SPEC-010-testing-and-smoke-strategy.md`
**Surface area:** docs
**Verification:** L0
Done when:
- [x] Every `NOW` task has a `PRD` reference.
- [x] Tasks with stale, missing, or conflicting PRD coverage are blocked until
the PRD/spec is updated.
- [x] If a task changes OpenSpec-covered behavior, the corresponding OpenSpec
workflow is used rather than hand-editing change artifacts.
#### Task 0.3: Add contract-test inventory for critical producer/consumer pairs
**PRD:** `docs/prd/PRD-001-netclaw-mvp.md`
**Spec:** `docs/spec/SPEC-010-testing-and-smoke-strategy.md`
**Surface area:** cross-cutting
**Verification:** L1
Done when:
- [x] Document the critical producer/consumer pairs in this plan or a linked
spec, including config editor -> runtime options, channel events -> ACL,
scheduler -> delivery gateway, tool schemas -> model/tool dispatcher, and
memory persistence -> prompt assembly.
- [x] For each pair, identify the canonical representation and the test file
that proves it.
- [x] Add missing tests or add explicit `NOW` tasks for gaps.
Inventory: `docs/spec/SPEC-010-testing-and-smoke-strategy.md` -> Critical
Producer/Consumer Contract Inventory. Remaining proof gaps are assigned to
explicit `NOW` tasks 3.1, 4.2, and 5.2-5.3.
#### Task 0.4: Automate recent regression classes
**PRD:** `docs/prd/PRD-001-netclaw-mvp.md`, `docs/prd/PRD-004-cli-onboarding-and-config.md`
**Spec:** `docs/spec/SPEC-010-testing-and-smoke-strategy.md`, `openspec/specs/netclaw-config-command/spec.md`
**Surface area:** testing, TUI, config
**Verification:** L3
Done when:
- [x] Every config/TUI task touching text input includes headless typed-input
tests for typed characters, paste, Tab, Enter, Escape, and re-entry when
applicable.
- [x] Every config leaf with dynamic validation has a fake-failure test proving
validation runs before persistence and leaves files unchanged.
- [x] Every config leaf ported from init/old editor paths has an existing-config
load/round-trip test covering dormant values and persisted secrets.
- [x] Every smoke tape with config writes has an assertion script that checks
canonical semantic output, not only screenshots or text.
- [x] Any async UI save/test action has a direct awaitable test path plus
fire-and-forget exception surfacing.
PR #2417 follow-up:
- [ ] Run `./evals/run-evals.sh` after the operator sets the provider type, endpoint, and model ID through `NETCLAW_EVAL_*`.
The local runner rejects the current environment because these values are unset.
#### Task 0.5: Add audit tests for plan-critical config editors
**PRD:** `docs/prd/PRD-004-cli-onboarding-and-config.md`
**Spec:** `openspec/specs/section-editor-abstraction/spec.md`, `openspec/specs/netclaw-config-command/spec.md`
**Surface area:** testing, config
**Verification:** L1
Done when:
- [x] A registry/audit test lists config leaf editors and fails when a visible
editor lacks round-trip coverage.
- [x] The audit requires each visible editor to declare whether it has dynamic
validation and, if yes, the test class that covers fake-failure behavior.
- [x] The audit requires each editor that writes secrets to have blank-preserve,
nonblank-replace, and explicit-delete coverage.
- [x] The audit requires each editor that writes runtime-consumed config to name
the runtime consumer and contract test file.
### Phase 1: Config Command And Channel Runtime Contracts
Purpose: finish the active config work all the way through runtime semantics.
`netclaw config` owns post-install tuning. It should cover ordinary changes an
operator might make after first run without re-entering bootstrap:
- Providers and Models route to their dedicated editors.
- Channels, Search, Security & Access, Exposure Mode, Skill Sources,
Telemetry & Alerting, Workspaces Directory, Inbound Webhooks, and Browser
Automation must not remain root-dashboard placeholders before this phase
closes.
- Identity/personality re-entry remains `netclaw init` / identity-owned work;
config may expose the Workspaces Directory because operators can move project
discovery roots after first run without regenerating identity files.
- Per-session project switching is runtime state owned by the
`set_working_directory` tool and the Audience Profiles `Change workspace`
permission, not a global config editor.
- General MCP server/permission editing remains `netclaw mcp`; Browser
Automation config may add/remove the canonical browser MCP profile, then route
grants to `netclaw mcp permissions`.
- Inbound webhook route-file authoring remains `netclaw webhooks` / route files
for this pass; config owns global enablement, execution timeout, route-count
visibility, and loud diagnostics when enabled with no routes.
- Advanced session tuning, logging verbosity, tool hard-deny overrides, and
low-level tool execution ceilings are not init-owned, but stay out of this
config-command close unless explicitly promoted.
#### Task 1.1: Complete Channels provider-backed validation and canonical persistence
**PRD:** `docs/prd/PRD-004-cli-onboarding-and-config.md`
**Spec:** `openspec/specs/netclaw-config-command/spec.md`, `openspec/specs/channel-audience-tui/spec.md`, `openspec/specs/netclaw-input-adapters/spec.md`
**Surface area:** UI, config, runtime contract
**Verification:** L3
Done when:
- [x] Slack channel names entered in config are resolved through Slack before
persistence.
- [x] Slack `AllowedChannelIds` persists canonical Slack channel IDs (`C...` or
`G...`) and never unresolved display names.
- [x] Slack channel audience keys are remapped to resolved channel IDs.
- [x] Discord channel IDs are checked through `IDiscordProbe.ResolveChannelIdsAsync`
before save.
- [x] Mattermost channel IDs are checked through a Mattermost config-time probe
before save.
- [x] Unresolved Slack, Discord, and Mattermost channel targets block save with
visible errors.
- [x] Existing configured secrets can be used for validation without prompting
on re-entry.
- [x] Tests cover Slack name -> ID resolution, Slack unresolved name rejection,
Discord unresolved ID rejection, Mattermost unresolved ID rejection, and secret
preservation.
- [x] Native smoke `./scripts/smoke/run-smoke.sh config-channels` passes with
semantic assertions on canonical persisted values.
- [x] Docker POC image rebuild/relaunch was not used for this task's verification;
native smoke provided the L3 gate.
#### Task 1.2: Finish generalized config leaf validation
**PRD:** `docs/prd/PRD-004-cli-onboarding-and-config.md`
**Spec:** `openspec/specs/netclaw-config-command/spec.md`, `openspec/specs/section-editor-abstraction/spec.md`
**Surface area:** config, UI, cross-cutting
**Verification:** L3
Done when:
- [x] Every `netclaw config` leaf has typed structural validation before save.
- [x] Runtime/probe validation is run where the leaf writes values consumed by
runtime startup, ACL, transport, tools, or daemon exposure.
- [x] Structurally invalid config is a hard block.
- [x] `Save anyway` exists only for transient runtime/probe failures, never for
schema violations, missing required security fields, or unresolved canonical
IDs.
- [x] Tests prove invalid path, URI, auth, binary, local-reference, and
reachability failures where those concepts apply.
- [x] Smoke assertions check semantic preservation and canonical output, not
byte-identical JSON.
#### Task 1.3: Complete `Security & Access` config area
**PRD:** `docs/prd/PRD-004-cli-onboarding-and-config.md`, `docs/prd/PRD-002-gateway-security-envelope.md`
**Spec:** `openspec/specs/netclaw-config-command/spec.md`, `openspec/specs/security-posture-tui/spec.md`, `openspec/specs/tool-authorization/spec.md`
**Surface area:** UI, config, security
**Verification:** L3
Done when:
- [x] `Security & Access` contains Security Posture, Enabled Features, Audience
Profiles, and Exposure Mode.
- [x] Security Posture remains distinct from runtime Enabled Features and
Audience Profiles.
- [x] Team/Public posture continues into Enabled Features; Personal posture does
not force that continuation.
- [x] Audience Profiles expose only curated high-level controls: Tool Access
(non-MCP), File Access, Incoming Attachments, Reset to posture default.
- [x] Reset to posture default resets the full underlying audience profile,
including hidden MCP and approval settings.
- [x] MCP permissions route to `netclaw mcp permissions`; they are not recreated
in this editor.
- [x] Tests cover round-trip, hidden-field reset semantics, and ACL consumer
expectations.
- [x] Native config smoke covers at least one posture change and one audience
profile reset with semantic assertions.
#### Human Review Checkpoint: Security & Access config editor
- [x] Completed 2026-06-01: human smoke passed in rebuilt
`netclaw-config-poc-local` container at commit `547c2c3`; no `401
Unauthorized` after enabling Reverse Proxy and entering MCP permissions.
Stop here after Task 1.3 is completed, verified, and committed. Do not continue
into Task 1.4 until a human has spot-checked the live `netclaw config` Security
& Access experience in a real terminal.
Human smoke focus:
- Security Posture reads clearly and continues to Enabled Features for Team and
Public, but not for Personal.
- Audience Profiles expose only curated controls and route MCP grants to
`netclaw mcp permissions`.
- Reset overrides visibly restores the posture baseline and the persisted JSON
clears hidden MCP and approval overrides.
- If Reverse Proxy is enabled from this TUI session, immediately entering MCP
permissions must not return `401 Unauthorized`; the local daemon client must
use the bootstrap `DeviceToken` written by the exposure-mode save.
- Exposure Mode is visible from Security & Access, but deeper Exposure Mode
behavior remains Task 1.4 work.
Human smoke finding 2026-06-01: enabling Reverse Proxy and then navigating into
MCP permissions in the same `netclaw config` process produced `401 Unauthorized`.
Treat this as a config/runtime credential refresh regression, not an acceptable
manual workaround. Regression coverage belongs with daemon-client authentication
tests because the config TUI reuses the same `DaemonApi` instance after exposure
mode writes a fresh bootstrap `DeviceToken`. Fixed by commit `547c2c37` and
confirmed by human retest in the rebuilt validation container.
#### Task 1.4: Complete Exposure Mode config leaf
**PRD:** `docs/prd/PRD-004-cli-onboarding-and-config.md`, `docs/prd/PRD-002-gateway-security-envelope.md`
**Spec:** `docs/spec/SPEC-006-gateway-exposure-and-remote-access.md`, `openspec/specs/daemon-exposure/spec.md`, `openspec/specs/device-pairing/spec.md`
**Surface area:** UI, config, daemon exposure
**Verification:** L3
Done when:
- [x] Explicit modes are Local, Reverse Proxy, Tailscale Serve, Tailscale
Funnel, and Cloudflare Tunnel.
- [x] `Daemon.ExposureMode` is the single active selector; no per-mode active
flags are introduced.
- [x] Inactive old values are preserved and ignored while inactive.
- [x] Each non-local mode has a mode-specific dialog; Local requires no extra
setup.
- [x] First non-local enablement auto-pairs the current configuring client when
no bootstrap/pairing state exists.
- [x] Orphaned or mismatched bootstrap state blocks with actionable guidance to
`netclaw doctor`, docs, and the tracked issue.
- [x] Tests prove config merge semantics and daemon exposure consumer binding.
- [x] Native config smoke covers at least one non-local mode and one return to
Local.
#### Task 1.5: Complete Workspaces, Inbound Webhooks, and Browser Automation config areas
**PRD:** `docs/prd/PRD-004-cli-onboarding-and-config.md`, `docs/prd/PRD-006-mcp-tool-integration.md`
**Spec:** `openspec/specs/netclaw-config-command/spec.md`, `openspec/specs/netclaw-mcp/spec.md`, `docs/spec/configuration.md`
**Surface area:** UI, config, workspaces, webhooks, MCP/browser tools
**Verification:** L3
Done when:
- [x] Workspaces Directory is editable from `netclaw config`, validates as a
local directory path, persists `Workspaces.Directory`, and preserves existing
identity files.
- [x] Tests prove `NetclawPaths.WorkspacesDirectory`, project discovery, and
prompt/workspace consumers read the saved `Workspaces.Directory` value.
- [x] Inbound Webhooks root entry routes to an implemented editor, not a
placeholder.
- [x] Inbound Webhooks editor controls `Webhooks.Enabled` and
`Webhooks.ExecutionTimeoutSeconds`; route-file editing stays in
`netclaw webhooks` / `~/.netclaw/config/webhooks/*.json` for this pass.
- [x] Enabling inbound webhooks with no valid routes fails loudly through doctor
or visible diagnostics; no dummy route is created silently.
- [x] Browser Automation root entry routes to an implemented editor, not a
placeholder.
- [x] Browser Automation detects required local runtime pieces, refuses enablement
when prerequisites are missing, and prints manual install guidance instead of
shelling out from the TUI.
- [x] Browser Automation persists/removes the canonical browser MCP server profile
(`browser_playwright` or `browser_chrome_devtools`) using the same shape runtime
MCP loading consumes.
- [x] Browser Automation grants route to `netclaw mcp permissions`; raw MCP grant
editing is not recreated in this editor.
- [x] Native smoke covers at least one successful save path and one blocked or
guidance-only path across these areas.
#### Task 1.6: Complete Skill Sources and Telemetry & Alerting config areas
**PRD:** `docs/prd/PRD-004-cli-onboarding-and-config.md`, `docs/prd/PRD-006-mcp-tool-integration.md`
**Spec:** `openspec/specs/netclaw-config-command/spec.md`, `openspec/specs/netclaw-mcp/spec.md`
**Surface area:** UI, config, operations
**Verification:** L3
Done when:
- [x] Skill Sources contains External Skills and Skill Feeds.
- [x] Skill Source validation covers paths, URIs, auth, and reachability where
relevant.
- [x] Telemetry & Alerting contains Telemetry and Outbound Webhooks only in this
pass.
- [x] Delivery-policy tuning stays parked.
- [x] Tests prove semantic round-trip, secret preservation, invalid URI/path
rejection, and runtime consumer binding where applicable.
- [x] Smoke tapes exercise both areas or document why an existing smoke covers
the route.
#### Human Review Checkpoint: Complete config surface
Stop here after Tasks 1.4, 1.5, and 1.6 are completed, verified, and committed.
Do not continue into Task 1.7 until a human has spot-checked the live
`netclaw config` experience in the rebuilt validation container.
Human smoke focus:
- Exposure Mode can switch to a non-local mode and back to Local without stale
runtime-active fields or missing local auth.
- Workspaces Directory, Inbound Webhooks, Browser Automation, Skill Sources,
Telemetry, and Outbound Webhooks are implemented pages, not root-dashboard
placeholders.
- Each page rejects structurally invalid values before persistence and preserves
unrelated config/secrets.
- Browser Automation creates/removes the canonical browser MCP profile and routes
grants to `netclaw mcp permissions`.
- Inbound Webhooks global enablement remains separate from route-file authoring;
no dummy route is silently created.
- `./scripts/smoke/run-smoke.sh light` has passed or any local blocker is
documented with evidence.
#### Task 1.7: Close the `netclaw config` OpenSpec change
**PRD:** `docs/prd/PRD-004-cli-onboarding-and-config.md`
**Spec:** `openspec/changes/netclaw-config-command/tasks.md`
**Surface area:** planning, config
**Verification:** L3
Done when:
- [ ] `openspec/changes/netclaw-config-command/tasks.md` accurately reflects
completed and incomplete implementation work.
- [ ] `openspec validate netclaw-config-command --type change` passes.
- [ ] `./scripts/smoke/run-smoke.sh light` passes on a clean runner or a local
blocker is documented with evidence.
- [ ] `/opsx-verify netclaw-config-command` passes.
- [ ] Spec deltas are synced or the change remains explicitly active with only
real unfinished tasks.
### Phase 2: Init Bootstrap Split
Purpose: keep first-run setup simple and move post-install editing to config.
#### Task 2.1: Simplify first-run `netclaw init`
**PRD:** `docs/prd/PRD-004-cli-onboarding-and-config.md`
**Spec:** `docs/spec/SPEC-007-guided-onboarding.md`, `openspec/changes/simplify-netclaw-init/tasks.md`
**Surface area:** TUI, config bootstrap
**Verification:** L3
Done when:
- [ ] Planning and code remove all `netclaw init --force` assumptions.
- [ ] First-run init contains bootstrap-owned steps only.
- [ ] Posture values remain `Personal`, `Team`, `Public`.
- [ ] Identity remains init-owned.
- [ ] Post-flight messaging points users to `netclaw chat` and `netclaw config`.
- [ ] Init smoke `./scripts/smoke/run-smoke.sh init-wizard` passes.
- [ ] Full light smoke passes or local blockers are documented with evidence.
#### Task 2.2: Implement existing-install init menu and destructive reset flow
**PRD:** `docs/prd/PRD-004-cli-onboarding-and-config.md`
**Spec:** `docs/spec/SPEC-007-guided-onboarding.md`, `openspec/changes/simplify-netclaw-init/tasks.md`
**Surface area:** TUI, config bootstrap, destructive actions
**Verification:** L3
Done when:
- [ ] Existing install shows exactly: `Redo identity setup`, `Open configuration
editor`, `Start over from scratch`, `Cancel`.
- [ ] `Open configuration editor` routes to `netclaw config`.
- [ ] `Redo identity setup` routes only into init-owned identity flow.
- [ ] Start-over dialog shows exactly: `Reset setup only`, `Full reset`,
`Cancel`.
- [ ] Both destructive actions require double confirmation.
- [ ] Tests cover refusal, menu routing, double confirmation, and preserved vs
deleted files.
- [ ] Smoke coverage exercises existing-install menu and start-over cancellation.
#### Task 2.3: Close the `simplify-netclaw-init` OpenSpec change
**PRD:** `docs/prd/PRD-004-cli-onboarding-and-config.md`
**Spec:** `openspec/changes/simplify-netclaw-init/tasks.md`
**Surface area:** planning, TUI
**Verification:** L3
Done when:
- [ ] `openspec validate simplify-netclaw-init --type change` passes.
- [ ] `/opsx-verify simplify-netclaw-init` passes.
- [ ] Init smoke and light smoke pass.
- [ ] Docs and skill guidance no longer describe stale init behavior.
### Phase 3: Runtime Adapter Contract Hardening
Purpose: prove each channel adapter accepts, denies, responds, and reports health
according to the same security envelope.
#### Task 3.1: Add adapter config-to-runtime contract tests
**PRD:** `docs/prd/PRD-009-input-adapters-and-unified-input.md`, `docs/prd/PRD-002-gateway-security-envelope.md`
**Spec:** `openspec/specs/netclaw-input-adapters/spec.md`, `openspec/specs/netclaw-slack-socket/spec.md`, `openspec/specs/netclaw-discord-socket/spec.md`, `openspec/specs/tool-authorization/spec.md`
**Surface area:** runtime, config, ACL
**Verification:** L2
Done when:
- [ ] Slack, Discord, and Mattermost options bind from the config shape emitted
by init/config editors.
- [ ] Allowed channel IDs and user IDs are consumed by runtime ACL in canonical
provider form.
- [ ] Denied channel, denied user, allowed channel, and DM policy cases are
covered per adapter.
- [ ] Misconfigured required tokens or server URLs fail closed for the affected
channel without enabling permissive ingress.
- [ ] Tests name the producer and consumer for each contract.
#### Task 3.2: Add runtime reply-path smoke for local/demo adapters
**PRD:** `docs/prd/PRD-009-input-adapters-and-unified-input.md`
**Spec:** `openspec/specs/netclaw-input-adapters/spec.md`, `openspec/specs/netclaw-testing/spec.md`
**Surface area:** runtime, smoke
**Verification:** L4
Done when:
- [ ] Mattermost demo smoke posts a user message and proves the daemon routes it
to a session and attempts a reply.
- [ ] Discord and Slack live smoke remain opt-in and credential-gated; absence
of credentials skips with clear output, not failure.
- [ ] Runtime logs expose enough detail to diagnose allowed/denied/routed/reply
states without leaking secrets.
- [ ] `TOOLING.md` documents the exact invocation and expected artifacts.
#### Task 3.3: Normalize channel diagnostics and doctor output
**PRD:** `docs/prd/PRD-003-operator-ux-ops-console.md`, `docs/prd/PRD-009-input-adapters-and-unified-input.md`
**Spec:** `docs/spec/SPEC-005-operator-ui-contract.md`, `openspec/specs/netclaw-operator-ui/spec.md`
**Surface area:** CLI, daemon diagnostics, operations
**Verification:** L2
Done when:
- [ ] `netclaw status` or doctor output distinguishes disconnected,
misconfigured, denied-by-policy, and healthy per channel.
- [ ] Slack/Discord/Mattermost health outputs use consistent terms.
- [ ] Tests cover status mapping from runtime channel health to CLI/doctor
display.
- [ ] Runbooks mention the deny and misconfiguration diagnostics operators
should look for.
### Phase 4: Model Provider And Tool Execution Contracts
Purpose: keep model/provider/tool execution reliable and diagnosable across
provider differences.
#### Task 4.1: Harden provider/model config-to-runtime binding
**PRD:** `docs/prd/PRD-005-model-provider-strategy.md`, `docs/prd/PRD-004-cli-onboarding-and-config.md`
**Spec:** `docs/spec/SPEC-008-model-provider-abstraction.md`, `openspec/specs/netclaw-model-providers/spec.md`, `openspec/specs/netclaw-model-capabilities/spec.md`
**Surface area:** config, runtime, providers
**Verification:** L2
Done when:
- [ ] Provider and model editors emit config that runtime provider selection
consumes without hidden defaults.
- [ ] Invalid provider IDs, missing model IDs, unsupported auth modes, and stale
capability metadata fail visibly.
- [ ] Tests cover config editor output -> provider registry/model selection
consumption.
- [ ] Eval suite is run if model/provider defaults or capability logic changes.
#### Task 4.2: Prove tool schema and permission contracts end-to-end
**PRD:** `docs/prd/PRD-006-mcp-tool-integration.md`, `docs/prd/PRD-002-gateway-security-envelope.md`
**Spec:** `openspec/specs/netclaw-tools/spec.md`, `openspec/specs/netclaw-mcp/spec.md`, `openspec/specs/tool-call-metadata/spec.md`, `openspec/specs/mcp-schema-coercion/spec.md`
**Surface area:** tools, MCP, security
**Verification:** L2
Done when:
- [ ] Tool schemas generated for models match dispatcher expectations.
- [ ] MCP schema coercion has negative tests for invalid/coercion-impossible
inputs.
- [ ] Tool approval and grant decisions are tested for allow, deny, prompt, and
malformed metadata.
- [ ] No tool can bypass audience/profile policy because a field is missing or
has a stale name.
#### Task 4.3: Keep streaming/progress execution contract coherent
**PRD:** `docs/prd/PRD-001-netclaw-mvp.md`, `docs/prd/PRD-006-mcp-tool-integration.md`
**Spec:** `docs/spec/SPEC-016-tool-liveness-and-stall-detection.md`, `openspec/changes/streaming-tool-call-execution/tasks.md`, `openspec/specs/session-state-machine/spec.md`
**Surface area:** runtime, actors, tools
**Verification:** L2
Done when:
- [ ] Tool-call streaming, progress reporting, session phase transitions, and
persistence snapshots agree on the same state names.
- [ ] Tool liveness is classified as `Opaque` or `SelfMonitoring`; generated
tools default to `Opaque`, and `spawn_agent` is explicitly `SelfMonitoring`.
- [ ] Opaque tools use one wall-clock budget; streamed stdout/stderr or other
output does not reset the budget.
- [ ] Self-monitoring tools use only a parent first-item startup guard after
which the child/tool-owned watchdog reports terminal success or failure.
- [ ] Actor tests prove progress events survive normal tool completion,
tool failure, cancellation, and session recovery.
- [ ] Actor tests prove a quiet-but-healthy sub-agent is not killed by the parent
`Session.ToolExecutionTimeoutSeconds`, while child prefill/no-progress stalls
still produce terminal failed `spawn_agent` results.
- [ ] No turn loop can report success while a tool result is still pending.
- [ ] Logs/traces correlate model call, tool call, approval, and session turn.
`openspec/changes/archive/2026-09-29-correlate-tool-authorization-attempts/` implements the
call-local authorization-attempt link across policy, correction, prompt,
decision, retry, recovery, and result. Model-call and session-turn links
remain part of the broader observability item.
### Phase 5: Memory, Identity, Scheduling, And Persistence Contracts
Purpose: ensure autonomous behavior survives restarts and carries the right
identity/context.
#### Task 5.1: Prove identity file and system prompt assembly contracts
**PRD:** `docs/prd/PRD-007-agent-personality-and-local-memory.md`, `docs/prd/PRD-004-cli-onboarding-and-config.md`
**Spec:** `openspec/specs/project-instructions/spec.md`, `openspec/specs/netclaw-agent-memory/spec.md`
**Surface area:** identity, prompt assembly, evals
**Verification:** L2 plus eval suite
Done when:
- [ ] Init writes identity files in the exact paths prompt assembly reads.
- [ ] Prompt assembly rejects missing or malformed required identity assets
visibly.
- [ ] Tests cover first-run, existing-install identity redo, missing file, and
malformed file cases.
- [ ] Eval suite passes when identity grounding rules change.
#### Task 5.2: Prove memory recall and compaction persistence contracts
**PRD:** `docs/prd/PRD-007-agent-personality-and-local-memory.md`
**Spec:** `openspec/specs/netclaw-agent-memory/spec.md`, `openspec/specs/netclaw-session/spec.md`, `openspec/specs/thread-history-backfill/spec.md`
**Surface area:** persistence, memory, session actors
**Verification:** L2 plus eval suite
Done when:
- [ ] Memory recall inputs, persisted observations, compaction summaries, and
prompt assembly use compatible serialization-safe types.
- [ ] Tests cover fresh session, resumed session, compacted session, and corrupt
or missing memory state.
- [ ] Eval suite passes for memory pipeline and compaction changes.
#### Task 5.3: Prove scheduling delivery contracts
**PRD:** `docs/prd/PRD-008-scheduling-and-periodic-tasks.md`, `docs/prd/PRD-009-input-adapters-and-unified-input.md`
**Spec:** `openspec/specs/netclaw-scheduling/spec.md`, `openspec/specs/reminder-execution-history/spec.md`
**Surface area:** scheduling, actors, channel delivery
**Verification:** L2
Done when:
- [ ] Reminder targets resolve to channel gateways using canonical provider IDs.
- [ ] Current-session delivery routes through the existing session gateway chain
without re-running inbound ACL checks.
- [ ] Future scheduled delivery uses policy appropriate for the stored target.
- [ ] Tests cover immediate reminder, periodic reminder, missed execution,
failed delivery, restart recovery, and invalid target.
- [ ] `TimeProvider` is used for all scheduling time.
### Phase 6: Release Readiness And Packaging
Purpose: keep install, Docker, demo, and CI aligned with product behavior.
#### Task 6.1: Keep Docker image and install artifacts contract-tested
**PRD:** `docs/prd/PRD-001-netclaw-mvp.md`, `docs/prd/PRD-004-cli-onboarding-and-config.md`
**Spec:** `openspec/specs/daemon-container/spec.md`, `openspec/specs/manifest-signature-verification/spec.md`
**Surface area:** packaging, install, Docker
**Verification:** L3
Done when:
- [ ] Docker image contains matching CLI and daemon binaries from the same
source build.
- [ ] Container default config path, health check, entrypoint, and self-update
behavior match docs.
- [ ] Install smoke passes for Linux/macOS/Windows stand-in archives.
- [ ] Manifest signature verification negative paths are covered.
- [ ] Local POC rebuild instructions are documented and reproducible.
#### Task 6.2: Maintain demo AppHost as the local end-to-end proof
**PRD:** `docs/prd/PRD-001-netclaw-mvp.md`, `docs/prd/PRD-009-input-adapters-and-unified-input.md`
**Spec:** `openspec/changes/netclaw-demo-apphost/tasks.md`, `TOOLING.md`
**Surface area:** demo, runtime, smoke
**Verification:** L4
Done when:
- [ ] Demo AppHost boots Mattermost, Ollama, and daemon to healthy.
- [ ] Seeded Mattermost user can post into the configured channel.
- [ ] Daemon logs prove message routing into a session and model invocation.
- [ ] Slow CPU inference remains documented as latency caveat, not hidden as a
failed wiring assertion.
- [ ] Opt-in demo integration test remains skipped by default and passes with
`NETCLAW_RUN_DEMO_SMOKE=1` on a suitable Docker host.
## NEXT
NEXT tasks are important but not eligible for autonomous execution unless moved
to `NOW` by the user.
- Webhook service identity and inbound webhook route hardening beyond the config
enablement/timeout editor.
- Subagent explicit model selection and parent-context alignment.
- GitHub Copilot provider refinements and VLLM capability strategy.
- Approval button label refinement and richer interactive approval UX.
- Config hot-reload beyond current startup/configure flows.
- Operator UX/Ops Console beyond CLI/TUI diagnostics.
## LATER
LATER tasks are product-direction items and should stay out of execution loops.
- Ambient monitoring workflows.
- Delegated coding task orchestration.
- Browser automation as a first-class feature beyond config-time MCP profile
enablement.
- Split gateway/agent process architecture.
- Hosted SaaS / multi-tenant operator console.
## Required Session Closure Checklist
Before declaring any implementation session done, record the closure state in
the final response and, if a task remains incomplete, leave a concrete follow-up
in this plan.
- [ ] Which `IMPLEMENTATION_PLAN.md` task was worked.
- [ ] Producer/consumer contract identified.
- [ ] Positive behavior verified.
- [ ] Negative behavior verified.
- [ ] Runtime/smoke/eval validation completed or explicitly blocked.
- [ ] Docs/spec/skill updates completed or explicitly not applicable.
- [ ] Commands run and results reported.
- [ ] Worktree state reported.