Skip to content

Key a session by $TOKENECONOMIZER_REPO when its directory holds no git metadata - #178

Merged
Aaron Li (aaron-jx-li) merged 2 commits into
microsoft:mainfrom
GeminiVibe:repo-key-env
Nov 10, 2026
Merged

Aaron Li (aaron-jx-li) merged 2 commits into
microsoft:mainfrom
GeminiVibe:repo-key-env

Conversation

@GeminiVibe

Copy link
Copy Markdown
Contributor

Why

Some setups run the agent in a directory that receives a checkout's working files but not its .git: a container or sandbox per session, or a fresh folder per run. In our case it is an orchestrator that runs several Claude Code sessions in parallel against the same repository, each in its own pod with an exported working tree.

repo_identity finds no checkout there and keys the session by its directory (BY_DIR). Every run then starts a new repository, and nothing learned carries over between runs on the same repository, so micro-skills and the loop guard's envelope never converge.

What

$TOKENECONOMIZER_REPO, when set, is the repository's remote URL and takes the place of the one read from git. It goes through the same normalize_remote, so such a session gets the same key (and BY_REMOTE, label and remote) as any clone of that remote, and shares what its clones learned.

  • Unset or empty: no change at all. This is the default.
  • Set: it names the repository whatever the directory, mirroring how $TOKENECONOMIZER_HOME overrides the state root. The person or tool that sets it is stating which repository the session belongs to.

Changes

  • tokeneconomizer/plugin/paths.py: REPO_ENV, read in repo_identity before the git config.
  • tests/test_repo_key_env.py: covers four cases:
    • an export keyed as its clones are;
    • two run folders sharing one key;
    • an empty value changing nothing;
    • a set value naming the repository of a checkout.

I kept the test in its own file to leave tests/test_live_repos.py alone. Happy to move it there instead.

python -m pytest tests experiments/tests passes.

If you want it documented, I suggest this sentence for docs/PLUGIN.md §4, after the GIT_CEILING_DIRECTORIES line:

$TOKENECONOMIZER_REPO, when set, is the repository's remote URL and takes the place of the one read from git: a session in a directory without git metadata (an export of a checkout in a container, a new folder per run) is keyed as a clone of that remote and shares what it learned.

…t metadata

A session in an export of a checkout (a container or sandbox that receives the working files
without `.git`, a fresh folder per run) is keyed by its directory today, so every run starts a
new repository and nothing learned carries over. `$TOKENECONOMIZER_REPO`, when set, is the
repository's remote URL and takes the place of the one read from git, so such a session is keyed
as a clone of that remote and shares what its clones learned. Unset or empty: no change.
@GeminiVibe

Copy link
Copy Markdown
Contributor Author

@microsoft-github-policy-service agree

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I read repo_identity at the PR head and its four callers (context.py:51, history.py:150, repos.py:483, cli.py:969).

The variable also overrides a real checkout, which is not what the title says. The title and the docstring say it is for a directory "that holds no git metadata". But paths.py reads env.get(REPO_ENV) first and only falls back to origin_url(root) when it is empty, and the second test asserts that a checkout of example.org/team/elsewhere is re-keyed as microsoft/tokeneconomizer. That matters because of the callers that resolve directories other than the live session's:

  • history.is_this_repository → _identity(cwd, env, ...) (history.py:150) keys every recorded cwd from past logs. With TOKENECONOMIZER_REPO set in the shell that runs the import, every logged session, from any repository, gets the same key, so the history import would learn facts from unrelated repos.
  • repos.plan_dir → _resolve (repos.py:483) does the same when it regroups sessions, so all of them would be planned into the one repository.
  • The variable is inherited by anything the agent starts. A session that cds into another clone and runs a nested agent would file that clone's learning under the wrong repository.

Applying it only where no checkout was found keeps the use case in the description and avoids all three:

remote = normalize_remote(origin_url(root)) if found else normalize_remote(env.get(REPO_ENV) or "")

Then the second half of test_an_empty_value_changes_nothing_and_a_set_one_names_the_repository would flip, asserting that elsewhere keeps its own key. It would also help to have one test through history.is_this_repository with the variable set, so the import path is pinned.

A smaller point: a value with no slash (e.g. TOKENECONOMIZER_REPO=myrepo) gives label == "myrepo" and a BY_REMOTE key that no clone can ever match. Ignoring values without a / after normalize_remote would make a typo fall back to BY_DIR instead of quietly creating a new repository.

Reviewed with help from Claude Code.

A checkout's own origin wins, so a value left set in a shell never merges other repositories into one key. A checkout without an origin takes the named remote. Documented in docs/PLUGIN.md.
@aaron-jx-li
Aaron Li (aaron-jx-li) merged commit fd6299a into microsoft:main Nov 10, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants