Repository navigation
feat(repo): record public_at and guidance:rev history for leak checks - #359
antonwolfy wants to merge 9 commits into
Conversation
Settles the recording half of intel#290 ("repo compare: flag replay tasks a guidance revision may have seen"), as decided on the issue: committer date, not author date; the field is public_at; the per-file latest commit is uncapped and the newest-first list is capped. - repo tasks replay writes public_at into each task.json: the earliest createdAt of a gold thread's first comment, compared as times, as the threads file gives it. Threads the task leaves out do not count; with no timed gold thread the key is absent. - A guidance:rev:<rev> arm's meta.json arm_rev adds guidance_latest (the commit reachable from <rev> that last touched each guidance file at <rev>), guidance_commits (the commits touching a path the guidance globs match, newest first by committer date, at most 200) and guidance_commits_truncated, each commit with %aI and %cI dates. One git log per (git dir, revision, globs) in a process, in the new repos/guidance_history.py. The keys are records only: meta.json feeds neither the cell name, the execution id nor the batch's reuse and replace checks, so an existing batch resumes as before. repo compare is unchanged; its flags come in a later change. Signed-off-by: Anton Volkov <antonwolfy@gmail.com>
|
✅ OpenCodeReview: Review complete: 0 finding(s) across 8 selected item(s). Review details
|
There was a problem hiding this comment.
🟡 Changes recommended
Valid subjects or filenames containing the record delimiter can abort guidance revision runs, while history collection omits symlink-target and merge-resolution edits and permits duplicate full-history reads under concurrency.
4 open issues
What changed in this PR
4 new findings.
This PR records replay review times and guidance revision history for future leak checks under #290. It adds metadata without changing repo compare.
Changes:
- Records optional
public_atfrom the earliest valid gold-thread timestamp. - Adds per-file latest commits and capped guidance history to
guidance:rev:metadata. - Documents the new fields and adds timestamp, history, and caching tests.
| File | Description |
|---|---|
dokimi/tests/test_repo_replay.py |
Tests timestamp ordering, excluded threads, and missing times. |
dokimi/tests/test_repo_guidance_history.py |
Tests history dates, ordering, caps, revision scope, and caching. |
dokimi/tests/test_repo_arms.py |
Allows additional revision metadata while preserving existing assertions. |
dokimi/dokimi/repos/workspace.py |
Attaches guidance history to revision-arm metadata. |
dokimi/dokimi/repos/replay/build.py |
Computes and writes optional public_at. |
dokimi/dokimi/repos/guidance_history.py |
Collects, filters, sorts, and caches guidance commits. |
dokimi/docs/repo-evaluation-contract.md |
Defines the new task and arm metadata fields. |
dokimi/changelog.d/20261101_120000_antonwolfy_replay-leak-record.md |
Announces metadata for future leak checks. |
Review effort: Balanced
Settles the review of intel#359 on the guidance history a guidance:rev: arm records in meta.json arm_rev (intel#290, first half). - The log was split on \x1e and \x1f, which a subject or a path may hold: one such commit anywhere in the history raised ValueError and aborted the arm's workspace preparation. It is now read with -z --raw, where every field ends with a NUL and each file is a ':'-led status record followed by its one path, so a path is never read as a header nor a header as a path. Git cuts a subject at a NUL; a header that does not start with a commit id is a PrepError. - lru_cache did not coalesce concurrent misses, so cells of one arm that repo run's thread pool prepared together each ran the full-history log. A lock per key now lets one thread read while the others wait for its answer; different keys still read in parallel. - A merge listed no files, so a guidance edit made while resolving a merge was in neither output. With --diff-merges=dense-combined a merge counts for a path its result changes against every parent (a conflict resolution, an edit made in the merge, or a clean merge of edits to different parts of one file); a merge that takes a file from one parent still does not. - The arm installs a guidance link as its target's bytes, but only paths the globs match were counted. The links now reach arm_rev_history and are in its cache key: a commit touching a link's target counts for the link's path. The contract (section 3) and the changelog fragment say what is counted. Signed-off-by: Anton Volkov <antonwolfy@gmail.com>
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #359 +/- ##
========================================
Coverage 94.29% 94.30%
========================================
Files 308 309 +1
Lines 39322 39441 +119
Branches 6651 6667 +16
========================================
+ Hits 37079 37193 +114
- Misses 1430 1434 +4
- Partials 813 814 +1
🚀 New features to boost your workflow:
|
Settles two review points on the guidance history of a guidance:rev: arm (intel#290, recording half). The repository's own config could change what the history's git log prints. log.showSignature=true put verification text outside the NUL-framed fields, so one signed commit anywhere in the history stopped the arm's workspace from being prepared. Checked against git 2.51, four local keys change the output: log.showSignature, log.showRoot=false (the root commit with no files), i18n.logOutputEncoding (a UTF-16 subject holds NULs) and diff.ignoreSubmodules=all (a commit that adds a gitlink is dropped). The log now passes --no-show-signature, --root, --encoding=UTF-8 and --ignore-submodules=none. A guidance link resolved through intermediate links (AGENTS.md -> docs/current -> old.md) recorded only its final file, so a commit that only retargets docs/current changed what the arm installs but was in neither history output. _guidance_at now also returns every path each link resolves through (split into _link_chain, which lowers its complexity below the baseline), and a commit that touches any of them counts for the link's path and is part of the cache key. The arm still installs the final file's bytes. Tests with real repositories: a signed commit under repository-local log.showSignature=true, each other pinned key leaving arm_rev as it is without it, and a commit that only retargets an intermediate link. Signed-off-by: Anton Volkov <antonwolfy@gmail.com>
|
Review rounds at
The
|
napetrov
left a comment
There was a problem hiding this comment.
Verdict: comment only, not approved. One blocking thread (a local shallow checkout gives a silently wrong arm history), two nits, one question. Head 1b75473, merge base 93a5062, lane L2. CI: 0 failing Actions jobs.
| # | Finding | Label | Provenance | Scope | Probe |
|---|---|---|---|---|---|
| 1 | local shallow checkout: arm history stops at the boundary commit | blocking | introduced | new surface | probes/lead_verify.py (a1) |
| 2 | a CR in a guidance path or subject drops the commit | nit | introduced | new surface | probes/lead_verify.py (b2) |
| 3 | date tests expect Z; git 2.43 prints +00:00 |
question | - | - | probes/lead_verify.py (b3) |
| 4 | _public_at docstring says Z counts as none |
nit | introduced | new surface | probes/d4.py |
Raised before: T1 to T7 (exai-bot, copilot) are fixed on head; no new thread repeats them. Finding 2 is near T1 (framing) but is a different root cause (decoding).
Unverified: a real repo run with a shallow source (precondition of 1); git 2.51 pins and the body's timings (not probed); eval_dir-only commits in guidance_commits (contract reads as intended).
Coverage: diff, contract doc and test assertions read; passes at level high with VERIFY on 3 candidates plus the docstring, probed on head (new code, no base); prior threads checked by reading. Not run: test suite, lenses. Not looked at: full-history git log cost on large repos.
| keep = ("PATH", "LANG", "TMPDIR", "SYSTEMROOT") | ||
| env = process.Env.only(keep, {"GIT_CONFIG_NOSYSTEM": "1", "GIT_CONFIG_GLOBAL": os.devnull, "HOME": os.devnull, | ||
| "GIT_TERMINAL_PROMPT": "0"}) | ||
| done = process.git("-C", str(git_dir), "log", "-z", "--raw", "--no-abbrev", "--no-renames", |
There was a problem hiding this comment.
nit: a CR in a guidance path or subject drops the commit from the history
process.git returns stdout through _decode (dokimi/dokimi/process.py), which turns each \r into \n: the path then matches no glob and the commit disappears without a report.
How to check: commit subject guidance\redit touching a\rb/AGENTS.md; arm_rev_history gives no guidance_latest and no guidance_commits (probes/lead_verify.py b2, head only: new code).
Expected: a\rb/AGENTS.md latest at that commit and the subject kept, as the PR body's framing table promises for the record delimiter.
Fix: read _git_log as bytes and split the fields before decoding; add a test with a CR in a path.
There was a problem hiding this comment.
Fixed in f6a9cb9. _git_log now returns stdout_bytes, and _parse_log splits on NUL as bytes, then decodes each field as UTF-8 with replace. That is the same decoding dokimi.process uses, so a path that is not UTF-8 gives the same string as the arm's own file listing. New test test_a_carriage_return_in_a_guidance_path_or_a_subject_is_kept: a commit with subject guidance\redit that touches a\rb/AGENTS.md is that path's guidance_latest, and the subject keeps its \r.
End to end, the arm's own file listing (_guidance_at's ls-tree, unchanged from main) still reads through process's text decoding. So prepare_workspace does not install a guidance file with a \r in its path, and that path is not in guidance_latest. The commit is listed in guidance_commits. That listing is on main and is left for a separate change.
Signed-off-by: Anton Volkov <antonwolfy@gmail.com>
Settles four review points on the guidance history a guidance:rev: arm records in meta.json arm_rev (intel#290, recording half). - A local source is used as it is, and in a shallow checkout git log takes the boundary commit for a root: with guidance v1 -> v2 -> a code commit cloned at --depth 1, the code commit was recorded as the last to touch AGENTS.md and the only guidance commit. The history now asks git rev-parse --is-shallow-repository first and raises PrepError for a shallow git dir, naming it and git fetch --unshallow. dokimi's own clones of a remote are bare and never shallow; a blob-less clone keeps working, as log --raw compares object ids only. - process.git's text output turns each \r into \n, so a guidance path or subject holding a \r lost it, and the commit matched no glob and was dropped without a report. The log is now split on NUL as bytes and each field decoded as UTF-8 on its own (with replacement, as dokimi.process decodes). - git 2.51 prints a UTC %aI / %cI as Z, git 2.43 as +00:00. A UTC date is now recorded as Z whichever git read it; other offsets stay as printed. - The _public_at docstring said a Z time counts as none. A time without an offset counts as none; GitHub's Z is UTC and counts, now pinned by a test. The contract (section 3) and the changelog fragment name the refusal and the date form. Signed-off-by: Anton Volkov <antonwolfy@gmail.com>
|
Review round at
The CI is green on |
vlad-perevezentsev
left a comment
There was a problem hiding this comment.
Review at f6a9cb9. The history read is right: NUL framing, the pinned flags, merges, link chains and the shallow refusal hold up in the tests and in my runs. public_at can come out later than the real time, and the history counts eval_dir fixtures. Both are in the line comments.
Agreed with @napetrov on the \r in a guidance path: the history keeps it now, and fixing _guidance_at's listing in a separate change is fine.
What I ran, at f6a9cb9:
make ci-local --offline: 0 failed, 19 passed, 13 skipped (network and Docker jobs).- The CI
external-catalog-smokejob (docker-oracleanddocker-probeon the pinned catalog task): both pass, every isolation probe closed. _read_guidance_commitson a local numpy clone (41,870 commits): 4.9 s and 19 MB ofgit logoutput for the first cell, 1.2 s of it in git.- A history with empty commits around a guidance commit: parsed right, and only the guidance commit is listed.
Checked, no issues
_link_chainsplit: same refusals as before (out of the tree, intoeval_dir, a cycle, a dangling link), and a plain file still gives no chain.guidance_latest: it is the newest commit by committer date that touched the path anywhere in the reachable history, as contract §3 says. An edit on a branch later dropped by-s ourscan therefore be named, but that errs toward reporting a leak, which is the safe side.- Concurrency and cache: one read per key; the lock map only grows by one small entry per key.
- Docs and changelog: contract §3 and §4.6 and the fragment describe the new fields and the shallow refusal. No scoring or
repo comparechange, so published numbers stay comparable.
Settles the review point on intel#359 that public_at was the minimum over only the gold threads with a usable time: a skipped thread posted first moved it later, so a guidance commit between the two times read as made before the answer was public. _public_at now returns None when any gold thread's first-comment createdAt is missing, not ISO 8601 or has no offset, and the build notes name those threads ("no public_at: ..."). The no-offset test now expects no public_at; a test with a missing createdAt is added. Contract 4.6 and the changelog fragment say when the key is absent. Signed-off-by: Anton Volkov <antonwolfy@gmail.com>
Settles the review point on intel#359 that the history counted paths under eval_dir, which the arm never installs: with **/AGENTS.md, a commit that only edited a task fixture's AGENTS.md was listed in guidance_commits and took a place under the cap. arm_rev_history takes eval_dir (manifest.run_paths()[0], as _guidance_at uses), it is part of the cache and lock key, and _counts_for skips a path within it before the glob match. A link chain needs no filter: _link_chain refuses one that reaches into eval_dir. The contract (section 3) and the changelog fragment say so. Signed-off-by: Anton Volkov <antonwolfy@gmail.com>
Settles the review point on intel#359 that the first guidance:rev: cell reads and parses the whole history of the repository. The read is not narrowed with pathspecs here: git log -- <pathspec> turns on history simplification, and git's :(glob) matching is not match_glob's, so a pre-filter needs a proof that it keeps every commit the matcher would. The Python side was mostly match_glob, which builds its pattern on every call; it now runs once per distinct path of a read. On a generated history of 42,000 commits (5-6 paths each), git log took about 1.0 s (28.8 MB) and the parse and match 0.92 s, now 0.12 s, with the same commits. The module docstring states the cost. Signed-off-by: Anton Volkov <antonwolfy@gmail.com>
Settles the review nit on intel#359 that guidance_history built again the isolated git environment workspace._git_env defines, and repeated base_opener's shallow check. source.isolated_git_env() is the one policy (PATH, LANG, TMPDIR and SYSTEMROOT kept; no system or global config, HOME at devnull, no prompt): workspace._git_env adds the fixed author and date to it, and the history's reads use it. source.is_shallow(git_dir, env) is the one check, called by base_opener and by the history's refusal. The history passes its own environment: source's default inherits the caller's, and a GIT_DIR there wins over -C, so the check would answer for another repository than the log after it reads. No behaviour change: the existing tests are unchanged, and a new one pins that a git dir git cannot read is still a PrepError, not source's SourceError. Signed-off-by: Anton Volkov <antonwolfy@gmail.com>
|
Round for @vlad-perevezentsev's review, at 3f2895a (CI green):
The |
vlad-perevezentsev
left a comment
There was a problem hiding this comment.
LGTM
I checked 3f2895a: each point from my last review is fixed, and the new commits change nothing else. The \r in _guidance_at's listing stays for the separate change agreed in its thread.
Merge: safe now: it merges cleanly into current main, 2 commits behind, and its tests pass on the merge (main changed growth_baseline.json). Once it is in, #338 and #333 conflict with it in workspace.py and need a rebase.
What I ran, at 3f2895a:
make ci-local --offline: 0 failed, 19 passed, 13 skipped (network and Docker jobs).- The PR's tests (
test_repo_guidance_history.py,test_repo_replay.py,test_repo_arms.py,test_growth_ratchet.py) on a merge with currentmain: 169 passed. _read_guidance_commitson a local numpy clone (41,870 commits): 1.2 s ofgit log, then 1.5 s to parse and match, down from 3.7 s.- Mutations: skipping a gold thread with an unusable time again fails three
public_attests; dropping theeval_dirskip fails the new fixture test.
Checked, no issues
public_at(3c1f93d): absent, with a build note naming the threads, when any gold time is missing, not ISO 8601 or has no offset. Contract §4.6 and the fragment say so.eval_dir(06f64a6): left out of the history as_guidance_atleaves it out of the files. It is part of the cache and lock key.- Shared git env (3f2895a):
workspace._git_envand the history both build onsource.isolated_git_env. The shallow check runs in that env, so a caller'sGIT_DIRcannot make it answer for another repository.base_openerkeeps its old env. - Pathspecs: your reason not to narrow the log yet holds. It would need
--full-historyand a proof that:(glob)keeps a superset ofmatch_glob.


Summary
First half of #290, "repo compare: flag replay tasks a guidance revision may have seen" (issue, decisions). A
guidance:rev:<sha>arm runs a later revision of a repository's guidance againstreview_replaytasks built from earlier PRs. If that guidance was edited after a task's review, the edit can encode the review. The batch has no way to show this today. Onmain, a replaytask.jsonholdssource_pr,source_url,reviewed_commit,merge_commitandleft_out_files, and no time. Aguidance:rev:cell'smeta.jsonarm_revis exactly{"rev", "sha", "removed"}, whichtests/test_repo_arms.pyasserted onmain. This PR records both facts. The flags inrepo compareare the second PR, as the issue decided;repo comparedoes not change here.The second commit settles this PR's first review: four points from five comments. The third commit settles two more comments.
f6a9cb9settles four more, and3c1f93d,06f64a6,bcb96c1and3f2895asettle the latest four, one commit each. At the commit before each fix:<rev>\x1e(e.g. subjectguidance\x1eedit)ValueError: not enough values to unpackfrom workspace preparation; by the code, not run: it is not one of the errorsrepo runrecords for a cell (repos/runner.py), so it ends the runrepo run's thread pool prepares togethergit logAGENTS.mdAGENTS.md'sguidance_latestis an older parent commitAGENTS.md'sguidance_latestAGENTS.md -> docs/GUIDE.md, a later commit edits onlydocs/GUIDE.mdAGENTS.md'sguidance_latestis the commit that made the linkAGENTS.md'sguidance_latestlog.showSignature=trueand a signed commit anywhere in the historyPrepError: git log: a commit starts with 'No signature\n<sha>', not a commit id; the cell is not preparedlog.showRoot=false,i18n.logOutputEncoding=UTF-16ordiff.ignoreSubmodules=allPrepErroron the UTF-16 output; a commit that adds a gitlink at a guidance path is missingarm_revas without the keyAGENTS.md -> docs/current -> old.md, a later commit only retargetsdocs/currentto an existingreview-derived.mdAGENTS.md'sguidance_latestis the commit that addedreview-derived.mdAGENTS.md'sguidance_latest2026-10-02T12:00:00Zand one at2026-10-01T00:00:00(no offset)f6a9cb9:public_atis2026-10-02T12:00:00Z, though the thread without an offset may have come firstpublic_at; the build notesno public_at: no first-comment createdAt with an offset on pr7-t2**/AGENTS.md, a commit that only edits.agent-eval/tasks/x/AGENTS.md(a task fixture)f6a9cb9: listed inguidance_commits, though the arm installs nothing undereval_dirChanges
repo tasks replaywritespublic_atinto eachtask.json(repos/replay/build.py::_public_at). It is the earliestcreatedAtof a gold thread's first comment, kept as the threads file gives it. Times are compared as datetimes, not strings:2026-10-02T20:00:00+09:00is earlier than2026-10-02T12:00:00Z. A thread the task leaves out does not count. GitHub'sZis UTC and counts. When any gold thread's time is missing, not ISO 8601 or has no offset, the earliest time is unknown: the key is absent, and the build notes name those threads.New
repos/guidance_history.py: for aguidance:rev:<rev>arm,meta.jsonarm_revnow also holds:guidance_latest: for each guidance file the arm wrote, the commit reachable from<rev>that last touched it, or for a link it or any path its resolution goes through (each intermediate link and the file it resolves to), by committer date. Not capped.guidance_commits: the commits reachable from<rev>that touch a path the guidance globs match outsideeval_dir, or a path a guidance link resolves through, newest first by committer date, each with itssubject. At mostGUIDANCE_COMMITS_CAP(200).guidance_commits_truncated:truewhen the cap cut the list.Each commit carries
sha,authored(%aI) andcommitted(%cI), a UTC date writtenZwhichever form the git that read it prints (git 2.43 prints+00:00, 2.51Z). The history comes from onegit log -z --raw --no-abbrev --no-renames --diff-merges=dense-combined --no-show-signature --root --encoding=UTF-8 --ignore-submodules=noneover<rev>, filtered withmatch_glob. It is memoized per (git dir, revision, globs,eval_dir, links), so every cell of an arm reads it once. The output is split on NUL as bytes and each field decoded on its own, so a\rin a subject or path is kept. A shallow git dir is refused with aPrepErrornaminggit fetch --unshallow: its history stops at the boundary commit and would name a later commit as the last to touch a guidance file.Review of this PR (second commit):
_parse_logreads the-z --rawoutput. Every field ends with a NUL. The four header fields are taken by position, and each file is a:-led status record followed by exactly one path. A path that looks like a commit id or a status record (:M) stays a path. Git cuts a subject at a NUL (git commitrefuses one), and a header field that is not a commit id is aPrepError, not aValueError._guidance_commitsholds a lock per key around thelru_cached read, so one thread reads and the others take its result. Different keys still read in parallel.--diff-merges=dense-combined, a merge counts for a path its result changes against every parent: a conflict resolution, or an edit made in the merge. A merge that takes a file as one parent has it is not listed; the commits it merged are. With git 2.51,--rawalso lists a clean merge that joins its parents' edits to different parts of one file, because the result differs from each parent. A test pins this, and the docstring and contract say so._apply_revpasses_guidance_at's links toarm_rev_history. A commit that touches a link's target counts for the link's path, and the links are part of the cache key.Review of this PR (third commit):
_git_logturns off the system and global config, but a local source's own git dir keeps its config. With git 2.51, four of the keys a repository can set change this command's output, and each is now overridden by a flag (guidance_history._PINNED):log.showSignature(--no-show-signature),log.showRoot=false(--root),i18n.logOutputEncoding(--encoding=UTF-8) anddiff.ignoreSubmodules=all(--ignore-submodules=none). Set one at a time, about 40 otherlog.*,diff.*,color.*,core.*andformat.*keys left the output byte for byte the same. Among them arecolor.ui=always,diff.relative,log.decorate,log.followandcore.abbrev. The keys that the explicit--no-renames,--diff-mergesand--formatalready override (diff.renames,log.diffMerges,format.pretty) need nothing._guidance_atnow also returns, for each guidance link, every path its resolution goes through: the intermediate links, then the final file. The walk moved into a new_link_chain, which lowers_guidance_at's complexity from 11 to 5 and removes its entry fromscripts/growth_baseline.json._apply_revpasses those chains toarm_rev_history. A commit that touches any path of a chain counts for the link's path, and the chains are part of the cache key._apply_revstill installs the final file's bytes.Review of this PR (
3c1f93d,06f64a6,bcb96c1,3f2895a):public_atfrom every gold thread (comment,3c1f93d)._public_atreturnsNonewhen any gold thread's first-commentcreatedAtis missing, not ISO 8601 or has no offset, and appends a build note naming those threads. The minimum of the others could be later than the real time, and a guidance commit between the two would then read as made before the answer was public. Contract §4.6, the docstring and the changelog fragment say when the key is absent.eval_dirleft out (comment,06f64a6).arm_rev_historytakeseval_dir(manifest.run_paths()[0], as_guidance_atuses it), and it is part of the cache and lock key._counts_forskips a path within it before the glob match. A link chain needs no filter:_link_chainrefuses one that reaches intoeval_dir. Contract §3 and the changelog fragment say so.bcb96c1). The log is not narrowed with pathspecs:git log -- <pathspec>turns on history simplification, so it would need--full-historyand thedense-combinedmerge behaviour proved again. Git's:(glob)is also notmatch_glob, so a pre-filter needs a proof that it keeps every commit the matcher keeps. The Python side was mostlymatch_glob, which builds its pattern on every call. It now runs once per distinct path of a read. The module docstring states the cost.3f2895a).source.isolated_git_env()is the one policy:PATH,LANG,TMPDIRandSYSTEMROOTkept, no system or global config,HOMEat devnull, no prompt.workspace._git_envadds the fixed author and date to it, and the history's reads use it.source.is_shallow(git_dir, env)is the one check, used bybase_openerand by the history's refusal. The history passes its own environment, because source's default inherits the caller's, and aGIT_DIRthere wins over-C(git 2.51: a shallow clone readsfalsewithGIT_DIRset to a full one).base_openerkeeps the caller's environment. No behaviour change.repos/workspace.py::_apply_rev: one call added wherearm_revis built, with no new branch.guidance:none|raw|bridgeandpatch:are unchanged.Contract: §3 describes the three
arm_revkeys, link chains,eval_dirand which merges count. §4.6 addspublic_atto the list of fields the replaytask.jsonadds for the reader, and says when it is absent.Changelog fragment (Added).
Risk
The output of
repo tasks replayandguidance:rev:cells gains keys; nothing is taken away. A replay task with a gold thread that has no usable time gets nopublic_at, rather than the earliest of the other threads' times. One input is now refused: aguidance:rev:cell whose local source is a shallow clone fails its preparation (dokimi's own clones of a remote are bare with no--depth, so they are never shallow). The new keys do not change a cell's identity. The cell name comes from task, arm, model and rep. The execution id comes from the agent options. The batch's reuse and replace checks (batch_ledger.reused_result,check_batch_executions) readresult.json. None of them readsmeta.jsonarm_rev. So rerunning an existing batch neither refuses nor reruns its finished cells; only cells that run again get the new keys. Aguidance:rev:cell now runs one moregit logover the revision's whole history, once per process. Its--rawoutput carries two object ids per file, so it is two to three times the--name-onlyoutput, and--diff-merges=dense-combinedalso diffs every merge against its parents. Measured on bare clones,--name-onlyagainst the second commit's form: this repository (485 commits, 5 merges) 0.4 MB against 1.2 MB, both under 0.1 s; dpnp (3,127 commits, 185 merges) 1.0 MB against 2.7 MB, both 0.1 s; numpy (42,470 commits, 10,335 merges) 9.3 MB in 0.67 s against 19.3 MB in 0.88 s. The first commit's text also cited 3.7 s (51 MB) for--name-onlyover a 9,384-commit repository; the--rawform was not run there. The third commit's four flags only bring back git's default output, so these sizes still hold for a repository that sets none of those keys; they were not measured again. The Python parse of the log is measured on a generated history of 42,000 commits with 5-6 paths each:git log1.0 s (28.8 MB); parse and match 0.92 s beforebcb96c1and 0.12 s after, with the same 210 commits listed. It still grows with the history, and the other cells of the arm wait for it. The time limit is 1800 s, the same as workspace preparation's other git calls. A merge that cleanly joins two edits to one guidance file is listed although it adds no text of its own, so it can make a file'sguidance_latestlater than the last commit that wrote text. The same goes for a link's chain: a commit that touches an intermediate link or the final file counts for the link, even when the bytes the arm installs did not change.Testing
On a Linux review host, at
3f2895a(onmain41b3d8d):make ci-local: 30 passed, 0 failed, 7 skipped. Skipped: external-catalog-smoke (needs docker), test-latest, docs deploy and pip-audit report (job conditions false on a PR), cli-surface "Package the result", conda-forge-deps (no micromamba), semgrep secrets. 12570 tests passed, 5 skipped, 6 xfailed; changed-line coverage 98% (151 lines; the two missed are thegit logtimeout and failure raises).Install gitleaksandScan commitsran: no leaks found.At
1b75473(onmain93a5062): 30 passed, 0 failed, 7 skipped. Skipped: external-catalog-smoke (needs docker), test-latest, docs deploy and pip-audit report (job conditions false on a PR), cli-surface "Package the result", conda-forge-deps (no micromamba), semgrep secrets. 12022 tests passed, 59 skipped, 6 xfailed; changed-line coverage 98% (121 lines; the two missed are thegit logtimeout and failure raises).At the second commit,
d35b581: 30 passed, 0 failed, 7 skipped; 12017 tests passed, 59 skipped, 6 xfailed; changed-line coverage 97% (96 lines). At the first commit,c21791a: 28 passed, 2 failed, 7 skipped. Both failures were the host:Install gitleaksgot HTTP 504 four times, soScan commitshad no binary to run (exit 127). The same scan, run separately with CI's pinned gitleaks 8.30.1 (checksum checked) over the commit: no leaks found. Both steps pass atd35b581.New tests:
tests/test_repo_guidance_history.pybuilds a toy history with per-commitGIT_AUTHOR_DATE/GIT_COMMITTER_DATE. It includes a commit authored two months before it was committed, and a commit whose committer date is older than its parent's. The tests cover:guidance:rawandpatch:cells;git logfor two cells of the arm;git logfor four threads asking for one key at once;\x1eand\x1f, a guidance path holding\x1e, a file named like a commit id and a file named:M;PrepError;AGENTS.md, a plain merge of a branch with a guidance commit, and a clean merge of edits to different parts ofAGENTS.md;AGENTS.md -> docs/GUIDE.md;ssh-keygenin the test's temporary directory (gpg.format=ssh), under repository-locallog.showSignature=true: it is read, listed andAGENTS.md'sguidance_latest. The test skips only withoutssh-keygen; it ran here;log.showRoot=false,i18n.logOutputEncoding=UTF-16anddiff.ignoreSubmodules=all, one at a time, over a history with a root commit that addsAGENTS.md, a non-ASCII subject and a commit that adds a gitlink atvendor/AGENTS.md: each gives the samearm_revas without the key;AGENTS.md -> docs/current -> old.md, then a commit that addsdocs/review-derived.md, then one that only retargetsdocs/currentto it: the retargeting commit isAGENTS.md'sguidance_latestand first inguidance_commits, and the workspace'sAGENTS.mdholdsreview-derived.md's text;06f64a6) with**/AGENTS.md, a commit that only edits.agent-eval/tasks/x/AGENTS.md: not inguidance_commitsorguidance_latest, while the rootAGENTS.mdcommit after it is;3f2895a) a directory git cannot read: still aPrepError(git rev-parse failed) out of the shared shallow check, notsource'sSourceError.In
tests/test_repo_replay.py, the tests coverpublic_atas the earliest gold thread's time, a left-out thread with an earlier time not counting, aZand+09:00pair compared as instants, and (3c1f93d) no key, with the note naming the threads, when a gold thread's time has no offset, when one has nocreatedAt, and when none has a time.Mutants: 15 breaks in a copy of the tree, 17 test runs, and every run failed. The breaks:
public_at;public_at: null;authoredholding the committer date;>=for truncated;git log --allinstead of<rev>;arm_rev;guidance:raw.Mutants for the second commit: 6 breaks, 7 test runs, and every run failed on its assertion. The breaks:
\x1e/\x1fsplit and--name-onlylog (ValueError);--diff-merges=dense-combineddropped (the resolution merge and the clean merge are not listed);--diff-merges=first-parentin its place (the plain merge is listed);{}passed in place of the links;The second commit's new test file against the first commit's code: 6 of its tests fail and 8 pass. The plain-merge test passes there, because that code listed no merge at all. The concurrency test and the commit-id test fail there on the new
linksargument and the new_parse_log, so the lock and commit-id mutants above are what pin them.Mutants for the third commit: 5 breaks, 5 test runs, and every run failed. The breaks:
--no-show-signaturedropped (the signed-commit test:PrepError: git log: a commit starts with 'No signature\n<sha>');--rootdropped (thelog.showRoot=falsecase);--encoding=UTF-8dropped (the UTF-16 case: aPrepError);--ignore-submodules=nonedropped (thediff.ignoreSubmodules=allcase);AGENTS.md'sguidance_latestis the commit that addedreview-derived.md).The third commit's test file against the second commit's code: its 5 new tests fail and the other 14 pass.
Mutants for
3c1f93d..3f2895a: 4 breaks, 4 test runs, and every run failed:continue): the three no-public_attests fail, and the all-usable test passes;_is_within(path, eval_dir)skip dropped: theeval_dirfixture test fails (the fixture commit is listed);3f2895a): the shallow test fails (DID NOT RAISE PrepError);source'sSourceErrorlet out of the shared shallow check: the new not-a-repository test fails (SourceError: git rev-parse failed: fatal: not a git repository).3f2895achanges no existing test;bcb96c1changes no output (the same 210 commits on the generated history, compared as JSON before and after).Not run: the new tests against
main's code. This is a new feature, and onmainthey fail at import (dokimi.repos.guidance_historydoes not exist).Merged together at
f6a9cb9with the branches for review_replay: optional judged recall over the credited hits #291 (judged recall,8e05118), novelty: spread the atom cap across the artifact instead of its opening #293 (novelty cap spread,82d576a) ontomain41b3d8d: the full suite (12641 passed, 5 skipped, 6 xfailed),mypy(318 files) andruff checkpass.make ci-localis green atf6a9cb9(30 passed, 0 failed).Revert
Revert the commits. The
public_atandarm_revkeys already written into task directories and batches stay. Nothing reads them yet.Checklist
pytestpassesruff,mypy, and schema checks passAGENTS.md.github/.licenserc.yaml)