Porch's post-merge state commits are stranded on every completed builder branch, and block worktree cleanup #233

Description

@pseudoseed

Porch writes chore(porch): <id> PR #N merged and chore(porch): <id> protocol completeafter
the PR merges. They therefore cannot be in the merge, they land on a branch whose work is already on
main, and nothing carries them anywhere.

Three times in one night:

ProjectStranded commits
bugfix-214a725af68f — carried on the #216 PR with a disclosure line
air-22063fb7a532, ae9956898 — cherry-picked onto air-219's branch
air-219f845ab590, fdbd50c81still stranded

Two distinct costs

1. The project record on main is wrong. Without those commits,
codev/projects/<id>/status.yaml on main says the project sits at the pr phase with nothing
after it — untrue of a completed, merged project. It is bookkeeping, and it is still a record that
misstates what happened.

2. afx cleanup refuses the worktree, correctly. The branch tip is not an ancestor of main,
so cleanup preserves the worktree and branch rather than destroying unmerged commits. That is the
right behaviour and it means every completed builder leaves a worktree behind, which then reads
like a live builder to the next person looking at .builders/.

Both air-219 and air-220 are sitting in .builders/ right now for exactly this reason, with
their work fully merged.

Why the obvious fixes are wrong

  • A follow-up PR is what porch itself refuses (porch declares PROTOCOL COMPLETE while the builder branch is still ahead of main #57): unmodelled, no phase, no gate, no
    notification, so nobody learns a builder is waiting on it.
  • Committing straight to main puts unreviewed commits on the default branch. The argument for
    it is good, which is precisely why the rule exists.
  • Dropping them leaves the record wrong, permanently, and the worktree still cannot be cleaned
    because the commits exist on the branch regardless.
  • Cherry-picking onto a sibling builder works and is what was done tonight, but it needs a
    sibling that is still open, that has the target status.yaml in its history, and an architect
    awake to sequence it. None of those hold in the general case.

The shape of a real fix

The state write and the merge need to stop being two events on two different refs. Options worth
weighing, none decided here:

  • Porch writes the completion state into the PR branch before the merge, deriving "will be
    merged" from the gate rather than observing it afterwards — accepting that a merge that then fails
    leaves a wrong record, which is the trade to argue about.
  • Porch pushes those commits directly to main itself, as a tool operation with its own audit
    trail, rather than leaving an agent to smuggle them through a PR.
  • status.yaml for merged projects stops living in git on the branch at all.

Also worth fixing regardless

afx cleanup should say why it preserved a worktree, naming the unmerged commits. Today it
reports Worktree preserved (unmerged) and leaves the reader to find out that the only unmerged
commits are porch's own bookkeeping — which is a very different situation from real unmerged work,
and the two currently read identically.

Found while merging spec 146 phases 9 and 11 (#221, #224).

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/porchProtocol orchestrator

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
       blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n 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;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
      }
      } 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

      Porch's post-merge state commits are stranded on every completed builder branch, and block worktree cleanup #233

      Description

      @pseudoseed

      Porch writes chore(porch): <id> PR #N merged and chore(porch): <id> protocol completeafter
      the PR merges. They therefore cannot be in the merge, they land on a branch whose work is already on
      main, and nothing carries them anywhere.

      Three times in one night:

      ProjectStranded commits
      bugfix-214a725af68f — carried on the #216 PR with a disclosure line
      air-22063fb7a532, ae9956898 — cherry-picked onto air-219's branch
      air-219f845ab590, fdbd50c81still stranded

      Two distinct costs

      1. The project record on main is wrong. Without those commits,
      codev/projects/<id>/status.yaml on main says the project sits at the pr phase with nothing
      after it — untrue of a completed, merged project. It is bookkeeping, and it is still a record that
      misstates what happened.

      2. afx cleanup refuses the worktree, correctly. The branch tip is not an ancestor of main,
      so cleanup preserves the worktree and branch rather than destroying unmerged commits. That is the
      right behaviour and it means every completed builder leaves a worktree behind, which then reads
      like a live builder to the next person looking at .builders/.

      Both air-219 and air-220 are sitting in .builders/ right now for exactly this reason, with
      their work fully merged.

      Why the obvious fixes are wrong

      • A follow-up PR is what porch itself refuses (porch declares PROTOCOL COMPLETE while the builder branch is still ahead of main #57): unmodelled, no phase, no gate, no
        notification, so nobody learns a builder is waiting on it.
      • Committing straight to main puts unreviewed commits on the default branch. The argument for
        it is good, which is precisely why the rule exists.
      • Dropping them leaves the record wrong, permanently, and the worktree still cannot be cleaned
        because the commits exist on the branch regardless.
      • Cherry-picking onto a sibling builder works and is what was done tonight, but it needs a
        sibling that is still open, that has the target status.yaml in its history, and an architect
        awake to sequence it. None of those hold in the general case.

      The shape of a real fix

      The state write and the merge need to stop being two events on two different refs. Options worth
      weighing, none decided here:

      • Porch writes the completion state into the PR branch before the merge, deriving "will be
        merged" from the gate rather than observing it afterwards — accepting that a merge that then fails
        leaves a wrong record, which is the trade to argue about.
      • Porch pushes those commits directly to main itself, as a tool operation with its own audit
        trail, rather than leaving an agent to smuggle them through a PR.
      • status.yaml for merged projects stops living in git on the branch at all.

      Also worth fixing regardless

      afx cleanup should say why it preserved a worktree, naming the unmerged commits. Today it
      reports Worktree preserved (unmerged) and leaves the reader to find out that the only unmerged
      commits are porch's own bookkeeping — which is a very different situation from real unmerged work,
      and the two currently read identically.

      Found while merging spec 146 phases 9 and 11 (#221, #224).

      Activity

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

      Metadata

      Metadata

      Assignees

      No one assigned

        Labels

        area/porchProtocol orchestrator

        Projects

        No projects

          Milestone

          No milestone

          Relationships

          None yet

          Development

          No branches or pull requests

          Issue actions

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

          Porch's post-merge state commits are stranded on every completed builder branch, and block worktree cleanup #233

          Description

          @pseudoseed

          Porch writes chore(porch): <id> PR #N merged and chore(porch): <id> protocol completeafter
          the PR merges. They therefore cannot be in the merge, they land on a branch whose work is already on
          main, and nothing carries them anywhere.

          Three times in one night:

          ProjectStranded commits
          bugfix-214a725af68f — carried on the #216 PR with a disclosure line
          air-22063fb7a532, ae9956898 — cherry-picked onto air-219's branch
          air-219f845ab590, fdbd50c81still stranded

          Two distinct costs

          1. The project record on main is wrong. Without those commits,
          codev/projects/<id>/status.yaml on main says the project sits at the pr phase with nothing
          after it — untrue of a completed, merged project. It is bookkeeping, and it is still a record that
          misstates what happened.

          2. afx cleanup refuses the worktree, correctly. The branch tip is not an ancestor of main,
          so cleanup preserves the worktree and branch rather than destroying unmerged commits. That is the
          right behaviour and it means every completed builder leaves a worktree behind, which then reads
          like a live builder to the next person looking at .builders/.

          Both air-219 and air-220 are sitting in .builders/ right now for exactly this reason, with
          their work fully merged.

          Why the obvious fixes are wrong

          • A follow-up PR is what porch itself refuses (porch declares PROTOCOL COMPLETE while the builder branch is still ahead of main #57): unmodelled, no phase, no gate, no
            notification, so nobody learns a builder is waiting on it.
          • Committing straight to main puts unreviewed commits on the default branch. The argument for
            it is good, which is precisely why the rule exists.
          • Dropping them leaves the record wrong, permanently, and the worktree still cannot be cleaned
            because the commits exist on the branch regardless.
          • Cherry-picking onto a sibling builder works and is what was done tonight, but it needs a
            sibling that is still open, that has the target status.yaml in its history, and an architect
            awake to sequence it. None of those hold in the general case.

          The shape of a real fix

          The state write and the merge need to stop being two events on two different refs. Options worth
          weighing, none decided here:

          • Porch writes the completion state into the PR branch before the merge, deriving "will be
            merged" from the gate rather than observing it afterwards — accepting that a merge that then fails
            leaves a wrong record, which is the trade to argue about.
          • Porch pushes those commits directly to main itself, as a tool operation with its own audit
            trail, rather than leaving an agent to smuggle them through a PR.
          • status.yaml for merged projects stops living in git on the branch at all.

          Also worth fixing regardless

          afx cleanup should say why it preserved a worktree, naming the unmerged commits. Today it
          reports Worktree preserved (unmerged) and leaves the reader to find out that the only unmerged
          commits are porch's own bookkeeping — which is a very different situation from real unmerged work,
          and the two currently read identically.

          Found while merging spec 146 phases 9 and 11 (#221, #224).

          Activity

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

          Metadata

          Metadata

          Assignees

          No one assigned

            Labels

            area/porchProtocol orchestrator

            Projects

            No projects

              Milestone

              No milestone

              Relationships

              None yet

              Development

              No branches or pull requests

              Issue actions

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

              Porch's post-merge state commits are stranded on every completed builder branch, and block worktree cleanup #233

              Description

              @pseudoseed

              Porch writes chore(porch): <id> PR #N merged and chore(porch): <id> protocol completeafter
              the PR merges. They therefore cannot be in the merge, they land on a branch whose work is already on
              main, and nothing carries them anywhere.

              Three times in one night:

              ProjectStranded commits
              bugfix-214a725af68f — carried on the #216 PR with a disclosure line
              air-22063fb7a532, ae9956898 — cherry-picked onto air-219's branch
              air-219f845ab590, fdbd50c81still stranded

              Two distinct costs

              1. The project record on main is wrong. Without those commits,
              codev/projects/<id>/status.yaml on main says the project sits at the pr phase with nothing
              after it — untrue of a completed, merged project. It is bookkeeping, and it is still a record that
              misstates what happened.

              2. afx cleanup refuses the worktree, correctly. The branch tip is not an ancestor of main,
              so cleanup preserves the worktree and branch rather than destroying unmerged commits. That is the
              right behaviour and it means every completed builder leaves a worktree behind, which then reads
              like a live builder to the next person looking at .builders/.

              Both air-219 and air-220 are sitting in .builders/ right now for exactly this reason, with
              their work fully merged.

              Why the obvious fixes are wrong

              • A follow-up PR is what porch itself refuses (porch declares PROTOCOL COMPLETE while the builder branch is still ahead of main #57): unmodelled, no phase, no gate, no
                notification, so nobody learns a builder is waiting on it.
              • Committing straight to main puts unreviewed commits on the default branch. The argument for
                it is good, which is precisely why the rule exists.
              • Dropping them leaves the record wrong, permanently, and the worktree still cannot be cleaned
                because the commits exist on the branch regardless.
              • Cherry-picking onto a sibling builder works and is what was done tonight, but it needs a
                sibling that is still open, that has the target status.yaml in its history, and an architect
                awake to sequence it. None of those hold in the general case.

              The shape of a real fix

              The state write and the merge need to stop being two events on two different refs. Options worth
              weighing, none decided here:

              • Porch writes the completion state into the PR branch before the merge, deriving "will be
                merged" from the gate rather than observing it afterwards — accepting that a merge that then fails
                leaves a wrong record, which is the trade to argue about.
              • Porch pushes those commits directly to main itself, as a tool operation with its own audit
                trail, rather than leaving an agent to smuggle them through a PR.
              • status.yaml for merged projects stops living in git on the branch at all.

              Also worth fixing regardless

              afx cleanup should say why it preserved a worktree, naming the unmerged commits. Today it
              reports Worktree preserved (unmerged) and leaves the reader to find out that the only unmerged
              commits are porch's own bookkeeping — which is a very different situation from real unmerged work,
              and the two currently read identically.

              Found while merging spec 146 phases 9 and 11 (#221, #224).

              Activity

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

              Metadata

              Metadata

              Assignees

              No one assigned

                Labels

                area/porchProtocol orchestrator

                Projects

                No projects

                  Milestone

                  No milestone

                  Relationships

                  None yet

                  Development

                  No branches or pull requests

                  Issue actions

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

                  Porch's post-merge state commits are stranded on every completed builder branch, and block worktree cleanup #233

                  Description

                  @pseudoseed

                  Porch writes chore(porch): <id> PR #N merged and chore(porch): <id> protocol completeafter
                  the PR merges. They therefore cannot be in the merge, they land on a branch whose work is already on
                  main, and nothing carries them anywhere.

                  Three times in one night:

                  ProjectStranded commits
                  bugfix-214a725af68f — carried on the #216 PR with a disclosure line
                  air-22063fb7a532, ae9956898 — cherry-picked onto air-219's branch
                  air-219f845ab590, fdbd50c81still stranded

                  Two distinct costs

                  1. The project record on main is wrong. Without those commits,
                  codev/projects/<id>/status.yaml on main says the project sits at the pr phase with nothing
                  after it — untrue of a completed, merged project. It is bookkeeping, and it is still a record that
                  misstates what happened.

                  2. afx cleanup refuses the worktree, correctly. The branch tip is not an ancestor of main,
                  so cleanup preserves the worktree and branch rather than destroying unmerged commits. That is the
                  right behaviour and it means every completed builder leaves a worktree behind, which then reads
                  like a live builder to the next person looking at .builders/.

                  Both air-219 and air-220 are sitting in .builders/ right now for exactly this reason, with
                  their work fully merged.

                  Why the obvious fixes are wrong

                  • A follow-up PR is what porch itself refuses (porch declares PROTOCOL COMPLETE while the builder branch is still ahead of main #57): unmodelled, no phase, no gate, no
                    notification, so nobody learns a builder is waiting on it.
                  • Committing straight to main puts unreviewed commits on the default branch. The argument for
                    it is good, which is precisely why the rule exists.
                  • Dropping them leaves the record wrong, permanently, and the worktree still cannot be cleaned
                    because the commits exist on the branch regardless.
                  • Cherry-picking onto a sibling builder works and is what was done tonight, but it needs a
                    sibling that is still open, that has the target status.yaml in its history, and an architect
                    awake to sequence it. None of those hold in the general case.

                  The shape of a real fix

                  The state write and the merge need to stop being two events on two different refs. Options worth
                  weighing, none decided here:

                  • Porch writes the completion state into the PR branch before the merge, deriving "will be
                    merged" from the gate rather than observing it afterwards — accepting that a merge that then fails
                    leaves a wrong record, which is the trade to argue about.
                  • Porch pushes those commits directly to main itself, as a tool operation with its own audit
                    trail, rather than leaving an agent to smuggle them through a PR.
                  • status.yaml for merged projects stops living in git on the branch at all.

                  Also worth fixing regardless

                  afx cleanup should say why it preserved a worktree, naming the unmerged commits. Today it
                  reports Worktree preserved (unmerged) and leaves the reader to find out that the only unmerged
                  commits are porch's own bookkeeping — which is a very different situation from real unmerged work,
                  and the two currently read identically.

                  Found while merging spec 146 phases 9 and 11 (#221, #224).

                  Activity

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

                  Metadata

                  Metadata

                  Assignees

                  No one assigned

                    Labels

                    area/porchProtocol orchestrator

                    Projects

                    No projects

                      Milestone

                      No milestone

                      Relationships

                      None yet

                      Development

                      No branches or pull requests

                      Issue actions

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

                      Porch's post-merge state commits are stranded on every completed builder branch, and block worktree cleanup #233

                      Description

                      @pseudoseed

                      Porch writes chore(porch): <id> PR #N merged and chore(porch): <id> protocol completeafter
                      the PR merges. They therefore cannot be in the merge, they land on a branch whose work is already on
                      main, and nothing carries them anywhere.

                      Three times in one night:

                      ProjectStranded commits
                      bugfix-214a725af68f — carried on the #216 PR with a disclosure line
                      air-22063fb7a532, ae9956898 — cherry-picked onto air-219's branch
                      air-219f845ab590, fdbd50c81still stranded

                      Two distinct costs

                      1. The project record on main is wrong. Without those commits,
                      codev/projects/<id>/status.yaml on main says the project sits at the pr phase with nothing
                      after it — untrue of a completed, merged project. It is bookkeeping, and it is still a record that
                      misstates what happened.

                      2. afx cleanup refuses the worktree, correctly. The branch tip is not an ancestor of main,
                      so cleanup preserves the worktree and branch rather than destroying unmerged commits. That is the
                      right behaviour and it means every completed builder leaves a worktree behind, which then reads
                      like a live builder to the next person looking at .builders/.

                      Both air-219 and air-220 are sitting in .builders/ right now for exactly this reason, with
                      their work fully merged.

                      Why the obvious fixes are wrong

                      • A follow-up PR is what porch itself refuses (porch declares PROTOCOL COMPLETE while the builder branch is still ahead of main #57): unmodelled, no phase, no gate, no
                        notification, so nobody learns a builder is waiting on it.
                      • Committing straight to main puts unreviewed commits on the default branch. The argument for
                        it is good, which is precisely why the rule exists.
                      • Dropping them leaves the record wrong, permanently, and the worktree still cannot be cleaned
                        because the commits exist on the branch regardless.
                      • Cherry-picking onto a sibling builder works and is what was done tonight, but it needs a
                        sibling that is still open, that has the target status.yaml in its history, and an architect
                        awake to sequence it. None of those hold in the general case.

                      The shape of a real fix

                      The state write and the merge need to stop being two events on two different refs. Options worth
                      weighing, none decided here:

                      • Porch writes the completion state into the PR branch before the merge, deriving "will be
                        merged" from the gate rather than observing it afterwards — accepting that a merge that then fails
                        leaves a wrong record, which is the trade to argue about.
                      • Porch pushes those commits directly to main itself, as a tool operation with its own audit
                        trail, rather than leaving an agent to smuggle them through a PR.
                      • status.yaml for merged projects stops living in git on the branch at all.

                      Also worth fixing regardless

                      afx cleanup should say why it preserved a worktree, naming the unmerged commits. Today it
                      reports Worktree preserved (unmerged) and leaves the reader to find out that the only unmerged
                      commits are porch's own bookkeeping — which is a very different situation from real unmerged work,
                      and the two currently read identically.

                      Found while merging spec 146 phases 9 and 11 (#221, #224).

                      Activity

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

                      Metadata

                      Metadata

                      Assignees

                      No one assigned

                        Labels

                        area/porchProtocol orchestrator

                        Projects

                        No projects

                          Milestone

                          No milestone

                          Relationships

                          None yet

                          Development

                          No branches or pull requests

                          Issue actions

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

                          Porch's post-merge state commits are stranded on every completed builder branch, and block worktree cleanup #233

                          Description

                          @pseudoseed

                          Porch writes chore(porch): <id> PR #N merged and chore(porch): <id> protocol completeafter
                          the PR merges. They therefore cannot be in the merge, they land on a branch whose work is already on
                          main, and nothing carries them anywhere.

                          Three times in one night:

                          ProjectStranded commits
                          bugfix-214a725af68f — carried on the #216 PR with a disclosure line
                          air-22063fb7a532, ae9956898 — cherry-picked onto air-219's branch
                          air-219f845ab590, fdbd50c81still stranded

                          Two distinct costs

                          1. The project record on main is wrong. Without those commits,
                          codev/projects/<id>/status.yaml on main says the project sits at the pr phase with nothing
                          after it — untrue of a completed, merged project. It is bookkeeping, and it is still a record that
                          misstates what happened.

                          2. afx cleanup refuses the worktree, correctly. The branch tip is not an ancestor of main,
                          so cleanup preserves the worktree and branch rather than destroying unmerged commits. That is the
                          right behaviour and it means every completed builder leaves a worktree behind, which then reads
                          like a live builder to the next person looking at .builders/.

                          Both air-219 and air-220 are sitting in .builders/ right now for exactly this reason, with
                          their work fully merged.

                          Why the obvious fixes are wrong

                          • A follow-up PR is what porch itself refuses (porch declares PROTOCOL COMPLETE while the builder branch is still ahead of main #57): unmodelled, no phase, no gate, no
                            notification, so nobody learns a builder is waiting on it.
                          • Committing straight to main puts unreviewed commits on the default branch. The argument for
                            it is good, which is precisely why the rule exists.
                          • Dropping them leaves the record wrong, permanently, and the worktree still cannot be cleaned
                            because the commits exist on the branch regardless.
                          • Cherry-picking onto a sibling builder works and is what was done tonight, but it needs a
                            sibling that is still open, that has the target status.yaml in its history, and an architect
                            awake to sequence it. None of those hold in the general case.

                          The shape of a real fix

                          The state write and the merge need to stop being two events on two different refs. Options worth
                          weighing, none decided here:

                          • Porch writes the completion state into the PR branch before the merge, deriving "will be
                            merged" from the gate rather than observing it afterwards — accepting that a merge that then fails
                            leaves a wrong record, which is the trade to argue about.
                          • Porch pushes those commits directly to main itself, as a tool operation with its own audit
                            trail, rather than leaving an agent to smuggle them through a PR.
                          • status.yaml for merged projects stops living in git on the branch at all.

                          Also worth fixing regardless

                          afx cleanup should say why it preserved a worktree, naming the unmerged commits. Today it
                          reports Worktree preserved (unmerged) and leaves the reader to find out that the only unmerged
                          commits are porch's own bookkeeping — which is a very different situation from real unmerged work,
                          and the two currently read identically.

                          Found while merging spec 146 phases 9 and 11 (#221, #224).

                          Activity

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

                          Metadata

                          Metadata

                          Assignees

                          No one assigned

                            Labels

                            area/porchProtocol orchestrator

                            Projects

                            No projects

                              Milestone

                              No milestone

                              Relationships

                              None yet

                              Development

                              No branches or pull requests

                              Issue actions

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

                              Porch's post-merge state commits are stranded on every completed builder branch, and block worktree cleanup #233

                              Description

                              @pseudoseed

                              Porch writes chore(porch): <id> PR #N merged and chore(porch): <id> protocol completeafter
                              the PR merges. They therefore cannot be in the merge, they land on a branch whose work is already on
                              main, and nothing carries them anywhere.

                              Three times in one night:

                              ProjectStranded commits
                              bugfix-214a725af68f — carried on the #216 PR with a disclosure line
                              air-22063fb7a532, ae9956898 — cherry-picked onto air-219's branch
                              air-219f845ab590, fdbd50c81still stranded

                              Two distinct costs

                              1. The project record on main is wrong. Without those commits,
                              codev/projects/<id>/status.yaml on main says the project sits at the pr phase with nothing
                              after it — untrue of a completed, merged project. It is bookkeeping, and it is still a record that
                              misstates what happened.

                              2. afx cleanup refuses the worktree, correctly. The branch tip is not an ancestor of main,
                              so cleanup preserves the worktree and branch rather than destroying unmerged commits. That is the
                              right behaviour and it means every completed builder leaves a worktree behind, which then reads
                              like a live builder to the next person looking at .builders/.

                              Both air-219 and air-220 are sitting in .builders/ right now for exactly this reason, with
                              their work fully merged.

                              Why the obvious fixes are wrong

                              • A follow-up PR is what porch itself refuses (porch declares PROTOCOL COMPLETE while the builder branch is still ahead of main #57): unmodelled, no phase, no gate, no
                                notification, so nobody learns a builder is waiting on it.
                              • Committing straight to main puts unreviewed commits on the default branch. The argument for
                                it is good, which is precisely why the rule exists.
                              • Dropping them leaves the record wrong, permanently, and the worktree still cannot be cleaned
                                because the commits exist on the branch regardless.
                              • Cherry-picking onto a sibling builder works and is what was done tonight, but it needs a
                                sibling that is still open, that has the target status.yaml in its history, and an architect
                                awake to sequence it. None of those hold in the general case.

                              The shape of a real fix

                              The state write and the merge need to stop being two events on two different refs. Options worth
                              weighing, none decided here:

                              • Porch writes the completion state into the PR branch before the merge, deriving "will be
                                merged" from the gate rather than observing it afterwards — accepting that a merge that then fails
                                leaves a wrong record, which is the trade to argue about.
                              • Porch pushes those commits directly to main itself, as a tool operation with its own audit
                                trail, rather than leaving an agent to smuggle them through a PR.
                              • status.yaml for merged projects stops living in git on the branch at all.

                              Also worth fixing regardless

                              afx cleanup should say why it preserved a worktree, naming the unmerged commits. Today it
                              reports Worktree preserved (unmerged) and leaves the reader to find out that the only unmerged
                              commits are porch's own bookkeeping — which is a very different situation from real unmerged work,
                              and the two currently read identically.

                              Found while merging spec 146 phases 9 and 11 (#221, #224).

                              Activity

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

                              Metadata

                              Metadata

                              Assignees

                              No one assigned

                                Labels

                                area/porchProtocol orchestrator

                                Projects

                                No projects

                                  Milestone

                                  No milestone

                                  Relationships

                                  None yet

                                  Development

                                  No branches or pull requests

                                  Issue actions