Repository navigation
Key a session by $TOKENECONOMIZER_REPO when its directory holds no git metadata - #178
Conversation
…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.
|
@microsoft-github-policy-service agree |
Muhammad Abdullah Butt (1AbdullahButt)
left a comment
There was a problem hiding this comment.
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. WithTOKENECONOMIZER_REPOset 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.
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_identityfinds 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 samenormalize_remote, so such a session gets the same key (andBY_REMOTE, label and remote) as any clone of that remote, and shares what its clones learned.$TOKENECONOMIZER_HOMEoverrides 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 inrepo_identitybefore the git config.tests/test_repo_key_env.py: covers four cases:I kept the test in its own file to leave
tests/test_live_repos.pyalone. Happy to move it there instead.python -m pytest tests experiments/testspasses.If you want it documented, I suggest this sentence for docs/PLUGIN.md §4, after the
GIT_CEILING_DIRECTORIESline: