Skip to content

fix(cli): hook enforcement follow-up — every preflight entrance, both-scope Pi carrier, string-safe Codex feature flag - #1407

Merged
Brad (bmethod) merged 5 commits into
mainfrom
vst-283-followup
Aug 17, 2026
Merged

fix(cli): hook enforcement follow-up — every preflight entrance, both-scope Pi carrier, string-safe Codex feature flag#1407
Brad (bmethod) merged 5 commits into
mainfrom
vst-283-followup

Conversation

@bmethod

Copy link
Copy Markdown
Collaborator

Follow-up to #1401, carrying the review findings accepted there plus what the pre-push cross-model review surfaced. Every fix has a pin that ran red first.

  • The uncovered-event preflight guards every entrance. It already ran before refresh's prune pass; now it also fails add before the first mutation (an installed hook whose source left the contract previously let the add write its items, then die in reconciliation) and the TUI inline update before ITS prune. When a filter admits any agent, the hook preflight ignores the filter — regenerating an agent consumes every locked hook, selected or not.
  • The Pi carrier counts from both scopes. Pi loads global and project packages; a globally deployed @vanillagreen/pi-hooks now backs a project hook instead of reading unsupported.
  • The Codex hooks feature flag is honest on both sides. The reader is a real TOML parse (an example [features] block inside a multiline string decides nothing; an unparsable file enables nothing), and the line-oriented feature writer now tracks multiline-string state, so a reinstall can no longer rewrite or delete text inside a user's developer_instructions.

Declined from the same review round, consistent with #1401: capability-resolved Pi support (the harnesses: allowlist is the documented per-port contract).

Proof: tools/validate-changed all lanes green, preflight --base origin/main clean, size-ratchet OK, full cargo suite green; second-opinion review ran pre-push and its three actionable blockers are the fixes above.

https://claude.ai/code/session_012epxJEzGqT7q3qcFhdZUt5

… carrier counts from both scopes, and the Codex feature flag is parsed TOML

The preflight now also runs before filtered add reconciliation and the
TUI inline update's prune pass. A globally deployed pi-hooks backs a
project hook, as Pi loads both scopes. The hooks feature flag comes from
a real TOML parse, so the same lines inside a multiline string decide
nothing.

Claude-Session: https://claude.ai/code/session_012epxJEzGqT7q3qcFhdZUt5
Copilot AI balanced review requested due to automatic review settings August 17, 2026 06:30
@linear-code

linear-code Bot commented Aug 17, 2026

Copy link
Copy Markdown

VST-283

@qodo-code-review

Copy link
Copy Markdown

ⓘ Qodo reviews are paused because your trial has ended. Ask your workspace admin to add credits to resume reviews. Manage billing

@macroscopeapp

macroscopeapp Bot commented Aug 17, 2026

Copy link
Copy Markdown

Approvability

Verdict: Needs human review

This PR introduces behavioral changes beyond simple fixes: enabling global Pi carriers to back project hooks, changing Codex config parsing from line-based to proper TOML, and adding new preflight validation that can refuse operations. While well-tested, these runtime behavior changes warrant human review.

You can customize Macroscope's approvability policy. Learn more.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

This PR tightens hook contract enforcement and update atomicity across add, refresh, and TUI inline updates, while improving Codex config feature detection to avoid false positives from embedded examples.

Changes:

  • Add preflight checks to refuse uncovered hook events before any mutation (including prune/reconcile paths).
  • Switch Codex hooks feature detection to a real TOML parse and harden line-based merges against multiline-string decoys.
  • Expand test coverage for the above regressions (Pi carrier scope, Codex feature decoys, atomic filtered add/inline update).

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
tools/size-ratchet-baseline.tsv Updates size baselines for the touched CLI modules/tests.
cli/tests/hook_contract.rs Adds regression tests for Pi carrier enforcement and contract/atomicity edge cases.
cli/src/tui/disk_mutations/tests.rs Adds inline-update regression tests around uncovered events and agent regeneration behavior.
cli/src/tui/disk_mutations.rs Refuses uncovered hook events before the prune pass during inline updates.
cli/src/installer/hooks/enforcement.rs Treats globally installed pi-hooks as backing project-scope hooks for enforcement summaries.
cli/src/installer/hooks.rs Uses TOML parsing for feature detection and adds multiline-string skipping in line merges.
cli/src/commands/refresh.rs Exposes uncovered_hook_event and ignores name filters when regenerating agents (since they consume all hooks).
cli/src/commands/add.rs Adds uncovered-event preflight before writing any new items and before reconciliation touches agents.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread cli/src/tui/disk_mutations.rs
Comment thread cli/src/commands/add.rs
Comment thread cli/src/installer/hooks.rs
Comment thread cli/src/installer/hooks/enforcement.rs Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3018386002

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "Codex (@codex) review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "Codex (@codex) address that feedback".

Comment thread cli/src/installer/hooks.rs Outdated
Comment thread cli/src/tui/disk_mutations.rs Outdated
Copilot AI review requested due to automatic review settings August 17, 2026 06:35

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.

Suppressed comments (2)

cli/src/installer/hooks.rs:802

  • When state is None, this scanner will treat """ / ''' appearing inside TOML comments (after #) as starting a multiline string, which can cause subsequent real TOML structure lines to be skipped and prevent merge_codex_hooks_feature / codex_features_state from applying correct updates. Consider ignoring/comment-stripping the portion after # when outside a multiline string, so delimiters in comments can’t toggle string_state.
fn advance_toml_string_state(line: &str, mut state: Option<&'static str>) -> Option<&'static str> {
    let mut rest = line;
    loop {
        match state {
            Some(delimiter) => match rest.find(delimiter) {
                Some(idx) => {
                    rest = &rest[idx + delimiter.len()..];
                    state = None;
                }
                None => return state,
            },
            None => {
                let double = rest.find("\"\"\"");
                let single = rest.find("'''");
                let (idx, delimiter) = match (double, single) {
                    (None, None) => return None,
                    (Some(d), None) => (d, "\"\"\""),
                    (None, Some(s)) => (s, "'''"),
                    (Some(d), Some(s)) if d < s => (d, "\"\"\""),
                    (_, Some(s)) => (s, "'''"),
                };
                rest = &rest[idx + delimiter.len()..];
                state = Some(delimiter);
            }
        }
    }
}

cli/src/installer/hooks/enforcement.rs:133

  • This can call is_pi_extension_operational twice for the project-scope case (global == false). If that function hits the filesystem (likely), consider caching the result(s) in locals (e.g., compute project_operational and global_operational once) to avoid duplicate IO during summary generation.
        crate::pi_extension::is_pi_extension_operational(PI_HOOKS_PACKAGE, global)
            || (!global
                && crate::pi_extension::is_pi_extension_operational(PI_HOOKS_PACKAGE, true));

Comment thread cli/src/tui/disk_mutations.rs
Comment thread cli/src/commands/add.rs
Copilot AI review requested due to automatic review settings August 17, 2026 06:37

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.

Suppressed comments (2)

cli/src/commands/add.rs:2382

  • The new preflight silently treats a lock-file load failure as “empty lock” via unwrap_or_default(), which can skip the uncovered-hook-event refusal and allow the add to proceed in a potentially inconsistent state. Since this preflight is intended to enforce atomic safety, it should not ignore lock parse/IO errors; instead, propagate the error (or fail the add with a clear message) so the user can repair the lock rather than bypassing this safety check.
        let lock = LockFile::load(&config::lock_file_path(global)).unwrap_or_default();
        let records = crate::refresh_sources::resolve_source_records_without_update(&lock);
        let all_hooks = crate::refresh_sources::all_source_hooks(
            &crate::refresh_sources::load_refresh_sources(&records.sources),
        );
        if let Some((name, error)) =
            crate::commands::refresh::uncovered_hook_event(&lock, &all_hooks, None)
        {
            anyhow::bail!("hook {name}: {error}");
        }

cli/src/installer/hooks.rs:776

  • The new multiline-string tracking is covered for full-line comment and multiline-string “decoy” cases, but there isn’t coverage for delimiters appearing in inline comments (e.g., key = 1 # """) or inside regular string literals (e.g., key = "\"\"\""). Adding targeted tests for these cases would prevent regressions where advance_toml_string_state incorrectly flips state and causes the feature writer/state detection to ignore real TOML structure.
fn advance_toml_string_state(line: &str, mut state: Option<&'static str>) -> Option<&'static str> {

Comment thread cli/src/installer/hooks.rs
@bmethod
Brad (bmethod) added this pull request to the merge queue Aug 17, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5a17a586f3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "Codex (@codex) review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "Codex (@codex) address that feedback".

Comment thread cli/src/installer/hooks.rs Outdated
@bmethod
Brad (bmethod) removed this pull request from the merge queue due to a manual request Aug 17, 2026
@github-actions

Copy link
Copy Markdown

Merge queue ejected this PR (removed_from_merge_queue, reason: MANUAL). The auto-merge arm is now DROPPED — nothing will merge this PR until someone re-arms it.

Ejecting merge-group run: https://github.com/vanillagreencom/vstack/actions/runs/32002645006 (Skill Tests)

Failing job(s):
(run 32002645006 has no failure-shaped jobs recorded — it may be the attempt's still-running or non-ejecting sibling; inspect the run link above)

No usable same-named comparison on the PR head (checks absent, skipped, or still running) — no flake-vs-genuine call is available; inspect the failing run before re-arming.

Automated by merge-queue-ejection-alert (VST-196). This alert never re-arms auto-merge.

Copilot AI review requested due to automatic review settings August 17, 2026 06:44

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated no new comments.

Suppressed comments (3)

cli/src/installer/hooks.rs:828

  • The multiline-string scanner will treat any occurrence of """ as a closing delimiter while inside a basic multiline string. In TOML, it’s valid to include sequences of quotes where the first quote is escaped (e.g. \"""), which should not terminate the multiline string; this implementation can incorrectly exit “string mode” early and then misinterpret subsequent lines as TOML structure. Consider either (a) tracking escapes while in Some("\"\"\"") and only treating """ as a terminator when it’s not escaped (accounting for an odd/even run of backslashes before the first quote), or (b) using a TOML-aware editor/parser for this transformation to avoid implementing string lexing rules manually.
/// Multiline-string state for a line-oriented TOML walk: `None` outside,
/// `Some(delimiter)` inside. Escapes are not tracked — TOML forbids an
/// unescaped `"""` inside a basic multiline string, and the configs this
/// walks are ones this tool and its users write.
fn advance_toml_string_state(line: &str, mut state: Option<&'static str>) -> Option<&'static str> {
    fn find_bytes(haystack: &[u8], needle: &[u8]) -> Option<usize> {
        haystack
            .windows(needle.len())
            .position(|window| window == needle)
    }
    let bytes = line.as_bytes();
    let mut i = 0;
    // A single-line string or a comment is content: a delimiter inside one
    // opens nothing. Escapes count only in basic (double-quoted) strings.
    let mut single_line: Option<u8> = None;
    while i < bytes.len() {
        match state {
            Some(delimiter) => match find_bytes(&bytes[i..], delimiter.as_bytes()) {
                Some(idx) => {
                    i += idx + delimiter.len();
                    state = None;
                }
                None => return state,
            },
            None => match single_line {
                Some(quote) => {
                    if quote == b'"' && bytes[i] == b'\\' {
                        i += 2;
                        continue;
                    }
                    if bytes[i] == quote {
                        single_line = None;
                    }
                    i += 1;
                }
                None => {
                    if bytes[i..].starts_with(b"\"\"\"") {
                        state = Some("\"\"\"");
                        i += 3;
                    } else if bytes[i..].starts_with(b"'''") {
                        state = Some("'''");
                        i += 3;
                    } else {
                        match bytes[i] {
                            b'#' => return None,
                            b'"' => single_line = Some(b'"'),
                            b'\'' => single_line = Some(b'\''),
                            _ => {}
                        }
                        i += 1;
                    }
                }
            },
        }
    }
    state
}

cli/src/commands/add.rs:2383

  • The uncovered-hook-event preflight is duplicated (same uncovered_hook_event call pattern and error wrapping) in two places. It would be more maintainable to extract this into a small helper (e.g., “validate_no_uncovered_hook_events(lock, global, sources…)”) so future adjustments to preflight behavior or error formatting don’t risk diverging between add-time checking and reconcile-time checking.
    // Reconciliation later renders every agent with every locked hook, so an
    // already-installed hook whose source left the contract fails the add
    // here, before this add's own items are written.
    {
        let lock = LockFile::load(&config::lock_file_path(global)).unwrap_or_default();
        let records = crate::refresh_sources::resolve_source_records_without_update(&lock);
        let all_hooks = crate::refresh_sources::all_source_hooks(
            &crate::refresh_sources::load_refresh_sources(&records.sources),
        );
        if let Some((name, error)) =
            crate::commands::refresh::uncovered_hook_event(&lock, &all_hooks, None)
        {
            anyhow::bail!("hook {name}: {error}");
        }
    }

cli/src/commands/add.rs:3059

  • The uncovered-hook-event preflight is duplicated (same uncovered_hook_event call pattern and error wrapping) in two places. It would be more maintainable to extract this into a small helper (e.g., “validate_no_uncovered_hook_events(lock, global, sources…)”) so future adjustments to preflight behavior or error formatting don’t risk diverging between add-time checking and reconcile-time checking.
    // Reconciliation regenerates every agent from every locked hook, so a
    // definition install would refuse fails it before any agent is touched.
    if let Some((name, error)) =
        crate::commands::refresh::uncovered_hook_event(&lock, &all_hooks, None)
    {
        anyhow::bail!("hook {name}: {error}");
    }

@bmethod
Brad (bmethod) added this pull request to the merge queue Aug 17, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fa8f6b1487

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "Codex (@codex) review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "Codex (@codex) address that feedback".

Comment thread cli/src/installer/hooks.rs
Comment thread cli/src/commands/add.rs Outdated
@bmethod
Brad (bmethod) removed this pull request from the merge queue due to a manual request Aug 17, 2026
@github-actions

Copy link
Copy Markdown

Merge queue ejected this PR (removed_from_merge_queue, reason: MANUAL). The auto-merge arm is now DROPPED — nothing will merge this PR until someone re-arms it.

Ejecting merge-group run: https://github.com/vanillagreencom/vstack/actions/runs/32003095181 (Skill Tests)

Failing job(s):
(run 32003095181 has no failure-shaped jobs recorded — it may be the attempt's still-running or non-ejecting sibling; inspect the run link above)

No usable same-named comparison on the PR head (checks absent, skipped, or still running) — no flake-vs-genuine call is available; inspect the failing run before re-arming.

Automated by merge-queue-ejection-alert (VST-196). This alert never re-arms auto-merge.

Copilot AI review requested due to automatic review settings August 17, 2026 06:50

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.

Suppressed comments (2)

cli/src/commands/add.rs:2386

  • unwrap_or_default() suppresses lockfile read/parse errors, which can cause this new uncovered-event preflight to be silently skipped and allow add to proceed in situations where the lock is present but unreadable/corrupt. Prefer propagating the error (or failing the add) so behavior is deterministic and the safety check can’t be bypassed due to an I/O/formatting problem.
        let lock = LockFile::load(&config::lock_file_path(global)).unwrap_or_default();
        let reconciles_agents = !selected_agents.is_empty()
            || lock
                .entries
                .values()
                .any(|entry| entry.kind == config::ItemKind::Agent);
        if reconciles_agents {
            let records = crate::refresh_sources::resolve_source_records_without_update(&lock);
            let all_hooks = crate::refresh_sources::all_source_hooks(
                &crate::refresh_sources::load_refresh_sources(&records.sources),
            );
            if let Some((name, error)) =

cli/src/installer/hooks.rs:776

  • The new multiline-string tracking is a correctness boundary for merge_codex_hooks_feature/codex_features_state and currently lacks a regression test for escaped delimiter sequences inside basic multiline strings (e.g., content that includes \\\"\"\"). Adding a focused test case for this scenario would help ensure the feature-writer doesn’t mis-detect string termination and accidentally rewrite real structure below.
fn advance_toml_string_state(line: &str, mut state: Option<&'static str>) -> Option<&'static str> {

Comment thread cli/src/installer/hooks.rs

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c7602de6b0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "Codex (@codex) review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "Codex (@codex) address that feedback".

Comment thread cli/src/tui/disk_mutations.rs
@bmethod
Brad (bmethod) added this pull request to the merge queue Aug 17, 2026
Merged via the queue into main with commit e66ac8f Aug 17, 2026
32 checks passed
@bmethod
Brad (bmethod) deleted the vst-283-followup branch August 17, 2026 07:09
Brad (bmethod) added a commit that referenced this pull request Aug 17, 2026
…t follow-up

origin/main landed e66ac8f (#1407) while this PR sat in the merge queue — the
follow-up to VST-283 that this branch reconciled with one round ago, landing in
the same files. Resolved as one three-way merge over the final states, no
rebase. Two conflicts: `cli/src/installer/hooks.rs` and
`tools/size-ratchet-baseline.tsv`.

MAIN'S CAPABILITY, TAKEN WHOLE

- `add`'s uncovered-event preflight now guards every entrance: before this
  add's own items are written whenever anything will reconcile agents, and
  again inside `reconcile_agents` before any agent is touched. An add that
  reconciles no agent is not held hostage by a hook nothing consumes.
- `refresh::hook_preflight_filter` is the one home for the filter rule —
  regenerating ANY agent consumes every locked hook, so a filter that admits an
  agent is ignored for hooks. Refresh and the TUI's inline update both call it;
  `uncovered_hook_event` is `pub(crate)` for those callers.
- The TUI inline update preflights before its prune pass, and a hook batch
  preflights every locked hook, because the batch later expands to every agent.
- `enforcement::summary` counts a globally deployed pi-hooks carrier as backing
  a project hook, because Pi loads packages from both scopes.

WHERE THE TWO OVERLAPPED, ONE IMPLEMENTATION SURVIVED

Main's "string-safe Codex feature flag" and this branch's parser-backed Codex
config are one fix arrived at twice. This branch's survived in both halves,
because it is the strict superset and main's rewrite was converging on it — a
real parse instead of a scanner taught to skip strings.

- Reader: `hooks/codex.rs::codex_hooks_feature_state`, a full
  `toml_edit::DocumentMut` parse, and the same parse the writer makes. Main's
  incoming `codex_hooks_feature_enabled` — rewritten in e66ac8f from a line
  walker to a `toml::Value` parse — is deleted. It answers the same question
  minus `CodexHooksFeature::Unreadable`, the state `check` and `verify` report
  by name. The previous reconciliation deleted it once; this merge does not
  resurrect it.
- Writer: `merge_codex_hooks_feature` edits the parsed document through
  `toml_edit`, so a `[features]` header or a `codex_hooks = …` line inside a
  string is content and never structure. Main's incoming
  `advance_toml_string_state` — a hand-rolled scanner for multiline strings,
  single-line strings and comments, threaded through `merge_codex_hooks_feature`
  and `codex_features_state` — is deleted along with the line walkers it existed
  to make safe: `codex_features_state`, `CodexFeaturesState`,
  `DeprecatedCodexHooksFeature`, `is_toml_table_header`, `toml_assignment_key`
  and `toml_assignment_value`.
- `codex_hook_prose_present` fell inside the same conflict hull and is deleted
  again in favour of `codex_hook_prose`.

The proof is main's own tests, taken unmodified and passing against this
branch's implementation: `a_features_example_inside_a_string_does_not_enable_codex_hooks`,
`a_reinstall_does_not_corrupt_feature_examples_inside_strings`,
`a_comment_showing_a_delimiter_does_not_derail_the_feature_writer` and
`a_delimiter_inside_a_single_line_string_does_not_derail_the_feature_writer`.

The split stands. All 102 of main's `installer/hooks.rs` lines are Codex config
reader/writer, a concern `hooks/codex.rs` already owns, so nothing was
reassembled and no seam file grew; the only seam file this merge touches is
`hooks/enforcement.rs`, for main's both-scope Pi carrier.

TESTS

936 tests, up from this branch's 925 and main's 782 — the exact union of both
sets by name. Nothing deleted, nothing renamed, nothing invented: 929 pass, 7
ignored (840 lib + 2 add_noninteractive_tty + 15 check_contract + 7
growth_guards_git_hooks + 38 hook_contract + 24 hook_lifecycle + 3
source_refusal_warning), against this branch's 918/7 and main's 775/7 from a
clean checkout. The union is taken on test names: 77 of main's tests differ from
this branch only in module path, from this branch's earlier file splits, and are
present under their new paths.

The 11 names this merge adds are main's, all passing. Eight in
`cli/tests/hook_contract.rs` — the four above plus
`a_filtered_add_refuses_before_installing_anything`,
`a_filtered_add_refuses_to_reconcile_from_an_uncovered_event`,
`an_add_with_no_agents_ignores_an_uncovered_hook_it_cannot_consume` and
`a_globally_installed_pi_carrier_backs_a_project_hook` — and three in
`tui/disk_mutations/tests.rs`:
`inline_update_refuses_an_uncovered_event_before_pruning`,
`inline_update_of_an_agent_refuses_an_unselected_uncovered_hook` and
`inline_update_of_a_hook_refuses_an_unselected_uncovered_hook_before_pruning`.
The same 7 tests are ignored on both sides.

One incoming line changed, in main's new test: `&[installed_rogue.clone()]` to
`std::slice::from_ref(&installed_rogue)`, which `clippy::cloned_ref_to_slice_refs`
rejects under clippy 1.96 — a clean checkout of main fails the same lint.

The size-ratchet baseline is re-derived from the merged tree, not picked from
either side; `--update` finds nothing left to tighten. `refresh.rs 1680`,
`disk_mutations/tests.rs 1717` and `hook_contract.rs 1548` stay exactly as main
set them, because the merged files measure exactly that. `add.rs` is the one row
the merge made wrong on both sides — this branch's 1740 plus main's 31 lines is
1771. Rows for the files this branch split (`agent.rs`, `installer/hooks.rs`,
`config/tests.rs` and the rest) stay as this branch left them.

Validated: cargo test, cargo clippy --all-targets -D warnings, cargo fmt
--check, size-ratchet (1509 files, threshold 1000), preflight --base origin/main
(clean, 74 files), cli/scripts/integration-check.sh (29),
skills/orch/tests/run-all.sh (57 across 37 files), hooks/tests
(50 + 55 + 55 + 43) and the pi-hooks bun suite (93).

Claude-Session: https://claude.ai/code/session_01MmBDMLngdEbAcgEuPYcBmi
Brad (bmethod) added a commit that referenced this pull request Aug 20, 2026
Phase 6 of the v1-parity plan. Pi has no per-hook artifact; the pi-hooks extension package hosts native listeners, and hook content rides in the registry vstack now renders beside them — hooks/<name>.sh plus hooks.json, keyed by the listeners Pi actually fires (tool call, tool result, turn end, session start). An event outside that map installs nothing on Pi and says so; a stale advisory claim is worse than none.

The capability row moves from unsupported to enforced-through-the-carrier, and the surfaces that label an installation read carrier reality: pi_ext::carrier checks every settings layer Pi loads, so a project-installed hook with only a global carrier is enforced (the v1 #1407 lesson, arriving as a test), and a scope with no carrier anywhere gets the downgrade said per item with the fix named.

The session-start drift report rides the same mechanism — the drift hook now declares for Pi too, same script, same kill-switch, and a resumed or reloaded session never repeats it.

Claude-Session: https://claude.ai/code/session_01GwNAzNp5ZMNLvwsJJdyHre
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.

2 participants