Route spec 146 phase 9/11 findings into the governance tier (arch-critical, lessons-critical, arch) #229

Description

@pseudoseed

Facts established during spec 146 phases 9 and 11 that belong in the governance tier and are
currently recorded only in PR bodies and issues. Filed by the architect rather than folded into
#221 or #224, because routing facts by tier is MAINTAIN work and neither PR should grow a
governance edit.

For arch-critical.md (HOT — consult before deciding)

Tower is multi-workspace; process-global state in it is a cross-workspace defect.
thread-runtime.ts held a bare let engine, which is harmless in the CLI (one process, one
workspace, then exit) and a misroute in Tower, which drains held mail for every workspace in
global.db. The first thread-configured workspace pinned the socket, projectId, dispatcher and
journal for all others, and a turn dispatched to the wrong server succeeds.

The fix is now the house rule: key by canonical workspace root, and the unkeyed slot is not a
fallback in either direction.
A keyed read that missed and then took the unkeyed one would
restore the bug one indirection further away. This sits alongside the existing mailbox-first fact,
because Tower being multi-workspace is the premise both depend on.

installThreadSpawnFactory is the same defect one layer up and is tracked in #227.

The CLI-awaits / Tower-does-not split is load-bearing and undefended by the type system.
ensureThreadBackendReady is CLI-only. Reaching for it from a request path reintroduces a
Tower-wide stall. The drain tick must never await an establishable resource — the synchronous
requestThreadBackend, returning a state union rather than a promise, is the pattern.

For lessons-critical.md (HOT)

The existing line — "I could not tell" must never be spelled the same way as "no" — now has
reference implementations worth naming, because the abstract rule kept being agreed with and then
violated:

  • found | none | unknown (project lookup, thread-backend.ts)
  • known: false only on spawn error or non-empty stderr (lsof, t3-server.mjs) — the exit code
    alone cannot separate "nothing is listening" from "I could not run the check"
  • UNDETERMINED as a distinct exit (NO_DATA_TO_KEEP)
  • the five-state availability enum
  • UNCONFIRMED as its own client band, distinct from refused, because an unreadable 200 may mean the
    gate is approved and reporting "no" sends a human to approve twice

New lesson, earned four separate times in two days:

A green suite is evidence only about what it ran. Before quoting one, name which suites exist and
which files each covers.

Instances: a unit run that excluded *.e2e.test.ts and so could not reach the file the change
broke; a fixture that named directories the way the code assumed, so it could not falsify the
assumption; a test that passed because a successful path never entered the catch block it was
written to exercise; and a determinism test whose precondition was violated by a concurrent
porch commit (#217).

The common mechanism is a measurement taken where the thing being measured is not present.

Second new lesson: a comment asserting a guarantee stops the guarantee being checked. Four
instances in two days, each found by a reviewer going straight to the sentence:
thread-backend.ts:370 ("can only fire after…"), ownsProcess ("ownership is proven from the
command line"), validateSnapshot (claiming a distinction the code did not make), and a guard
whose stated justification did not hold. Write what makes a claim true, or write the limit —
argv heuristic, not proof of parentage is the corrected form.

For arch.md (COLD — Monorepo Structure, Integration Points)

  • apps/client — the codev-client, React 19 / Vite 6 / Vitest 4 / Playwright.
  • tools/codev-agent-host — mounts the route table over a copy of global.db with a scratch
    credential root; the two-server e2e harness.
  • The two new agent routes: POST /api/agent/v1/human-sessions and .../gates/approve.
  • Five test suites, not one: packages/codev unit, its vitest.e2e.config, its vitest.cli.config,
    apps/client unit, apps/client Playwright.

Not in scope

Do not edit codev/specs/146-*.md or the phase plans. Phase 11's unmet criteria are #228.

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/coreCore libraries and shared logic

    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

      Route spec 146 phase 9/11 findings into the governance tier (arch-critical, lessons-critical, arch) #229

      Description

      @pseudoseed

      Facts established during spec 146 phases 9 and 11 that belong in the governance tier and are
      currently recorded only in PR bodies and issues. Filed by the architect rather than folded into
      #221 or #224, because routing facts by tier is MAINTAIN work and neither PR should grow a
      governance edit.

      For arch-critical.md (HOT — consult before deciding)

      Tower is multi-workspace; process-global state in it is a cross-workspace defect.
      thread-runtime.ts held a bare let engine, which is harmless in the CLI (one process, one
      workspace, then exit) and a misroute in Tower, which drains held mail for every workspace in
      global.db. The first thread-configured workspace pinned the socket, projectId, dispatcher and
      journal for all others, and a turn dispatched to the wrong server succeeds.

      The fix is now the house rule: key by canonical workspace root, and the unkeyed slot is not a
      fallback in either direction.
      A keyed read that missed and then took the unkeyed one would
      restore the bug one indirection further away. This sits alongside the existing mailbox-first fact,
      because Tower being multi-workspace is the premise both depend on.

      installThreadSpawnFactory is the same defect one layer up and is tracked in #227.

      The CLI-awaits / Tower-does-not split is load-bearing and undefended by the type system.
      ensureThreadBackendReady is CLI-only. Reaching for it from a request path reintroduces a
      Tower-wide stall. The drain tick must never await an establishable resource — the synchronous
      requestThreadBackend, returning a state union rather than a promise, is the pattern.

      For lessons-critical.md (HOT)

      The existing line — "I could not tell" must never be spelled the same way as "no" — now has
      reference implementations worth naming, because the abstract rule kept being agreed with and then
      violated:

      • found | none | unknown (project lookup, thread-backend.ts)
      • known: false only on spawn error or non-empty stderr (lsof, t3-server.mjs) — the exit code
        alone cannot separate "nothing is listening" from "I could not run the check"
      • UNDETERMINED as a distinct exit (NO_DATA_TO_KEEP)
      • the five-state availability enum
      • UNCONFIRMED as its own client band, distinct from refused, because an unreadable 200 may mean the
        gate is approved and reporting "no" sends a human to approve twice

      New lesson, earned four separate times in two days:

      A green suite is evidence only about what it ran. Before quoting one, name which suites exist and
      which files each covers.

      Instances: a unit run that excluded *.e2e.test.ts and so could not reach the file the change
      broke; a fixture that named directories the way the code assumed, so it could not falsify the
      assumption; a test that passed because a successful path never entered the catch block it was
      written to exercise; and a determinism test whose precondition was violated by a concurrent
      porch commit (#217).

      The common mechanism is a measurement taken where the thing being measured is not present.

      Second new lesson: a comment asserting a guarantee stops the guarantee being checked. Four
      instances in two days, each found by a reviewer going straight to the sentence:
      thread-backend.ts:370 ("can only fire after…"), ownsProcess ("ownership is proven from the
      command line"), validateSnapshot (claiming a distinction the code did not make), and a guard
      whose stated justification did not hold. Write what makes a claim true, or write the limit —
      argv heuristic, not proof of parentage is the corrected form.

      For arch.md (COLD — Monorepo Structure, Integration Points)

      • apps/client — the codev-client, React 19 / Vite 6 / Vitest 4 / Playwright.
      • tools/codev-agent-host — mounts the route table over a copy of global.db with a scratch
        credential root; the two-server e2e harness.
      • The two new agent routes: POST /api/agent/v1/human-sessions and .../gates/approve.
      • Five test suites, not one: packages/codev unit, its vitest.e2e.config, its vitest.cli.config,
        apps/client unit, apps/client Playwright.

      Not in scope

      Do not edit codev/specs/146-*.md or the phase plans. Phase 11's unmet criteria are #228.

      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/coreCore libraries and shared logic

        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

          Route spec 146 phase 9/11 findings into the governance tier (arch-critical, lessons-critical, arch) #229

          Description

          @pseudoseed

          Facts established during spec 146 phases 9 and 11 that belong in the governance tier and are
          currently recorded only in PR bodies and issues. Filed by the architect rather than folded into
          #221 or #224, because routing facts by tier is MAINTAIN work and neither PR should grow a
          governance edit.

          For arch-critical.md (HOT — consult before deciding)

          Tower is multi-workspace; process-global state in it is a cross-workspace defect.
          thread-runtime.ts held a bare let engine, which is harmless in the CLI (one process, one
          workspace, then exit) and a misroute in Tower, which drains held mail for every workspace in
          global.db. The first thread-configured workspace pinned the socket, projectId, dispatcher and
          journal for all others, and a turn dispatched to the wrong server succeeds.

          The fix is now the house rule: key by canonical workspace root, and the unkeyed slot is not a
          fallback in either direction.
          A keyed read that missed and then took the unkeyed one would
          restore the bug one indirection further away. This sits alongside the existing mailbox-first fact,
          because Tower being multi-workspace is the premise both depend on.

          installThreadSpawnFactory is the same defect one layer up and is tracked in #227.

          The CLI-awaits / Tower-does-not split is load-bearing and undefended by the type system.
          ensureThreadBackendReady is CLI-only. Reaching for it from a request path reintroduces a
          Tower-wide stall. The drain tick must never await an establishable resource — the synchronous
          requestThreadBackend, returning a state union rather than a promise, is the pattern.

          For lessons-critical.md (HOT)

          The existing line — "I could not tell" must never be spelled the same way as "no" — now has
          reference implementations worth naming, because the abstract rule kept being agreed with and then
          violated:

          • found | none | unknown (project lookup, thread-backend.ts)
          • known: false only on spawn error or non-empty stderr (lsof, t3-server.mjs) — the exit code
            alone cannot separate "nothing is listening" from "I could not run the check"
          • UNDETERMINED as a distinct exit (NO_DATA_TO_KEEP)
          • the five-state availability enum
          • UNCONFIRMED as its own client band, distinct from refused, because an unreadable 200 may mean the
            gate is approved and reporting "no" sends a human to approve twice

          New lesson, earned four separate times in two days:

          A green suite is evidence only about what it ran. Before quoting one, name which suites exist and
          which files each covers.

          Instances: a unit run that excluded *.e2e.test.ts and so could not reach the file the change
          broke; a fixture that named directories the way the code assumed, so it could not falsify the
          assumption; a test that passed because a successful path never entered the catch block it was
          written to exercise; and a determinism test whose precondition was violated by a concurrent
          porch commit (#217).

          The common mechanism is a measurement taken where the thing being measured is not present.

          Second new lesson: a comment asserting a guarantee stops the guarantee being checked. Four
          instances in two days, each found by a reviewer going straight to the sentence:
          thread-backend.ts:370 ("can only fire after…"), ownsProcess ("ownership is proven from the
          command line"), validateSnapshot (claiming a distinction the code did not make), and a guard
          whose stated justification did not hold. Write what makes a claim true, or write the limit —
          argv heuristic, not proof of parentage is the corrected form.

          For arch.md (COLD — Monorepo Structure, Integration Points)

          • apps/client — the codev-client, React 19 / Vite 6 / Vitest 4 / Playwright.
          • tools/codev-agent-host — mounts the route table over a copy of global.db with a scratch
            credential root; the two-server e2e harness.
          • The two new agent routes: POST /api/agent/v1/human-sessions and .../gates/approve.
          • Five test suites, not one: packages/codev unit, its vitest.e2e.config, its vitest.cli.config,
            apps/client unit, apps/client Playwright.

          Not in scope

          Do not edit codev/specs/146-*.md or the phase plans. Phase 11's unmet criteria are #228.

          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/coreCore libraries and shared logic

            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

              Route spec 146 phase 9/11 findings into the governance tier (arch-critical, lessons-critical, arch) #229

              Description

              @pseudoseed

              Facts established during spec 146 phases 9 and 11 that belong in the governance tier and are
              currently recorded only in PR bodies and issues. Filed by the architect rather than folded into
              #221 or #224, because routing facts by tier is MAINTAIN work and neither PR should grow a
              governance edit.

              For arch-critical.md (HOT — consult before deciding)

              Tower is multi-workspace; process-global state in it is a cross-workspace defect.
              thread-runtime.ts held a bare let engine, which is harmless in the CLI (one process, one
              workspace, then exit) and a misroute in Tower, which drains held mail for every workspace in
              global.db. The first thread-configured workspace pinned the socket, projectId, dispatcher and
              journal for all others, and a turn dispatched to the wrong server succeeds.

              The fix is now the house rule: key by canonical workspace root, and the unkeyed slot is not a
              fallback in either direction.
              A keyed read that missed and then took the unkeyed one would
              restore the bug one indirection further away. This sits alongside the existing mailbox-first fact,
              because Tower being multi-workspace is the premise both depend on.

              installThreadSpawnFactory is the same defect one layer up and is tracked in #227.

              The CLI-awaits / Tower-does-not split is load-bearing and undefended by the type system.
              ensureThreadBackendReady is CLI-only. Reaching for it from a request path reintroduces a
              Tower-wide stall. The drain tick must never await an establishable resource — the synchronous
              requestThreadBackend, returning a state union rather than a promise, is the pattern.

              For lessons-critical.md (HOT)

              The existing line — "I could not tell" must never be spelled the same way as "no" — now has
              reference implementations worth naming, because the abstract rule kept being agreed with and then
              violated:

              • found | none | unknown (project lookup, thread-backend.ts)
              • known: false only on spawn error or non-empty stderr (lsof, t3-server.mjs) — the exit code
                alone cannot separate "nothing is listening" from "I could not run the check"
              • UNDETERMINED as a distinct exit (NO_DATA_TO_KEEP)
              • the five-state availability enum
              • UNCONFIRMED as its own client band, distinct from refused, because an unreadable 200 may mean the
                gate is approved and reporting "no" sends a human to approve twice

              New lesson, earned four separate times in two days:

              A green suite is evidence only about what it ran. Before quoting one, name which suites exist and
              which files each covers.

              Instances: a unit run that excluded *.e2e.test.ts and so could not reach the file the change
              broke; a fixture that named directories the way the code assumed, so it could not falsify the
              assumption; a test that passed because a successful path never entered the catch block it was
              written to exercise; and a determinism test whose precondition was violated by a concurrent
              porch commit (#217).

              The common mechanism is a measurement taken where the thing being measured is not present.

              Second new lesson: a comment asserting a guarantee stops the guarantee being checked. Four
              instances in two days, each found by a reviewer going straight to the sentence:
              thread-backend.ts:370 ("can only fire after…"), ownsProcess ("ownership is proven from the
              command line"), validateSnapshot (claiming a distinction the code did not make), and a guard
              whose stated justification did not hold. Write what makes a claim true, or write the limit —
              argv heuristic, not proof of parentage is the corrected form.

              For arch.md (COLD — Monorepo Structure, Integration Points)

              • apps/client — the codev-client, React 19 / Vite 6 / Vitest 4 / Playwright.
              • tools/codev-agent-host — mounts the route table over a copy of global.db with a scratch
                credential root; the two-server e2e harness.
              • The two new agent routes: POST /api/agent/v1/human-sessions and .../gates/approve.
              • Five test suites, not one: packages/codev unit, its vitest.e2e.config, its vitest.cli.config,
                apps/client unit, apps/client Playwright.

              Not in scope

              Do not edit codev/specs/146-*.md or the phase plans. Phase 11's unmet criteria are #228.

              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/coreCore libraries and shared logic

                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

                  Route spec 146 phase 9/11 findings into the governance tier (arch-critical, lessons-critical, arch) #229

                  Description

                  @pseudoseed

                  Facts established during spec 146 phases 9 and 11 that belong in the governance tier and are
                  currently recorded only in PR bodies and issues. Filed by the architect rather than folded into
                  #221 or #224, because routing facts by tier is MAINTAIN work and neither PR should grow a
                  governance edit.

                  For arch-critical.md (HOT — consult before deciding)

                  Tower is multi-workspace; process-global state in it is a cross-workspace defect.
                  thread-runtime.ts held a bare let engine, which is harmless in the CLI (one process, one
                  workspace, then exit) and a misroute in Tower, which drains held mail for every workspace in
                  global.db. The first thread-configured workspace pinned the socket, projectId, dispatcher and
                  journal for all others, and a turn dispatched to the wrong server succeeds.

                  The fix is now the house rule: key by canonical workspace root, and the unkeyed slot is not a
                  fallback in either direction.
                  A keyed read that missed and then took the unkeyed one would
                  restore the bug one indirection further away. This sits alongside the existing mailbox-first fact,
                  because Tower being multi-workspace is the premise both depend on.

                  installThreadSpawnFactory is the same defect one layer up and is tracked in #227.

                  The CLI-awaits / Tower-does-not split is load-bearing and undefended by the type system.
                  ensureThreadBackendReady is CLI-only. Reaching for it from a request path reintroduces a
                  Tower-wide stall. The drain tick must never await an establishable resource — the synchronous
                  requestThreadBackend, returning a state union rather than a promise, is the pattern.

                  For lessons-critical.md (HOT)

                  The existing line — "I could not tell" must never be spelled the same way as "no" — now has
                  reference implementations worth naming, because the abstract rule kept being agreed with and then
                  violated:

                  • found | none | unknown (project lookup, thread-backend.ts)
                  • known: false only on spawn error or non-empty stderr (lsof, t3-server.mjs) — the exit code
                    alone cannot separate "nothing is listening" from "I could not run the check"
                  • UNDETERMINED as a distinct exit (NO_DATA_TO_KEEP)
                  • the five-state availability enum
                  • UNCONFIRMED as its own client band, distinct from refused, because an unreadable 200 may mean the
                    gate is approved and reporting "no" sends a human to approve twice

                  New lesson, earned four separate times in two days:

                  A green suite is evidence only about what it ran. Before quoting one, name which suites exist and
                  which files each covers.

                  Instances: a unit run that excluded *.e2e.test.ts and so could not reach the file the change
                  broke; a fixture that named directories the way the code assumed, so it could not falsify the
                  assumption; a test that passed because a successful path never entered the catch block it was
                  written to exercise; and a determinism test whose precondition was violated by a concurrent
                  porch commit (#217).

                  The common mechanism is a measurement taken where the thing being measured is not present.

                  Second new lesson: a comment asserting a guarantee stops the guarantee being checked. Four
                  instances in two days, each found by a reviewer going straight to the sentence:
                  thread-backend.ts:370 ("can only fire after…"), ownsProcess ("ownership is proven from the
                  command line"), validateSnapshot (claiming a distinction the code did not make), and a guard
                  whose stated justification did not hold. Write what makes a claim true, or write the limit —
                  argv heuristic, not proof of parentage is the corrected form.

                  For arch.md (COLD — Monorepo Structure, Integration Points)

                  • apps/client — the codev-client, React 19 / Vite 6 / Vitest 4 / Playwright.
                  • tools/codev-agent-host — mounts the route table over a copy of global.db with a scratch
                    credential root; the two-server e2e harness.
                  • The two new agent routes: POST /api/agent/v1/human-sessions and .../gates/approve.
                  • Five test suites, not one: packages/codev unit, its vitest.e2e.config, its vitest.cli.config,
                    apps/client unit, apps/client Playwright.

                  Not in scope

                  Do not edit codev/specs/146-*.md or the phase plans. Phase 11's unmet criteria are #228.

                  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/coreCore libraries and shared logic

                    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

                      Route spec 146 phase 9/11 findings into the governance tier (arch-critical, lessons-critical, arch) #229

                      Description

                      @pseudoseed

                      Facts established during spec 146 phases 9 and 11 that belong in the governance tier and are
                      currently recorded only in PR bodies and issues. Filed by the architect rather than folded into
                      #221 or #224, because routing facts by tier is MAINTAIN work and neither PR should grow a
                      governance edit.

                      For arch-critical.md (HOT — consult before deciding)

                      Tower is multi-workspace; process-global state in it is a cross-workspace defect.
                      thread-runtime.ts held a bare let engine, which is harmless in the CLI (one process, one
                      workspace, then exit) and a misroute in Tower, which drains held mail for every workspace in
                      global.db. The first thread-configured workspace pinned the socket, projectId, dispatcher and
                      journal for all others, and a turn dispatched to the wrong server succeeds.

                      The fix is now the house rule: key by canonical workspace root, and the unkeyed slot is not a
                      fallback in either direction.
                      A keyed read that missed and then took the unkeyed one would
                      restore the bug one indirection further away. This sits alongside the existing mailbox-first fact,
                      because Tower being multi-workspace is the premise both depend on.

                      installThreadSpawnFactory is the same defect one layer up and is tracked in #227.

                      The CLI-awaits / Tower-does-not split is load-bearing and undefended by the type system.
                      ensureThreadBackendReady is CLI-only. Reaching for it from a request path reintroduces a
                      Tower-wide stall. The drain tick must never await an establishable resource — the synchronous
                      requestThreadBackend, returning a state union rather than a promise, is the pattern.

                      For lessons-critical.md (HOT)

                      The existing line — "I could not tell" must never be spelled the same way as "no" — now has
                      reference implementations worth naming, because the abstract rule kept being agreed with and then
                      violated:

                      • found | none | unknown (project lookup, thread-backend.ts)
                      • known: false only on spawn error or non-empty stderr (lsof, t3-server.mjs) — the exit code
                        alone cannot separate "nothing is listening" from "I could not run the check"
                      • UNDETERMINED as a distinct exit (NO_DATA_TO_KEEP)
                      • the five-state availability enum
                      • UNCONFIRMED as its own client band, distinct from refused, because an unreadable 200 may mean the
                        gate is approved and reporting "no" sends a human to approve twice

                      New lesson, earned four separate times in two days:

                      A green suite is evidence only about what it ran. Before quoting one, name which suites exist and
                      which files each covers.

                      Instances: a unit run that excluded *.e2e.test.ts and so could not reach the file the change
                      broke; a fixture that named directories the way the code assumed, so it could not falsify the
                      assumption; a test that passed because a successful path never entered the catch block it was
                      written to exercise; and a determinism test whose precondition was violated by a concurrent
                      porch commit (#217).

                      The common mechanism is a measurement taken where the thing being measured is not present.

                      Second new lesson: a comment asserting a guarantee stops the guarantee being checked. Four
                      instances in two days, each found by a reviewer going straight to the sentence:
                      thread-backend.ts:370 ("can only fire after…"), ownsProcess ("ownership is proven from the
                      command line"), validateSnapshot (claiming a distinction the code did not make), and a guard
                      whose stated justification did not hold. Write what makes a claim true, or write the limit —
                      argv heuristic, not proof of parentage is the corrected form.

                      For arch.md (COLD — Monorepo Structure, Integration Points)

                      • apps/client — the codev-client, React 19 / Vite 6 / Vitest 4 / Playwright.
                      • tools/codev-agent-host — mounts the route table over a copy of global.db with a scratch
                        credential root; the two-server e2e harness.
                      • The two new agent routes: POST /api/agent/v1/human-sessions and .../gates/approve.
                      • Five test suites, not one: packages/codev unit, its vitest.e2e.config, its vitest.cli.config,
                        apps/client unit, apps/client Playwright.

                      Not in scope

                      Do not edit codev/specs/146-*.md or the phase plans. Phase 11's unmet criteria are #228.

                      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/coreCore libraries and shared logic

                        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

                          Route spec 146 phase 9/11 findings into the governance tier (arch-critical, lessons-critical, arch) #229

                          Description

                          @pseudoseed

                          Facts established during spec 146 phases 9 and 11 that belong in the governance tier and are
                          currently recorded only in PR bodies and issues. Filed by the architect rather than folded into
                          #221 or #224, because routing facts by tier is MAINTAIN work and neither PR should grow a
                          governance edit.

                          For arch-critical.md (HOT — consult before deciding)

                          Tower is multi-workspace; process-global state in it is a cross-workspace defect.
                          thread-runtime.ts held a bare let engine, which is harmless in the CLI (one process, one
                          workspace, then exit) and a misroute in Tower, which drains held mail for every workspace in
                          global.db. The first thread-configured workspace pinned the socket, projectId, dispatcher and
                          journal for all others, and a turn dispatched to the wrong server succeeds.

                          The fix is now the house rule: key by canonical workspace root, and the unkeyed slot is not a
                          fallback in either direction.
                          A keyed read that missed and then took the unkeyed one would
                          restore the bug one indirection further away. This sits alongside the existing mailbox-first fact,
                          because Tower being multi-workspace is the premise both depend on.

                          installThreadSpawnFactory is the same defect one layer up and is tracked in #227.

                          The CLI-awaits / Tower-does-not split is load-bearing and undefended by the type system.
                          ensureThreadBackendReady is CLI-only. Reaching for it from a request path reintroduces a
                          Tower-wide stall. The drain tick must never await an establishable resource — the synchronous
                          requestThreadBackend, returning a state union rather than a promise, is the pattern.

                          For lessons-critical.md (HOT)

                          The existing line — "I could not tell" must never be spelled the same way as "no" — now has
                          reference implementations worth naming, because the abstract rule kept being agreed with and then
                          violated:

                          • found | none | unknown (project lookup, thread-backend.ts)
                          • known: false only on spawn error or non-empty stderr (lsof, t3-server.mjs) — the exit code
                            alone cannot separate "nothing is listening" from "I could not run the check"
                          • UNDETERMINED as a distinct exit (NO_DATA_TO_KEEP)
                          • the five-state availability enum
                          • UNCONFIRMED as its own client band, distinct from refused, because an unreadable 200 may mean the
                            gate is approved and reporting "no" sends a human to approve twice

                          New lesson, earned four separate times in two days:

                          A green suite is evidence only about what it ran. Before quoting one, name which suites exist and
                          which files each covers.

                          Instances: a unit run that excluded *.e2e.test.ts and so could not reach the file the change
                          broke; a fixture that named directories the way the code assumed, so it could not falsify the
                          assumption; a test that passed because a successful path never entered the catch block it was
                          written to exercise; and a determinism test whose precondition was violated by a concurrent
                          porch commit (#217).

                          The common mechanism is a measurement taken where the thing being measured is not present.

                          Second new lesson: a comment asserting a guarantee stops the guarantee being checked. Four
                          instances in two days, each found by a reviewer going straight to the sentence:
                          thread-backend.ts:370 ("can only fire after…"), ownsProcess ("ownership is proven from the
                          command line"), validateSnapshot (claiming a distinction the code did not make), and a guard
                          whose stated justification did not hold. Write what makes a claim true, or write the limit —
                          argv heuristic, not proof of parentage is the corrected form.

                          For arch.md (COLD — Monorepo Structure, Integration Points)

                          • apps/client — the codev-client, React 19 / Vite 6 / Vitest 4 / Playwright.
                          • tools/codev-agent-host — mounts the route table over a copy of global.db with a scratch
                            credential root; the two-server e2e harness.
                          • The two new agent routes: POST /api/agent/v1/human-sessions and .../gates/approve.
                          • Five test suites, not one: packages/codev unit, its vitest.e2e.config, its vitest.cli.config,
                            apps/client unit, apps/client Playwright.

                          Not in scope

                          Do not edit codev/specs/146-*.md or the phase plans. Phase 11's unmet criteria are #228.

                          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/coreCore libraries and shared logic

                            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

                              Route spec 146 phase 9/11 findings into the governance tier (arch-critical, lessons-critical, arch) #229

                              Description

                              @pseudoseed

                              Facts established during spec 146 phases 9 and 11 that belong in the governance tier and are
                              currently recorded only in PR bodies and issues. Filed by the architect rather than folded into
                              #221 or #224, because routing facts by tier is MAINTAIN work and neither PR should grow a
                              governance edit.

                              For arch-critical.md (HOT — consult before deciding)

                              Tower is multi-workspace; process-global state in it is a cross-workspace defect.
                              thread-runtime.ts held a bare let engine, which is harmless in the CLI (one process, one
                              workspace, then exit) and a misroute in Tower, which drains held mail for every workspace in
                              global.db. The first thread-configured workspace pinned the socket, projectId, dispatcher and
                              journal for all others, and a turn dispatched to the wrong server succeeds.

                              The fix is now the house rule: key by canonical workspace root, and the unkeyed slot is not a
                              fallback in either direction.
                              A keyed read that missed and then took the unkeyed one would
                              restore the bug one indirection further away. This sits alongside the existing mailbox-first fact,
                              because Tower being multi-workspace is the premise both depend on.

                              installThreadSpawnFactory is the same defect one layer up and is tracked in #227.

                              The CLI-awaits / Tower-does-not split is load-bearing and undefended by the type system.
                              ensureThreadBackendReady is CLI-only. Reaching for it from a request path reintroduces a
                              Tower-wide stall. The drain tick must never await an establishable resource — the synchronous
                              requestThreadBackend, returning a state union rather than a promise, is the pattern.

                              For lessons-critical.md (HOT)

                              The existing line — "I could not tell" must never be spelled the same way as "no" — now has
                              reference implementations worth naming, because the abstract rule kept being agreed with and then
                              violated:

                              • found | none | unknown (project lookup, thread-backend.ts)
                              • known: false only on spawn error or non-empty stderr (lsof, t3-server.mjs) — the exit code
                                alone cannot separate "nothing is listening" from "I could not run the check"
                              • UNDETERMINED as a distinct exit (NO_DATA_TO_KEEP)
                              • the five-state availability enum
                              • UNCONFIRMED as its own client band, distinct from refused, because an unreadable 200 may mean the
                                gate is approved and reporting "no" sends a human to approve twice

                              New lesson, earned four separate times in two days:

                              A green suite is evidence only about what it ran. Before quoting one, name which suites exist and
                              which files each covers.

                              Instances: a unit run that excluded *.e2e.test.ts and so could not reach the file the change
                              broke; a fixture that named directories the way the code assumed, so it could not falsify the
                              assumption; a test that passed because a successful path never entered the catch block it was
                              written to exercise; and a determinism test whose precondition was violated by a concurrent
                              porch commit (#217).

                              The common mechanism is a measurement taken where the thing being measured is not present.

                              Second new lesson: a comment asserting a guarantee stops the guarantee being checked. Four
                              instances in two days, each found by a reviewer going straight to the sentence:
                              thread-backend.ts:370 ("can only fire after…"), ownsProcess ("ownership is proven from the
                              command line"), validateSnapshot (claiming a distinction the code did not make), and a guard
                              whose stated justification did not hold. Write what makes a claim true, or write the limit —
                              argv heuristic, not proof of parentage is the corrected form.

                              For arch.md (COLD — Monorepo Structure, Integration Points)

                              • apps/client — the codev-client, React 19 / Vite 6 / Vitest 4 / Playwright.
                              • tools/codev-agent-host — mounts the route table over a copy of global.db with a scratch
                                credential root; the two-server e2e harness.
                              • The two new agent routes: POST /api/agent/v1/human-sessions and .../gates/approve.
                              • Five test suites, not one: packages/codev unit, its vitest.e2e.config, its vitest.cli.config,
                                apps/client unit, apps/client Playwright.

                              Not in scope

                              Do not edit codev/specs/146-*.md or the phase plans. Phase 11's unmet criteria are #228.

                              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/coreCore libraries and shared logic

                                Projects

                                No projects

                                  Milestone

                                  No milestone

                                  Relationships

                                  None yet

                                  Development

                                  No branches or pull requests

                                  Issue actions