GH_WRITE_GUARD_ALLOW_PRIMARY_CHECKOUT is a session-wide, path-blind grant, so a standalone-clone fallback also unlocks the maintainer's base checkout #1130

Description

@ptr727

What

gh-write-guard.py's rule 6 escape hatch is evaluated before the target path is resolved, so it grants writes to every primary checkout the session touches rather than to the one the grant was intended for.

# host-setup/agent-safety/claude/gh-write-guard.py:1046-1050environ=environifenvironisnotNoneelseos.environgrant_value=environ.get("GH_WRITE_GUARD_ALLOW_PRIMARY_CHECKOUT", "").strip().lower()
ifgrant_valuenotin_FALSY_ENV_VALUES:
return"allow", ""

The for ... in _all_git_invocations(cmd) loop that resolves resolved, repo_git_dir, and the identity/file dimensions begins at line 1054, after that early return. Nothing about the target reaches the grant decision. The self-test states the same behavior plainly at line 3329: "the escape hatch allows even a denied shape when granted".

Why This Matters

repo-worktree documents setting the grant for the standalone-clone fallback, because that fallback is structurally a primary checkout to the hook's own primary-vs-worktree test and would otherwise be denied the commits it exists to make. That is a legitimate need.

But the grant that unblocks the fallback clone simultaneously unblocks the maintainer's base checkout for the rest of that session. A later git add, git commit, git reset --hard, or branch switch aimed at the base clone is allowed with no further prompt, which is precisely the failure rule 6 exists to prevent, and precisely the incident (#1073) that motivated the whole rule.

The hazard is not theoretical for an agent session: the base clone is usually the working directory the session was launched in, so an inherited-cwd mutation is the easy mistake to make, and the grant removes the one mechanical thing that would have caught it.

The Prose Is Also Stronger Than the Code

GOVERNANCE.md "Repository Boundaries and Write Safety" and .agents/skills/repo-worktree/SKILL.md both describe the grant as scoped to the fallback clone. Neither says it is session-wide and path-blind, which is what it actually is. A reader following either document would not expect the base checkout to be unlocked as a side effect.

Suggested Direction

Not a proposed diff, since the design is the hub's call. Three shapes that would close it, roughly in order of how well they match what the docs already promise:

  1. Path-scope the grant. Read it as a path (or a colon-separated list) rather than a boolean, and allow only when the resolved target is at or under one of those paths. Both the identity and file dimensions the rule already computes would need to match, so a --git-dir/--work-tree split cannot straddle the boundary.
  2. Require a separate session for the standalone clone, and say so in the two documents, leaving the boolean grant as-is but never used in a session that also touches a base clone. Cheapest, but relies entirely on discipline, which is what the hook exists to replace.
  3. Keep the boolean but re-deny the base clone specifically, i.e. grant everything except a checkout that has a linked worktree registered, since a repository with worktrees is exactly the case where another task may be live.

Option 1 is the one that makes the existing prose true as written.

How This Surfaced

Raised by CodeRabbit against a downstream re-vendor of the repo-worktree skill and the GOVERNANCE.md section in ptr727/PhotoCleaner#99, and verified against this repository at mainf3b4cc9 (tag 2.0.526) by reading the hook rather than taking the finding text. Declined downstream and routed here, since neither the hook nor either document is fixable in a carrier: ptr727/PhotoCleaner#99 (comment)

Not This Issue

  • #1129 covers hub-local references inside the same verbatim-carried files, which is a text defect rather than a behavioral one.
  • #1128 is the analogous "the code is weaker than the sentence describing it" gap in merge-bot-task.yml's handling of the no-auto-merge- marker.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      , 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
       blocks
      (function() {
      function addCopyButtons() {
      document.querySelectorAll('pre code').forEach(function(codeBlock) {
      if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
      codeBlock.parentElement.setAttribute('data-copy-added', 'true');
      var btn = document.createElement('button');
      btn.textContent = 'Copy';
      btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
      btn.onmouseover = function() { this.style.opacity = '1'; };
      btn.onmouseout = function() { this.style.opacity = '0.7'; };
      btn.onclick = function() {
      navigator.clipboard.writeText(codeBlock.textContent).then(function() {
      btn.textContent = 'Copied!';
      setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
      });
      };
      codeBlock.parentElement.style.position = 'relative';
      codeBlock.parentElement.appendChild(btn);
      });
      }
      addCopyButtons();
      // Re-run on dynamic content
      var observer = new MutationObserver(addCopyButtons);
      observer.observe(document.body, { childList: true, subtree: true });
      })();
      }
      } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
      })();
      (function(){
      try {
      var __m = "github.com";
      var __re = new RegExp('^' + "github\\.com" + '
      
      Skip to content

      GH_WRITE_GUARD_ALLOW_PRIMARY_CHECKOUT is a session-wide, path-blind grant, so a standalone-clone fallback also unlocks the maintainer's base checkout #1130

      Description

      @ptr727

      What

      gh-write-guard.py's rule 6 escape hatch is evaluated before the target path is resolved, so it grants writes to every primary checkout the session touches rather than to the one the grant was intended for.

      # host-setup/agent-safety/claude/gh-write-guard.py:1046-1050environ=environifenvironisnotNoneelseos.environgrant_value=environ.get("GH_WRITE_GUARD_ALLOW_PRIMARY_CHECKOUT", "").strip().lower()
      ifgrant_valuenotin_FALSY_ENV_VALUES:
      return"allow", ""

      The for ... in _all_git_invocations(cmd) loop that resolves resolved, repo_git_dir, and the identity/file dimensions begins at line 1054, after that early return. Nothing about the target reaches the grant decision. The self-test states the same behavior plainly at line 3329: "the escape hatch allows even a denied shape when granted".

      Why This Matters

      repo-worktree documents setting the grant for the standalone-clone fallback, because that fallback is structurally a primary checkout to the hook's own primary-vs-worktree test and would otherwise be denied the commits it exists to make. That is a legitimate need.

      But the grant that unblocks the fallback clone simultaneously unblocks the maintainer's base checkout for the rest of that session. A later git add, git commit, git reset --hard, or branch switch aimed at the base clone is allowed with no further prompt, which is precisely the failure rule 6 exists to prevent, and precisely the incident (#1073) that motivated the whole rule.

      The hazard is not theoretical for an agent session: the base clone is usually the working directory the session was launched in, so an inherited-cwd mutation is the easy mistake to make, and the grant removes the one mechanical thing that would have caught it.

      The Prose Is Also Stronger Than the Code

      GOVERNANCE.md "Repository Boundaries and Write Safety" and .agents/skills/repo-worktree/SKILL.md both describe the grant as scoped to the fallback clone. Neither says it is session-wide and path-blind, which is what it actually is. A reader following either document would not expect the base checkout to be unlocked as a side effect.

      Suggested Direction

      Not a proposed diff, since the design is the hub's call. Three shapes that would close it, roughly in order of how well they match what the docs already promise:

      1. Path-scope the grant. Read it as a path (or a colon-separated list) rather than a boolean, and allow only when the resolved target is at or under one of those paths. Both the identity and file dimensions the rule already computes would need to match, so a --git-dir/--work-tree split cannot straddle the boundary.
      2. Require a separate session for the standalone clone, and say so in the two documents, leaving the boolean grant as-is but never used in a session that also touches a base clone. Cheapest, but relies entirely on discipline, which is what the hook exists to replace.
      3. Keep the boolean but re-deny the base clone specifically, i.e. grant everything except a checkout that has a linked worktree registered, since a repository with worktrees is exactly the case where another task may be live.

      Option 1 is the one that makes the existing prose true as written.

      How This Surfaced

      Raised by CodeRabbit against a downstream re-vendor of the repo-worktree skill and the GOVERNANCE.md section in ptr727/PhotoCleaner#99, and verified against this repository at mainf3b4cc9 (tag 2.0.526) by reading the hook rather than taking the finding text. Declined downstream and routed here, since neither the hook nor either document is fixable in a carrier: ptr727/PhotoCleaner#99 (comment)

      Not This Issue

      • #1129 covers hub-local references inside the same verbatim-carried files, which is a text defect rather than a behavioral one.
      • #1128 is the analogous "the code is weaker than the sentence describing it" gap in merge-bot-task.yml's handling of the no-auto-merge- marker.

      Metadata

      Metadata

      Assignees

      No one assigned

        Labels

        No labels
        No labels

        Projects

        No projects

          Milestone

          No milestone

          Relationships

          None yet

          Development

          No branches or pull requests

          Issue actions

          , 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
          Skip to content

          GH_WRITE_GUARD_ALLOW_PRIMARY_CHECKOUT is a session-wide, path-blind grant, so a standalone-clone fallback also unlocks the maintainer's base checkout #1130

          Description

          @ptr727

          What

          gh-write-guard.py's rule 6 escape hatch is evaluated before the target path is resolved, so it grants writes to every primary checkout the session touches rather than to the one the grant was intended for.

          # host-setup/agent-safety/claude/gh-write-guard.py:1046-1050environ=environifenvironisnotNoneelseos.environgrant_value=environ.get("GH_WRITE_GUARD_ALLOW_PRIMARY_CHECKOUT", "").strip().lower()
          ifgrant_valuenotin_FALSY_ENV_VALUES:
          return"allow", ""

          The for ... in _all_git_invocations(cmd) loop that resolves resolved, repo_git_dir, and the identity/file dimensions begins at line 1054, after that early return. Nothing about the target reaches the grant decision. The self-test states the same behavior plainly at line 3329: "the escape hatch allows even a denied shape when granted".

          Why This Matters

          repo-worktree documents setting the grant for the standalone-clone fallback, because that fallback is structurally a primary checkout to the hook's own primary-vs-worktree test and would otherwise be denied the commits it exists to make. That is a legitimate need.

          But the grant that unblocks the fallback clone simultaneously unblocks the maintainer's base checkout for the rest of that session. A later git add, git commit, git reset --hard, or branch switch aimed at the base clone is allowed with no further prompt, which is precisely the failure rule 6 exists to prevent, and precisely the incident (#1073) that motivated the whole rule.

          The hazard is not theoretical for an agent session: the base clone is usually the working directory the session was launched in, so an inherited-cwd mutation is the easy mistake to make, and the grant removes the one mechanical thing that would have caught it.

          The Prose Is Also Stronger Than the Code

          GOVERNANCE.md "Repository Boundaries and Write Safety" and .agents/skills/repo-worktree/SKILL.md both describe the grant as scoped to the fallback clone. Neither says it is session-wide and path-blind, which is what it actually is. A reader following either document would not expect the base checkout to be unlocked as a side effect.

          Suggested Direction

          Not a proposed diff, since the design is the hub's call. Three shapes that would close it, roughly in order of how well they match what the docs already promise:

          1. Path-scope the grant. Read it as a path (or a colon-separated list) rather than a boolean, and allow only when the resolved target is at or under one of those paths. Both the identity and file dimensions the rule already computes would need to match, so a --git-dir/--work-tree split cannot straddle the boundary.
          2. Require a separate session for the standalone clone, and say so in the two documents, leaving the boolean grant as-is but never used in a session that also touches a base clone. Cheapest, but relies entirely on discipline, which is what the hook exists to replace.
          3. Keep the boolean but re-deny the base clone specifically, i.e. grant everything except a checkout that has a linked worktree registered, since a repository with worktrees is exactly the case where another task may be live.

          Option 1 is the one that makes the existing prose true as written.

          How This Surfaced

          Raised by CodeRabbit against a downstream re-vendor of the repo-worktree skill and the GOVERNANCE.md section in ptr727/PhotoCleaner#99, and verified against this repository at mainf3b4cc9 (tag 2.0.526) by reading the hook rather than taking the finding text. Declined downstream and routed here, since neither the hook nor either document is fixable in a carrier: ptr727/PhotoCleaner#99 (comment)

          Not This Issue

          • #1129 covers hub-local references inside the same verbatim-carried files, which is a text defect rather than a behavioral one.
          • #1128 is the analogous "the code is weaker than the sentence describing it" gap in merge-bot-task.yml's handling of the no-auto-merge- marker.

          Metadata

          Metadata

          Assignees

          No one assigned

            Labels

            No labels
            No labels

            Projects

            No projects

              Milestone

              No milestone

              Relationships

              None yet

              Development

              No branches or pull requests

              Issue actions

              , 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
              Skip to content

              GH_WRITE_GUARD_ALLOW_PRIMARY_CHECKOUT is a session-wide, path-blind grant, so a standalone-clone fallback also unlocks the maintainer's base checkout #1130

              Description

              @ptr727

              What

              gh-write-guard.py's rule 6 escape hatch is evaluated before the target path is resolved, so it grants writes to every primary checkout the session touches rather than to the one the grant was intended for.

              # host-setup/agent-safety/claude/gh-write-guard.py:1046-1050environ=environifenvironisnotNoneelseos.environgrant_value=environ.get("GH_WRITE_GUARD_ALLOW_PRIMARY_CHECKOUT", "").strip().lower()
              ifgrant_valuenotin_FALSY_ENV_VALUES:
              return"allow", ""

              The for ... in _all_git_invocations(cmd) loop that resolves resolved, repo_git_dir, and the identity/file dimensions begins at line 1054, after that early return. Nothing about the target reaches the grant decision. The self-test states the same behavior plainly at line 3329: "the escape hatch allows even a denied shape when granted".

              Why This Matters

              repo-worktree documents setting the grant for the standalone-clone fallback, because that fallback is structurally a primary checkout to the hook's own primary-vs-worktree test and would otherwise be denied the commits it exists to make. That is a legitimate need.

              But the grant that unblocks the fallback clone simultaneously unblocks the maintainer's base checkout for the rest of that session. A later git add, git commit, git reset --hard, or branch switch aimed at the base clone is allowed with no further prompt, which is precisely the failure rule 6 exists to prevent, and precisely the incident (#1073) that motivated the whole rule.

              The hazard is not theoretical for an agent session: the base clone is usually the working directory the session was launched in, so an inherited-cwd mutation is the easy mistake to make, and the grant removes the one mechanical thing that would have caught it.

              The Prose Is Also Stronger Than the Code

              GOVERNANCE.md "Repository Boundaries and Write Safety" and .agents/skills/repo-worktree/SKILL.md both describe the grant as scoped to the fallback clone. Neither says it is session-wide and path-blind, which is what it actually is. A reader following either document would not expect the base checkout to be unlocked as a side effect.

              Suggested Direction

              Not a proposed diff, since the design is the hub's call. Three shapes that would close it, roughly in order of how well they match what the docs already promise:

              1. Path-scope the grant. Read it as a path (or a colon-separated list) rather than a boolean, and allow only when the resolved target is at or under one of those paths. Both the identity and file dimensions the rule already computes would need to match, so a --git-dir/--work-tree split cannot straddle the boundary.
              2. Require a separate session for the standalone clone, and say so in the two documents, leaving the boolean grant as-is but never used in a session that also touches a base clone. Cheapest, but relies entirely on discipline, which is what the hook exists to replace.
              3. Keep the boolean but re-deny the base clone specifically, i.e. grant everything except a checkout that has a linked worktree registered, since a repository with worktrees is exactly the case where another task may be live.

              Option 1 is the one that makes the existing prose true as written.

              How This Surfaced

              Raised by CodeRabbit against a downstream re-vendor of the repo-worktree skill and the GOVERNANCE.md section in ptr727/PhotoCleaner#99, and verified against this repository at mainf3b4cc9 (tag 2.0.526) by reading the hook rather than taking the finding text. Declined downstream and routed here, since neither the hook nor either document is fixable in a carrier: ptr727/PhotoCleaner#99 (comment)

              Not This Issue

              • #1129 covers hub-local references inside the same verbatim-carried files, which is a text defect rather than a behavioral one.
              • #1128 is the analogous "the code is weaker than the sentence describing it" gap in merge-bot-task.yml's handling of the no-auto-merge- marker.

              Metadata

              Metadata

              Assignees

              No one assigned

                Labels

                No labels
                No labels

                Projects

                No projects

                  Milestone

                  No milestone

                  Relationships

                  None yet

                  Development

                  No branches or pull requests

                  Issue actions

                  , 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
                  Skip to content

                  GH_WRITE_GUARD_ALLOW_PRIMARY_CHECKOUT is a session-wide, path-blind grant, so a standalone-clone fallback also unlocks the maintainer's base checkout #1130

                  Description

                  @ptr727

                  What

                  gh-write-guard.py's rule 6 escape hatch is evaluated before the target path is resolved, so it grants writes to every primary checkout the session touches rather than to the one the grant was intended for.

                  # host-setup/agent-safety/claude/gh-write-guard.py:1046-1050environ=environifenvironisnotNoneelseos.environgrant_value=environ.get("GH_WRITE_GUARD_ALLOW_PRIMARY_CHECKOUT", "").strip().lower()
                  ifgrant_valuenotin_FALSY_ENV_VALUES:
                  return"allow", ""

                  The for ... in _all_git_invocations(cmd) loop that resolves resolved, repo_git_dir, and the identity/file dimensions begins at line 1054, after that early return. Nothing about the target reaches the grant decision. The self-test states the same behavior plainly at line 3329: "the escape hatch allows even a denied shape when granted".

                  Why This Matters

                  repo-worktree documents setting the grant for the standalone-clone fallback, because that fallback is structurally a primary checkout to the hook's own primary-vs-worktree test and would otherwise be denied the commits it exists to make. That is a legitimate need.

                  But the grant that unblocks the fallback clone simultaneously unblocks the maintainer's base checkout for the rest of that session. A later git add, git commit, git reset --hard, or branch switch aimed at the base clone is allowed with no further prompt, which is precisely the failure rule 6 exists to prevent, and precisely the incident (#1073) that motivated the whole rule.

                  The hazard is not theoretical for an agent session: the base clone is usually the working directory the session was launched in, so an inherited-cwd mutation is the easy mistake to make, and the grant removes the one mechanical thing that would have caught it.

                  The Prose Is Also Stronger Than the Code

                  GOVERNANCE.md "Repository Boundaries and Write Safety" and .agents/skills/repo-worktree/SKILL.md both describe the grant as scoped to the fallback clone. Neither says it is session-wide and path-blind, which is what it actually is. A reader following either document would not expect the base checkout to be unlocked as a side effect.

                  Suggested Direction

                  Not a proposed diff, since the design is the hub's call. Three shapes that would close it, roughly in order of how well they match what the docs already promise:

                  1. Path-scope the grant. Read it as a path (or a colon-separated list) rather than a boolean, and allow only when the resolved target is at or under one of those paths. Both the identity and file dimensions the rule already computes would need to match, so a --git-dir/--work-tree split cannot straddle the boundary.
                  2. Require a separate session for the standalone clone, and say so in the two documents, leaving the boolean grant as-is but never used in a session that also touches a base clone. Cheapest, but relies entirely on discipline, which is what the hook exists to replace.
                  3. Keep the boolean but re-deny the base clone specifically, i.e. grant everything except a checkout that has a linked worktree registered, since a repository with worktrees is exactly the case where another task may be live.

                  Option 1 is the one that makes the existing prose true as written.

                  How This Surfaced

                  Raised by CodeRabbit against a downstream re-vendor of the repo-worktree skill and the GOVERNANCE.md section in ptr727/PhotoCleaner#99, and verified against this repository at mainf3b4cc9 (tag 2.0.526) by reading the hook rather than taking the finding text. Declined downstream and routed here, since neither the hook nor either document is fixable in a carrier: ptr727/PhotoCleaner#99 (comment)

                  Not This Issue

                  • #1129 covers hub-local references inside the same verbatim-carried files, which is a text defect rather than a behavioral one.
                  • #1128 is the analogous "the code is weaker than the sentence describing it" gap in merge-bot-task.yml's handling of the no-auto-merge- marker.

                  Metadata

                  Metadata

                  Assignees

                  No one assigned

                    Labels

                    No labels
                    No labels

                    Projects

                    No projects

                      Milestone

                      No milestone

                      Relationships

                      None yet

                      Development

                      No branches or pull requests

                      Issue actions

                      , 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
                      Skip to content

                      GH_WRITE_GUARD_ALLOW_PRIMARY_CHECKOUT is a session-wide, path-blind grant, so a standalone-clone fallback also unlocks the maintainer's base checkout #1130

                      Description

                      @ptr727

                      What

                      gh-write-guard.py's rule 6 escape hatch is evaluated before the target path is resolved, so it grants writes to every primary checkout the session touches rather than to the one the grant was intended for.

                      # host-setup/agent-safety/claude/gh-write-guard.py:1046-1050environ=environifenvironisnotNoneelseos.environgrant_value=environ.get("GH_WRITE_GUARD_ALLOW_PRIMARY_CHECKOUT", "").strip().lower()
                      ifgrant_valuenotin_FALSY_ENV_VALUES:
                      return"allow", ""

                      The for ... in _all_git_invocations(cmd) loop that resolves resolved, repo_git_dir, and the identity/file dimensions begins at line 1054, after that early return. Nothing about the target reaches the grant decision. The self-test states the same behavior plainly at line 3329: "the escape hatch allows even a denied shape when granted".

                      Why This Matters

                      repo-worktree documents setting the grant for the standalone-clone fallback, because that fallback is structurally a primary checkout to the hook's own primary-vs-worktree test and would otherwise be denied the commits it exists to make. That is a legitimate need.

                      But the grant that unblocks the fallback clone simultaneously unblocks the maintainer's base checkout for the rest of that session. A later git add, git commit, git reset --hard, or branch switch aimed at the base clone is allowed with no further prompt, which is precisely the failure rule 6 exists to prevent, and precisely the incident (#1073) that motivated the whole rule.

                      The hazard is not theoretical for an agent session: the base clone is usually the working directory the session was launched in, so an inherited-cwd mutation is the easy mistake to make, and the grant removes the one mechanical thing that would have caught it.

                      The Prose Is Also Stronger Than the Code

                      GOVERNANCE.md "Repository Boundaries and Write Safety" and .agents/skills/repo-worktree/SKILL.md both describe the grant as scoped to the fallback clone. Neither says it is session-wide and path-blind, which is what it actually is. A reader following either document would not expect the base checkout to be unlocked as a side effect.

                      Suggested Direction

                      Not a proposed diff, since the design is the hub's call. Three shapes that would close it, roughly in order of how well they match what the docs already promise:

                      1. Path-scope the grant. Read it as a path (or a colon-separated list) rather than a boolean, and allow only when the resolved target is at or under one of those paths. Both the identity and file dimensions the rule already computes would need to match, so a --git-dir/--work-tree split cannot straddle the boundary.
                      2. Require a separate session for the standalone clone, and say so in the two documents, leaving the boolean grant as-is but never used in a session that also touches a base clone. Cheapest, but relies entirely on discipline, which is what the hook exists to replace.
                      3. Keep the boolean but re-deny the base clone specifically, i.e. grant everything except a checkout that has a linked worktree registered, since a repository with worktrees is exactly the case where another task may be live.

                      Option 1 is the one that makes the existing prose true as written.

                      How This Surfaced

                      Raised by CodeRabbit against a downstream re-vendor of the repo-worktree skill and the GOVERNANCE.md section in ptr727/PhotoCleaner#99, and verified against this repository at mainf3b4cc9 (tag 2.0.526) by reading the hook rather than taking the finding text. Declined downstream and routed here, since neither the hook nor either document is fixable in a carrier: ptr727/PhotoCleaner#99 (comment)

                      Not This Issue

                      • #1129 covers hub-local references inside the same verbatim-carried files, which is a text defect rather than a behavioral one.
                      • #1128 is the analogous "the code is weaker than the sentence describing it" gap in merge-bot-task.yml's handling of the no-auto-merge- marker.

                      Metadata

                      Metadata

                      Assignees

                      No one assigned

                        Labels

                        No labels
                        No labels

                        Projects

                        No projects

                          Milestone

                          No milestone

                          Relationships

                          None yet

                          Development

                          No branches or pull requests

                          Issue actions

                          , 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
                          Skip to content

                          GH_WRITE_GUARD_ALLOW_PRIMARY_CHECKOUT is a session-wide, path-blind grant, so a standalone-clone fallback also unlocks the maintainer's base checkout #1130

                          Description

                          @ptr727

                          What

                          gh-write-guard.py's rule 6 escape hatch is evaluated before the target path is resolved, so it grants writes to every primary checkout the session touches rather than to the one the grant was intended for.

                          # host-setup/agent-safety/claude/gh-write-guard.py:1046-1050environ=environifenvironisnotNoneelseos.environgrant_value=environ.get("GH_WRITE_GUARD_ALLOW_PRIMARY_CHECKOUT", "").strip().lower()
                          ifgrant_valuenotin_FALSY_ENV_VALUES:
                          return"allow", ""

                          The for ... in _all_git_invocations(cmd) loop that resolves resolved, repo_git_dir, and the identity/file dimensions begins at line 1054, after that early return. Nothing about the target reaches the grant decision. The self-test states the same behavior plainly at line 3329: "the escape hatch allows even a denied shape when granted".

                          Why This Matters

                          repo-worktree documents setting the grant for the standalone-clone fallback, because that fallback is structurally a primary checkout to the hook's own primary-vs-worktree test and would otherwise be denied the commits it exists to make. That is a legitimate need.

                          But the grant that unblocks the fallback clone simultaneously unblocks the maintainer's base checkout for the rest of that session. A later git add, git commit, git reset --hard, or branch switch aimed at the base clone is allowed with no further prompt, which is precisely the failure rule 6 exists to prevent, and precisely the incident (#1073) that motivated the whole rule.

                          The hazard is not theoretical for an agent session: the base clone is usually the working directory the session was launched in, so an inherited-cwd mutation is the easy mistake to make, and the grant removes the one mechanical thing that would have caught it.

                          The Prose Is Also Stronger Than the Code

                          GOVERNANCE.md "Repository Boundaries and Write Safety" and .agents/skills/repo-worktree/SKILL.md both describe the grant as scoped to the fallback clone. Neither says it is session-wide and path-blind, which is what it actually is. A reader following either document would not expect the base checkout to be unlocked as a side effect.

                          Suggested Direction

                          Not a proposed diff, since the design is the hub's call. Three shapes that would close it, roughly in order of how well they match what the docs already promise:

                          1. Path-scope the grant. Read it as a path (or a colon-separated list) rather than a boolean, and allow only when the resolved target is at or under one of those paths. Both the identity and file dimensions the rule already computes would need to match, so a --git-dir/--work-tree split cannot straddle the boundary.
                          2. Require a separate session for the standalone clone, and say so in the two documents, leaving the boolean grant as-is but never used in a session that also touches a base clone. Cheapest, but relies entirely on discipline, which is what the hook exists to replace.
                          3. Keep the boolean but re-deny the base clone specifically, i.e. grant everything except a checkout that has a linked worktree registered, since a repository with worktrees is exactly the case where another task may be live.

                          Option 1 is the one that makes the existing prose true as written.

                          How This Surfaced

                          Raised by CodeRabbit against a downstream re-vendor of the repo-worktree skill and the GOVERNANCE.md section in ptr727/PhotoCleaner#99, and verified against this repository at mainf3b4cc9 (tag 2.0.526) by reading the hook rather than taking the finding text. Declined downstream and routed here, since neither the hook nor either document is fixable in a carrier: ptr727/PhotoCleaner#99 (comment)

                          Not This Issue

                          • #1129 covers hub-local references inside the same verbatim-carried files, which is a text defect rather than a behavioral one.
                          • #1128 is the analogous "the code is weaker than the sentence describing it" gap in merge-bot-task.yml's handling of the no-auto-merge- marker.

                          Metadata

                          Metadata

                          Assignees

                          No one assigned

                            Labels

                            No labels
                            No labels

                            Projects

                            No projects

                              Milestone

                              No milestone

                              Relationships

                              None yet

                              Development

                              No branches or pull requests

                              Issue actions

                              , 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
                              Skip to content

                              GH_WRITE_GUARD_ALLOW_PRIMARY_CHECKOUT is a session-wide, path-blind grant, so a standalone-clone fallback also unlocks the maintainer's base checkout #1130

                              Description

                              @ptr727

                              What

                              gh-write-guard.py's rule 6 escape hatch is evaluated before the target path is resolved, so it grants writes to every primary checkout the session touches rather than to the one the grant was intended for.

                              # host-setup/agent-safety/claude/gh-write-guard.py:1046-1050environ=environifenvironisnotNoneelseos.environgrant_value=environ.get("GH_WRITE_GUARD_ALLOW_PRIMARY_CHECKOUT", "").strip().lower()
                              ifgrant_valuenotin_FALSY_ENV_VALUES:
                              return"allow", ""

                              The for ... in _all_git_invocations(cmd) loop that resolves resolved, repo_git_dir, and the identity/file dimensions begins at line 1054, after that early return. Nothing about the target reaches the grant decision. The self-test states the same behavior plainly at line 3329: "the escape hatch allows even a denied shape when granted".

                              Why This Matters

                              repo-worktree documents setting the grant for the standalone-clone fallback, because that fallback is structurally a primary checkout to the hook's own primary-vs-worktree test and would otherwise be denied the commits it exists to make. That is a legitimate need.

                              But the grant that unblocks the fallback clone simultaneously unblocks the maintainer's base checkout for the rest of that session. A later git add, git commit, git reset --hard, or branch switch aimed at the base clone is allowed with no further prompt, which is precisely the failure rule 6 exists to prevent, and precisely the incident (#1073) that motivated the whole rule.

                              The hazard is not theoretical for an agent session: the base clone is usually the working directory the session was launched in, so an inherited-cwd mutation is the easy mistake to make, and the grant removes the one mechanical thing that would have caught it.

                              The Prose Is Also Stronger Than the Code

                              GOVERNANCE.md "Repository Boundaries and Write Safety" and .agents/skills/repo-worktree/SKILL.md both describe the grant as scoped to the fallback clone. Neither says it is session-wide and path-blind, which is what it actually is. A reader following either document would not expect the base checkout to be unlocked as a side effect.

                              Suggested Direction

                              Not a proposed diff, since the design is the hub's call. Three shapes that would close it, roughly in order of how well they match what the docs already promise:

                              1. Path-scope the grant. Read it as a path (or a colon-separated list) rather than a boolean, and allow only when the resolved target is at or under one of those paths. Both the identity and file dimensions the rule already computes would need to match, so a --git-dir/--work-tree split cannot straddle the boundary.
                              2. Require a separate session for the standalone clone, and say so in the two documents, leaving the boolean grant as-is but never used in a session that also touches a base clone. Cheapest, but relies entirely on discipline, which is what the hook exists to replace.
                              3. Keep the boolean but re-deny the base clone specifically, i.e. grant everything except a checkout that has a linked worktree registered, since a repository with worktrees is exactly the case where another task may be live.

                              Option 1 is the one that makes the existing prose true as written.

                              How This Surfaced

                              Raised by CodeRabbit against a downstream re-vendor of the repo-worktree skill and the GOVERNANCE.md section in ptr727/PhotoCleaner#99, and verified against this repository at mainf3b4cc9 (tag 2.0.526) by reading the hook rather than taking the finding text. Declined downstream and routed here, since neither the hook nor either document is fixable in a carrier: ptr727/PhotoCleaner#99 (comment)

                              Not This Issue

                              • #1129 covers hub-local references inside the same verbatim-carried files, which is a text defect rather than a behavioral one.
                              • #1128 is the analogous "the code is weaker than the sentence describing it" gap in merge-bot-task.yml's handling of the no-auto-merge- marker.

                              Metadata

                              Metadata

                              Assignees

                              No one assigned

                                Labels

                                No labels
                                No labels

                                Projects

                                No projects

                                  Milestone

                                  No milestone

                                  Relationships

                                  None yet

                                  Development

                                  No branches or pull requests

                                  Issue actions