- add time handler that unwraps the bare reserved-word form time <command> and re-evaluates the full de-wrapped compound like timeout (ADR 0009) - defer fail-closed on dash-leading modifiers, bare time, and nested wrappers in both directions - generalize isRecognizedWrapper to timeout and time, and classifyWrapper to recognized/unsupported/other with a wrapper name - rename defer events to inner_cmd.nested_wrapper and inner_cmd.unsupported_wrapper_syntax with a wrapper field - extract stripWrapperUnit into handlers/strip.ts for shared use
2.7 KiB
status
| status |
|---|
| accepted |
The bare time wrapper unwraps like timeout (ADR 0009)
pi-permission-inner-cmd recognized only timeout <duration> <command>
(ADR 0001). time <command> poses the same authorization question — a
wrapper around an inner command — and appears routinely in agent Bash calls
(time pnpm build). A wrapper qualifies for unwrapping only when its
modifier args are transparent (CONTEXT.md, Wrappers).
Decision
Recognize exactly the bare reserved-word form:
^time[ \t]+(?![-\s])(.+)$
It is transparent: time runs the inner command unchanged and only adds
timing, so the timeout unwrap pipeline applies verbatim — strip the wrapper
from the FULL command (scaffold included) and re-evaluate the whole de-wrapped
compound, so sibling commands stay under judgment. An inner allow allows,
anything else defers fail-closed.
Everything else that begins with time defers with
inner_cmd.unsupported_wrapper_syntax:
- any dash-leading modifier (
time -p ls,time -- ls,time -o FILE ls). The command string cannot distinguish the Bash reserved word (which accepts only-p) from/usr/bin/time(whose-o FILEwrites a file and-vdumps environment-bearing stats), and the lookahead also blocks regex backtracking from smuggling a leading space past a wide separator. A modifier arg is therefore treated as potentially non-transparent until individually proven otherwise. - a bare
time— it times the shell itself; there is no inner command to re-evaluate.
/usr/bin/time cmd (full path) is not claimed by any handler and defers
silently like any unrecognized command.
Generalizations that ride along
isRecognizedWrappernow covers timeout ∪ time, sotime timeout 10 cmd,timeout 10 time cmd, andtime time cmdall defer at most-one-level nested wrappers (previously the check was timeout-only).- The defer events generalize from
inner_cmd.nested_timeout/inner_cmd.unsupported_timeout_syntaxtoinner_cmd.nested_wrapper/inner_cmd.unsupported_wrapper_syntax, each carrying awrapper: "timeout" | "time"field, so future wrapper handlers reuse the same event names. Downstream analysis joins only on the decisiveinner_cmd.allow|denymarkers and is unaffected.
Consequences
time pnpm testno longer reaches the human dialog / AI judge when the inner command is already allowed.- Handler precedence stays irrelevant:
timeoutandtimeprefixes are disjoint, and both are tried before the claiming non-transparent handlers (env,xargs). - If
time -ptransparency is ever wanted, it is a one-line grammar change plus tests — recorded here as deliberately out of scope for v0.1.