Skip to content

feat(host-diff): describe an inline hook command by its structure, or say to open the config (#934) - #999

Merged
pengfei-threemoonslab merged 1 commit into
mainfrom
claude/934-inline-hook-command-summary
Oct 8, 2026
Merged

pengfei-threemoonslab merged 1 commit into
mainfrom
claude/934-inline-hook-command-summary

Conversation

@pengfei-threemoonslab

Copy link
Copy Markdown
Contributor

Closes #934

Problem

CirrusRedOrg/EntityFrameworkCore.Jet#303 printed PreToolUse: command changed (<not-shown> sha256:f23acba4b10f → <not-shown> sha256:3f1dc36980b7): two digests, and no name because the script opens with an assignment. A reviewer could not tell whether the edit added a program or only text, and no row told an edit that explains itself from one that needs the config opened.

Design

Both outcomes the issue asked for, display-only, in host-grants 0.9 (unreleased, extended in place; no schema file present in v1.2.0 changes).

  • Described shapes. src/agents_shipgate/core/hook_command_shape.py is a bounded, linear, static reader of the command as config_sha256's input holds it (never run). It accepts simple commands, | |& && || ; & and newlines, groups, if/while/until/for, [[ ]], assignments, $( ), quoting, redirects and a literal bash -c '…' (two levels), and refuses everything else whole. command.shape publishes: plain-token programs at command positions (max 12, never a URL or a generated-looking token), counts of statements, pipes, substitutions, control-flow keywords and quoted strings, file redirects (target only for /dev/null or a repo-relative path), and a script path by the args rule (Hook exec-form args changes are detected but hidden behind no difference in matcher, command or timeout #972).
  • Not described. command.shape_limit is too_long (> 8,192 chars), unsupported_syntax (here-doc, backquote, $((, case, functions, unterminated quote/group, word/nesting bound) or unsupported_shell (shell not bash/sh). The row then says not described: …; the digest moved, open the config to read the change. A same-structure argument edit says same programs and structure; the change is in an argument or in quoted text this output does not show, open the config to read the change.
  • Never published: arguments, quoted strings, variables, URLs, absolute paths. Nothing infers effects or direction; the row stays hook edit; authority direction is unknown. The shape is a function of the sanitized text (a backslash-continued line is read joined and re-sanitized, because the string rule leaves --token \<newline>value on the next line), so fix(privacy): withhold bearer tokens and curl user passwords in published values #987 shapes stay withheld and rotations stay quiet. The existing leak corpus caught that case and a URL-host-as-program case during development; both are pinned.
  • Left out of grant equality, digests and saved baselines like the rest of handlers: no row, severity, direction, expands, expansion signal or check decision moves.

Surface discipline: extends an existing grant field in place, adds no command, schema version or report block. Headline metric: reviewer-checkable hook rows (#811 comprehension; content-free rows were the noise #819 targeted).

Before / after (the real pair)

Before: PreToolUse: command changed (<not-shown> sha256:f23acba4b10f → <not-shown> sha256:3f1dc36980b7)

After: PreToolUse: command changed (<not-shown> sha256:f23acba4b10f → <not-shown> sha256:3f1dc36980b7; same programs (cat, printf, sed, grep, echo, true); simple commands 15 → 25; pipes 5 → 9; conditionals and loops 4 → 7; quoted strings 18 → 32) / hook edit; authority direction is unknown

Tests

New tests/test_hook_command_shape.py (real pair as MIT fixtures under tests/fixtures/hook_command_shape/, grammar table, redirects, scripts, refusals, bounds, credential and #987 shapes, row wording, route parity for diff/verify/PR comment/check/drift, schema, fuzz). Existing hook-detail tests updated for the new text. Full CI mirror: only the two known machine-local local_settings_enabled_plugins-* failures and one wall-clock test (test_workflow_label_redaction linear-time) which passes alone; tests/test_adapter_static_only.py passes alone; ruff check . clean; generate_schemas.py --check clean.

🤖 Generated with Claude Code

… say to open the config (#934)

A hook row for a command that opens with an assignment, pipes or branches read
two digests and no name. Each handler's command now publishes a shape (host-grants
0.9, extended in place) read by a bounded static reader: the plain-token programs
at command positions, counts of commands, pipes, substitutions, control-flow
keywords and quoted strings, file redirects and a script path. A command the reader
refuses is shape_limit, and its row says the digest moved and the config has to be
opened. Display only: no row, direction, severity, expansion signal or check
decision moves.
@pengfei-threemoonslab
pengfei-threemoonslab force-pushed the claude/934-inline-hook-command-summary branch from 0b2de09 to 329e8dc Compare October 8, 2026 10:17
@pengfei-threemoonslab
pengfei-threemoonslab merged commit 4e212eb into main Oct 8, 2026
14 checks passed
@pengfei-threemoonslab
pengfei-threemoonslab deleted the claude/934-inline-hook-command-summary branch October 8, 2026 10:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

diff 1.2.0: digest-only inline hook edits do not expose a reviewer-judgeable capability impact

1 participant