Add console chat loop with OllamaSharp and reorder Phase 1 tasks#9
Merged
Conversation
…nvelopes
Implements the complete protocol type layer for Netclaw's actor framework:
- SendUserMessage command with SourceMetadata (adapter type, sender identity,
channel ID, timestamp) for transport-agnostic session input
- TurnRecorded and SessionCompacted persistence events with protobuf-net
serialization for safe Akka.Persistence journal/snapshot storage
- TurnBroadcast and CompactionBroadcast pub/sub events for adapter reply delivery
- SerializableChatMessage and ChatRole — framework-owned chat types that never
directly persist Microsoft.Extensions.AI types across the serialization boundary
- SessionMessageExtractor static utility with entity key factories for all three
input adapter patterns: Slack ({channelId}/{threadTs}), scheduled tasks
(schedule/{taskId}/{runTs}), and TUI (tui/{sessionId})
Also adds Akka.Agents.Tests project with 25 tests:
- 7 protobuf-net serialization round-trip tests (all protocol types)
- 18 entity key extraction and formatting tests
All tests pass; slopwatch and openspec validate both clean.
OpenSpec: openspec/changes/expand-mvp-for-autonomous-agent-vision/ section 1
RALPH: run 20260221-135611 iteration 1
Replace the premature "framework" abstraction with bespoke Netclaw code built on proven Akka.NET patterns from the email gateway reference impl. Project rename: - Akka.Agents -> Netclaw.Actors - Akka.Agents.Tests -> Netclaw.Actors.Tests - Delete stale Akka.Console directory Routing overhaul: - SessionMessageExtractor now extends HashCodeMessageExtractor - IWithSessionId marker interface for type-safe message routing - SessionKeys static class for entity key factory methods - Remove EntityKeyType enum and ParseKeyType (not needed for routing) Protocol cleanup: - EntityKey renamed to SessionId on all message types - SendUserMessage implements IWithSessionId - AdapterTypes constants class added - All namespaces updated to Netclaw.Actors.* Hosting infrastructure (from email gateway patterns): - GenericChildPerEntityParent for child-per-entity routing - SessionManagerActor marker type for ActorRegistry - GenericChildPerEntityResolver skeleton for akka-reminders - NetclawAkkaHostingExtensions.WithSessionManager() wiring Package updates: - Add Akka.Cluster.Sharding and Aaron.Akka.Reminders - Bump Akka.Hosting 1.5.59 -> 1.5.60 (reminders dependency)
…nvelopes Add SessionId value object (readonly record struct with protobuf-net support) to replace raw string primitives across all protocol types. Introduce IPubSubMediator with LocalPubSubMediator for string-keyed topic pub/sub, and flesh out LlmSessionActor with the turn loop shape (history tracking, stub LLM reply, TurnBroadcast publishing). - SessionId: strongly-typed identity with implicit string conversion - IPubSubMediator: transport-agnostic pub/sub interface - LocalPubSubMediator: ConcurrentDictionary-backed in-memory implementation - LlmSessionActor: receives SendUserMessage, manages history, publishes via pub/sub - All protocol types updated to use SessionId value object - SessionKeys factory methods return SessionId instead of raw strings - 23 tests passing, slopwatch clean
…DeathWatch - Delete SessionKeys and its trivial string-concatenation tests — session ID construction belongs in adapters, not the actor layer - Rewrite LocalPubSubMediator as a ReceiveActor instead of ConcurrentDictionary with locks — concurrent state belongs in actors when actors are available - Use WatchWith to auto-unsubscribe terminated subscribers via pre-composed Unsubscribe message, no reverse index needed - Replace IPubSubMediator interface with Subscribe/Unsubscribe/Publish protocol messages — callers Tell the mediator actor ref directly - Add PubSubMediatorActor registry marker and wire mediator into hosting - Add testing guidelines to CLAUDE.md: no trivial tests for zero-logic paths
Delete SessionMessageExtractorTests (trivial pattern-match assertions) and replace with two integration tests that exercise the full pipeline: message extraction, GenericChildPerEntityParent routing, LlmSessionActor turn loop, pub/sub mediator delivery to TestProbe subscribers. - Switch xunit.v3 to xunit v2 for Akka.Hosting.TestKit compatibility - Add TestEnvironmentInitializer with [ModuleInitializer] to disable config file watchers (prevents inotify exhaustion on Linux) - Fix GenericChildPerEntityParent to URI-encode entity IDs for actor names (session IDs contain '/' which is illegal in actor paths) - Decompose WithNetclawActors into WithPubSubMediator + WithSessionManager for independent test wiring - LlmSessionActor takes IRequiredActor<PubSubMediatorActor> via DI instead of raw IActorRef closure - Add dotnet-skills routing index to CLAUDE.md for better skill activation 11 tests (9 serialization + 2 integration), all passing.
Transport metadata (adapter type, sender identity, channel ID) is a gateway concern, not a session actor concern. ACL checks and audit logging belong at the adapter layer before messages enter the actor system. The session actor only needs SessionId and Content. - Delete SourceMetadata.cs and AdapterTypes constants - Remove Source property from SendUserMessage - Simplify SendUserMessage to just SessionId + Content - Remove SourceMetadata serialization test - Clean up integration tests
…er model Convert LlmSessionActor from ReceiveActor to ReceivePersistentActor with event-sourced state recovery. Replace pub/sub mediator with direct session subscriptions using OutputFilter bitmask for per-subscriber content filtering. Session actor changes: - IChatClient integration with fire-and-forget async pattern - Ready/Processing behavior switching with message batching - Persist TurnRecorded events on LLM response, snapshot periodically - Recover state from journal/snapshots on actor restart - Accept SessionConfig for model identity and context window tracking Protocol additions: - SessionOutput discriminated union (TextOutput, ThinkingOutput, ToolCallOutput, ToolResultOutput, UsageOutput, TurnCompleted, SessionTitleOutput, ErrorOutput) - OutputFilter flags enum (Text, Thinking, ToolCalls, Usage + presets) - JoinSession/LeaveSession subscription protocol with DeathWatch cleanup - CommandAck/CommandNack for deterministic delivery confirmation - ChatMessageConverter for MEAI boundary conversion - SystemPromptSet, SessionTitleSet persistence events - SessionSnapshot for fast recovery Infrastructure: - Add Akka.Persistence and Akka.Persistence.Hosting packages - Remove unused LocalPubSubMediator and IPubSubMediator - Rename SessionManagerActor to SessionManagerActorKey Tests: 14 passing including kill-and-restore recovery scenario
Extract conversation state (history, turnCount, title) into immutable SessionState record with pure Apply methods for each event type. The actor now holds a single SessionState field replaced on each event, retaining only transient concerns (subscribers, buffer, behavior). Remove implicit string conversion from SessionId value object and add a constitution rule against implicit conversions on value objects. Add ChatMessageConverter tests covering role mapping round-trips between persistence-safe types and Microsoft.Extensions.AI types.
…indow metadata Research 9 major LLM agent SDKs (OpenAI, LangChain, Semantic Kernel, Anthropic, Google ADK, LlamaIndex, AutoGen, CrewAI, Haystack) plus independent evaluations from JetBrains Research and Factory.ai to inform session actor context management design. Key design decisions captured: - Tiered compaction: clear tool results before summarizing - Structured summarization with domain-specific sections - Pre-compaction memory flush to external storage - Compacting behavior state with message buffering - Tool call/result pair integrity during compaction Enrich UsageOutput with ContextWindowTokens and UsagePercent so subscribers can display context consumption without duplicating session config. Update specs and implementation plan to reflect the research-driven compaction design.
Task 1.4 - Layered system prompt: - NetclawPaths: standard ~/.netclaw/ directory layout - SystemPromptAssembler: pure function assembling prompt from layers - ISystemPromptProvider: pluggable interface with Static, Null, FileSystem impls - 9 unit tests for prompt assembly with missing/partial layers Task 1.6 - Tool framework and MEAI registration: - ToolRegistry: register AITool definitions with ACL grant categories - IToolExecutor + IToolAuditLogger: execution and audit interfaces - SerializableToolCall: protobuf-safe tool call persistence fields - ChatMessageConverter: full FunctionCallContent/FunctionResultContent roundtrip - LlmSessionActor agentic tool loop: detect tool calls, execute async, feed results back, fire follow-up LLM call until text response - Akka.NET 1.5.60 WithContext logging enrichment (SessionId on all logs) - Semantic logging templates throughout actor 62 tests pass including 3 tool execution integration tests.
Compacting behavior state with three-phase tiered approach: - Phase 1: Clear old tool results (replace with placeholder, keep N recent) - Phase 2: Pre-compaction memory extraction LLM call (persisted via IMemoryExtractor interface, NullMemoryExtractor default) - Phase 3: Structured summarization LLM call with domain-specific sections (task overview, current state, decisions, pending actions, tool usage) Compaction triggers when UsageDetails.InputTokenCount exceeds SessionConfig.CompactionTokenLimit (ContextWindowTokens * CompactionThreshold). New types: - CompactionPromptBuilder: structured prompt templates for extraction and summarization (Factory.ai findings: domain-specific sections outperform generic "summarize this" prompts) - IMemoryExtractor: pluggable interface for pre-compaction memory flush - CompactionOutput: lifecycle output notifying subscribers of compaction - SessionConfig: added CompactionModelId, KeepRecentToolResults - SessionState.ClearOldToolResults: tool result clearing with pair integrity 81 tests pass including 5 compaction integration tests.
Check off completed OpenSpec tasks matching implementation: - Section 2 (Session Actor Core): 2.1-2.4, 2.6 done (2.5 source metadata deferred) - Section 4 (System Prompt): 4.1-4.4 done - Section 6 (Tool Framework): 6.1-6.5 done Task descriptions updated to reflect actual implementation details.
New research doc: actor-llm-optimization-patterns.md covering prompt cache design (3 tiers), max tool iterations safety, parallel tool execution, streaming responses, retry with backoff, and sub-agent isolation. Add Future Considerations section to IMPLEMENTATION_PLAN.md with near-term, medium-term, and long-term pattern adoption timeline, cross-referenced to all three research documents.
Safety: MaxToolIterationsPerTurn (default 10) in SessionConfig prevents unbounded agentic loops. When the limit is reached, the next LLM call omits tools to force a text response. Performance: ExecuteToolsAsync now runs independent tool calls in parallel via Task.WhenAll instead of sequential foreach.
Inject TimeProvider into LlmSessionActor (defaults to TimeProvider.System). All persistence timestamps and audit entries now use _timeProvider.GetUtcNow() instead of DateTimeOffset.UtcNow, enabling time virtualization in tests. Add TimeProvider convention to CLAUDE.md: standardize on DateTimeOffset, never DateTime.
Mirror the TimeProvider guideline from CLAUDE.md: use TimeProvider instead of DateTimeOffset.UtcNow, standardize on DateTimeOffset.
…alidation Wire OllamaApiClient as IChatClient, rewrite Netclaw.App from web scaffold to IHostBuilder console host with in-memory Akka persistence, and add ConsoleAdapter hosted service with subscriber actor for proving the actor system works end-to-end with a real LLM. Reorder remaining Phase 1 tasks into priority tiers: Hello World (done), Make It Useful, Production Hardening, Full Capability, Polish and Ship. Gateway restoration noted for Task 1.11 (CLI scaffold).
Load appsettings.Local.json (gitignored) for local overrides like Ollama endpoint and model. Defaults to big-gpu:11434 with qwen3:30b on dev machines.
Session activity is logged to ~/.netclaw/logs/{sessionId}.log with
timestamped entries for user input, assistant output, tool calls,
usage stats, errors, and compaction events. Console output is
reserved exclusively for the chat UI.
Also adds appsettings.Local.json support (gitignored) for
machine-specific Ollama endpoint configuration and fixes
Slopwatch SW003 violation on empty catch block.
- Add Exception Cause property to ErrorOutput for diagnostic logging - Wire exception details through LlmCallFailed and ToolExecutionFailed - Log full exception stack traces to per-session log files - Load config from ~/.netclaw/config/ instead of project-relative path - Suppress Akka StandardOutLogger via ConfigureLoggers API to keep console clean for chat UI
Aaronontheweb
added a commit
to Aaronontheweb/netclaw
that referenced
this pull request
Jun 3, 2026
… check - doctor: inject DaemonConfig for the channel instead of hand-reading netclaw.json, so a non-string Daemon.UpdateChannel no longer crashes the whole `netclaw doctor` run (findings #1, netclaw-dev#4). - update-check: drop the low-value 1h TTL and keep a plain last-result store for the daemon /status API (finding netclaw-dev#3). Make `channel` required (no default) on EvaluateManifest + CheckForUpdateAsync so a forgotten arg can't silently fall back to stable (finding netclaw-dev#9). - SemVer: parse numeric prerelease identifiers as long, matching the bash generator's unbounded ints (finding netclaw-dev#6). - /status: report FullVersion in the no-check-yet branch for consistency with the post-check branch (finding netclaw-dev#8). - dotted prerelease convention (beta.N): update docs/examples + widen the property-test generators + add example cases; the release version gate now rejects mixed identifiers like `beta1` so a non-dotted tag can't ship and silently mis-order the channel (finding #2). - conformance: extract the generator's precedence key to feeds/scripts/semver_key.py and assert BOTH it and the C# SemVer comparator order one shared fixture (feeds/scripts/semver-order.txt) — via a C# test and a CI check — so the two implementations can't drift (finding netclaw-dev#7). Also updates the release-channels OpenSpec change (dotted-tag requirement plus risk/decision notes).
Aaronontheweb
added a commit
that referenced
this pull request
Jun 3, 2026
* feat(update): channel-aware, semver-correct update check (#1027) Make the binary update check honor an opt-in beta channel and compare versions by SemVer 2.0.0 precedence, so beta testers are notified of the next prerelease while stable users are never offered one. - BinaryFeedManifest: add `latestPrerelease` (newest of {stable, prerelease}). - SemVer: self-contained 2.0.0 precedence comparator (no NuGet.Versioning), matching the bash manifest generator's rules; IsNewerVersion uses it instead of System.Version (which couldn't parse a prerelease suffix at all). - BuildInfo.FullVersion: read the assembly informational version (keeps `-beta1`) so a beta build doesn't report its stripped core and strand. - DaemonConfig.UpdateChannel (stable default | beta) + config schema; parse fails loudly on an unknown value. - EvaluateManifest/CheckForUpdateAsync are channel-aware: stable reads only `latest`; beta reads `latestPrerelease` and rolls onto a superseding stable. - Thread channel + FullVersion through the daemon check, `netclaw update`, the startup notice, `netclaw status`, and `netclaw doctor`. Stable clients structurally never read `latestPrerelease`. The check stays advisory-only; Daemon.DisableSelfUpdate still blocks in-place update. Tests: SemVer precedence, channel-aware evaluation, ParseUpdateChannel. * docs(openspec): add release-channels capability spec Capture the beta (prerelease) release-channel capability end-to-end: manifest pointer semantics, prerelease-aware publishing, installer/Docker channel selection, and the channel-aware update-check policy. Documents both PR #1314 (merged) and the update-check work in this PR. Key invariant specified: a stable client is never offered a prerelease. * test(semver): add CsCheck property-based tests for SemVer Generate thousands of random valid SemVers and assert the comparator's correctness laws: parse-totality, antisymmetry, transitivity, IsNewer/compare consistency, build-metadata invariance, stable-outranks-prerelease, and numeric-below-alphanumeric precedence. Complements the fixed-example SemVerTests with algebraic coverage over a large random space. Adds CsCheck 4.7.0 (test-only) via central package management. * fix(update): address code-review findings on the channel-aware update check - doctor: inject DaemonConfig for the channel instead of hand-reading netclaw.json, so a non-string Daemon.UpdateChannel no longer crashes the whole `netclaw doctor` run (findings #1, #4). - update-check: drop the low-value 1h TTL and keep a plain last-result store for the daemon /status API (finding #3). Make `channel` required (no default) on EvaluateManifest + CheckForUpdateAsync so a forgotten arg can't silently fall back to stable (finding #9). - SemVer: parse numeric prerelease identifiers as long, matching the bash generator's unbounded ints (finding #6). - /status: report FullVersion in the no-check-yet branch for consistency with the post-check branch (finding #8). - dotted prerelease convention (beta.N): update docs/examples + widen the property-test generators + add example cases; the release version gate now rejects mixed identifiers like `beta1` so a non-dotted tag can't ship and silently mis-order the channel (finding #2). - conformance: extract the generator's precedence key to feeds/scripts/semver_key.py and assert BOTH it and the C# SemVer comparator order one shared fixture (feeds/scripts/semver-order.txt) — via a C# test and a CI check — so the two implementations can't drift (finding #7). Also updates the release-channels OpenSpec change (dotted-tag requirement plus risk/decision notes).
Aaronontheweb
added a commit
to Aaronontheweb/netclaw
that referenced
this pull request
Jun 15, 2026
Resolves the 11 findings from the /code-review pass: #1 Multi-line secret redaction: per-line redaction in JobOutputLog misses secrets spanning lines (e.g. PEM blocks). Re-redact the assembled tail at every LLM-surface point (execution-actor completion, manager HandleQuery, NotifyLostJob) so multi-line secrets can't reach the model. #2 Journaled reap event (SessionBackgroundJobsReaped): reap marks were snapshot-only and lost on recovery when the passivation snapshot is skipped (parked approval), rehydrating killed jobs as 'running'. FinishJobReap now persists the reap; recovery replays it. Full serializer plumbing + round-trip test. netclaw-dev#3 Dispose the Process in BackgroundJobExecutionActor.PostStop — stops the kernel handle / wait-handle leak (amplified by the no-default-timeout). netclaw-dev#4 Audience-gate the [active-background-jobs] block (commands, rationales, and the output-log path) for Public, matching WorkingContext. netclaw-dev#5 JobOutputLog.ReadTail falls back to the rotated .1 file when the current log is momentarily absent mid-rotation, instead of returning an empty tail. netclaw-dev#6 A transient File.Move failure in Rotate() is non-fatal: capture continues on the current log and retries next threshold, rather than permanently going silent. netclaw-dev#7 Back WriteFailure with a volatile field (un-gated fast-path read crosses threads). netclaw-dev#8 Correlate reap Ask replies with an epoch so a late reply from a superseded passivation can't resolve a newer handshake. netclaw-dev#10 Centralize the reap-reply handler (CommandJobReapResolved) across all non-terminal phases so a future phase can't silently drop the reply. netclaw-dev#11 Apply(TurnRecorded) now delegates job dedup/prune to the single shared CompleteTurnBackgroundJobBookkeeping helper so replay and live paths can't drift. netclaw-dev#9 AutoFlush is kept (live monitoring requires per-line visibility; a write() to the page cache is cheap and a time-throttle risks an unflushed quiescent ready-line) — documented as a deliberate decision. Tests: +6 (reaped-event round-trip, ReadTail rotation fallback + rethrow, SessionBackgroundJobsReaped apply, Public/Personal active-jobs gating); updated RotationFailure test to the new non-fatal contract. Full Actors suite 2412 green x2; slopwatch + headers clean.
Aaronontheweb
added a commit
that referenced
this pull request
Jun 15, 2026
… kill timer, reap on passivation (#1405) * Background jobs as detached processes: stream logs live, no default kill timer, reap on passivation, Lost notifications A background job is now a detached process with no expectation of completion (OpenSpec: background-jobs-detached-process-redesign). Fixes the hung-session class where a dev server (jekyll serve / npm run dev) could never be used: both execution paths blocked on process exit. - Stream stdout/stderr to ~/.netclaw/jobs/{id}/output.log while the process runs (per-line secret redaction, 5MB single-slot rotation). The existing check_background_job tail query and file_read/grep monitoring now work mid-run; output survives daemon crashes. Completion tails read from disk. - Remove the silent default kill timer on background routing: omitted _timeout_seconds now means no timer (was: synchronous default, killing un-hinted jobs early). Submit ACK includes the output log path. - Reap on session passivation: KillJobsForSession handshake before the final snapshot; new Reaped status (distinct from Cancelled); no turn delivery on reap (would rehydrate the session being torn down); reaped entries surface exactly once in [active-background-jobs] on rehydration, then prune. - Wire up session-side job tracking (TrackBackgroundJob had no production caller — the active-jobs context block was always empty). - Daemon-restart reconciliation now delivers Lost notifications to owning sessions with the pre-crash log path. - Remove the vestigial pending-approval passivation deferral: approvals are journaled and the response path already rehydrates and resumes. - AGENTS.md template, netclaw-operations SKILL.md (v2.13.0), and the background-jobs runbook document the new lifecycle; eval suite gains a background-job lifecycle regression case. * Fix background-job lifecycle eval: multi-turn harness, pre-trusted verb, tightened assertion The new tool_background_job_lifecycle case scored 0/5 for instrumentation reasons, not model behavior (per the eval-debugging guidance): 1. run_case treats multiple prompts as alternate phrasings (pick_variant) — sequential conversations need run_multi_turn_case, which resumes one session and accumulates stdout across turns for the assertion. 2. Even then, every background submission died at the approval gate: the headless eval container has no approval requester and 'sleep' is not on the safe-command allowlist. Passing runs were vacuous (the model probed check_background_job with a made-up ID while flailing). The eval setup now pre-trusts the sleep verb via 'netclaw approvals trust-verb' against the bind-mounted tool-approvals.json before the container starts, so the case exercises the real lifecycle: submit -> job id -> status -> cancel. 3. The assertion now requires the actual _background":true submission, not just any shell_execute call. Result: 5/5, with transcripts showing the genuine flow (job id returned, ACK steering to the streaming log path, live status with elapsed time, cancel confirmed). * Fix CI: SW003 empty-catch marker, parallel-test isolation for real-process job tests Two PR CI failures: 1. Slopwatch SW003 — the write-failure path in JobOutputLog had an empty inner catch with the rationale as a body comment instead of the repo's 'catch // slopwatch-ignore: SW003 <reason>' marker convention. (Passed locally because slopwatch 0.4.1 only scans the git diff vs local HEAD; CI's PR-merge scans the whole new file.) 2. Test-ubuntu-latest flake — KillJobsForSession_ReapsOwnedJobs and BackgroundJob_Completes_And_DeliversResult_ViaGateway intermittently failed with the owning manager's freshly-created jobs showing 'Lost'. Root cause (reproduced reliably by running the Jobs test classes together): under heavy parallel load, concurrent process/FS pressure makes a manager's message handler throw transiently, the actor restarts, and startup reconciliation correctly marks its in-flight jobs Lost — a spurious restart to induce in a unit test. Fix: serialize the three real-process-spawning job test classes via a DisableParallelization collection (repo's established pattern) so they don't mutually starve. Verified: full assembly 4/4 green, the prior ~Jobs repro 3/3 green. Also register TimeProvider in LlmSessionTestBase to mirror production DI (Daemon Program.cs) — WithNetclawActors() constructs the background-job and reminder managers via the DI resolver, which need it; without it they died with ActorInitializationException at startup, adding restart churn. * Address code-review findings on background-jobs feature Resolves the 11 findings from the /code-review pass: #1 Multi-line secret redaction: per-line redaction in JobOutputLog misses secrets spanning lines (e.g. PEM blocks). Re-redact the assembled tail at every LLM-surface point (execution-actor completion, manager HandleQuery, NotifyLostJob) so multi-line secrets can't reach the model. #2 Journaled reap event (SessionBackgroundJobsReaped): reap marks were snapshot-only and lost on recovery when the passivation snapshot is skipped (parked approval), rehydrating killed jobs as 'running'. FinishJobReap now persists the reap; recovery replays it. Full serializer plumbing + round-trip test. #3 Dispose the Process in BackgroundJobExecutionActor.PostStop — stops the kernel handle / wait-handle leak (amplified by the no-default-timeout). #4 Audience-gate the [active-background-jobs] block (commands, rationales, and the output-log path) for Public, matching WorkingContext. #5 JobOutputLog.ReadTail falls back to the rotated .1 file when the current log is momentarily absent mid-rotation, instead of returning an empty tail. #6 A transient File.Move failure in Rotate() is non-fatal: capture continues on the current log and retries next threshold, rather than permanently going silent. #7 Back WriteFailure with a volatile field (un-gated fast-path read crosses threads). #8 Correlate reap Ask replies with an epoch so a late reply from a superseded passivation can't resolve a newer handshake. #10 Centralize the reap-reply handler (CommandJobReapResolved) across all non-terminal phases so a future phase can't silently drop the reply. #11 Apply(TurnRecorded) now delegates job dedup/prune to the single shared CompleteTurnBackgroundJobBookkeeping helper so replay and live paths can't drift. #9 AutoFlush is kept (live monitoring requires per-line visibility; a write() to the page cache is cheap and a time-throttle risks an unflushed quiescent ready-line) — documented as a deliberate decision. Tests: +6 (reaped-event round-trip, ReadTail rotation fallback + rethrow, SessionBackgroundJobsReaped apply, Public/Personal active-jobs gating); updated RotationFailure test to the new non-fatal contract. Full Actors suite 2412 green x2; slopwatch + headers clean. * Fix racy ReminderManagerActorTests.Startup_emits_alert_for_legacy_reminder_missing_trust_fields Root cause (per akka-net + dotnet-concurrency analysis): the legacy-schema alert is emitted synchronously inside the actor's PreStart, and the test waited for it with a fixed 5s AwaitAssertAsync poll. Under heavy parallel CI load the shared ThreadPool is saturated (many TestKit ActorSystems, WithSerializationVerification overhead), so the actor's PreStart can be scheduled later than the 5s budget and the poll gives up with an empty sink. Not a logic/visibility bug — the sink is lock-guarded and the store records the rejection synchronously in its constructor. Fix: await a deterministic readiness signal instead of polling a wall clock. An actor processes mailbox messages only after PreStart completes, so a successful Ask<ReminderHealthResponse>(GetReminderHealthQuery) reply guarantees the emit has run. This is the same readiness pattern already used elsewhere in this test file; the generous Ask timeout absorbs scheduling latency and returns as soon as the actor is ready (no wasted time in the common case). No existing GitHub issue covers this test. Does not reproduce locally even at full-assembly parallelism (CI-runner-only starvation).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
IChatClientto Ollama for real LLM interactionNetclaw.Appfrom web scaffold toIHostBuilderconsole host with in-memory Akka persistenceConsoleAdapterhosted service +ConsoleSubscriberActorfor bare console chat loopThis branch includes all Phase 1 work to date:
Gateway note:
Netclaw.Appwas temporarily changed fromMicrosoft.NET.Sdk.WebtoMicrosoft.NET.Sdkfor the proof-of-concept. Web gateway will be restored at Task 1.11 (CLI scaffold).Test plan
dotnet build Netclaw.slnx— 0 warnings, 0 errorsdotnet test Netclaw.slnx— 84/84 tests passdotnet run --project src/Netclaw.Appwith Ollama running — type a message, get a response