---
id: 11155
title: 'Restore github-workflow projectRoot to v12.1.0 cwd-primary shape (revert #11149)'
state: CLOSED
labels:
  - bug
  - ai
  - regression
  - architecture
  - model-experience
assignees:
  - neo-opus-ada
createdAt: '2026-05-10T21:56:05Z'
updatedAt: '2026-05-10T23:11:24Z'
githubUrl: 'https://github.com/neomjs/neo/issues/11155'
author: neo-opus-ada
commentsCount: 0
parentIssue: null
subIssues: []
subIssuesCompleted: 0
subIssuesTotal: 0
blockedBy: []
blocking: []
closedAt: '2026-05-10T23:11:24Z'
---
# Restore github-workflow projectRoot to v12.1.0 cwd-primary shape (revert #11149)

## Context

Operator-surfaced 2026-05-10: the v12.1.0 release had github-workflow's projectRoot resolution working correctly for the consumer-workspace install pattern (Repo-A installs `neo.mjs` as a dependency, configures gh-workflow server via `.gemini/settings.json` to sync from a DIFFERENT repo Repo-B, and the synced files land in **Repo-A's root**, not inside `Repo-A/node_modules/neo.mjs/`).

Operator-quoted V-B-A anchor: *"context of what worked in neo v12.1 => repo => install neo as a dependency. add e.g. .gemini/settings.json => configure the gh worklow server to a different repo. sync => into the repo root, not the neo node module. we had that working fine."*

PR #11149 (closed-merged 2026-05-10, addressing #11147) replaced the v12.1 shape with a 3-tier heuristic. Then PR #11153 (closed-unmerged via Cycle-3 V-B-A retraction) attempted to extend with a hallucinated pnpm 4th tier. Today's drift cascade exposed that #11149's 3-tier heuristic itself diverges from the v12.1 working shape.

## The Problem

`ai/mcp/server/github-workflow/config.template.mjs` (post-#11149) resolves `projectRoot` via:

```js
let projectRoot;
if (process.env.NEO_WORKSPACE_ROOT) {
    projectRoot = process.env.NEO_WORKSPACE_ROOT;
} else {
    const nodeModulesIndex = __dirname.indexOf(`${path.sep}node_modules${path.sep}neo.mjs`);
    if (nodeModulesIndex !== -1) {
        projectRoot = __dirname.slice(0, nodeModulesIndex) || path.sep;
    } else {
        projectRoot = path.resolve(__dirname, '../../../../');
    }
}
```

Three problems with this shape, in increasing severity:

1. **Aesthetic (operator-flagged):** `__dirname.indexOf(\`${path.sep}node_modules${path.sep}neo.mjs\`)` is string-walking with separator interpolation. Operator-quoted: *"this is UGLY. there is path.join() and other methods that resolve slashes cross OS nicer."* The whole heuristic uses path-as-string semantics where path-as-structure semantics exist.

2. **Substrate-divergent (right-hemisphere precedent):** Sibling MCP configs all derive `projectRoot` (or equivalent `neoRootDir`) via `path.resolve(__dirname, '../../../../')` directly with no heuristic chains:
   - `ai/mcp/server/memory-core/config.template.mjs`: `const neoRootDir = path.resolve(__dirname, '../../../../'); const cwd = neoRootDir;`
   - `ai/mcp/server/knowledge-base/config.template.mjs`: `const neoRootDir = path.resolve(__dirname, '../../../../');`
   - `ai/mcp/server/neural-link/config.template.mjs`: `const neoRootDir = path.resolve(__dirname, '../../../../'); const cwd = process.cwd();`

   None walks `node_modules`. None probes for env-var overrides.

3. **Functional regression (operator-validated):** v12.1.0 had github-workflow working with the consumer-workspace pattern operator described. Verified via `git show 12.1.0:ai/mcp/server/github-workflow/config.mjs`:

   ```js
   const packageRoot = path.resolve(__dirname, '../../../../');
   const projectRoot = process.cwd() === '/' ? packageRoot : process.cwd();
   ```

   This handles BOTH the `/` cwd edge (the symptom #11147 described) AND the consumer-workspace case (projectRoot = consumer's cwd, NOT `node_modules/neo.mjs/`). The `process.cwd() === '/'` check is the explicit guard against the MCP-host-context bug #11147 reported.

   PR #11149's 3-tier heuristic broke the consumer-workspace expectation: for a consumer running `npm run ai:mcp-server-github-workflow` from Repo-A, the `__dirname.indexOf('node_modules/neo.mjs')` check fires and slices `projectRoot` back to `Repo-A` (correct) — but ONLY if Repo-A's neo install is at exactly `node_modules/neo.mjs/`. If neo is installed via any non-default layout (workspaces, monorepo tools, etc.), the heuristic mis-resolves.

## The Architectural Reality

- The MCP server is invoked via `npm run ai:mcp-server-github-workflow`, which means `process.cwd()` is the **invocation workspace root** by npm's contract — already the right answer in 99% of cases.
- The `process.cwd() === '/'` edge case happens when the MCP host (Claude Code / Antigravity / Codex Desktop) spawns the server with cwd not propagated. v12.1's fallback to `path.resolve(__dirname, '../../../../')` correctly anchors to the install location in that case.
- These are the only two cases. Both handled cleanly by the v12.1 shape. The 3-tier heuristic addresses no third real case.

The shape v12.1.0 used is also conceptually equivalent to the LEFT-hemisphere build-script canonical (`buildScripts/build/all.mjs`, `buildScripts/build/themes.mjs`) which solves the same "find workspace root in repo OR consumer-install" problem via `process.cwd()` + package.json discriminator. The right-hemisphere github-workflow special case (project-data is deployment-context-specific, unlike Neo-internal substrate in kb/mc/nl) makes `process.cwd()` the natural primary source — matching v12.1.

## The Fix

Revert `ai/mcp/server/github-workflow/config.template.mjs` projectRoot resolution to the exact v12.1.0 shape:

```js
const __filename  = fileURLToPath(import.meta.url);
const __dirname   = path.dirname(__filename);
const packageRoot = path.resolve(__dirname, '../../../../');
const projectRoot = process.cwd() === '/' ? packageRoot : process.cwd();
```

Scope is config.template.mjs ONLY. `ai/services/github-workflow/toolService.mjs`'s `defaultBranchDetector` (introduced in #11146 + improved in #11149 to use `config.projectRoot` instead of bare `process.cwd()`) is the **right shape** and stays unchanged — once `projectRoot` resolves correctly via cwd-primary, downstream consumers of `config.projectRoot` (including the branch detector) inherit the correct value.

## Contract Ledger Matrix

| Target Surface | Source of Authority | Proposed Behavior | Fallback | Docs | Evidence |
|---|---|---|---|---|---|
| `config.template.mjs` projectRoot resolution | v12.1.0 working shape (operator-validated empirical anchor) | `process.cwd() === '/' ? packageRoot : process.cwd()` | None — both branches deterministic | Inline JSDoc on the const declarations | L2: `git show 12.1.0:ai/mcp/server/github-workflow/config.mjs` + sandbox-runtime via `npm run test-unit` |
| Removed env-var `NEO_WORKSPACE_ROOT` | n/a — removed | Not consumed; documentation removed | Use cwd directly | n/a | L1: grep confirms only this file consumes the env-var |
| Removed `__dirname.indexOf('node_modules/neo.mjs')` heuristic | n/a — removed | Not consumed; logic deleted | Use cwd directly | n/a | L1: only consumer was this file |

## Acceptance Criteria

- [ ] **(AC1)** `ai/mcp/server/github-workflow/config.template.mjs` projectRoot resolution restored to v12.1.0 exact shape (4 lines: `packageRoot` + `projectRoot` derivations). NO env-var. NO `__dirname.indexOf`. NO multi-tier branching.
- [ ] **(AC2)** `ai/services/github-workflow/toolService.mjs` `defaultBranchDetector` unchanged — still uses `config.projectRoot` (correct downstream consumption).
- [ ] **(AC3)** `test/playwright/unit/ai/services/github-workflow/toolService.spec.mjs` passes unchanged. No test asserted the 3-tier heuristic, so no test updates needed.
- [ ] **(AC4)** Empirical re-validation: invoke `npm run ai:mcp-server-github-workflow` from worktree root, confirm `projectRoot` resolves to worktree root (sandbox-runtime verify L2).
- [ ] **(AC5)** Empirical re-validation: simulate the #11147 symptom (cwd=`/`) by spawning the server with `cwd: '/'`, confirm `projectRoot` falls back to `packageRoot` (sandbox-runtime verify L2).
- [ ] **(AC6)** PR body cites this ticket + v12.1.0 shape diff + operator-validated working-pattern anchor.

## Out of Scope

- **Adjusting kb/mc/nl projectRoot resolution to match github-workflow:** rejected. Those are Neo-internal substrate (Memory Core memories, KB docs, Neural Link app target) — `neoRootDir`-pinning via `__dirname` is correct for them. github-workflow is the outlier because its data is consumer-workspace-relative.
- **Removing/modifying `defaultBranchDetector`:** out of scope. That's correct.
- **Documenting consumer-workspace install pattern:** could be a follow-up doc-ticket; not blocking the revert.
- **Branch deletion of closed PR's stale branch `agent/11152-pnpm-layout-and-docs`:** orthogonal cleanup. Operator decision per shared-checkout/worktree identity incident handling.

## Avoided Traps

- **Follow #11147's stated prescription verbatim** (`projectRoot = path.resolve(__dirname, '../../../../')`): rejected. This is the kb/mc/nl right-hemisphere shape and is correct for THOSE configs, but breaks github-workflow's consumer-workspace contract. In a consumer install where `npm i neo.mjs`, this resolves to `node_modules/neo.mjs/` — meaning the consumer's `.gemini/settings.json`-targeted issue sync would land in `node_modules/neo.mjs/resources/content/issues/`, polluting the neo package. Operator-confirmed v12.1 worked with files landing in **repo root, not the node module**. cwd-primary is required.
- **Use `path.join` or `path.relative` to improve indexOf aesthetic without changing semantics**: rejected. Operator's "indexOf is ugly" critique was a substrate-quality signal, not a request for a cosmetic patch. The right fix removes the entire heuristic chain by returning to v12.1's cwd-primary shape — no path-walking at all.
- **Preserve `NEO_WORKSPACE_ROOT` env-var for future flexibility**: rejected. Truth-in-code memory note applies — no production consumer exists. Speculative-support-code is worse than absent-support; remove unverified branches.
- **Frame as new design instead of revert**: rejected. v12.1.0 is the operator-validated working shape. Revert is precise, minimal, and respects substrate continuity (rather than introducing yet another shape).
- **Bundle the toolService.mjs `defaultBranchDetector` change**: rejected. That change is correct (uses config.projectRoot once projectRoot is right). Bundling would inflate scope. Single-concern PR.

## Related

- **Parent regression**: #11149 (merged, Gemini-authored, my reviewer of record) — introduced the 3-tier heuristic. This ticket reverts the projectRoot portion only.
- **Original symptom ticket**: #11147 (closed) — described the cwd=`/` symptom. v12.1.0's `process.cwd() === '/' ? packageRoot : process.cwd()` handles this correctly; #11149's heuristic was an over-engineering response.
- **Closed-unmerged exploration**: #11152 + #11153 — attempted pnpm 4th-tier extension. Now properly retired.
- **Substrate-meta**: #11154 — cross-PR reviewer-seeded drift discipline (extends pr-review-guide §7.4). This ticket is the third drift instance in the same cascade (plant → implement → retraction-V-B-A failure).
- **Empirical anchor**: v12.1.0 release tag — `git show 12.1.0:ai/mcp/server/github-workflow/config.mjs`
- **Right-hemisphere precedent**: `ai/mcp/server/{memory-core,knowledge-base,neural-link}/config.template.mjs` — all use `path.resolve(__dirname, '../../../../')` directly. github-workflow is the deliberate cwd-primary outlier due to consumer-workspace-relative data semantics.

Origin Session ID: c2912891-b459-4a03-b2af-154d5e264df1

Retrieval Hint: `query_raw_memories(query="github-workflow projectRoot revert v12.1 cwd-primary consumer workspace install neo.mjs as dependency 11149 3-tier heuristic regression")`

## Timeline

- 2026-05-10T21:56:07Z @neo-opus-ada added the `bug` label
- 2026-05-10T21:56:07Z @neo-opus-ada added the `ai` label
- 2026-05-10T21:56:08Z @neo-opus-ada added the `regression` label
- 2026-05-10T21:56:08Z @neo-opus-ada added the `architecture` label
- 2026-05-10T21:56:08Z @neo-opus-ada added the `model-experience` label
- 2026-05-10T21:56:13Z @neo-opus-ada assigned to @neo-opus-ada
- 2026-05-10T22:52:34Z @neo-gemini-pro cross-referenced by PR #11158
- 2026-05-10T22:59:59Z @neo-gemini-pro referenced in commit `ea2c3a0` - "fix(github-workflow): revert 3-tier projectRoot heuristic to cwd-primary shape (#11155)"
- 2026-05-10T23:11:24Z @tobiu referenced in commit `b19f9c2` - "fix(github-workflow): revert 3-tier projectRoot heuristic to cwd-primary shape (#11155) (#11158)

Co-authored-by: neo-gemini-pro <neo-gemini-pro@users.noreply.github.com>"
- 2026-05-10T23:11:24Z @tobiu closed this issue
- 2026-05-10T23:58:01Z @neo-opus-ada cross-referenced by PR #11166

