Skip to content

v1.6.4

Choose a tag to compare

@github-actions github-actions released this 10 Jul 02:20
· 8 commits to main since this release
3327b15

IJFW could adopt a writable directory as its project root even when that
directory lived inside another app's signed bundle. When Wayland spawned the
IJFW MCP server with its working directory inside Wayland.app (an
app.asar.unpacked path), safeProjectDir() accepted it and the layout
migration wrote .ijfw/.layout-version inside the bundle — breaking the
code-signature seal, which on hardened-runtime macOS makes the OS deny every
child process the host app tries to spawn (FerroxLabs/wayland#755). This
release refuses any bundle-internal root, everywhere a root can enter.

Fixed

  • safeProjectDir() rejects bundle-internal candidates. A new
    isBundleInternalPath guard refuses any path containing an *.asar /
    *.asar.unpacked segment or an *.app bundle interior (fs-backed check for
    a sibling Contents), case-insensitive, both separator styles. The check
    runs before the writability probe, and a rejected candidate falls through to
    the next signal (IJFW_PROJECT_DIR → CLAUDE_PROJECT_DIR → cwd) and finally
    to homedir() — never a crash.
  • All entry points share one vetted resolver. The guard was extracted to
    mcp-server/src/lib/project-root-guard.js and applied at every site that
    previously fell back to a raw IJFW_PROJECT_DIR || process.cwd(): the
    colon-syntax dispatch layer (which feeds fts5.openDb() and would otherwise
    mkdir .ijfw/index under an unvetted root), the memory/compute FTS5
    openers, cli-run, the state-SDK path, and the ijfw_run / ijfw_search
    caller-supplied roots.
  • Symlinked roots can't smuggle a bundle path in. Env- and caller-supplied
    candidates are realpath-resolved before the bundle check.

Notes

  • Installed copies pick this up on the next ijfw update (or a fresh
    npx @ijfw/install).

IJFW used to write its project files into every directory a session started in,
including throwaway scratch dirs and ephemeral "temporary spaces" (e.g. Wayland).
A one-shot chat in a temp space got ijfw/, AGENTS.md, and CLAUDE.md it never
asked for. This release gates all on-disk writes behind a single rule.

Changed (behavior)

  • IJFW now only materializes project files in a real project. "Real project"
    means the directory carries a recognized marker (a VCS dir like .git, or a
    language manifest like package.json / pyproject.toml / go.mod / Cargo.toml
    / tsconfig.json and friends), or you explicitly ran ijfw init (which drops
    .ijfw/project). With no marker, memory recall still works in-session, but
    nothing is written to disk. This is the same rule the codebase indexer already
    enforced (issue #16), now applied to every write surface: the visible ijfw/
    brain layer (dream pipeline), AGENTS.md, CLAUDE.md / GEMINI.md, and the
    .ijfw/project.type cold scan.
  • ijfw init is the one-command override for a real project that lacks a VCS dir
    or manifest.

Fixed

  • Codex and Gemini session-start hooks gained the $HOME / filesystem-root
    refusal
    they never had. The shared seed gate refuses to author config in the
    home directory, its ancestors, or the filesystem root, closing a latent
    global-config-bleed path on those two platforms (Claude already had this guard).
  • Release-gate flake: the gitleaks preflight gate runs detect --no-git,
    which walks node_modules (~95 MB) and could exceed the gate's 30s spawn
    timeout, killing a clean scan mid-flight and reporting a false FAIL. The spawn
    timeout is now 120s, and node_modules / dist / .git are allowlisted so a
    third-party fixture cannot itself flag the gate.
  • esbuild bumped to 0.28.1 (build-only devDep) to clear advisory
    GHSA-gv7w-rqvm-qjhr. Not shipped to users; build tooling only.

The seed rule lives once per language surface (seed-gate.js, seed-gate.sh, and
the indexer guard) and the three marker lists are locked together by a drift test.