Skip to content

bug(799): render breadcrumb lineage below the archive root and unify suggestion-path projection - #802

Merged
drmoisan merged 14 commits into
mainfrom
bug/breadcrumb-lineage-below-archive-root-799
Sep 7, 2026
Merged

bug(799): render breadcrumb lineage below the archive root and unify suggestion-path projection#802
drmoisan merged 14 commits into
mainfrom
bug/breadcrumb-lineage-below-archive-root-799

Conversation

@drmoisan

@drmoisan drmoisan commented Sep 7, 2026

Copy link
Copy Markdown
Owner

Summary

Breadcrumb suggestion and search-result rows rendered the ancestor chain from the mailbox root, so a
row for a folder three levels below the archive root showed the store and Archive segments before the
segments the user cares about. Separately, four archive-root stripping sites had drifted apart, and one
of them stripped a single leading separator when the archive root was empty.

This change trims the ancestor chain at the archive root inside the hierarchy provider, which is the one
seam both the QuickFiler item view and the Efc view route through, and collapses the drifted stripping
sites onto a single shared projection built on ArchiveStemContract.TryMakeArchiveRelative.

Closes #799.

What changed

  • Two new production types. ArchiveStemProjection is the shared display projection; ArchiveChainProjection
    is the chain trim. Both are covered at 100 percent line and branch.
  • OutlookFolderHierarchyProvider gains the trim, an optional root-accessor constructor parameter of
    delegate type, an error log plus single-segment fallback when a chain does not pass through the archive
    root, and a per-instance absence gate published through a small new interface.
  • FolderPredictor delegates the suggestion and recents projections to the shared projection, and the
    empty-root one-separator strip is eliminated.
  • BreadcrumbBridgeRouter projects the Efc score paths so archive-rooted suggestions keep their
    percentage, and suppresses zero-candidate rows on the Efc surface.
  • QfcItemController gains a new partial that receives the relocated breadcrumb pipeline helper.

The archive-relative stem remains the filing target and the score-lookup key, so the issue 439 constraint
is preserved and all ten of its tests pass unmodified.

Two deviations from the specification's own prose, both pre-authorised and recorded

Row suppression is delivered on the Efc surface only. The QuickFiler presented row set is composed
entirely inside a file owned by a concurrently running sibling item, and at the point the QuickFiler
handler hands its row model to the viewer no provider resolution has been attempted, so the zero-candidate
classification does not exist there yet. Producing it would have required a second synchronous resolution
pass on the UI thread, duplicating the router's work and changing the very ordering the sibling owns. The
specification's decision D-B pre-authorises this escalation; the QuickFiler surface keeps today's fallback
rendering, and the logging half is delivered on both surfaces because it lives in the shared provider.

The projected score is added alongside the raw score rather than substituted for it. The join index
assigns through its indexer, so a substitution would have fixed the stem-presented case and silently broken
the rooted-presented case, whose test does not assert the percentage and would therefore have stayed green
through the regression.

Verification

Full toolchain, one unbroken pass with zero restarts:

Gate Exit Result
dotnet tool run csharpier check . 0 Checked 1601 files, no drift
analyzer gate, /t:Rebuild 0 0 Warning(s), 0 Error(s)
nullable gate, /t:Rebuild 0 0 Warning(s), 0 Error(s), zero CS86
coverage test run, nine assemblies 0 7085 of 7085 passed

Regression discipline was fail-before then pass-after: Phase 1 left 33 tests deliberately red and Phase 2
turned all 33 green, with full suites moving from 4786 to 4816 and from 1363 to 1370 and NEWLY-FAILING: NONE.

Coverage did not regress. On the pinned first-party comparability index, line moved 84.55 to 84.58 and
branch 79.24 to 79.28, with PACKAGES_MATCHED=9 on both sides. Of 135 executable changed lines, none has
lower post-change hits than baseline; the four zero-hit lines were checked against the baseline document
and were already zero before this change.

The change footprint is exactly the 20 paths the plan declared. Six files owned by a concurrent sibling
item, and the two issue 439 router test files, were each queried individually and carry no hunk.

Review outcome

Feature review returned PASS with zero blocking findings and seven non-blocking observations. All eight
acceptance criteria evaluate to PASS.

The one finding worth scheduling is non-blocking and is tracked separately rather than fixed here, because
its two sites are outside this change's declared write set: the recents projections read the archive root
through a getter that throws when the root is unresolvable, so the combination of zero suggestions and a
non-empty recents list is a newly exposed throw path. Follow-up: #801

Notes for the reviewer

The branch merges origin/main at 2085504e, which brought in a cohort sibling whose declared change
surface overlapped this one. That merge is the base for every anchored diff and coverage comparison in the
evidence, so the figures measure this item's own footprint rather than the sibling's merged work.

Manual validation against a live Outlook profile has not been performed; the specification states explicitly
that it does not gate the automated review.

🤖 Generated with Claude Code

https://claude.ai/code/session_01MS8fb2wX1Sx1bG67LAr41e

drmoisan and others added 14 commits September 6, 2026 22:11
…older

Records the promoted potential entry and seeds the active bug folder for
issue #799 in full-bug work mode. The promoted record carries the eight
acceptance criteria AC1-AC8 settled with the maintainer on 2026-09-06
verbatim, and issue.md carries the full-bug work-mode marker.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MS8fb2wX1Sx1bG67LAr41e
Records the verified research artifact covering the seven archive-root
stripping sites, the chain-trim seam, the Efc score join, the stale-label
log gate, and the leading-underscore question. Authors spec.md with the
eight maintainer-settled acceptance criteria verbatim, an explicit
statement that this supersedes the issue #439 root-to-leaf lineage rather
than fixing a regression, and a Write Set section.

Findings of note: the leading-underscore report is a transcription
artifact and needs no code change; the ToDoModel sort utility is not a
compile item and has no live caller; the recents row-model mirror is a
second unprojected site the issue did not report.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MS8fb2wX1Sx1bG67LAr41e
Four phases and 74 tasks covering worktree bootstrap, fail-before
regression tests, the implementation, and the final quality-control loop.
The plan validator passes; its single warning is a false positive raised
by the plan's own delta self-check prose, which names the git diff rule it
satisfies while every real invocation is anchored to the recorded base
commit.

Three planner determinations depart from the spec's literal wording and
are recorded with their evidence: the acceptance-criterion 7 row
suppression takes the spec's documented escalation to the Efc surface
because the QuickFiler row set is composed only inside a file owned by a
concurrent sibling item; the acceptance-criterion 6 score projection is
additive rather than substitutive so a rooted presented row keeps its
percentage; and the two issue 439 router test files need no change
because their provider is a strict mock above the trimming seam.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Preflight returned revisions required with twelve blocking findings. The
reviewer could not execute any command, because the pre-tool guard refuses
pwsh inside an isolated agent worktree, so its runtime claims were
adjudicated against the committed evidence of the completed issue 791 run
on this same host.

Confirmed by that evidence: vstest prints no failed-count line on a fully
passing run, so any acceptance condition reading one from console output
is unsatisfiable. Refuted by that evidence: the claim that full-framework
MSBuild is absent from the path, which a recorded bare invocation
contradicts. Narrowed: the execution-environment finding, whose proposed
substitute installer was rejected as scope creep.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Applies every accepted finding, applies the two narrowed findings in
their narrowed form, and leaves the refuted MSBuild finding unapplied.

The most consequential change closes an acceptance-criterion delivery
gap: the row-suppression branch for criterion 7 was executed by no test
and would have shipped uncovered while being checked off on the strength
of provider-level tests that cannot reach it. Two named tests now pin the
zero-candidate suppression and the ambiguous-case non-suppression.

Three assertions that read a failed-count line from console output are
corrected to read the test result file instead, because the runner prints
no such line on a fully passing run. Declarations added to a
nullable-enabled file now carry the annotations the warnings-as-errors
gate requires, and the four consuming tasks gained the guards that
follow. The new test file is constrained to the older language version
its project compiles at.

The plan validator now passes with no warnings.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The confirming round returned revisions required with four blocking
findings, all local corrections to acceptance clauses and one task body.
All four are accepted; none is rejected or narrowed.

Two were verified directly against the tree: a relocation task demanded
zero matches for an identifier whose call site sits outside the moved
range, and the fifth consumer of a newly nullable return was missed when
the previous round narrowed its two siblings.

One finding is a defect the orchestrator introduced, by accepting a
replacement clause in round one that asserts an observation no scheduled
command in that phase produces. It is recorded as such rather than
attributed elsewhere.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Closes the four blocking findings from the confirming review. A
relocation task no longer demands zero matches for an identifier whose
call site sits outside the moved range. The fifth consumer of the newly
nullable projection return now carries the same reasoned suppression its
siblings received. An assets check now derives its result from an
anchored diff with a porcelain companion instead of asserting an absence
that was true for every possible execution. A clause asserting an
observation no scheduled command produced now reads two diagnostic counts
the phase-one build actually emits.

The plan file no longer carries any line matching the preflight or
convergence signal vocabulary. Clearance is the executor return recorded
in the orchestrator checkpoint, not an unmaintained assertion inside the
plan.

The plan validator passes with no warnings.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Executes Phase 0 tasks P0-T1 through P0-T15 of the approved atomic plan. Bootstraps the worktree (repo-local SDK 8.0.205, 172 NuGet packages, CSharpier 1.2.6, dotnet-coverage) and records the base commit, the two MSBuild gate baselines (0 warnings, 0 errors each), the CSharpier check baseline (1593 files, exit 0), the two suite baselines (4786 and 1363 tests, 0 failures), the nine-assembly coverage baseline (7048 tests, 84.55 percent comparability line index), Write Set measurability and line-count baselines, and the pre-change status of the 18 named tests.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Adds the issue #799 declaration seams and the test-first regression set that
Phase 2 must turn green. No production behaviour changes in this commit: every
seam body either throws NotImplementedException or stores a value nothing reads.

Seams:
- ArchiveStemProjection.ToDisplayStem — lenient display projection (AC4-AC6)
- ArchiveChainProjection.TryTrimBelowArchiveRoot — chain trim (AC1, AC2)
- OutlookFolderHierarchyProvider: IFolderLabelAbsenceReport, optional lazy
  archive-root accessor, and an injected diagnostic error sink (AC7)

Tests: 4 new UtilitiesCS.Test classes, 1 new QuickFiler.Test class, and 2
retargeted tests whose expectations encoded the empty-root separator strip that
AC4 eliminates.

Evidence: 33 tests are red at the end of Phase 1 (19 SEAM-BLOCKED, 11 NEW,
3 RETARGETED); both fail-before runs exit 1 as intended. Both Phase 1 builds
exit 0 with 0 warnings and 0 errors, and CS8625-COUNT and CS8618-COUNT are both
zero.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…jection

Phase 2 of the atomic plan: production implementation for issue #799.

- ArchiveStemProjection.ToDisplayStem: lenient display projection over
  ArchiveStemContract, returning the input unchanged for a null/empty/
  whitespace root, which removes the empty-root one-separator strip (AC4).
- ArchiveChainProjection.TryTrimBelowArchiveRoot: yields the lineage below
  the archive-root node, false when the chain misses the root or the leaf
  is the root (AC1, AC2).
- OutlookFolderHierarchyProvider: applies the trim inside GetAncestorChainAsync
  behind a lazy, fault-tolerant root accessor; adds the AC7 once-per-label
  log gate and the zero-candidate absence classification as two distinct
  per-instance concurrent sets, with the absence signal cleared on both
  resolution success routes.
- FolderPredictor: ProjectSuggestionPath delegates to the shared projection,
  both recents surfaces are projected (AC5), and the include-children branch
  of GetOlSubpath becomes a verified prefix removal.
- QfcItemController.ProjectPredeterminedFolder delegates to the same shared
  projection, so the two members agree by construction (AC4).
- BreadcrumbBridgeRouter: adds an archive-relative alias alongside every raw
  score key so archive-rooted suggestions keep their percentage (AC6), and
  filters known-absent suggestion rows before row construction so row ids and
  segment keys stay aligned (AC7, Efc surface only per decision D5).
- Relocates EnsureBreadcrumbPipeline into a new QfcItemController partial
  before adding the root-accessor argument, so ViewerSetup.cs never passes
  through 501 lines.

All 33 tests left deliberately red by Phase 1 now pass, the seven
deliberately-green pins remain green, and both suites are fully passing
(UtilitiesCS.Test 4816/4816, QuickFiler.Test 1370/1370) with no newly
failing test.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Close out the atomic plan for issue #799 with the full C# toolchain loop,
coverage comparison and acceptance-criteria check-off.

Toolchain loop closed clean in one uninterrupted pass, in the mandated order:

- CSharpier format: exit 0, "Formatted 1601 files in 8171ms.". Nine files
  touched, five with a content change, all inside the plan's Write Set, so
  nothing was reverted and the scope boundary is unchanged.
- CSharpier check: exit 0, "Checked 1601 files in 6748ms.", no drift. The
  count is the 1593-file baseline plus exactly the eight new .cs files.
- Analyzer gate (/t:Rebuild, EnableNETAnalyzers, EnforceCodeStyleInBuild):
  exit 0, 0 Warning(s), 0 Error(s), matching the baseline.
- Nullable gate (/t:Rebuild, TreatWarningsAsErrors): exit 0, 0 Warning(s),
  0 Error(s), zero CS86 diagnostics, matching the baseline.
- Nine-assembly run under dotnet-coverage: exit 0, 7085 tests, 7085 passed,
  0 failed, NEWLY-FAILING: NONE against a 7048-test baseline.

Coverage, on the pinned comparability index applied identically to both
documents with PACKAGES_MATCHED 9 on each side: lines 112855/133485 (84.55
percent) to 113143/133765 (84.58 percent); branches 26642/33624 (79.24
percent) to 26746/33736 (79.28 percent). The denominator grew, so the
percentages are the comparison used. Neither decreased. Of 315 changed
production lines, 0 have lower post-change hits than their baseline. Both
new production types reach 100 percent line and branch coverage.

Size budgets after the final format: ViewerSetup 467/500, EfcFormController
1321/1322, FolderPredictor 1002/1003, every other .cs at or below 500. The
formatter did not touch the two files carrying one line of headroom.

Scope: the changed source set under '*.cs' '*.csproj' is exactly the twenty
Write Set paths. All six sibling-owned files and both issue-439 test files
were queried individually and carry no hunk.

All eight acceptance criteria AC1 through AC8 are checked off in spec.md.
spec.md Status is now Implemented and records the four deviations from its
own prose, with the AC7 escalation (row suppression on the Efc surface only,
logging on both) written before the AC7 box was checked. issue.md records
the outcome and states that this is a specification change superseding #439
rather than a regression fix against it, with #439's filing-target and
score-key constraint carried forward as AC3.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MS8fb2wX1Sx1bG67LAr41e
Feature review of the breadcrumb archive-root lineage fix returned PASS
with zero blocking findings and seven non-blocking observations. All
eight acceptance criteria evaluate to PASS and were already checked off
in spec.md, so no checkbox changed.

The reviewer tested the three documented judgment calls rather than
accepting them: the decision D5 AC7 escalation is warranted because the
QuickFiler presented row set is composed only inside a sibling-owned
file; the decision D7 additive score projection is correct because
BuildProbabilityIndex assigns through its indexer, so substitution would
have silently broken the rooted-presented case; and the decision D8
no-hunk finding holds because every provider in both issue-439 files is
a strict mock, placing the trim below that boundary.

The one finding worth scheduling is non-blocking CR-1: AddRecents and
AddRecentRows now read ArchiveRootPath unconditionally, and that getter
throws InvalidOperationException, so with zero suggestions and non-empty
recents they become the first reader on a path that previously never
touched it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MS8fb2wX1Sx1bG67LAr41e
The issue #799 code review raised CR-1 as a non-blocking finding: the two
recents projections in FolderPredictor read the archive root through a
getter that throws InvalidOperationException, so with zero suggestions and
a non-empty recents list they become the first reader on a path that
previously never touched that property.

Recording it only as prose in the feature folder would lose it at merge,
so it is promoted through the lifecycle into a tracked bug with the
reachability argument, the two sites and the proposed guard preserved.

The finding is deliberately not fixed on this branch. It is outside the
issue #799 write set, and widening that set here would falsify the
scope assertion the plan already verified.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MS8fb2wX1Sx1bG67LAr41e
@drmoisan
drmoisan merged commit dc8ca6d into main Sep 7, 2026
9 of 10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: breadcrumb-lineage-below-archive-root-and-suggestion-path-consistency

1 participant