d18075925d
The exemption added in 6a8a7b31 was broader than the invariant it stood on. "Tool
declares a SOURCE_PATH" is a claim about the parameter list; the safe property is
"every byte written derives from an existing source object rather than from
model-supplied content". A future transform or import tool could name a source and
still write model-controlled output, and would have inherited the exemption.
ToolCapability.CONTENT_FROM_SOURCE now carries that provenance claim explicitly.
file_copy declares it; ReadBeforeWriteRule.appliesTo stands down only for calls that
do, so ToolCallAssessor skips the rule rather than the rule skipping itself. The
capability is recorded on the invocation event like every other one, so replay
classifies a call by what it actually claimed instead of re-deriving it from
parameters.
Tool availability is by declared tool name, not capability-set containment, so the
extra capability does not narrow which stages can reach file_copy.
Tests: the exemption is asserted through ToolCallAssessor, plus a source-naming tool
WITHOUT the provenance capability that stays gated. ./gradlew check green.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
48 lines
3.7 KiB
Markdown
48 lines
3.7 KiB
Markdown
# core/toolintent — AGENTS.md
|
|
|
|
## Purpose
|
|
|
|
Plane-2 tool-call intent validation: evaluates proposed tool calls against workspace policy rules before execution, records the assessment as an event, and provides `WorldProbe` for recording environment observations needed by rules.
|
|
|
|
## Ownership
|
|
|
|
CORREX kernel team. This module enforces Hard Invariant #9 for the tool-call path.
|
|
|
|
## Local Contracts
|
|
|
|
- `ToolCallAssessor` — evaluates a proposed tool call against all active `ToolCallRule`s; returns a `ToolCallAssessmentRecord`.
|
|
- `ToolCallRule` — interface for a single validation rule. Built-in rules:
|
|
- `PathContainmentRule` — tool must write within the workspace root.
|
|
- `ReadBeforeWriteRule` — file must be read before being overwritten.
|
|
- `StaleWriteRule` — detects write to a file that has changed on disk since last read.
|
|
- `NetworkHostRule` — egress must be on the allowlist.
|
|
- `ManifestContainmentRule` — write target must appear in the write manifest.
|
|
- `WriteScopeRule` — enforces declared write scope.
|
|
- `ReferenceExistsRule` — referenced entities must exist.
|
|
- `ExecInterpreterRule` — interpreter must be in the allowed list.
|
|
- `CycleExitRule` (in `core:validation`) — imported separately; not defined here.
|
|
- `WorkspacePolicy` — aggregates rules and configuration for a workspace.
|
|
- `WorldProbe` — performs environment checks (filesystem, network) and records the observations as events immediately (Hard Invariant #9). Never call `WorldProbe` during replay.
|
|
- `EgressAllowlist` — current egress allowlist; rebuilt from `EgressAllowlistProjection` (in `core:events`).
|
|
- `ParamValueExtractor` — extracts typed parameter values from tool call arguments. `candidatePathStrings` = every path-like argument (`ParamRole.PATH` + `ParamRole.SOURCE_PATH`), used by the containment/existence gates; `writeTargetPathStrings` = only the paths a call MUTATES (`ParamRole.PATH`), used by the write-target gates. A tool declaring none of those roles falls back to sniffing path-like strings, so `shell` is unaffected.
|
|
- `RiskMapping` — maps rule violations to risk levels for `core:risk`.
|
|
- `SessionContext` — session-scoped context passed to rules during evaluation.
|
|
|
|
## Work Guidance
|
|
|
|
- Hard Invariant #9: all `WorldProbe` calls record observations as events. Replay reads those recorded events — it must not call `WorldProbe` again.
|
|
- Hard Invariant #5: every tool call must be assessed before execution. Assessment result is recorded as `ToolCallAssessmentEvents` in `core:events`.
|
|
- New rules implement `ToolCallRule` and are registered in `WorkspacePolicy`. Do not add rule logic directly to `ToolCallAssessor`.
|
|
- Resolve every model-supplied path through `ToolPath.resolve` (`core:tools`) — the one canonical normalization rule, shared with the filesystem tools. A rule that resolves paths itself will judge a different path than the tool operates on (the `~` bug: `~/x` resolved to `<workspace>/~/x`, so a real home-directory file was reported as a non-existent in-workspace file and the out-of-workspace prompt never fired).
|
|
- `ReadBeforeWriteRule` exempts calls declaring `ToolCapability.CONTENT_FROM_SOURCE` — every byte written comes from an existing source object, so there is no model-authored content to clobber with, and requiring a read of a copied binary is unsatisfiable. The exemption keys on that declared PROVENANCE, never on the presence of a `SOURCE_PATH` parameter: a transform or import tool may name a source and still write model-controlled output, and must stay gated. It lives in `appliesTo`, so `ToolCallAssessor` skips the rule entirely.
|
|
|
|
## Verification
|
|
|
|
```bash
|
|
./gradlew :core:toolintent:test --rerun-tasks
|
|
```
|
|
|
|
## Child DOX Index
|
|
|
|
No child AGENTS.md (leaf module).
|