Ctrl K
ComunidadGuías…

AGENTS.md

Rama dev · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# Netclaw Agent Constitution

This file is the repository's stable agent constitution.
Keep it small. Keep it durable. Keep it routing-focused.

## Authority and Scope

- You are authorized to plan, design, implement, test, and document Netclaw.
- Default to smallest safe change that advances MVP.
- Prefer explicit tradeoffs over hidden complexity.

## Communication Standard

Write all agent output in ASD-STE100 Simplified Technical English (STE). This
applies to chat replies, commit messages, PR descriptions, code comments, docs,
and spec text.

Rules:

- Give one sentence one idea. Keep an instruction to 20 words or fewer. Keep a
  descriptive sentence to 25 words or fewer.
- Use the active voice. Name the actor that does the action.
- Use simple tenses: past, present, and future. Do not use perfect or
  progressive tenses.
- Do not use an `-ing` word as a noun or as an adjective. Use a noun or a
  relative clause.
- Keep the articles `a`, `an`, and `the`. Do not delete words to make a
  sentence shorter.
- Give one word one meaning. Do not use the same word as a noun and as a verb.
- Keep a descriptive paragraph to six sentences or fewer.
- Put complex information in a vertical list.
- Put a warning or a caution before the step that it applies to.

STE does not apply to code identifiers, file paths, log text, or quoted
material. Keep those exact.

STE does not override accuracy. If the approved vocabulary cannot state a
technical fact correctly, state the fact correctly and keep the sentence short.

## Current Product Direction

- Netclaw is an open-source, self-hosted autonomous operations agent built on Akka.Agents.
- MVP is single process, actor-driven, and persistence-backed.
- Session identity is Slack thread: `{channelId}/{threadTs}`.
- Security posture is default deny.

Read first:

- `PROJECT_CONTEXT.md`
- `TOOLING.md`
- `IMPLEMENTATION_PLAN.md`
- `docs/prd/README.md`
- `docs/architecture/*.md`
- `.opencode/skills/netclaw-*/SKILL.md`
- `.claude/skills/ralph-*.md`
- relevant `openspec/specs/*/spec.md`

## Required Task Routing

Use these modes based on requested outcome.

### MODE=planning

Use when producing PRDs, specifications, risk analysis, IA, mockups, or
execution plans.

Expected outputs:

- updates in `docs/prd/`, `docs/spec/`, `docs/ui/`
- OpenSpec changes and spec deltas in `openspec/`
- explicit traceability to PRD IDs

### MODE=build

Use when implementing production code, tests, and runtime wiring.

Expected outputs:

- code changes with validation steps
- matching spec updates when behavior changes
- no undocumented behavior drift

## OpenSpec Workflow

Use OpenSpec skills for substantial planning and spec work — new features,
capability additions, or changes that touch multiple components. Skip it for
targeted bug fixes, small refactors, and changes where scope is already clear.
Do not manually create or edit OpenSpec artifacts (specs, changes, proposals,
delta specs, design docs, task files); use the skills below instead.

### When Planning (new feature, capability, or spec change)

1. `/opsx-new` — create a new OpenSpec change
2. `/opsx-continue` — create next artifact in the change workflow
3. `/opsx-ff` — fast-forward: generate all remaining artifacts at once

### When Implementing (building code from a change)

4. `/opsx-apply` — implement tasks from an OpenSpec change

### When Finishing (syncing and archiving)

5. `/opsx-sync` — sync delta specs from a change to main specs
6. `/opsx-verify` — verify implementation matches change artifacts
7. `/opsx-archive` — archive a completed change

### Supporting Workflows

- `/opsx-explore` — think through ideas before creating a change
- `/opsx-onboard` — guided walkthrough of the full OpenSpec workflow
- `/opsx-bulk-archive` — archive multiple completed changes

If you do need to create or modify files under `openspec/`, use the appropriate
skill above rather than editing them directly. The only exception is updating
task checkboxes in `openspec/changes/*/tasks.md` during RALPH iterations.

## Specification Review Contract

- Link [the engineering glossary](docs/spec/GLOSSARY.md) for cross-cutting
  terms. Do not copy its definitions into each specification.
- Add a glossary term when two or more capabilities need the same meaning.
- Show an ordered flow with pseudocode or a small diagram when sequence,
  authority, state, or persistence affects the design.
- Give at least one concrete positive example and one negative example for each
  new policy or security boundary.
- Name the component that owns each decision. State whether its data is
  call-local, actor-local, or durable.
- Label pseudocode as schematic when it omits a security gate or runtime step.

## Architecture Document Rule

`docs/architecture/` holds the canonical architecture documents for people.
`docs/architecture/tool-authorization.md` describes tool authorization: its
contexts, owners, diagrams, guidelines, and future scenarios.

- A PR that changes an authorization context must update
  `docs/architecture/tool-authorization.md` in the same diff.
- An authorization context change includes a new decision owner, a moved
  check, a new grant scope, a new candidate kind, or a new consent surface.
- The document describes the current code. Put a planned shape only in its
  "Future scenarios" section.
- Keep testable rules in `openspec/specs/tool-authorization/spec.md`. The
  document links to those rules and does not copy them.

## Discovery Rules

Before coding a capability, discover in this order:

1. active task in `IMPLEMENTATION_PLAN.md`
2. matching PRD in `docs/prd/`
3. matching engineering spec in `docs/spec/`
4. matching OpenSpec capability in `openspec/specs/`
5. active change plan in `openspec/changes/<name>/`

If planning and implementation artifacts conflict, fix planning artifacts first.
If discovery artifacts conflict with each other, update them before implementing.

## Cross-Boundary Contract Rule

When a change writes data consumed by another subsystem, identify the consumer
before implementation and verify the producer emits the consumer's canonical
representation. This applies to config editors, persistence records, actor
messages, protocol payloads, tool schemas, and security policy inputs.

For configuration changes, tests must prove both:

- invalid or unresolved values are rejected before persistence
- persisted values match what runtime ACL/routing/startup code expects

Do not treat UI-level save success or schema validity as sufficient when runtime
behavior depends on provider IDs, canonical names, permissions, or security
policy keys.

## Tool Composition Rule

Model agent behavior as a composition of existing tools before you add a new
tool. Return machine-actionable paths or identifiers when the session already
owns the resource and current policy permits access.

Prefer small tools that agents can combine. For example, an agent must use
`file_read`, `file_list`, and `file_search` for its own session logs. Do not add
a special session-log reader for that behavior.

Add a new tool only when existing tools cannot express the operation or its
authority boundary safely. State the missing contract and explain why tool
composition cannot satisfy it.

## Shell Approval Abstraction Rule

Netclaw shell approval code must not parse an executable's private command,
subcommand, option, or operand grammar. Approval analysis must use general shell
facts. These facts include syntax, control flow, typed values, path scopes, and
authority boundaries.

Explicit safe-verb and hard-deny lists are policy data. They must not become
executable-specific parsers. If ShellSyntaxTree lacks a required fact, keep the
input unresolved. Propose a general ShellSyntaxTree capability instead. Do not
move a Netclaw executable special case into ShellSyntaxTree.

## Automation Floor

Recent regressions define mandatory automated proof classes. TUI text input must
have headless typed-key coverage and native smoke coverage for critical flows.
Dynamic validation must have fake-failure tests proving save is blocked before
persistence. Legacy/new config paradigm changes must have load/round-trip tests
from the old shape to the runtime-consumed shape. Human manual testing is a
last-mile confidence check, not a substitute for these gates.

## Focused Mutation Test Rule

Use focused Stryker tests for critical security and authority boundaries, and
for load-bearing reliability boundaries.
Review the mutation scope after each security fix, authority policy change, or
reliability change.
Also review the mutation scope at each minor release.
Add one small target when deterministic tests reject a specific unsafe mutation.
Keep each target narrow. Keep the total CI cost within the documented budget.
CI must fail if an expected mutant survives or does not run.
Do not use a broad mutation score as a coverage target.

See [TOOLING.md](TOOLING.md#focused-mutation-tests) for the current targets,
procedure, cost limits, and expansion criteria.

## Configuration Schema Sync Rule

When adding or changing properties on any `*Config` type in `Netclaw.Configuration`,
update `src/Netclaw.Configuration/Schemas/netclaw-config.v1.schema.json` in the same PR.
Most schema objects set `"additionalProperties": false`, so a new property that is missing from the
schema is rejected by `ConfigSchemaDoctorCheck` at runtime, and `netclaw doctor --fix` deletes it
without a backup. Provider entries under `Providers` stay open because the binder ignores key case.

**Migration-friendly schema changes:** `netclaw doctor --fix` uses `SchemaFixResolver` to
auto-fix common schema validation errors. To ensure smooth upgrades for existing configs:
- When adding a new **required** property, include a `"default"` value in the schema so
  the fix resolver can insert it automatically.
- When adding an **enum** property, always use `"type": "string"` with named values (not
  integers) so the resolver can coerce stale numeric values.
- When **removing** a property, no special action is needed — the resolver detects and
  removes properties disallowed by `additionalProperties: false`.

## Release Channel Rule

Releases are cut by pushing a git tag (full procedure: `CONTRIBUTING.md` → Releasing).
Two conventions the release version gate enforces — violate them and the release fails:

- The tag MUST match `Directory.Build.props`: `<VersionPrefix>` for a stable tag,
  `<VersionPrefix>` + `<VersionSuffix>` for a prerelease.
- Prerelease tags MUST use the **dotted** `beta.N` form (`0.23.0-beta.1`, never `beta1`).
  A mixed identifier like `beta1` is one opaque token that SemVer orders lexically, so
  `beta10` would rank below `beta2` — in the C# comparator *and* the manifest generator.

A tag with a `-` is a prerelease: it becomes a GitHub prerelease and only advances the
Docker `:beta` tag and the manifest `latestPrerelease`. A stable tag moves `:latest`,
`:major.minor`, and the manifest `latest`. The C# `SemVer` comparator and the bash
generator's `feeds/scripts/semver_key.py` are two implementations of one precedence rule
kept in lockstep by a shared fixture (`feeds/scripts/semver-order.txt`) — change both
(and the fixture) together if precedence ever changes. Never hand-edit the release
manifest or installer feed.

## Stacked Pull Requests

Stack PRs only when a change needs several reviewable steps that depend on
each other. Otherwise, open one PR.

- You need contributor (write) access to `netclaw-dev/netclaw` to stack. A
  PR's base must be a branch in the base repository, so each stack branch must
  live upstream. A contributor without write access opens one PR from a fork.
- Name each branch that another PR uses as its base `feature/<topic>` from
  the start. `pr_validation` runs only for PRs into `dev`, `main`, `master`,
  or `feature/*`; a PR into another base gets no test CI.
- Caution: a branch rename retargets the PRs that use the branch as their
  base, but it closes the PR whose head is that branch. That PR cannot be
  reopened. If a head branch must be renamed, open a replacement PR from the
  renamed branch and link the closed PR to it.
- Write each PR description so that it stands alone, and name its base PR.
- Verify each PR on the combined stack, not on its isolated branch.
- Merge from the bottom up. GitHub treats dependent PRs as a native stack, and
  `gh pr merge` fails with "must be merged using the asynchronous merge REST
  API". Use `PUT /repos/{owner}/{repo}/pulls/{n}/merge-async` with
  `merge_method=squash` and `sha=<head>`. Then poll
  `GET /repos/{owner}/{repo}/pulls/{n}/merge-async/{uuid}` until the state is
  `merged`.
- After a squash merge, GitHub retargets the next PR to `dev` and can rebase
  it. If the next PR shows a conflict, run
  `git rebase --onto origin/dev <old base head>` so that only its own commits
  remain. Then push with `--force-with-lease`.
- The repository deletes merged head branches automatically.

## Universal Quality Bar

- secure-by-default behavior for gateway and tools
- no hidden bypasses around ACL/policy checks
- no north-star/deferred features in MVP without explicit PRD update
- actor boundaries remain transport-agnostic (pub/sub over direct transport asks)
- persistence types remain framework-owned and serialization-safe
- **No silent fallbacks.** When something fails or is misconfigured, fail
  loudly — do not silently degrade to a default. Fallbacks hide bugs and
  misconfigurations, and on security-relevant paths they can silently escalate
  privileges. A fallback is only justified when partial failure is a normal
  runtime condition (e.g., a network retry). If you think you need one, ask
  first.
- no new Slopwatch violations: run `/dotnet-skills:slopwatch` after code changes
- use `TimeProvider` (not `DateTime.UtcNow` / `DateTimeOffset.UtcNow`) so time
  can be virtualized in tests. Inject `TimeProvider` via DI; default to
  `TimeProvider.System` in production. Standardize on `DateTimeOffset`, not
  `DateTime`. Usage: `_timeProvider.GetUtcNow()` returns `DateTimeOffset`,
  `.ToUnixTimeMilliseconds()` for persistence timestamps.
- **Reuse before you add — look for existing constructs before creating new
  ones.** Before introducing a new config knob, constructor parameter, field,
  interface, or helper, check what already carries the data you need — especially
  what is *already flowing through the seam you're editing* (the execution
  context, an injected policy, an existing `*Paths`/`*Config` type). A new
  construct that parallels an existing one is a defect: it duplicates state,
  drifts from the original, and forces plumbing (threading a value through N
  constructors) that the existing path already solved. Anchor the design on the
  data already present at the call site; if you find yourself adding plumbing to
  feed a new construct, stop — the value is usually already reachable. Adding a
  new construct is justified only when no existing one fits and you can name why.
- **NEVER add implicit conversions to/from primitive types on value objects.**
  Value objects exist to prevent accidental misuse — an implicit conversion back
  to the primitive defeats the purpose. Use `.Value` for explicit access and
  explicit casts where truly needed. If a value object can silently become a
  string, it provides no more safety than a raw string.
- **Never use the `global::` namespace qualifier in C# source.** Resolve name
  collisions with an ordinary `using` directive or type alias instead.
- **Optional/nullable parameters are rare by default — make dependencies
  required.** A constructor or method parameter should be optional (nullable or
  defaulted) only when its absence is a genuine, intended runtime state the
  callee handles explicitly (a real feature toggle: "no search backend
  configured" → no web-search tool). NEVER add an optional parameter for
  backward/source compatibility or to avoid updating call sites — update the
  call sites instead. This is acute for security-relevant dependencies (path
  roots, deny-list / access / command policies): a nullable security dependency
  consumed with `?.` (`_policy?.IsDenied(p) == true`, `if (_policy is not null)`)
  means a null silently **disables the check** — exactly how trust-zone and
  privilege-escalation bugs hide. Require it so the type system guarantees it is
  always present; if it is unconditionally constructed in production, there is no
  justification for letting it be null.
- **Comments: skip noise, keep signal.** Don't narrate what code does when
  identifiers already say it (`// increment counter`). Do write comments
  that help a human reviewer scanning in isolation: security gate
  explanations (what's blocked, for which audience, why), hidden
  constraints, subtle invariants, non-obvious fallback strategies, and
  cross-cutting concerns that aren't visible from the call site. A good
  comment answers "why would this surprise me?" — not "what does this
  line do?"
- **File organization: do NOT enforce "one type per file."** This repo
  groups closely related types in the same file when that keeps the call
  chain readable — an interface plus its implementations
  (`IToolApprovalMatcher` + `ShellApprovalMatcher` + `DefaultApprovalMatcher`),
  a base type plus its concrete subtypes, a record plus the helpers that
  produce it. Splitting types into separate files purely for the sake of
  separation creates review noise without improving navigation. Reviewers
  (and agents running cleanup passes) SHALL NOT suggest file splits on
  the basis of "one type per file" alone. Real reasons to split: a file
  has grown past ~600 lines AND the contained types serve distinct
  callers; a type genuinely belongs in a different namespace or
  assembly; circular-include relief in generated code.

## Testing Guidelines

- Do not write tests for trivial code — string formatting, simple concatenation,
  constructor assignment, and other zero-logic paths are not worth testing.
- Tests should exercise meaningful behavior: state transitions, error handling,
  serialization round-trips, routing decisions, integration boundaries.
- If the test is just asserting that `$"{a}/{b}"` equals `"a/b"`, delete it.
- Prefer fewer tests that cover real behavior over many tests that pad coverage.
- **NEVER use `Thread.Sleep` or `Task.Delay` in tests to wait for conditions.**
  This is a design smell, not just a test smell — if you need a sleep to make a
  test pass, the production code lacks a proper synchronization signal. Fix the
  design:
  - Add request/response acks (e.g., `Ask<CommandAck>`) so callers know a state
    transition has occurred before proceeding.
  - Use Akka.TestKit's `AwaitAssertAsync` for polling assertions on async state.
  - `Task.Delay` in fake/mock services to simulate latency is acceptable only in
    the fake itself, never in test orchestration logic.
- **A test MUST delete every file and folder that it creates in the temp
  directory.** Use `DisposableTempDir` or `TestSessionTempDirectory`. Delete
  the path in `Dispose`. A `TestKit` class has `IAsyncDisposable`, so xunit
  does not call its `IDisposable.Dispose`. `TestKit` also stops its actor system
  after `AfterAllAsync` returns, and it fails the test when `AfterAllAsync`
  takes more than 5 seconds. A `TestKit` class that owns a temp folder MUST
  re-implement `IAsyncDisposable.DisposeAsync`. It calls `base.DisposeAsync()`,
  and then deletes the folder. A class that derives from `LlmSessionTestBase`
  overrides `DeleteOwnedDirectories` instead. `TestKitTeardownGuardTests` fails
  when an `AfterAllAsync` override deletes or disposes. Each test process gets
  a private temp root
  (`tests/Shared/TestRunTempRoot.cs`). The test run fails with "Test Assembly
  Cleanup Failure" when a test leaves an entry in that root. The CI log prints
  the leaks. A test project opts in with
  `<UseTestRunTempRoot>true</UseTestRunTempRoot>`.
- **TUI / Termina changes MUST be validated with the native smoke
  harness** before being marked done. xUnit cannot drive Spectre-style
  prompts, and the non-interactive smoke scenarios only cover the
  HTTP/JSON surface, so a Termina change that breaks the wizard, model
  picker, provider picker, or any other prompt flow will pass every
  other gate. Run:

  ```bash
  ./scripts/smoke/run-smoke.sh light          # all PR-gating tapes + scenarios
  ./scripts/smoke/run-smoke.sh init-wizard    # one tape, fastest loop
  ```

  See `TOOLING.md` § Interactive CLI Smoke Tests for the full set of
  commands and `tests/smoke/tapes/README.md` for tape authoring
  conventions (no `Sleep`, anchor every step, pair with an assertion).

## Post-Code Quality Check

After any code changes, run:

```bash
dotnet slopwatch analyze                   # Detect reward hacking (new violations fail CI)
./scripts/Add-FileHeaders.ps1 -Verify      # Ensure all .cs files have copyright headers
```

Slopwatch detects: disabled/skipped tests, suppressed warnings, empty catch
blocks, hardcoded values, TODO-as-done comments. Baseline is in
`.slopwatch/baseline.json` — existing entries are accepted, new violations
must be fixed or explicitly baselined with justification.

## Eval Suite

Run the behavioral eval suite (`./evals/run-evals.sh`) only when changing
the prompt or identity grounding that the agent reads:

- Identity file templates (`SOUL.md`, `AGENTS.md`, `TOOLING.md` in init wizard)
- System prompt assembly (`SystemPromptAssembler`, `FileSystemPromptProvider`)
- Skill content (any `SKILL.md` under `feeds/skills/.system/files/`)
- Skill matching logic (`SkillRegistry` keyword handling)

Do not run the eval suite for other changes. For those changes, the normal
test gates in Definition of Done apply.

Update eval cases when:

- Adding a new system skill — add a skill auto-load case
- Changing identity grounding rules — update identity assertion patterns
- A production session exhibits a new failure pattern — add a regression case

**Debugging eval failures:** Eval failures are VERY RARELY the model's fault.
Almost always the root cause is an instrumentation issue — how we're parsing or
asserting on the model's output (regex mismatch, output format change, assertion
too brittle). If instrumentation checks out, the next most likely cause is a
genuine alignment problem (system prompt, skill content, or context assembly not
giving the model the right information). Only after ruling out both should you
consider model capability as the cause.

## System Skills Sync Rule

Runtime skill use is logical: call `skill_load` by canonical name and
`skill_read_resource` for bundled files. Do not expose physical skill roots in
prompt indexes or teach agents to derive `SKILL.md` paths. Direct filesystem
inspection is reserved for explicit operator diagnostics.

One exception applies. The `skill_read_resource` result starts with the
resolved absolute path of that one resource, so an agent can run a bundled
script by its real path. Do not put that path, or any skill root, in skill
indexes, skill listings, or `skill_load` output.

System skills in `feeds/skills/.system/files/` are the agent's operational
guidance — they tell the running agent how to use features. When you change a
feature area, the corresponding skill **must** be updated in the same PR.

| Feature area changed | Skill to update |
|----------------------|-----------------|
| Identity files, SOUL/AGENTS/TOOLING paths, progressive disclosure | `netclaw-operations` |
| Memory provider routing, SQLite memory tools, general memory guidance | `netclaw-memory` |
| Config format, daemon health, logs, MCP wiring, diagnostics CLI, doctor | `netclaw-operations` |
| Skill file format, discovery, authoring workflow | `skill-authoring` |
| Tool definitions, CLI commands, grant categories, search_tools, scheduling tools | `netclaw-operations` |
| Search tool behavior, citation policy, web_search/web_fetch usage guidance | `search-citation` |
| Workspaces, project creation/discovery | `netclaw-projects` |

**Workflow:**
1. Edit the skill at `feeds/skills/.system/files/{name}/SKILL.md`
2. Bump `metadata.version` in the YAML frontmatter
3. Do NOT run `generate-skill-manifest.sh` locally — the maintenance workflow
   generates the legacy manifest on manual `workflow_dispatch`

**Publishing:**
- The binary release does not publish system skills.
- Use `gh workflow run publish_skills.yml` for legacy feed maintenance.
- Normal dev pushes do NOT publish to the live feed

If a new feature area needs agent guidance, create a new skill file and add a
row to this table.

## Definition of Done

Done means all of the following are true:

- behavior aligns with PRD + spec
- acceptance criteria are testable and verified
- `dotnet slopwatch analyze` passes (no new violations)
- copyright headers present on all `.cs` files (`./scripts/Add-FileHeaders.ps1 -Verify`)
- operational impact is documented (runbooks or CLI help)
- OpenSpec artifacts are updated or archived appropriately
- system skills updated if a mapped feature area was changed (see table above)
- eval suite passes for changes to identity or skill grounding (see
  Eval Suite section)
- interactive tape harness passes for changes to Termina TUI surfaces
  (init wizard, model/provider/webhook pickers, chat page) — see
  Testing Guidelines and `TOOLING.md` § Interactive CLI Smoke Tests

## Agent Guidance: dotnet-skills

IMPORTANT: Prefer retrieval-led reasoning over pretraining for any .NET work.
Workflow: skim repo patterns -> consult dotnet-skills by name -> implement
smallest-change -> note conflicts.

Routing (invoke by name):

- Akka.NET: akka-best-practices, akka-hosting-actor-patterns, akka-testing-patterns
- C# / code quality: csharp-coding-standards, csharp-concurrency-patterns,
  csharp-api-design, csharp-type-design-performance
- DI / config: microsoft-extensions-dependency-injection, microsoft-extensions-configuration
- Serialization: serialization
- Testing: akka-testing-patterns, snapshot-testing, testing-strategy
- Project structure: project-structure, package-management

Quality gates (use when applicable):

- dotnet-skills:slopwatch — after substantial new/refactor/LLM-authored code
- dotnet-skills:crap-analysis — after tests added/changed in complex code

Specialist agents:

- akka-net-specialist, dotnet-concurrency-specialist, dotnet-performance-analyst,
  dotnet-benchmark-designer

## Continuous Improvement Rule

- If a workflow repeats twice, extract or refine a skill/workflow doc.
- Put volatile detail in repo-owned docs, not this constitution.
- Keep this file stable and high-signal.
- **Compressing a skill into a rule is a retrieval operation, not a
  memory operation.** When distilling a dotnet-skills skill (or any
  upstream authority) into an audit rule, review rubric, or one-line
  bullet, open the skill first. Preserve the distinctions the skill
  draws — not just its headline. Rules that collapse two orthogonal
  axes into one sentence produce false positives that burn
  trust in the audit. If a rule cannot be written without losing a
  distinction the source makes, drop it rather than eliding.