fix(sessions): freeze one entry per distinct session identity - #189
Merged
phodal merged 1 commit intoSep 24, 2026
Merged
Conversation
The population binding counts eligible sessions by distinct trimmed sessionId, but freezeSessionPopulation stored the raw prepared inventory, so a provider that emits two entries with the same content-derived id (reachable today via Copilot session-state directories that resolve to one content id) made every raw-count consumer contradict the binding and fail the whole bundle with SESSION_POPULATION_BINDING_MISMATCH. The freeze now keeps at most one entry per distinct trimmed identity (first-wins) and records dropped duplicate or empty-identity entries in an additive binding omission field, duplicateIdentitySessions, so the invariant population.sessions.length === eligible.count holds for every provider without touching adapter discovery. The workspace-CWD candidate map follows the same first-wins rule so the surviving entry never inherits a dropped duplicate's CWD candidates. Validated with new unit and real-fixture Copilot regression tests that were verified red on the pre-fix implementation, the full root suite (1774 tests), npm run pack:verify, and unchanged Claude end-to-end behavior. Spec: docs/specs/2026-09-24-session-population-duplicate-identity.md, related to QoderAI#164. Co-authored-by: ZCode (GLM 5.3) <zcode@z.ai>
1339190177
force-pushed
the
fix/session-population-duplicate-identity
branch
from
September 24, 2026 02:01
627dddf to
bfdf766
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
freezeSessionPopulationnow keeps at most one entry per distinct trimmedsession identity, and dropped duplicate or empty-identity entries are recorded
in an additive binding omission field
duplicateIdentitySessions. Theinvariant
population.sessions.length === binding.eligible.countnow holds forevery provider without touching adapter discovery.
Why
Issue/Story: same failure family as [Bug]: lead lane always fails with SESSION_POPULATION_BINDING_MISMATCH on Claude while sessionEvidence succeeds #164 (no linked
issue; this is a self-found, previously unreported defect).
User or maintainer outcome: the population binding counts eligible sessions
by distinct trimmed sessionId, but the freeze stored the raw prepared
inventory. A provider whose discovery can emit two entries with the same
content-derived sessionId therefore freezes an inventory whose raw length
contradicts its own binding count, and the Session facts lane
(
all-eligiblescope counts),selectSessions, and every other raw-countconsumer fail the whole bundle with
SESSION_POPULATION_BINDING_MISMATCH—no report can ever be produced for that workspace.
Static review of all 13 platform adapters: the reachable trigger today is
Copilot — session identity comes from file content (
workspace.yamlid:, then thesession.startevent'sdata.sessionId) rather than aunique filesystem location, and the discovery collector is a plain array.
Two session-state directories that resolve to one content id (copied,
re-synced, or resumed session state) reproduce the failure deterministically
on current
main. Augment shares the content-derived identity pattern butis not an evidence-bundle provider, so its duplicates cannot reach the
freeze; the other eleven adapters are Map-keyed or path-derived and cannot
emit duplicates.
Traceability and Scope
docs/specs/2026-09-24-session-population-duplicate-identity.mdequals binding count; AC-2 additive
duplicateIdentitySessionsomissionfield with unchanged fingerprints/schema/
samePopulation; AC-3 the Copilotduplicate fixture freezes a one-entry population and the Session facts lane
stays available with matching counts.
scripts/session-analysis/session-population.mjsplus focused tests in
test/sessions/session-population.test.mjsandtest/reporting/better-harness-evidence-bundle.test.mjs, and the new spec.source-ref merging), no selection-strategy or lane-envelope changes, no
binding schema version bump.
Change Type
Test and Review Evidence
failed; Session facts lane and lead bothSESSION_POPULATION_BINDING_MISMATCH; diagnosticsconflictpopulation.sessions2 vsbinding.eligible.count1 →selectSessions.eligibleCount2omission.duplicateIdentitySessions: 1npx vitest run test/sessions/session-population.test.mjs test/reporting/better-harness-evidence-bundle.test.mjsnpm test(root suite)npm run pack:verify(the freeze module ships in the npm package)Manual or visual evidence: none (no rendered surface changed).
Risk and Recovery
neutral, no OS-specific code. Additive omission field;
samePopulationcompares only the pre-existing omission fields. Forduplicate-free inventories (all current tests and providers except the
trigger scenario) the frozen population is byte-identical in shape.
list, schema version, or generated artifact changes).
workspaces return to the hard bundle failure.
keeps the first entry's source refs; the dropped duplicate's evidence is not
merged (recorded only as an omission count). Merging refs is adapter-level
work and deliberately out of scope. Windows/macOS verified only through the
platform-neutral unit and fixture contracts here; CI matrix is the
authoritative receipt.
AI Involvement
spec, and tests were produced by ZCode (GLM 5.3) under the fork owner's
direction; the fork owner approved the change and this PR text. Red-green
verification and the suite runs above are the recorded evidence.
Checklist
AGENTS.md,CONTRIBUTING.md, and the relevant canonical-owner guidance.CHANGELOG.md(not edited, following the repository's change-scope rule; happy to add an entry if maintainers prefer).