From b17ecd536b2f2392f498284f018f57db0b2c6580 Mon Sep 17 00:00:00 2001 From: "d3mlabs-ai-flow[bot]" <305891656+d3mlabs-ai-flow[bot]@users.noreply.github.com> Date: Sat, 1 Aug 2026 16:16:51 -0400 Subject: [PATCH 1/2] ai-flow /build: capture learnings from the build pass Co-authored-by: JPDuchesne <2636122+JPDuchesne@users.noreply.github.com> --- .cursor/rules/learnings-index.mdc | 19 +++++---- .../architecture/command-dispatch/SKILL.md | 4 +- .../skills/architecture/hook-points/SKILL.md | 23 ++++++----- .../skills/architecture/module-map/SKILL.md | 7 ++-- .../runner-shell-bundler-leak/SKILL.md | 39 +++++++++++++++++++ 5 files changed, 70 insertions(+), 22 deletions(-) create mode 100644 .cursor/skills/learnings/runner-shell-bundler-leak/SKILL.md diff --git a/.cursor/rules/learnings-index.mdc b/.cursor/rules/learnings-index.mdc index a0fb205..b77f346 100644 --- a/.cursor/rules/learnings-index.mdc +++ b/.cursor/rules/learnings-index.mdc @@ -34,15 +34,15 @@ propose a retirement, a consolidation, or a glob-scoped sub-index split. ## architecture - [architecture/command-dispatch] Global builtins (cd, plan, cred, - knowledge) dispatch before the dev.yml gate; project commands are + learnings) dispatch before the dev.yml gate; project commands are yaml-declared and run through Runner. → .cursor/skills/architecture/command-dispatch/ - [architecture/module-map] What owns what: src/dev is the typed CLI core; each `lib/dev/` is one feature whose Accessor is its only CLI surface. → .cursor/skills/architecture/module-map/ - [architecture/hook-points] `dev up` / `install-deps` / `dev plan` double - as the idempotent hygiene hooks (skill links, knowledge sync): never - raise, never block on the network. + as the idempotent hygiene hooks (skill links, learnings sync): never + raise, network only as a bounded pull before distribution. → .cursor/skills/architecture/hook-points/ ## toolchain @@ -52,11 +52,16 @@ propose a retirement, a consolidation, or a glob-scoped sub-index split. same-minor brew libruby — bundler's Gemfile ruby-pin mismatch is the symptom, not a project bug. → .cursor/skills/learnings/rbenv-libruby-rpath-hijack/ +- [toolchain/runner-shell-bundler-leak] ai-flow runner shells leak the + harness's bundler env (RUBYOPT, BUNDLE_GEMFILE, GEM_HOME) into worktree + commands — scrub it before running dev, or the project resolves + .ai-flow's Ruby pin. + → .cursor/skills/learnings/runner-shell-bundler-leak/ ## org tier Org-wide invariants and knowledge live in the configured org knowledge -repo (`knowledge_repo:`), not here: dev renders the always-on slice into -the generated `.cursor/rules/org-invariants.mdc` and links the on-demand -corpus into `~/.cursor/skills/`. A lesson about how we build software — -not about this repo — belongs there. +repo (`knowledge_repo:`), not here: dev renders the always-on slice once +machine-side and links it in as `.cursor/rules/org-invariants.mdc`, and +links the on-demand corpus into `~/.cursor/skills/`. A lesson about how +we build software — not about this repo — belongs there. diff --git a/.cursor/skills/architecture/command-dispatch/SKILL.md b/.cursor/skills/architecture/command-dispatch/SKILL.md index b3434a0..5f6dfb2 100644 --- a/.cursor/skills/architecture/command-dispatch/SKILL.md +++ b/.cursor/skills/architecture/command-dispatch/SKILL.md @@ -12,7 +12,7 @@ routes argv through two layers: 1. **Global builtins** — `Dev::GlobalDispatch` (`src/dev/global_dispatch.rb`) runs first, before any dev.yml lookup, - so `cd`, `plan`, `cred`, and `knowledge` work from any directory. Each + so `cd`, `plan`, `cred`, and `learnings` work from any directory. Each owns host- or workspace-global state, never project config. 2. **Project commands** — everything else builds `Dev::Runner` (`src/dev/runner.rb`), which requires a dev.yml in the cwd's ancestry @@ -25,7 +25,7 @@ The seams: - A new global command joins `GlobalDispatch::GLOBAL_COMMANDS` and gets a feature module under `lib/dev//` whose `Accessor` is its only CLI surface (usage, arg parsing, clean failures) — see `Cd::Accessor`, - `Plan::Accessor`, `Knowledge::Accessor`. + `Plan::Accessor`, `Learnings::Accessor`. - Project commands are declared in each repo's dev.yml, never hardcoded in dev's core. - Workspace-global commands resolve their root as nearest dev.yml, else diff --git a/.cursor/skills/architecture/hook-points/SKILL.md b/.cursor/skills/architecture/hook-points/SKILL.md index 59d7f8a..eb88ebe 100644 --- a/.cursor/skills/architecture/hook-points/SKILL.md +++ b/.cursor/skills/architecture/hook-points/SKILL.md @@ -9,22 +9,25 @@ description: >- `dev up` / `dev install-deps` (`Runner#install_locked_deps`) and every `dev plan` invocation (`Plan::Accessor#run`) double as the refresh points -for agent-facing hygiene: shipped-skill links, gem-skill links, the org -knowledge TTL fetch, and the org-invariants render. There is no separate -setup step by design — riding existing commands is what keeps the -artifacts fresh without asking anything of the user. +for agent-facing hygiene: shipped-skill links, gem-skill links, the +knowledge repo cache pull, and the org-invariants render + project link. +There is no separate setup step by design — riding existing commands is +what keeps the artifacts fresh without asking anything of the user. Anything added to a hook point must obey both rules: - **Hygiene must not block correctness.** The hook paths never raise — failures warn on stderr and the carrying command proceeds (see - `GemSkillLinker#link_all`, `Knowledge::Synchronizer#sync`, + `GemSkillLinker#link_all`, `Learnings::Synchronizer#sync`, `SkillInstaller#install`). -- **No network on the hot path.** Remote refreshes are TTL-gated and - async (`Knowledge::Cache#refresh_async`); the synchronous work is - idempotent, content-compared, millisecond-scale symlink and render - checks. Blocking network belongs only behind an explicit command - (`dev knowledge sync`). +- **Network on the hot path is bounded and ordered.** The cache pull runs + inline *before* distribution (a hook never renders content it just + found stale), capped by a ~2s timeout with a ~30s courtesy floor + (`Learnings::Cache#refresh_bounded`, constants, not settings); on + timeout or offline the current cache is served. The rest is idempotent, + content-compared, millisecond-scale symlink and render checks. + Unbounded blocking network belongs only behind an explicit command + (`dev learnings sync` — the runner bootstrap contract's step). origin: seeded by the dev#58 architecture pass date: 2026-07-25 diff --git a/.cursor/skills/architecture/module-map/SKILL.md b/.cursor/skills/architecture/module-map/SKILL.md index dd4f6ef..3e03864 100644 --- a/.cursor/skills/architecture/module-map/SKILL.md +++ b/.cursor/skills/architecture/module-map/SKILL.md @@ -19,9 +19,10 @@ description: >- `.cursor/rules/separation-of-concerns.mdc`: Repository resolves, Integration installs, Lockfile serializes, the orchestrator coordinates — one class, one layer. -- **`lib/dev/knowledge/`** — org knowledge distribution: Cache (TTL git - clone), Synchronizer (orchestration), InvariantsRenderer (the generated - org-invariants.mdc). +- **`lib/dev/learnings/`** — the learnings read path: Cache (bounded git + clone/pull of the knowledge repo), Synchronizer (orchestration), + InvariantsRenderer (one machine-side org-invariants.mdc render plus the + per-project symlink and the `dev learnings invariants` prompt seam). - **`lib/dev/skill_installer.rb`** — the one symlink mechanism behind all three skill channels (shipped, org, gem); the gem channel's lockfile scan is `lib/dev/deps/gem_skill_linker.rb`. diff --git a/.cursor/skills/learnings/runner-shell-bundler-leak/SKILL.md b/.cursor/skills/learnings/runner-shell-bundler-leak/SKILL.md new file mode 100644 index 0000000..342661e --- /dev/null +++ b/.cursor/skills/learnings/runner-shell-bundler-leak/SKILL.md @@ -0,0 +1,39 @@ +--- +name: runner-shell-bundler-leak +description: >- + MUST be used when dev/bundler commands in an ai-flow runner worktree fail + with a Gemfile ruby-pin mismatch or resolve the wrong Ruby — the runner + shell leaks its .ai-flow bundler environment into the project. +--- + +# Runner shells leak the .ai-flow bundler env + +Shells spawned under the ai-flow runner carry the harness's bundler +activation — `RUBYOPT` preloading bundler/setup, `BUNDLE_GEMFILE` pointing +at `.ai-flow/Gemfile`, `GEM_HOME`/`GEM_PATH`/`RUBYLIB` — so `dev`/`bundle` +in the worktree resolves the harness's pins, not the project's (symptom: +`Bundler::RubyVersionMismatch` naming a Ruby the project never declared). +Strip the leaked variables first; `CI=true` is also set, so dev's +staleness guard errors (not warns) until `dev up` has stamped the checkout. + +Wrong: + +```sh +dev test # inherits RUBYOPT/BUNDLE_GEMFILE → mismatch against .ai-flow's pin +``` + +Right: + +```sh +env -u RUBYOPT -u BUNDLE_GEMFILE -u BUNDLER_SETUP -u BUNDLE_BIN_PATH \ + -u BUNDLER_VERSION -u GEM_HOME -u GEM_PATH -u GEM_ROOT -u RUBYLIB \ + -u RUBY_VERSION -u RUBY_ROOT -u RUBY_ENGINE -u __shadowenv_data \ + dev test +``` + +Sibling: `rbenv-libruby-rpath-hijack` — same ruby-pin-mismatch symptom, +different mechanism (Linux rpath, not env leakage). + +learned-from: the dev#74 build pass — dev test failed under the runner env +until the shell was scrubbed. +date: 2026-08-01 From 5938238c8b88b68f2385d35cc7df07de64aa3a09 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Date: Sat, 1 Aug 2026 16:36:39 -0400 Subject: [PATCH 2/2] Prune runner-shell-bundler-leak: it's a bug to fix, not a lesson to keep The skill taught agents a 13-variable env -u workaround for pollution ai-flow's own executor creates (the agent inherits the harness's bundler/shadowenv activation). Workarounds for our own write-path bugs fail the capture rubric; the fix is filed as d3mlabs/ai-flow#38. Co-authored-by: Cursor --- .cursor/rules/learnings-index.mdc | 5 --- .../runner-shell-bundler-leak/SKILL.md | 39 ------------------- 2 files changed, 44 deletions(-) delete mode 100644 .cursor/skills/learnings/runner-shell-bundler-leak/SKILL.md diff --git a/.cursor/rules/learnings-index.mdc b/.cursor/rules/learnings-index.mdc index b77f346..054feed 100644 --- a/.cursor/rules/learnings-index.mdc +++ b/.cursor/rules/learnings-index.mdc @@ -52,11 +52,6 @@ propose a retirement, a consolidation, or a glob-scoped sub-index split. same-minor brew libruby — bundler's Gemfile ruby-pin mismatch is the symptom, not a project bug. → .cursor/skills/learnings/rbenv-libruby-rpath-hijack/ -- [toolchain/runner-shell-bundler-leak] ai-flow runner shells leak the - harness's bundler env (RUBYOPT, BUNDLE_GEMFILE, GEM_HOME) into worktree - commands — scrub it before running dev, or the project resolves - .ai-flow's Ruby pin. - → .cursor/skills/learnings/runner-shell-bundler-leak/ ## org tier diff --git a/.cursor/skills/learnings/runner-shell-bundler-leak/SKILL.md b/.cursor/skills/learnings/runner-shell-bundler-leak/SKILL.md deleted file mode 100644 index 342661e..0000000 --- a/.cursor/skills/learnings/runner-shell-bundler-leak/SKILL.md +++ /dev/null @@ -1,39 +0,0 @@ ---- -name: runner-shell-bundler-leak -description: >- - MUST be used when dev/bundler commands in an ai-flow runner worktree fail - with a Gemfile ruby-pin mismatch or resolve the wrong Ruby — the runner - shell leaks its .ai-flow bundler environment into the project. ---- - -# Runner shells leak the .ai-flow bundler env - -Shells spawned under the ai-flow runner carry the harness's bundler -activation — `RUBYOPT` preloading bundler/setup, `BUNDLE_GEMFILE` pointing -at `.ai-flow/Gemfile`, `GEM_HOME`/`GEM_PATH`/`RUBYLIB` — so `dev`/`bundle` -in the worktree resolves the harness's pins, not the project's (symptom: -`Bundler::RubyVersionMismatch` naming a Ruby the project never declared). -Strip the leaked variables first; `CI=true` is also set, so dev's -staleness guard errors (not warns) until `dev up` has stamped the checkout. - -Wrong: - -```sh -dev test # inherits RUBYOPT/BUNDLE_GEMFILE → mismatch against .ai-flow's pin -``` - -Right: - -```sh -env -u RUBYOPT -u BUNDLE_GEMFILE -u BUNDLER_SETUP -u BUNDLE_BIN_PATH \ - -u BUNDLER_VERSION -u GEM_HOME -u GEM_PATH -u GEM_ROOT -u RUBYLIB \ - -u RUBY_VERSION -u RUBY_ROOT -u RUBY_ENGINE -u __shadowenv_data \ - dev test -``` - -Sibling: `rbenv-libruby-rpath-hijack` — same ruby-pin-mismatch symptom, -different mechanism (Linux rpath, not env leakage). - -learned-from: the dev#74 build pass — dev test failed under the runner env -until the shell was scrubbed. -date: 2026-08-01