Problem
Verb chain extraction greedily includes pure integer tokens in the approval pattern because the spec only stops extraction at flags, paths, and URLs. This creates overly-specific approval entries that do not generalize.
Example
Extracted verb chain: freshdesk ticket get 123
When the user approves this command, the persisted pattern is the full 4-token chain. On the next invocation with a different ticket ID:
The verb chain is freshdesk ticket get 456 — which does not match freshdesk ticket get 123. Every unique ticket ID triggers a new approval prompt.
Root Cause
From the open spec:
The verb chain SHALL consist of non-flag tokens from the start of the command until the first flag (-), path, or URL argument. Extraction is greedy: bare-word operands that are neither flags, paths, nor URLs (subcommands, remote names, branch names, refs) SHALL remain in the verb chain.
Pure integers are not flags, not paths, and not URLs — so they pass through as bare-word operands and get baked into the verb chain. A number is never a CLI subcommand.
Impact
- User-facing: Every unique integer argument (ticket IDs, port numbers, record IDs) produces a distinct approval pattern. Users must approve
cmd arg 1, then cmd arg 2, then cmd arg 3, etc.
- Approval store bloat: Each unique integer creates a new
ApprovalEntry in tool-approvals.json.
Other Affected Cases
| Command |
Current Verb |
Expected Verb |
freshdesk ticket get 123 |
freshdesk ticket get 123 |
freshdesk ticket get |
nc host 8080 |
nc host 8080 |
nc host |
timeout 30 curl http://… |
timeout 30 curl |
timeout |
ssh user@host 22 |
ssh user@host 22 |
ssh user@host |
Proposed Fix
Add integer detection as a verb-chain termination condition in TryGreedyExtract (ShellApprovalSemantics.cs). When the greedy walk encounters a token that is purely digits (with optional leading - for negative numbers, distinguished from flags by not starting with --), stop extraction.
The change should live in ExtractVerbChain — either:
- In
TryGreedyExtract: stop the greedy walk when int.TryParse(token) succeeds, or
- In the post-extraction pass: trim trailing numeric tokens from the result
Option 1 is preferred because it matches the existing pattern (stop at flags, stop at paths, stop at URLs → stop at numbers).
Files Involved
src/Netclaw.Security/ShellApprovalSemantics.cs — TryGreedyExtract / ExtractVerbChain
src/Netclaw.Security/ShellTokenizer.cs — ApplyVerbShortCircuit (may need coordination)
src/Netclaw.Security.Tests/ShellTokenizerTests.cs — ExtractVerbChain_extracts_expected_chain
openspec/specs/tool-approval-gates/spec.md — spec update for termination conditions
Problem
Verb chain extraction greedily includes pure integer tokens in the approval pattern because the spec only stops extraction at flags, paths, and URLs. This creates overly-specific approval entries that do not generalize.
Example
Extracted verb chain:
freshdesk ticket get 123When the user approves this command, the persisted pattern is the full 4-token chain. On the next invocation with a different ticket ID:
The verb chain is
freshdesk ticket get 456— which does not matchfreshdesk ticket get 123. Every unique ticket ID triggers a new approval prompt.Root Cause
From the open spec:
Pure integers are not flags, not paths, and not URLs — so they pass through as bare-word operands and get baked into the verb chain. A number is never a CLI subcommand.
Impact
cmd arg 1, thencmd arg 2, thencmd arg 3, etc.ApprovalEntryintool-approvals.json.Other Affected Cases
freshdesk ticket get 123freshdesk ticket get 123freshdesk ticket getnc host 8080nc host 8080nc hosttimeout 30 curl http://…timeout 30 curltimeoutssh user@host 22ssh user@host 22ssh user@hostProposed Fix
Add integer detection as a verb-chain termination condition in
TryGreedyExtract(ShellApprovalSemantics.cs). When the greedy walk encounters a token that is purely digits (with optional leading-for negative numbers, distinguished from flags by not starting with--), stop extraction.The change should live in
ExtractVerbChain— either:TryGreedyExtract: stop the greedy walk whenint.TryParse(token)succeeds, orOption 1 is preferred because it matches the existing pattern (stop at flags, stop at paths, stop at URLs → stop at numbers).
Files Involved
src/Netclaw.Security/ShellApprovalSemantics.cs—TryGreedyExtract/ExtractVerbChainsrc/Netclaw.Security/ShellTokenizer.cs—ApplyVerbShortCircuit(may need coordination)src/Netclaw.Security.Tests/ShellTokenizerTests.cs—ExtractVerbChain_extracts_expected_chainopenspec/specs/tool-approval-gates/spec.md— spec update for termination conditions