Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
83863a9
feat(repo): guidance:overlay arm and overlay reach check
napetrov Nov 1, 2026
ee38237
Address review: overlay arm link walk, kept overlays, setup-patch files
napetrov Nov 1, 2026
a52e675
Address review: leave a shared-target load unattributed in the overla…
napetrov Nov 1, 2026
f442d5d
Address review: an overlay cell is identified by its overlay
napetrov Nov 1, 2026
7d4fd3c
Address review: document ambiguous overlays in the reach schema
napetrov Nov 1, 2026
4f33efc
Address review: pin no load on an ambiguous overlay link
napetrov Nov 1, 2026
3d7334b
fix(overlay): a native name in a link group pins nothing; readlink ta…
napetrov Nov 1, 2026
b880a3e
fix(overlay): pin both names of an overlay link group; say when a mis…
napetrov Nov 1, 2026
d94e397
Merge branch 'main' of https://github.com/intel/dokimi into pr338-lan…
napetrov Nov 1, 2026
e761468
fix(overlay): drop the unused own parameter of _pinned_names
napetrov Nov 1, 2026
6232e4d
fix(overlay): refuse to unlink an overlay under a path a setup patch …
napetrov Nov 1, 2026
e9a486f
fix(overlay): refuse a kept path under a removed link; unlink a file …
napetrov Nov 1, 2026
ba6dbfd
fix(overlay): load builtin profiles by path, not name
antonwolfy Nov 1, 2026
5b80dbd
Merge remote-tracking branch 'intel/main' into pr338-land1031
napetrov Nov 1, 2026
ae4e427
Merge remote-tracking branch 'intel/main' into pr338-land1031
napetrov Nov 2, 2026
7e46426
fix(overlay): unlink under a removed alias first, attribute linked lo…
napetrov Nov 2, 2026
d70dd57
fix(overlay): unlink removed overlays by canonical path; native names…
napetrov Nov 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions dokimi/changelog.d/20261031_120000_napetrov_ce-overlay.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<!--
Copyright (C) 2026 Intel Corporation
SPDX-License-Identifier: Apache-2.0
-->

### Added

- **`guidance:overlay` arm for per-agent AGENTS.md overlays** — `dokimi repo run --arm guidance:overlay` gives a cell the base `AGENTS.md` and the `AGENTS.<agent>.md` of the harness under test, and removes the overlays of other agents (agentsmd/agents.md#185). A harness profile names its agent with the new `overlay` key (`claude` for Claude Code, `codex`, `copilot`). The arm refuses to run without that key, or when the base has no overlay for the agent. `dokimi repo reach` now probes every `AGENTS.<name>.md`, and reports a foreign overlay that the harness loaded as a `finding:` line and in the `overlays` block of `guidance_reach.v1`. Its `own_loaded` field says whether the harness loads its own overlay at all.
7 changes: 5 additions & 2 deletions dokimi/docs/preparing-a-repository.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ SPDX-License-Identifier: Apache-2.0
# Preparing a repository for coding agents

> **Status: recommendations.** §1–§7 apply today. The measurement loop in §8 runs
> today for guidance arms (`guidance:none`, `guidance:raw`, `guidance:bridge`).
> today for guidance arms (`guidance:none`, `guidance:raw`, `guidance:bridge`,
> `guidance:overlay`).
> `guidance:bridge` measures the `CLAUDE.md` import (§7). The `skill:` and
> `skill-catalog:` arms, which install skills, run today too
> ([skill arms](benchmarking-and-comparison.md#skill-arms-in-repository-mode)). Arms
Expand Down Expand Up @@ -251,7 +252,9 @@ Treat each item above as a hypothesis for your repository.
2. Run the baseline and the arm with `dokimi repo run`, on the current models you
use. Today an arm can remove or keep the guidance (`guidance:none`,
`guidance:raw`), or inject a file (`guidance:bridge`, which writes a `CLAUDE.md`
containing `@AGENTS.md` next to each `AGENTS.md` for profiles with a bridge key).
containing `@AGENTS.md` next to each `AGENTS.md` for profiles with a bridge key), or
keep only the base and the harness's own `AGENTS.<agent>.md` (`guidance:overlay`, for
profiles with an `overlay` key).
Testing an added tool, other than a skill (`skill:`, `skill-catalog:`), needs planned arms. Today, test one added guidance line
with two task directories that differ only in a `setup_patches` entry (same
prompt, same graders, distinct ids), or in their `base` commit (see
Expand Down
26 changes: 25 additions & 1 deletion dokimi/docs/repo-evaluation-contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ belongs to the repository's grader and is passed through unread, except `grader`
3. `git init` and commit (no history, so fixes cannot leak)
4. apply `setup_patches`
5. apply the arm: `guidance:none`, `guidance:raw`, `guidance:bridge`,
`guidance:rev:<rev>` or `patch:<file>`
`guidance:overlay`, `guidance:rev:<rev>` or `patch:<file>`
6. amend the commit
7. apply `review_patch` with `git am` under a neutral author
8. expire the reflog and prune unreachable objects: the
Expand All @@ -341,6 +341,15 @@ content:
depends on its version: Claude Code 2.1.286 loads `AGENTS.md` itself, but
only when no `CLAUDE.md` is in its start-up chain, so a repository-side
bridge arm would mean different things on different versions.
- `overlay` exists only for a profile with an `overlay` key (the agent name, for
Claude Code `claude`). It is the per-agent overlay of agentsmd/agents.md#185:
`AGENTS.<agent>.md` beside an `AGENTS.md`, at any depth. The workspace keeps
the base `AGENTS.md` and the `AGENTS.<overlay>.md` files and removes every
other `AGENTS.<name>.md` (not the harness's own names such as
`AGENTS.override.md`, nor files under `.claude`). The files are recognised by
name, not by `guidance.globs`. A base with no `AGENTS.<overlay>.md` is a
preparation error, so a cell cannot be the base alone by accident. The arm
does not make a harness load its overlay: the reach probe reports that.
- `rev:<rev>` keeps the base's code and replaces its guidance with that of
another revision of the source: the base's matched files are deleted and the
files the globs match at `<rev>` (outside `eval_dir`) are written. It answers
Expand Down Expand Up @@ -892,6 +901,7 @@ guidance: # DECLARED; verified by the reach probe, ne
resolution: ancestors-of-cwd + lazy-on-subtree-access
imports: "@path"
bridge: {for: AGENTS.md, write: CLAUDE.md, content: "@AGENTS.md"}
overlay: claude # the AGENTS.claude.md the guidance:overlay arm keeps (agentsmd/agents.md#185)
args_if_present: # argv added after the "{tools}" element when the workspace file exists and the tool mode is `all`
.mcp.json: ["--mcp-config", ".mcp.json"]
```
Expand Down Expand Up @@ -1073,6 +1083,20 @@ per scenario).
- `unknown`: tokens the model reported that were never seeded;
- `prose`: words besides tokens. A high count usually means an explanation
or a refusal, so read that trace before trusting its `not_loaded` cells.
- Overlays (`overlays` in `guidance_reach.v1`, the design note of §3): every
`AGENTS.<name>.md` is probed, even where the globs miss it, so a foreign overlay
is tested rather than assumed absent. The expectation is that foreign overlays are
`not_loaded`. `cross_loaded` lists each foreign overlay some scenario reported
as `loaded`, `truncated` or `fetched`; the text report prints it as a `finding:`
line. `own_loaded` lists the harness's own overlays that loaded in some scenario;
when none did, the text report says so, but only if some own-overlay scenario
read as `not_loaded` and none errored; otherwise it says loading could not be
determined. `own` is the profile's `overlay`.
`ambiguous` lists the names of a load that could have been read under another
name: a file reached through an alias group that holds a native guidance name
or two overlay names. Those loads are neither own nor cross loads. The text
Comment on lines +1095 to +1097

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

documentation · low
Nit: this says a native guidance name must belong to the alias group, but _pinned_names marks a linked load ambiguous whenever any harness-native file is present anywhere in the probe. For example, with separate CLAUDE.md and AGENTS.codex.md -> docs/shared.md, a loaded docs/shared.md is ambiguous even though its alias group has no native name. Document the probe-wide condition so readers can correctly interpret missing cross_loaded findings; the implementation's conservative rule can remain unchanged.

Suggestion:

Suggested change
`ambiguous` lists the names of a load that could have been read under another
name: a file reached through an alias group that holds a native guidance name
or two overlay names. Those loads are neither own nor cross loads. The text
`ambiguous` lists the names of a load that could have been read under another
name: a file with aliases when any harness-native guidance file is present in
the probe (it could import the target), or when the alias group holds two overlay
names. Those loads are neither own nor cross loads. The text

report prints no own-overlay note for them, and a foreign target with an own
alias is listed there too, not reported as a cross load.
- If payload capture is available, a `payload` field records whether each
token was in the request bytes. This settles cells where the model
under-reports, which is how a small model produces false `truncated` cells.
Expand Down
3 changes: 2 additions & 1 deletion dokimi/dokimi/commands/repo_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,8 @@ def register(repo_sub, positive_int, source_help: str, cache_help: str) -> None:
"result.json per cell, already finished cells skipped")
_common(run_p, source_help, cache_help, positive_int)
run_p.add_argument("--arm", action="append", default=None, required=True,
help="guidance:none | guidance:raw | guidance:bridge | guidance:rev:REV (the base with "
help="guidance:none | guidance:raw | guidance:bridge | guidance:overlay (the base with the AGENTS.<agent>.md "
"of the harness only) | guidance:rev:REV (the base with "
"the guidance files of revision REV of the source; the arm is named by REV's full SHA, "
"resolved once per batch) | patch:FILE (a unified diff on this host, applied with git "
"apply on top of guidance:raw; named patch:<file name>@<sha256 prefix>, path and "
Expand Down
13 changes: 9 additions & 4 deletions dokimi/dokimi/repos/batch_ledger.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,11 @@ def harness_sha256(path: Path) -> str:


def execution_id(profile: HarnessProfile, tools: str, delegation: str = "native", harness_sha256: str | None = None,
harness_version: str | None = None, companion_sha256: dict[str, str | None] | None = None) -> str:
harness_version: str | None = None, companion_sha256: dict[str, str | None] | None = None,
overlay: str | None = None) -> str:
"""Short hash of how the agent is started: the profile's ``invoke`` template with its tool arguments,
environment, config, prompt suffix, egress, bridge, trace, answer format and companions.
environment, config, prompt suffix, egress, bridge, trace, answer format and companions, and for a cell of
the overlay arm the overlay it keeps (``overlay``; the other cells carry none, so their ids do not change).

Two runs with the same id ran the same command line up to the per-cell values (prompt, model,
caps); ``repo run`` reuses a finished cell only when its id matches. Includes the harness
Expand All @@ -95,14 +97,17 @@ def execution_id(profile: HarnessProfile, tools: str, delegation: str = "native"
if profile.bin_companions:
key["bin_companions"] = (list(profile.bin_companions) if companion_sha256 is None
else {n: companion_sha256.get(n) for n in profile.bin_companions})
if overlay is not None:
key["overlay"] = overlay
return hashlib.sha256(json.dumps(key, sort_keys=True).encode("utf-8")).hexdigest()[:12]


def execution_record(profile: HarnessProfile, tools: str, delegation: str, harness_sha256: str,
harness_version: str | None,
companion_sha256: dict[str, str | None] | None = None) -> dict[str, str]:
companion_sha256: dict[str, str | None] | None = None,
overlay: str | None = None) -> dict[str, str]:
"""How a cell's agent was started, as ``meta.json`` and ``result.json`` record it."""
return {"id": execution_id(profile, tools, delegation, harness_sha256, harness_version, companion_sha256),
return {"id": execution_id(profile, tools, delegation, harness_sha256, harness_version, companion_sha256, overlay),
"tools": tools, "delegation": profile.effective_delegation(tools, delegation)}


Expand Down
9 changes: 9 additions & 0 deletions dokimi/dokimi/repos/harness_profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ class HarnessProfile:
endpoint_env: str | None = None
guidance: dict[str, Any] = field(default_factory=dict)
bridge: dict[str, str] | None = None
overlay: str | None = None # the agent name of its AGENTS.<overlay>.md (guidance:overlay, repos/overlays.py)
prompt_suffix: str = ""
config_files: dict[str, str] = field(default_factory=dict)
bin_companions: tuple[str, ...] = () # files beside the executable that it needs; mounted next to it
Expand Down Expand Up @@ -213,6 +214,12 @@ def _check_tools_element(raw: dict[str, Any], path: str) -> None:
raise ProfileError(f"{path}: invoke must have exactly one {TOOLS_ELEMENT} element")


def _check_overlay(raw: dict[str, Any], path: str) -> None:
overlay = raw.get("overlay")
if overlay is not None and not re.fullmatch(r"[a-z0-9][a-z0-9-]*", str(overlay)): # fullmatch: `$` takes "\n"
raise ProfileError(f"{path}: overlay {overlay!r} is not an agent name (lower case, digits, dashes)")


def parse_profile(raw: Any, path: str = "<profile>") -> HarnessProfile:
errors = schema_errors("harness_profile.v1", raw)
if errors:
Expand All @@ -237,6 +244,7 @@ def parse_profile(raw: Any, path: str = "<profile>") -> HarnessProfile:
raise ProfileError(f"{path}: invoke never passes {{prompt}} (set native_prompt for a harness that "
"uses its own)")
_check_tools_element(raw, path)
_check_overlay(raw, path)
return HarnessProfile(
name=raw["name"],
bin=raw["bin"],
Expand All @@ -251,6 +259,7 @@ def parse_profile(raw: Any, path: str = "<profile>") -> HarnessProfile:
endpoint_env=raw.get("endpoint_env"),
guidance=dict(raw.get("guidance") or {}),
bridge=raw.get("bridge"),
overlay=raw.get("overlay"),
prompt_suffix=raw.get("prompt_suffix") or "",
config_files=dict(raw.get("config_files") or {}),
bin_companions=tuple(raw.get("bin_companions") or ()),
Expand Down
41 changes: 41 additions & 0 deletions dokimi/dokimi/repos/overlays.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Copyright (C) 2026 Intel Corporation
# SPDX-License-Identifier: Apache-2.0

"""Per-agent overlays of AGENTS.md (agentsmd/agents.md#185): ``AGENTS.<agent>.md`` beside an ``AGENTS.md``.

An overlay is named by the agent it is for, in lower case (``AGENTS.claude.md``). A harness profile names
its overlay with its ``overlay`` key. The ``guidance:overlay`` arm keeps the overlay of that name and removes
the others, and the reach probe reports any foreign overlay a harness loaded anyway.
"""

from __future__ import annotations

import functools
import re
from pathlib import PurePosixPath

from .harness_profile import PROFILE_DIR, builtin_profiles, load_profile

OVERLAY_RE = re.compile(r"AGENTS\.([A-Za-z0-9][A-Za-z0-9-]*)\.md")


@functools.lru_cache(maxsize=1)
def native_names() -> frozenset[str]:
"""Guidance names a builtin harness loads by its own convention (``AGENTS.override.md``): never an overlay."""
return frozenset(
n
for name in builtin_profiles()
for n in load_profile(str(PROFILE_DIR / f"{name}.yaml")).guidance_names
)


def overlay_of(path: str) -> str | None:
"""The agent the guidance file ``path`` is an overlay for, ``None`` when it is not one.

A file under ``.claude`` is skill content, not guidance, so it is never an overlay.
"""
parts = PurePosixPath(path).parts
if ".claude" in parts or parts[-1] in native_names():
return None
match = OVERLAY_RE.fullmatch(parts[-1])
return match.group(1).lower() if match else None
1 change: 1 addition & 0 deletions dokimi/dokimi/repos/profiles/claude-code.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,4 @@ bridge:
for: AGENTS.md
write: CLAUDE.md
content: "@AGENTS.md"
overlay: claude # AGENTS.claude.md, the guidance:overlay arm (agentsmd/agents.md#185)
1 change: 1 addition & 0 deletions dokimi/dokimi/repos/profiles/codex-review.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,4 @@ guidance:
it opened AGENTS.md but reported only defects its P0-P3 rubric keeps;
convention rules moved it only when the guidance called a violation
merge-blocking (P1). It reports zero token usage in this mode.
overlay: codex # AGENTS.codex.md, the guidance:overlay arm (agentsmd/agents.md#185)
1 change: 1 addition & 0 deletions dokimi/dokimi/repos/profiles/codex.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,4 @@ guidance:
Measured on 0.160 with gpt-6-luna (oneDAL review task): root AGENTS.md
applied, subtree AGENTS.md applied only after the agent printed them with a
shell command; AGENTS.override.md hid the root AGENTS.md.
overlay: codex # AGENTS.codex.md, the guidance:overlay arm (agentsmd/agents.md#185)
1 change: 1 addition & 0 deletions dokimi/dokimi/repos/profiles/copilot-review.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,4 @@ guidance:
REVIEW.md included. Measured with /review: the same files reached it, and the
existing oneDAL guidance did not change its recall until the review rules sat in
the root AGENTS.md.
overlay: copilot # AGENTS.copilot.md, the guidance:overlay arm (agentsmd/agents.md#185)
1 change: 1 addition & 0 deletions dokimi/dokimi/repos/profiles/copilot.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,4 @@ guidance:
.github/instructions files are probed through the manifest's globs. The
hosted Copilot code review (on a pull request) reads a different set,
REVIEW.md included.
overlay: copilot # AGENTS.copilot.md, the guidance:overlay arm (agentsmd/agents.md#185)
Loading
Loading