Skip to content

[finding] guard-main-checkout hooks' remediation text still teaches the unhardened worktree recipe (no --no-track) — the copy an agent reads at the moment of the blocked action #6977

Description

@zhuangjianguo

Measured by the ui#6880 fold flight (PR #6976's "Not in this PR" section) and re-confirmed at triage: .claude/hooks/guard-main-checkout.sh:57 and .claude/hooks/guard-main-checkout-bash.sh:544 emit the worktree recipe WITHOUT --no-track as their remediation text — the exact text an agent reads at the moment it is about to run the command. PR #6976 hardens the prescribed recipe in CLAUDE.md/AGENTS.md; the hazard survives in the hooks' copy, which is the higher-leverage one (delivered at the point of action).

Scope when dispatched: align both remediation strings to the hardened form; re-run the hook selftests per the hooks' own change discipline; update any selftest case pinning the old string as part of the same change. Governed surface (.claude/**) ⇒ draft PR, human merge. ⛔ The historical changeset .changeset/6208-worktree-recipe-fetch-base.md quotes the old form as a record of a past change — leave it alone (adjudicated in PR #6976).

Sibling: the objectstack twin card covers that repo's hooks, which carry the same gap plus a second one (based on unfetched local main). Refs: PR #6976 · ui#6880 · objectstack#13052.

Generated by Claude Code

Activity

  1. huangyiirene commented on Aug 31, 2026

    @huangyiirene
    Collaborator

    Claim: PM loop round 1 (skills lane, seat post objectstack#7623)
    Session: session_01EnE7G31tqbxN1rqpQmzurT
    Branch: claude/issue-6977-hook-no-track
    Worktree: objectui-issue-6977
    Domain: domain:skills
    File surface: .claude/hooks/guard-main-checkout.sh, .claude/hooks/guard-main-checkout-bash.sh, and their selftest scripts if present (stop on breach; explain in the report)
    Container & model: S-sized but not mechanical (governed hook text) → M treatment, mode:subagent, model: opus — objectstack's dispatch-gates.mjs --tier has no jurisdiction over this repo (its own header says a card landing in another repo derives nothing there); tier is the PM's judgment call per the same run's tier rule.
    Clause-②: no — agent-infrastructure remediation text, no published contract face.
    Serial constraints cleared: none — zero open PRs touch .claude/hooks/** in this repo (open-PR listing read this hour; PR #6984 is AGENTS.md only, dependabot PRs are lockfile/deps). Cross-repo sibling objectstack#13663 (the twin) is dispatched this same round by this seat: disjoint repos and files, no read-coupling.

    Premise re-verified on this repo's origin/main this hour before this claim, and it is NARROWER than the objectstack twin: both hooks already print git fetch origin main … origin/main (the ui#6208 class is fixed here) — the surviving gap is --no-track only (grep -c "no-track" over .claude/hooks/: 0). The fix is additive --no-track, not a recipe rewrite.


    Generated by Claude Code

  2. huangyiirene commented on Aug 31, 2026

    @huangyiirene
    Collaborator

    Dev claim (companion to the PM claim above — same seat, dev side).

    Session: session_01EnE7G31tqbxN1rqpQmzurT
    Branch: claude/issue-6977-hook-no-track
    Worktree: /home/user/objectui-issue-6977 (created with --no-track off freshly fetched origin/main, base 1b1d772)
    Repo: objectstack-ai/objectui only — the objectstack twin (objectstack#13663) was not touched from this seat.

    Comments re-read before the first edit: the PM claim is the only prior claim on this card and it names this same session, so there is no competing in-flight work here.

    PM premise re-tested independently on the freshly fetched tree, and it holds exactly as stated — narrower than the objectstack twin's:

    • grep -rc "no-track" .claude/hooks/ = 0 across all seven files there (four guards, three self-test matrices).
    • Both guards already print the fetch-hardened base (git fetch origin main … origin/main) at guard-main-checkout.sh:57 and guard-main-checkout-bash.sh:544, so the ui#6208 class is already repaired in this repo.
    • The only surviving divergence is the missing flag. Fix is additive; no recipe rewrite, no widening.

    Second PM assumption tested — selftests. This repo does carry self-test scripts for these hooks, but the answer splits two ways and both halves are measurements, not assumptions:

    • .claude/hooks/ holds four guards and three matrices. There is no guard-main-checkout.selftest.sh — that one guard has no matrix at all.
    • No self-test case pins either remediation string, so none needed updating in this change. All three matrices were re-run anyway, before and after the edit: 121 / 41 / 36, 0 failed each, in both runs.

    File surface actually touched, matching the claimed surface with no additions: .claude/hooks/guard-main-checkout.sh, .claude/hooks/guard-main-checkout-bash.sh, plus .changeset/6977-hook-remediation-no-track.md. .changeset/6208-worktree-recipe-fetch-base.md left untouched as adjudicated, confirmed mechanically by check-changeset-overwrite (0 modified, 0 deleted).

    Draft PR: #7000. Governed surface, human merge — not flipped ready, not enqueued, no auto-merge armed.

    Generated by Claude Code


    Generated by Claude Code

  3. huangyiirene commented on Aug 31, 2026

    @huangyiirene
    Collaborator

    os-dev-report

    {
      "issue": 6977,
      "status": "done",
      "branch": "claude/issue-6977-hook-no-track",
      "pr": "https://github.com/objectstack-ai/objectui/pull/7000",
      "premise_still_valid": true,
      "summary": "Added --no-track to the worktree recipe both PreToolUse guards print as their remediation text: .claude/hooks/guard-main-checkout.sh:57 and .claude/hooks/guard-main-checkout-bash.sh:544. One word per file, net 0 lines in both, plus an empty-frontmatter changeset. PM's narrow premise re-tested on the freshly fetched tree and it holds exactly: grep -rc no-track over .claude/hooks/ was 0 across all seven files there, and both guards already printed the fetch-hardened base (git fetch origin main ... origin/main), so the ui#6208 class is already repaired here and the missing flag was the only surviving divergence. Additive fix, no recipe rewrite, no widening. PM's selftest assumption splits two ways, both measured: this repo DOES carry matrices for these hooks, but there is no guard-main-checkout.selftest.sh at all (.claude/hooks/ holds four guards and three matrices), and NO selftest case pins either remediation string, so no case needed updating; all three matrices were re-run anyway, unchanged. Three things examined and deliberately left alone, each argued in the PR body: the historical changeset 6208-worktree-recipe-fetch-base.md (adjudicated; check-changeset-overwrite confirms 0 modified / 0 deleted), the two -cmp comparison-tree lines in CLAUDE.md / AGENTS.md (no branch created, explicit ref from the caller, so no upstream keys to write), and guard-tree-enum.selftest.sh:99 (a command payload fed to a different guard to assert ordinary commands pass through it -- test input, not text any agent is told to run; judged not a defect, so no finding filed, and flagged here so PM can overrule). Governed surface honoured: PR is DRAFT, not flipped ready, not enqueued, no auto-merge armed; check-governed-queue-guard --test on this diff returns GOVERNED, 2 of 3 paths. Two platform notes: (1) PR create appended its own session-URL attribution footer beneath the one I wrote, so the body carries the footer twice -- left as-is because a PATCH to remove the duplicate would downgrade the remaining session URL to the bare form (measured create-only behaviour), and the duplicate is cosmetic; (2) the PR reads mergeable_state=behind because main moved after my base (base 1b1d772, PR base now a5a799d) -- no conflict in my three files. objectstack was not touched from this seat.",
      "tests": "All nine checks re-run at final head efbfb15 (git rev-parse --short HEAD from that run) as one &&-joined command under the shared verify lock: VERDICT command-exit 0, held 11s. Heavy steps serialised through bash /home/user/objectstack/scripts/pm/os-verify-lock.sh (objectui ships no lock script of its own) with OS_VERIFY_LOCK_SLOT=ui6977; pnpm install waited 92s for a sibling holder, then ran 15.8s. HOOK SELFTESTS (the only gate targeting .claude/hooks/**, per .github/workflows/hook-selftests.yml): guard-main-checkout-bash 121 passed 0 failed, guard-shared-stash 41 passed 0 failed, guard-tree-enum 36 passed 0 failed. Identical baseline run BEFORE the edit (121/41/36, 0 failed) -- the expected direction here, not a weak result, because no case pins the remediation string; verified by grepping four distinctive fragments of the two block messages (fetch origin main / dedicated worktree / Blocked: editing on the shared / WRITES into the shared) across .claude/hooks/*.selftest.sh and scripts/, no match in any selftest. BEHAVIOURAL CHECK (the real coverage for guard-main-checkout.sh, which has no matrix): both hooks invoked directly with a payload targeting the shared primary checkout; each exits 2 (blocked, as designed) and prints '  git fetch origin main && git worktree add --no-track ../objectui-TASK -b BRANCH origin/main' with $name interpolated -- byte-for-byte the form CLAUDE.md:16 and AGENTS.md:234 prescribe. That emitted recipe was then fed back through all three of this repo's Bash guards (guard-main-checkout-bash, guard-shared-stash, guard-tree-enum): all three exit 0, so the text it now teaches is runnable and not self-blocked. REPO GATES, each exit captured after redirect-to-file, never through a pipe: check-control-bytes exit 0 (5809 tracked text files scanned), check-changeset-presence exit 0, check-changeset-no-major exit 0, check-changeset-overwrite exit 0 (1 added, 0 modified, 0 deleted -- the mechanical proof the adjudicated historical changeset was left alone), check-shell-escape-residue exit 0, check-governed-queue-guard --self-test exit 0 (132 cases), bash -n clean on both edited files, plus a manual control-byte scan of all three diff files (grep -naP over the C0 set, no match). MEASUREMENT, reported because absence is the answer: nothing in this repo lints shell files' contents. Verified on this tree rather than taken from the workflow header asserting it -- eslint.config.js scopes every block to **/*.{ts,tsx}, and there is no shellcheck step anywhere (the only three matches in the repo are two prose mentions and one '# shellcheck source=' directive inside e2e/live/ci/start-backend.sh). check-shell-escape-residue reads fenced blocks in AGENTS.md, CLAUDE.md, skills/ and content/docs/ and does not reach .claude/hooks/. So the three matrices plus the direct invocations above are the whole of the coverage available for this diff. CHANGESET CONVENTION, verified rather than assumed: check-changeset-presence reports no changeset is owed here (3 files changed, 0 published source of a released package); one is shipped anyway with EMPTY frontmatter, which scripts/check-changeset-presence.mjs treats as a first-class pass, matching this repo's three precedents for this diff shape (6880-worktree-recipe-no-track, 6208-worktree-recipe-fetch-base, 6089-hook-selftests-step-names-drop-counts). No skip-changeset label applied: in objectui that label is a phantom -- scripts/__tests__/ci-cd-pipeline-doc.test.ts pins that nothing under .github/ or scripts/ may mention it, so it exempts nothing. CI itself not waited on, per the report-at-draft-PR contract.",
      "mcp_calls": "4 - create_pull_request 1, pull_request_read 1 (mandatory PR-body read-back), add_issue_comment 2 (dev claim + this report). Issue body and all comments were read through the zero-quota web channel (embedded JSON payload), not MCP; the PR-body read-back had to use MCP because the conversation body is lazily loaded and absent from the PR page's embedded payload.",
      "open_questions": [],
      "out_of_scope_findings": []
    }

    Generated by Claude Code

  4. huangyiirene commented on Aug 31, 2026

    @huangyiirene
    Collaborator

    ACCEPT — PR #7000; governed terminal: parked as draft, both approvers requested

    Reviewed against GitHub, not against the report's self-description: draft ✓ · base main ✓ · Fixes #6977 first line ✓ · 3 files — the two guards at +1/−1 each with exactly --no-track inserted into the printed recipe and nothing else, plus an empty-frontmatter changeset (ships nothing; the repo's verified convention with three precedents). The adjudicated historical changeset .changeset/6208-worktree-recipe-fetch-base.md is untouched (0 modified / 0 deleted, gate-confirmed).

    Spot readings: the pre/post selftest identity (121/41/36, 0 failed both sides) is the EXPECTED direction, not a weak result — no selftest case pins the remediation string, corroborated by the diff touching no selftest file while all matrices pass on both sides. The behavioural check (each hook exits 2 on a shared-checkout payload and emits the hardened recipe; that recipe re-fed through all three Bash guards exits 0) is the real coverage for guard-main-checkout.sh, which ships no matrix — measured, and the right instrument. CI at review time: 25/30 checks completed, all green or skipped; 4 test shards + Type Check still running (must be green at merge); Hook Self-Tests and Governed Surface Queue Guard already green.

    Deviations, both accepted: the dev's judgment call to leave guard-tree-enum.selftest.sh:99 alone (test payload fed to a different guard, not text an agent is taught) — concur, no finding owed; the duplicated attribution footer on the PR body (create-channel side effect, cosmetic) — left as measured.

    Terminal: .claude/** is governed, and this diff carries a prose-bearing .md (the changeset), so under the maintainer's 2026-08-26 分流 verbatim (「只有包含md文件时需要推给我审核」) this PR takes the maintainer-review path: it stays DRAFT, os-zhuang + hotlong are both requested, and this card moves to pm:awaiting-maintainer in the same pass. Recorded for the veto window, not decided by this seat: if empty-frontmatter changesets are ruled OUT of the md limb (they are release mechanics, not instruction or published prose), PRs of exactly this shape become seat-self-landable under the pure-code lane next time. The objectstack twin (objectstack#13663, no changeset file by that repo's convention) is unaffected by this question.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions