The index discloses its root but never its commit, so an agent pinned to one worktree was served another's content and could not tell #182

Closed
opened 2026-09-06 09:53:02 +02:00 by buildagent · 3 comments
Member

Found by dogfooding during the 2026-09-06 triage session that closed 22 issues. It caused a real error inside that session and was caught only by accident.

Sibling of #181 (no reply field says the answering binary is older than the tree it indexed). Same family — the answer does not disclose what produced it. #181 is the binary axis; this is the tree axis. A reader debugging a wrong answer needs both, and they should be one block rather than two unrelated fields.

Measured — the misread, exactly as it happened

The triage lane created a read-only worktree pinned to origin/master at f6a878a:

git worktree add -f --detach /tmp/cosi-lane-triage origin/master

The code-index MCP server, meanwhile, indexes /home/master/code/rust/cosi-mcp, which another lane advanced to 45cf6e4 partway through the session.

A verification agent asked the index about the Windows disk pre-flight and was served:

  • a $minFreeGb = 40 floor
  • a .forgejo/scripts/windows_disk_report.ps1 file

Neither exists at f6a878a. Both are #179's fix, landed in 45cf6e4. The agent had been told to verify f6a878a and was reading 45cf6e4 with no indication of it.

It noticed only because a test it ran contradicted a file it had already read. Without that accident, the citations would have gone into a public issue comment as facts about a commit that does not contain them.

The tool's own disclosure is what makes this sharp. index_coverage on a triage path answers:

path_outside_known_roots
did_you_mean: ["primary=/home/master/code/rust/cosi-mcp"]

The root is disclosed. The commit is not. The reply is precise about which directory and silent about which version of it, and the second is the one that was wrong.

We got lucky on blast radius: git diff --stat f6a878a..45cf6e4 touches only CI, workflow and test files. Had a production file moved, every file:line citation in this session's issue comments would have been silently off.

Mechanism

project_overview, index_coverage and list_files all name the project root. Nothing anywhere names the indexed tree's HEAD or its dirty state.

This is not an oversight so much as an unasked question. The server instructions state, correctly and prominently, that "code-index indexes your WORKING TREE, not the last commit" — which is the product's whole point and must not change. But that makes "which commit" ill-defined rather than irrelevant, and the tree quietly treats ill-defined as not-worth-reporting.

HEAD plus a dirty flag is well-defined for a working tree, and it is what a reviewer actually needs: "I am reading HEAD 45cf6e4, with uncommitted changes" is both true and sufficient to catch this.

Why existing gates cannot catch it

Every test indexes a fixture tree it just created. There is no second version for the answer to disagree with, so the skew is unrepresentable in the harness — structurally the same blindness as #181, and for the same reason.

Note also that the two worktree-vs-primary paths look correct at every step: relative paths resolve, absolute paths auto-route to the owning project by longest root-prefix, and path_outside_known_roots fires honestly when they do not. Nothing malfunctioned. The tool answered a different question from the one asked and had no vocabulary to say so.

Repro

  1. git worktree add -f --detach /tmp/probe <some older commit>
  2. Leave the MCP server indexing the primary checkout.
  3. Advance the primary checkout to a newer commit.
  4. Ask an agent to verify a claim about the older commit, using the index.

Every path resolves. Every citation looks plausible. The line numbers are from the wrong tree, and no field in any reply says which tree they came from.

This is a normal configuration here, not a contrived one: multi-lane sessions with per-lane worktrees are the standing workflow, and the memory notes already record that concurrent lanes share more than the tree.

What must NOT be done

  • Do not report a commit for a dirty tree without saying it is dirty. That is worse than silence: it asserts a specific tree state that does not exist, and would have been equally wrong here in a way that looks more authoritative.
  • Do not shell out to git per call. The cost belongs at index/reconcile time, not on the read path — this repo has already refused a read-path convenience that cost a hot write path (#143).
  • Do not put it only in project_overview. Today's misread happened on read_code and search_text, not on an orientation call. An agent that never calls project_overview is exactly the agent that gets caught.
  • Do not let absent read as "clean at HEAD". Three states, as everywhere: absent = did not report; reported-and-clean; reported-with-uncommitted-changes.
  • Do not solve it by refusing worktree queries or by auto-switching roots. The routing behaviour is correct; only the disclosure is missing.
  • Do not grade it with a fixture that indexes a tree it just wrote — that is the vacuity that hides it. The test needs two real checkouts at different commits, or a stamped indexed-HEAD that the harness can move independently.

Relationship to #181, stated so they are not solved twice

Two different questions, one reader need:

question today's answer
#181 which binary produced this answer? nothing (DaemonBuild::Matched is silent, and correctly so — it compares client⇄daemon, not binary⇄tree)
this which tree state was that binary reading? the root, and nothing else

Both were violated in the same session, in opposite directions: #181 served a stale binary's answer about a current tree; this served a current binary's answer about a different tree. Whoever implements either should design the block for both.

Severity argument

This is our own tool telling an agent something false, with no channel by which the agent could detect it. The product's entire pitch is that it returns targeted handles an agent can trust more than a grep — and a file:line handle from an undisclosed tree is worse than a grep, because grep at least ran against the bytes in front of you.

🤖 Filed by the triage lane, 2026-09-06, from a live misread. Worktree f6a878a; index 45cf6e4.

Found by **dogfooding during the 2026-09-06 triage session that closed 22 issues**. It caused a real error inside that session and was caught only by accident. Sibling of **#181** (*no reply field says the answering binary is older than the tree it indexed*). Same family — **the answer does not disclose what produced it**. #181 is the *binary* axis; this is the *tree* axis. A reader debugging a wrong answer needs both, and they should be one block rather than two unrelated fields. ## Measured — the misread, exactly as it happened The triage lane created a read-only worktree pinned to `origin/master` at **`f6a878a`**: ``` git worktree add -f --detach /tmp/cosi-lane-triage origin/master ``` The `code-index` MCP server, meanwhile, indexes **`/home/master/code/rust/cosi-mcp`**, which another lane advanced to **`45cf6e4`** partway through the session. A verification agent asked the index about the Windows disk pre-flight and was served: - a `$minFreeGb = 40` floor - a `.forgejo/scripts/windows_disk_report.ps1` file **Neither exists at `f6a878a`.** Both are #179's fix, landed in `45cf6e4`. The agent had been told to verify `f6a878a` and was reading `45cf6e4` with no indication of it. It noticed **only because a test it ran contradicted a file it had already read.** Without that accident, the citations would have gone into a public issue comment as facts about a commit that does not contain them. The tool's own disclosure is what makes this sharp. `index_coverage` on a triage path answers: ``` path_outside_known_roots did_you_mean: ["primary=/home/master/code/rust/cosi-mcp"] ``` **The root is disclosed. The commit is not.** The reply is precise about *which directory* and silent about *which version of it*, and the second is the one that was wrong. We got lucky on blast radius: `git diff --stat f6a878a..45cf6e4` touches only CI, workflow and test files. Had a production file moved, every `file:line` citation in this session's issue comments would have been silently off. ## Mechanism `project_overview`, `index_coverage` and `list_files` all name the project **root**. Nothing anywhere names the indexed tree's **HEAD** or its dirty state. This is not an oversight so much as an unasked question. The server instructions state, correctly and prominently, that *"code-index indexes your WORKING TREE, not the last commit"* — which is the product's whole point and must not change. But that makes "which commit" *ill-defined* rather than *irrelevant*, and the tree quietly treats ill-defined as not-worth-reporting. `HEAD` **plus** a dirty flag is well-defined for a working tree, and it is what a reviewer actually needs: *"I am reading HEAD `45cf6e4`, with uncommitted changes"* is both true and sufficient to catch this. ## Why existing gates cannot catch it **Every test indexes a fixture tree it just created.** There is no second version for the answer to disagree with, so the skew is unrepresentable in the harness — structurally the same blindness as #181, and for the same reason. Note also that the two worktree-vs-primary paths *look* correct at every step: relative paths resolve, absolute paths auto-route to the owning project by longest root-prefix, and `path_outside_known_roots` fires honestly when they do not. Nothing malfunctioned. The tool answered a different question from the one asked and had no vocabulary to say so. ## Repro 1. `git worktree add -f --detach /tmp/probe <some older commit>` 2. Leave the MCP server indexing the primary checkout. 3. Advance the primary checkout to a newer commit. 4. Ask an agent to verify a claim about the older commit, using the index. Every path resolves. Every citation looks plausible. The line numbers are from the wrong tree, and no field in any reply says which tree they came from. This is a **normal** configuration here, not a contrived one: multi-lane sessions with per-lane worktrees are the standing workflow, and the memory notes already record that concurrent lanes share more than the tree. ## What must NOT be done - **Do not report a commit for a dirty tree without saying it is dirty.** That is worse than silence: it asserts a specific tree state that does not exist, and would have been *equally* wrong here in a way that looks more authoritative. - **Do not shell out to `git` per call.** The cost belongs at index/reconcile time, not on the read path — this repo has already refused a read-path convenience that cost a hot write path (#143). - **Do not put it only in `project_overview`.** Today's misread happened on `read_code` and `search_text`, not on an orientation call. An agent that never calls `project_overview` is exactly the agent that gets caught. - **Do not let absent read as "clean at HEAD".** Three states, as everywhere: absent = did not report; reported-and-clean; reported-with-uncommitted-changes. - **Do not solve it by refusing worktree queries or by auto-switching roots.** The routing behaviour is correct; only the disclosure is missing. - **Do not grade it with a fixture that indexes a tree it just wrote** — that is the vacuity that hides it. The test needs two real checkouts at different commits, or a stamped indexed-HEAD that the harness can move independently. ## Relationship to #181, stated so they are not solved twice Two different questions, one reader need: | | question | today's answer | |---|---|---| | **#181** | which **binary** produced this answer? | nothing (`DaemonBuild::Matched` is silent, and correctly so — it compares client⇄daemon, not binary⇄tree) | | **this** | which **tree state** was that binary reading? | the root, and nothing else | Both were violated in the same session, in opposite directions: #181 served a *stale binary's* answer about a current tree; this served a *current binary's* answer about a different tree. Whoever implements either should design the block for both. ## Severity argument This is our own tool telling an agent something false, with no channel by which the agent could detect it. The product's entire pitch is that it returns targeted handles an agent can trust more than a grep — and a `file:line` handle from an undisclosed tree is worse than a grep, because grep at least ran against the bytes in front of you. 🤖 Filed by the triage lane, 2026-09-06, from a live misread. Worktree `f6a878a`; index `45cf6e4`.
Author
Member

CONFIRMED and FIXED, as one block with #181 — you asked for that explicitly and it is one function, one key, one placement. lane/provenance (worktree /tmp/cosi-lane-provenance, based on fc329a8).

The shape

"answer_provenance": {
  "build": "0.26.1 (e56c6c7)",
  "indexed_trees": { "primary": { "head": "45cf6e4abcde", "uncommitted_changes": true } }
}

Three states for the tree, and they are an enum, not three optional fields on one struct, so "all three absent" — a {} — is unrepresentable:

shape meaning
{"head": "<12 hex>", "uncommitted_changes": <bool>} the commit, and a worktree comparison that ran
{"head": "<12 hex>", "uncommitted_changes_unmeasured": "<why>"} the commit is known; the comparison did not run
{"unavailable": "<why>"} not a checkout, git absent, or an unborn branch with no commit to name

A commit is never reported without one of the other two beside it. That is your rule, and it is enforced structurally rather than by convention.

uncommitted_changes counts staged, unstaged and untracked, because this index reads the working tree: a brand-new unstaged file is queryable, so a tree carrying one is not the commit.

Your four prohibitions, one by one

"Do not report a commit for a dirty tree without saying it is dirty." Held by the enum above. HeadOnly exists precisely so a commit whose worktree scan did not run cannot borrow a false.

"Do not shell out to git per call." One git rev-parse (O(1)) plus one git status --porcelain (O(worktree)), cached per root behind CODE_INDEX_TREE_STATE_TTL_MS (60s default, 0 disables — the graders set it). A burst of tool calls costs one measurement. The lock is held across the measurement so two racing calls do not both spawn git.

The two commands are deliberately not one --porcelain=v2 --branch: keeping them apart is what makes HeadOnly a state that happens rather than one that is declared. git status fails on its own — most often against a concurrent git holding index.lock — and when it does, the commit is still known and must still be reported, with the comparison's absence named beside it.

"Do not put it only in project_overview." It rides annotate_answer_provenance, above the tool router, plus read_resource. every_published_tool_carries_the_block enumerates tools/list and checks every non-error reply, with a floor on how many it actually checked so a change that turned every reply into an error could not leave it green.

"Do not let absent read as clean at HEAD." Absent means this build did not report. Documented in code-index://docs/answer-provenance and asserted.

Why it STATES instead of comparing — the part that decided the design

Every other three-state field here reports a comparison and stays quiet when it agreed. This one cannot borrow that argument, because there is nothing for the server to compare against: it does not hold your belief about which commit you are reading. That belief lived in a /tmp worktree path nobody told it about. Your case is precisely the one where every value the server holds is correct and the answer is still wrong. So the identities are stated unconditionally and the comparison is the reader's.

Grading — you named the vacuity by name

"Do not grade it with a fixture that indexes a tree it just wrote." No test here asserts a constant; every one makes the answer move:

  • the_reported_head_follows_the_tree — a second commit lands under the running server and the same call must name it.
  • an_uncommitted_change_is_disclosed_beside_the_commit — a clean fixture reports false (asserted, so a missing field cannot pass), then an edit and an untracked file flip it to true while head stays put.
  • two_checkouts_at_two_commits_do_not_report_the_same_tree — your repro, run: two real checkouts at different commits, two servers, and the blocks must disagree. This is the only test that fails when the head is hardcoded; every single-fixture test above stays green, which is why it exists.
  • a_tree_that_is_not_a_checkout_says_so_rather_than_going_silent — unavailable present, head and uncommitted_changes both absent.

Mutations, run, all RED: cache never expires (the_reported_head_follows_the_tree); porcelain_is_dirty always false (an_uncommitted_change…); untracked lines skipped (an_untracked_file_is_uncommitted_changes); measure_tree error arms return Measured{head:"",false} (a_tree_that_is_not_a_checkout…); indexed_trees hardcoded (two_checkouts…); HeadOnly rendered with uncommitted_changes:false (each_state_renders_its_own_key); parse_head without the hex-digit guard (only_an_oid_is_a_head — an error message on stdout must never be reported as a commit); first_line returning "" (a_reason_is_never_the_empty_string); the annotator moved into project_overview; the resource-leg insertion deleted.

Cost, and one thing you will want to know

32 estimated tokens per reply, measured as served and confirmed independently by the agent-task benchmark at +32.4/+32.6/+32.1 per call across three corpus tiers. The ratchet bands are re-recorded with the attribution, against a clean fc329a8 rather than against the stale record. Full accounting in the #181 comment.

The flag is only meaningful if .code-index/ is ignored. The daemon writes its lockfile and database there, so a checkout that does not ignore it reports uncommitted_changes: true permanently. This repository ignores /.code-index/; the docs page says so. Filing that as a product nit rather than fixing it here, since writing a .gitignore into the user's tree is a separate decision.

Docs: code-index://docs/answer-provenance.

**CONFIRMED and FIXED**, as one block with #181 — you asked for that explicitly and it is one function, one key, one placement. `lane/provenance` (worktree `/tmp/cosi-lane-provenance`, based on `fc329a8`). ## The shape ```json "answer_provenance": { "build": "0.26.1 (e56c6c7)", "indexed_trees": { "primary": { "head": "45cf6e4abcde", "uncommitted_changes": true } } } ``` Three states for the tree, and they are an **enum**, not three optional fields on one struct, so "all three absent" — a `{}` — is unrepresentable: | shape | meaning | |---|---| | `{"head": "<12 hex>", "uncommitted_changes": <bool>}` | the commit, and a worktree comparison that ran | | `{"head": "<12 hex>", "uncommitted_changes_unmeasured": "<why>"}` | the commit is known; the comparison did not run | | `{"unavailable": "<why>"}` | not a checkout, git absent, or an unborn branch with no commit to name | **A commit is never reported without one of the other two beside it.** That is your rule, and it is enforced structurally rather than by convention. `uncommitted_changes` counts staged, unstaged **and untracked**, because this index reads the working tree: a brand-new unstaged file is queryable, so a tree carrying one is not the commit. ## Your four prohibitions, one by one **"Do not report a commit for a dirty tree without saying it is dirty."** Held by the enum above. `HeadOnly` exists precisely so a commit whose worktree scan did not run cannot borrow a `false`. **"Do not shell out to `git` per call."** One `git rev-parse` (O(1)) plus one `git status --porcelain` (O(worktree)), cached per root behind `CODE_INDEX_TREE_STATE_TTL_MS` (60s default, `0` disables — the graders set it). A burst of tool calls costs one measurement. The lock is held across the measurement so two racing calls do not both spawn git. The two commands are **deliberately not one** `--porcelain=v2 --branch`: keeping them apart is what makes `HeadOnly` a state that *happens* rather than one that is declared. `git status` fails on its own — most often against a concurrent git holding `index.lock` — and when it does, the commit is still known and must still be reported, with the comparison's absence named beside it. **"Do not put it only in `project_overview`."** It rides `annotate_answer_provenance`, above the tool router, plus `read_resource`. `every_published_tool_carries_the_block` enumerates `tools/list` and checks every non-error reply, with a floor on how many it actually checked so a change that turned every reply into an error could not leave it green. **"Do not let absent read as clean at HEAD."** Absent means *this build did not report*. Documented in `code-index://docs/answer-provenance` and asserted. ## Why it STATES instead of comparing — the part that decided the design Every other three-state field here reports a comparison and stays quiet when it agreed. This one cannot borrow that argument, **because there is nothing for the server to compare against**: it does not hold your belief about which commit you are reading. That belief lived in a `/tmp` worktree path nobody told it about. Your case is precisely the one where every value the server holds is correct and the answer is still wrong. So the identities are stated unconditionally and the comparison is the reader's. ## Grading — you named the vacuity by name *"Do not grade it with a fixture that indexes a tree it just wrote."* No test here asserts a constant; every one makes the answer **move**: - `the_reported_head_follows_the_tree` — a second commit lands **under the running server** and the same call must name it. - `an_uncommitted_change_is_disclosed_beside_the_commit` — a clean fixture reports `false` (asserted, so a missing field cannot pass), then an edit **and** an untracked file flip it to `true` while `head` stays put. - `two_checkouts_at_two_commits_do_not_report_the_same_tree` — **your repro, run**: two real checkouts at different commits, two servers, and the blocks must disagree. This is the only test that fails when the head is hardcoded; every single-fixture test above stays green, which is why it exists. - `a_tree_that_is_not_a_checkout_says_so_rather_than_going_silent` — `unavailable` present, `head` and `uncommitted_changes` both absent. **Mutations, run, all RED:** cache never expires (`the_reported_head_follows_the_tree`); `porcelain_is_dirty` always false (`an_uncommitted_change…`); untracked lines skipped (`an_untracked_file_is_uncommitted_changes`); `measure_tree` error arms return `Measured{head:"",false}` (`a_tree_that_is_not_a_checkout…`); `indexed_trees` hardcoded (`two_checkouts…`); `HeadOnly` rendered with `uncommitted_changes:false` (`each_state_renders_its_own_key`); `parse_head` without the hex-digit guard (`only_an_oid_is_a_head` — an error message on stdout must never be reported as a commit); `first_line` returning `""` (`a_reason_is_never_the_empty_string`); the annotator moved into `project_overview`; the resource-leg insertion deleted. ## Cost, and one thing you will want to know **32 estimated tokens per reply**, measured as served and confirmed independently by the agent-task benchmark at +32.4/+32.6/+32.1 per call across three corpus tiers. The ratchet bands are re-recorded with the attribution, against a clean `fc329a8` rather than against the stale record. Full accounting in the #181 comment. **The flag is only meaningful if `.code-index/` is ignored.** The daemon writes its lockfile and database there, so a checkout that does not ignore it reports `uncommitted_changes: true` permanently. This repository ignores `/.code-index/`; the docs page says so. Filing that as a product nit rather than fixing it here, since writing a `.gitignore` into the user's tree is a separate decision. Docs: `code-index://docs/answer-provenance`.
Author
Member

Evidence for this issue from a near-miss today, and a defect report it disproves

A triage lane reported search_text returning total: 0 for terms that exist — recv_proof → 0 while "recv_proof" → 6, POPULATION_FLOOR → 0 while population_floor → 2 — and noted that without a grep cross-check it would have reported #188 and #189 as unimplemented. Two wrong verdicts, narrowly avoided.

It is not a query-parsing defect, and that was established by construction rather than by failing to reproduce

I wrote a file containing two fresh tokens, waited 8 seconds, and queried both unquoted:

crates/indexer/src/zz_searchprobe.rs
  // probe token: zz_probe_underscore_token
  pub const ZZ_PROBE_UPPER_TOKEN: u8 = 0;

search_text("zz_probe_underscore_token")  -> total: 1   found
search_text("ZZ_PROBE_UPPER_TOKEN")       -> total: 1   found

Underscore terms work unquoted. Uppercase underscore terms work unquoted. A newly written file is searchable within seconds. Both of the lane's suspected mechanisms are ruled out. (Probe removed; tree clean.)

Re-running its exact two queries now also returns 6 and 2 respectively.

So the remaining explanation is the one this issue is about

The zeros were almost certainly served from an index that predated the files being asked about — several lane merges had just landed in the primary checkout in quick succession, and project_overview was reporting resolve_progress: "no resolve transaction has run yet in this daemon; committed counters reflect whatever the last durable index held".

That is exactly #182's defect, and this is the sharpest evidence for it so far:

  • The reply was not silent. It shipped separator_scan asserting matched_as: "literal substring (FTS5 trigram)" — an affirmative claim that the literal was searched.
  • So the caller was not told "I do not know"; it was told "I looked, and it is not there."
  • A confident zero is strictly worse than an absent answer, because it terminates the search. The lane only escaped it by distrusting a result that contradicted a file it had already read.

What this issue should therefore require, beyond naming the commit: a reply whose index is older than the tree it is answering about must say so in the reply, and a total: 0 from such an index must not be renderable as a measured absence. search_text's empty_population block already distinguishes "the query matched nothing" from "a filter emptied it" — this is the third case, and it is the one that produces wrong conclusions rather than merely unhelpful ones.

One residual I could not settle: the lane reports the quoted and unquoted forms differing in the same moment, which staleness alone does not explain. I have asked it for the raw reply, and if it cannot produce it I will record this as unreproduced on the parsing axis and keep only the staleness finding above — which stands on its own evidence.

🤖 Generated with Claude Code

https://claude.ai/code/session_01K1zj5VcFJvJt3pQxe9259K

## Evidence for this issue from a near-miss today, and a defect report it *disproves* A triage lane reported `search_text` returning `total: 0` for terms that exist — `recv_proof` → 0 while `"recv_proof"` → 6, `POPULATION_FLOOR` → 0 while `population_floor` → 2 — and noted that **without a grep cross-check it would have reported #188 and #189 as unimplemented**. Two wrong verdicts, narrowly avoided. ### It is not a query-parsing defect, and that was established by construction rather than by failing to reproduce I wrote a file containing two fresh tokens, waited 8 seconds, and queried both **unquoted**: ``` crates/indexer/src/zz_searchprobe.rs // probe token: zz_probe_underscore_token pub const ZZ_PROBE_UPPER_TOKEN: u8 = 0; search_text("zz_probe_underscore_token") -> total: 1 found search_text("ZZ_PROBE_UPPER_TOKEN") -> total: 1 found ``` Underscore terms work unquoted. **Uppercase** underscore terms work unquoted. A newly written file is searchable within seconds. Both of the lane's suspected mechanisms are ruled out. (Probe removed; tree clean.) Re-running its exact two queries now also returns 6 and 2 respectively. ### So the remaining explanation is the one this issue is about The zeros were almost certainly served from an index that **predated the files being asked about** — several lane merges had just landed in the primary checkout in quick succession, and `project_overview` was reporting `resolve_progress: "no resolve transaction has run yet in this daemon; committed counters reflect whatever the last durable index held"`. That is exactly #182's defect, and this is the sharpest evidence for it so far: - The reply was **not silent**. It shipped `separator_scan` asserting `matched_as: "literal substring (FTS5 trigram)"` — an affirmative claim that the literal *was* searched. - So the caller was not told "I do not know"; it was told **"I looked, and it is not there."** - A confident zero is strictly worse than an absent answer, because it terminates the search. The lane only escaped it by distrusting a result that contradicted a file it had already read. **What this issue should therefore require**, beyond naming the commit: a reply whose index is older than the tree it is answering about must say so **in the reply**, and a `total: 0` from such an index must not be renderable as a measured absence. `search_text`'s `empty_population` block already distinguishes "the query matched nothing" from "a filter emptied it" — this is the third case, and it is the one that produces wrong conclusions rather than merely unhelpful ones. One residual I could not settle: the lane reports the quoted and unquoted forms differing **in the same moment**, which staleness alone does not explain. I have asked it for the raw reply, and if it cannot produce it I will record this as unreproduced on the parsing axis and keep only the staleness finding above — which stands on its own evidence. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01K1zj5VcFJvJt3pQxe9259K
Author
Member

FIXED in fb37a49, merged as 4f866e5, together with #181 — as this issue asked, one block rather than two unrelated fields.

The index now discloses the tree head it read alongside the build hash that answered, with a dirty flag. two_checkouts_at_two_commits_do_not_report_the_same_tree grades the axis directly, and a_daemon_that_records_no_build_reports_unavailable_not_a_match keeps the three states apart: absent = this build did not report, never "current".

Correction to something I asserted while planning this: I claimed #195's mid-reconcile case would land in this block. It does not — that surfaces through coverage_reasons from #155, and #195 stays open on its own terms.

Closing.

FIXED in `fb37a49`, merged as `4f866e5`, together with #181 — as this issue asked, one block rather than two unrelated fields. The index now discloses the tree head it read alongside the build hash that answered, with a dirty flag. `two_checkouts_at_two_commits_do_not_report_the_same_tree` grades the axis directly, and `a_daemon_that_records_no_build_reports_unavailable_not_a_match` keeps the three states apart: absent = this build did not report, never "current". Correction to something I asserted while planning this: I claimed #195's mid-reconcile case would land in this block. It does not — that surfaces through `coverage_reasons` from #155, and #195 stays open on its own terms. Closing.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
h-dv/code-index#182
No description provided.