From 5f13b3adb6792acd7c7cb6dc0811d6ebfb12c1e4 Mon Sep 17 00:00:00 2001 From: Malcolm Nixon Date: Mon, 22 Jun 2026 20:05:19 -0400 Subject: [PATCH 1/6] fix: remove [global]/[local] labels from context output; add README global context example - Context files in --elaborate output are now listed as plain paths without [global]/[local] scope prefixes - Global context files still appear before local context files - Add top-level context: example to README .reviewmark.yaml schema - Update tests and docs to match new plain-path output Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- README.md | 3 +++ .../configuration/review-mark-configuration.md | 5 +++-- docs/user_guide/introduction.md | 4 ++-- .../Configuration/ReviewMarkConfiguration.cs | 8 +++----- .../Configuration/ReviewMarkConfigurationTests.cs | 10 ++++------ 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 59f3bdd..fe36337 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,9 @@ evidence-source: type: url # 'none', 'url', or 'fileshare' location: https://reviews.example.com/evidence/index.json +context: # optional: reference docs for all reviewers + - "docs/design/introduction.md" + reviews: - id: Core-Logic title: Review of core business logic diff --git a/docs/design/review-mark/configuration/review-mark-configuration.md b/docs/design/review-mark/configuration/review-mark-configuration.md index 70e9440..a25c417 100644 --- a/docs/design/review-mark/configuration/review-mark-configuration.md +++ b/docs/design/review-mark/configuration/review-mark-configuration.md @@ -98,8 +98,9 @@ and a boolean indicating whether any review-set is non-current. Looks up the review-set with the given `id`, resolves its file list and fingerprint, and returns a Markdown document with a heading at `markdownDepth`, a metadata table (ID, Title, Fingerprint), an optional Context subsection, and a Files subheading. The Context subsection -lists all resolved context files labeled `[global]` (from `GlobalContext`) or `[local]` (from -`review.Context`), and is omitted when no context files resolve. Throws `ArgumentException` +lists all resolved context files as plain paths (global context files from `GlobalContext` +first, followed by per-review-set files from `review.Context`), and is omitted when no +context files resolve. Throws `ArgumentException` for unknown IDs; throws `ArgumentOutOfRangeException` when `markdownDepth > 5`. **Coverage note:** Context entries are reference material only. A file listed in `context:` is diff --git a/docs/user_guide/introduction.md b/docs/user_guide/introduction.md index 9bd0db4..e6b2e44 100644 --- a/docs/user_guide/introduction.md +++ b/docs/user_guide/introduction.md @@ -524,8 +524,8 @@ The command prints a Markdown document to the console: ## Context -- `[global] docs/design/introduction.md` -- `[local] docs/design/core.md` +- `docs/design/introduction.md` +- `docs/design/core.md` ## Files diff --git a/src/DemaConsulting.ReviewMark/Configuration/ReviewMarkConfiguration.cs b/src/DemaConsulting.ReviewMark/Configuration/ReviewMarkConfiguration.cs index 6644d2d..a2a8b9f 100644 --- a/src/DemaConsulting.ReviewMark/Configuration/ReviewMarkConfiguration.cs +++ b/src/DemaConsulting.ReviewMark/Configuration/ReviewMarkConfiguration.cs @@ -1102,11 +1102,9 @@ internal ElaborateResult ElaborateReviewSet(string reviewSetId, string directory sb.AppendLine($"| Fingerprint | `{fingerprint}` |"); sb.AppendLine(); - // Resolve global and local context files, labeling each with its scope - var globalContextFiles = GlobMatcher.GetMatchingFiles(directory, GlobalContext) - .Select(f => $"[global] {f}"); - var localContextFiles = GlobMatcher.GetMatchingFiles(directory, review.Context) - .Select(f => $"[local] {f}"); + // Resolve global and local context files; global files appear first + var globalContextFiles = GlobMatcher.GetMatchingFiles(directory, GlobalContext); + var localContextFiles = GlobMatcher.GetMatchingFiles(directory, review.Context); var allContext = globalContextFiles.Concat(localContextFiles).ToList(); // Emit the context subsection only when at least one context file resolves diff --git a/test/DemaConsulting.ReviewMark.Tests/Configuration/ReviewMarkConfigurationTests.cs b/test/DemaConsulting.ReviewMark.Tests/Configuration/ReviewMarkConfigurationTests.cs index d7240b3..c4f08c1 100644 --- a/test/DemaConsulting.ReviewMark.Tests/Configuration/ReviewMarkConfigurationTests.cs +++ b/test/DemaConsulting.ReviewMark.Tests/Configuration/ReviewMarkConfigurationTests.cs @@ -1166,7 +1166,7 @@ public void ReviewSet_GetFingerprint_ContextNotIncluded() } /// - /// Test that ElaborateReviewSet includes a Context subsection with [global] labels + /// Test that ElaborateReviewSet includes a Context subsection with plain file paths /// when global context files resolve. /// [Fact] @@ -1196,14 +1196,13 @@ public void ReviewMarkConfiguration_ElaborateReviewSet_GlobalContext_AppearsInOu // Act var result = config.ElaborateReviewSet("Core-Logic", _testDirectory); - // Assert — context subsection is present and the file is labeled [global] + // Assert — context subsection is present and the file appears as a plain path Assert.Contains("## Context", result.Markdown); - Assert.Contains("[global]", result.Markdown); Assert.Contains("docs/design.md", result.Markdown); } /// - /// Test that ElaborateReviewSet includes a Context subsection with [local] labels + /// Test that ElaborateReviewSet includes a Context subsection with plain file paths /// when per-review-set context files resolve. /// [Fact] @@ -1233,9 +1232,8 @@ public void ReviewMarkConfiguration_ElaborateReviewSet_LocalContext_AppearsInOut // Act var result = config.ElaborateReviewSet("Core-Logic", _testDirectory); - // Assert — context subsection is present and the file is labeled [local] + // Assert — context subsection is present and the file appears as a plain path Assert.Contains("## Context", result.Markdown); - Assert.Contains("[local]", result.Markdown); Assert.Contains("docs/design.md", result.Markdown); } From a46c1eff9a17bfb2803b545e6267292041990af7 Mon Sep 17 00:00:00 2001 From: Malcolm Nixon Date: Mon, 22 Jun 2026 20:07:55 -0400 Subject: [PATCH 2/6] fix: remove [global]/[local] labels from requirements, design, and verification docs Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- docs/design/review-mark/configuration.md | 4 ++-- .../review-mark/configuration/review-mark-configuration.yaml | 4 ++-- .../review-mark/configuration/review-mark-configuration.md | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/design/review-mark/configuration.md b/docs/design/review-mark/configuration.md index 1ae2adc..50a5c5b 100644 --- a/docs/design/review-mark/configuration.md +++ b/docs/design/review-mark/configuration.md @@ -76,8 +76,8 @@ enumeration and owns SHA-256 fingerprinting, document generation, and elaboratio 4. `PublishReviewReport()` accepts `ReviewIndex` as a parameter and calls `GetEvidence()` for each review-set to determine Current, Stale, Missing, or Failed status. 5. `ElaborateReviewSet()` generates a Markdown document that includes an optional Context - subsection listing resolved context files labeled `[global]` (from `GlobalContext`) or - `[local]` (from `ReviewSet.Context`); the subsection is omitted when no context files + subsection listing resolved context files as plain paths (global context files first, + followed by per-review-set context files); the subsection is omitted when no context files resolve. `ReviewSet.Context` holds per-review-set context glob patterns; these patterns identify reference material shown during elaboration and do not provide review coverage. diff --git a/docs/reqstream/review-mark/configuration/review-mark-configuration.yaml b/docs/reqstream/review-mark/configuration/review-mark-configuration.yaml index fa46083..e18c4f0 100644 --- a/docs/reqstream/review-mark/configuration/review-mark-configuration.yaml +++ b/docs/reqstream/review-mark/configuration/review-mark-configuration.yaml @@ -232,8 +232,8 @@ sections: - id: ReviewMark-Config-ContextInElaboration title: >- ElaborateReviewSet shall include a Context subsection listing all resolved context - files labeled [global] (from the top-level list) or [local] (from the - per-review-set list), omitting the subsection when no context files exist. + files as plain paths (global context first, then per-review-set context), + omitting the subsection when no context files exist. justification: | Reviewers need to know which reference files to consult. tests: diff --git a/docs/verification/review-mark/configuration/review-mark-configuration.md b/docs/verification/review-mark/configuration/review-mark-configuration.md index a928d5d..6bfd879 100644 --- a/docs/verification/review-mark/configuration/review-mark-configuration.md +++ b/docs/verification/review-mark/configuration/review-mark-configuration.md @@ -121,14 +121,14 @@ by `ReviewSet_GetFingerprint_ContextNotIncluded`. **ReviewMarkConfiguration_ElaborateReviewSet_GlobalContext_AppearsInOutput**: `ElaborateReviewSet` is called on a configuration with a global `context:` list; the context files exist on disk. Expected outcome: the output Markdown contains a `Context` subsection and -each context file is listed with the `[global]` label. Requirement coverage: +each context file is listed as a plain path. Requirement coverage: `ReviewMark-Config-ContextInElaboration`. This scenario is tested by `ReviewMarkConfiguration_ElaborateReviewSet_GlobalContext_AppearsInOutput`. **ReviewMarkConfiguration_ElaborateReviewSet_LocalContext_AppearsInOutput**: `ElaborateReviewSet` is called on a configuration with a per-review-set `context:` list; the context files exist on disk. Expected outcome: the output Markdown contains a `Context` -subsection and each context file is listed with the `[local]` label. Requirement coverage: +subsection and each context file is listed as a plain path. Requirement coverage: `ReviewMark-Config-ContextInElaboration`. This scenario is tested by `ReviewMarkConfiguration_ElaborateReviewSet_LocalContext_AppearsInOutput`. From 90f7775b0381e75c2b5371d5f8492c1b1c1a9b5e Mon Sep 17 00:00:00 2001 From: Malcolm Nixon Date: Mon, 22 Jun 2026 20:13:34 -0400 Subject: [PATCH 3/6] fix: align context comment positions in README.md example Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index fe36337..ab7ace2 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ evidence-source: type: url # 'none', 'url', or 'fileshare' location: https://reviews.example.com/evidence/index.json -context: # optional: reference docs for all reviewers +context: # optional: reference docs for all reviewers - "docs/design/introduction.md" reviews: @@ -80,7 +80,7 @@ reviews: - "src/Core/**/*.cs" - "src/Core/**/*.yaml" - "!src/Core/Generated/**" - context: # optional: reference docs a reviewer must read + context: # optional: reference docs a reviewer must read - "docs/design/core.md" - id: Security-Layer title: Review of authentication and authorization From 3a57abe6b2a964bffc024cec946b2e30941a70ab Mon Sep 17 00:00:00 2001 From: Malcolm Nixon Date: Mon, 22 Jun 2026 20:17:02 -0400 Subject: [PATCH 4/6] fix: improve README.md example YAML comment style - Move block-level evidence-source and context comments to own lines - Remove redundant inline comment from per-review-set context: - Normalise inline comment alignment to column 26 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- README.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index ab7ace2..24c3914 100644 --- a/README.md +++ b/README.md @@ -63,14 +63,16 @@ named review-sets: needs-review: - "**/*.cs" - "**/*.yaml" - - "!**/obj/**" # exclude build output - - "!src/Generated/**" # exclude auto-generated files + - "!**/obj/**" # exclude build output + - "!src/Generated/**" # exclude auto-generated files +# Configuration for the review evidence source evidence-source: - type: url # 'none', 'url', or 'fileshare' + type: url # 'none', 'url', or 'fileshare' location: https://reviews.example.com/evidence/index.json -context: # optional: reference docs for all reviewers +# Optional reference files included in all reviews +context: - "docs/design/introduction.md" reviews: @@ -80,8 +82,9 @@ reviews: - "src/Core/**/*.cs" - "src/Core/**/*.yaml" - "!src/Core/Generated/**" - context: # optional: reference docs a reviewer must read + context: - "docs/design/core.md" + - id: Security-Layer title: Review of authentication and authorization paths: From 315f05fcc95503ad6b636142f64dd04b42894de7 Mon Sep 17 00:00:00 2001 From: Malcolm Nixon Date: Mon, 22 Jun 2026 20:22:04 -0400 Subject: [PATCH 5/6] fix: strengthen context elaboration tests - Assert exact list-item format (- \path\) not just substring contains - Assert DoesNotContain [global]/[local] labels in both context tests - Add GlobalContextBeforeLocalContext test to verify ordering Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../ReviewMarkConfigurationTests.cs | 55 +++++++++++++++++-- 1 file changed, 51 insertions(+), 4 deletions(-) diff --git a/test/DemaConsulting.ReviewMark.Tests/Configuration/ReviewMarkConfigurationTests.cs b/test/DemaConsulting.ReviewMark.Tests/Configuration/ReviewMarkConfigurationTests.cs index c4f08c1..1f7f9eb 100644 --- a/test/DemaConsulting.ReviewMark.Tests/Configuration/ReviewMarkConfigurationTests.cs +++ b/test/DemaConsulting.ReviewMark.Tests/Configuration/ReviewMarkConfigurationTests.cs @@ -1196,9 +1196,11 @@ public void ReviewMarkConfiguration_ElaborateReviewSet_GlobalContext_AppearsInOu // Act var result = config.ElaborateReviewSet("Core-Logic", _testDirectory); - // Assert — context subsection is present and the file appears as a plain path + // Assert — context subsection is present, file is a plain-path list item, no scope labels Assert.Contains("## Context", result.Markdown); - Assert.Contains("docs/design.md", result.Markdown); + Assert.Contains("- `docs/design.md`", result.Markdown); + Assert.DoesNotContain("[global]", result.Markdown); + Assert.DoesNotContain("[local]", result.Markdown); } /// @@ -1232,9 +1234,54 @@ public void ReviewMarkConfiguration_ElaborateReviewSet_LocalContext_AppearsInOut // Act var result = config.ElaborateReviewSet("Core-Logic", _testDirectory); - // Assert — context subsection is present and the file appears as a plain path + // Assert — context subsection is present, file is a plain-path list item, no scope labels Assert.Contains("## Context", result.Markdown); - Assert.Contains("docs/design.md", result.Markdown); + Assert.Contains("- `docs/design.md`", result.Markdown); + Assert.DoesNotContain("[global]", result.Markdown); + Assert.DoesNotContain("[local]", result.Markdown); + } + + /// + /// Test that ElaborateReviewSet lists global context files before per-review-set context files. + /// + [Fact] + public void ReviewMarkConfiguration_ElaborateReviewSet_GlobalContextBeforeLocalContext() + { + // Arrange — create a global context file and a local context file + var srcDir = PathHelpers.SafePathCombine(_testDirectory, "src"); + var docsDir = PathHelpers.SafePathCombine(_testDirectory, "docs"); + var specDir = PathHelpers.SafePathCombine(_testDirectory, "spec"); + Directory.CreateDirectory(srcDir); + Directory.CreateDirectory(docsDir); + Directory.CreateDirectory(specDir); + File.WriteAllText(PathHelpers.SafePathCombine(srcDir, "A.cs"), "class A {}"); + File.WriteAllText(PathHelpers.SafePathCombine(docsDir, "global.md"), "# Global"); + File.WriteAllText(PathHelpers.SafePathCombine(specDir, "local.md"), "# Local"); + + var yaml = """ + context: + - "docs/**/*.md" + evidence-source: + type: none + reviews: + - id: Core-Logic + title: Review of core business logic + paths: + - "src/**/*.cs" + context: + - "spec/**/*.md" + """; + var config = ReviewMarkConfiguration.Parse(yaml); + + // Act + var result = config.ElaborateReviewSet("Core-Logic", _testDirectory); + + // Assert — both files appear as plain-path list items, global before local + var globalIndex = result.Markdown.IndexOf("- `docs/global.md`", StringComparison.Ordinal); + var localIndex = result.Markdown.IndexOf("- `spec/local.md`", StringComparison.Ordinal); + Assert.True(globalIndex >= 0, "Global context file not found in output"); + Assert.True(localIndex >= 0, "Local context file not found in output"); + Assert.True(globalIndex < localIndex, "Global context file should appear before local context file"); } /// From 1155be26b27f0b2847c167ae66dc634df07e233b Mon Sep 17 00:00:00 2001 From: Malcolm Nixon Date: Mon, 22 Jun 2026 20:34:39 -0400 Subject: [PATCH 6/6] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../Configuration/ReviewMarkConfiguration.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/DemaConsulting.ReviewMark/Configuration/ReviewMarkConfiguration.cs b/src/DemaConsulting.ReviewMark/Configuration/ReviewMarkConfiguration.cs index a2a8b9f..85c360e 100644 --- a/src/DemaConsulting.ReviewMark/Configuration/ReviewMarkConfiguration.cs +++ b/src/DemaConsulting.ReviewMark/Configuration/ReviewMarkConfiguration.cs @@ -1105,7 +1105,10 @@ internal ElaborateResult ElaborateReviewSet(string reviewSetId, string directory // Resolve global and local context files; global files appear first var globalContextFiles = GlobMatcher.GetMatchingFiles(directory, GlobalContext); var localContextFiles = GlobMatcher.GetMatchingFiles(directory, review.Context); - var allContext = globalContextFiles.Concat(localContextFiles).ToList(); + var allContext = globalContextFiles + .Concat(localContextFiles) + .Distinct(StringComparer.Ordinal) + .ToList(); // Emit the context subsection only when at least one context file resolves var subHeading = new string('#', markdownDepth + 1);