diff --git a/docs/design/cli/context.md b/docs/design/cli/context.md index eed5e1e..50827f0 100644 --- a/docs/design/cli/context.md +++ b/docs/design/cli/context.md @@ -26,9 +26,9 @@ arguments: | `ReportFile` | string? | Output path for the Review Report document | | `ReportDepth` | int | Heading depth for the Review Report | | `IndexPaths` | string[]? | Paths to scan when building an evidence index | -| `WorkingDirectory` | string | Base directory for resolving relative paths | +| `WorkingDirectory` | string? | Base directory for resolving relative paths | | `Enforce` | bool | Fail if any review-set is not Current | -| `Elaborate` | bool | Expand file lists in generated documents | +| `ElaborateId` | string? | Review-set ID to elaborate, or null if `--elaborate` was not specified | ## Argument Parsing diff --git a/docs/design/program.md b/docs/design/program.md index 6d260cc..e0e93cd 100644 --- a/docs/design/program.md +++ b/docs/design/program.md @@ -31,17 +31,24 @@ than by `Program.Main` explicitly returning a non-zero value. executing the first matching action and returning: 1. If `--version` — print version and return -2. If `--help` — print banner and return -3. If `--validate` — run self-validation and return -4. If `--lint` — run configuration lint and return -5. If `--index` paths provided — scan and write evidence index, then return -6. Otherwise — generate Review Plan and/or Review Report and return +2. Print application banner +3. If `--help` — print help and return +4. If `--validate` — run self-validation and return +5. If `--lint` — run configuration lint and return +6. Otherwise — run main tool logic (index scanning and/or Review Plan/Report/Elaborate) +The application banner (step 2) is always printed unless `--version` is specified. Only one top-level action is performed per invocation. Actions later in the priority order are not reached if an earlier flag is set. ## PrintBanner() -`Program.PrintBanner(Context)` writes the help text to the console via -`Context.WriteLine()`. The banner lists all supported flags and arguments with brief +`Program.PrintBanner(Context)` writes the application name, version, and copyright +notice to the console via `Context.WriteLine()`. The banner is printed for every +invocation except `--version`. + +## PrintHelp() + +`Program.PrintHelp(Context)` writes usage information to the console via +`Context.WriteLine()`. The help text lists all supported flags and arguments with brief descriptions. diff --git a/docs/reqstream/reviewmark-system.yaml b/docs/reqstream/reviewmark-system.yaml index 5e51e6e..9e890ed 100644 --- a/docs/reqstream/reviewmark-system.yaml +++ b/docs/reqstream/reviewmark-system.yaml @@ -25,8 +25,8 @@ sections: justification: | Auditors need evidence that the review evidence for each review-set is current — that the reviewed files have not changed since the review was conducted. The Review - Report provides this evidence automatically, showing Current, Stale, or Missing - status for each review-set. + Report provides this evidence automatically, showing Current, Stale, Missing, or + Failed status for each review-set. tests: - ReviewMark_ReviewReportGeneration @@ -34,8 +34,11 @@ sections: title: The tool shall return a non-zero exit code when enforcement is enabled and any review-set is not current. justification: | CI/CD pipelines must be able to gate releases on review coverage. The --enforce flag - enables this by causing the tool to exit with a non-zero code when any review-set has - Stale or Missing status, making incomplete review coverage a build-breaking condition. + enables this by causing the tool to exit with a non-zero code in two situations: when + the Review Plan shows that files matching needs-review are not covered by any + review-set, or when the Review Report shows that any review-set has Stale, Missing, or + Failed status. This makes incomplete file coverage, out-of-date reviews, and failed + reviews all build-breaking conditions. tests: - ReviewMark_Enforce diff --git a/docs/user_guide/introduction.md b/docs/user_guide/introduction.md index de6ca85..be0ecd5 100644 --- a/docs/user_guide/introduction.md +++ b/docs/user_guide/introduction.md @@ -152,8 +152,9 @@ Lint checks the following: - **File readability** — the definition file exists and can be read. - **YAML syntax** — the file is valid YAML; syntax errors include the filename and line number. -- **`evidence-source` block** — the block is present, has a `type` field (`url` or `fileshare`), - and has a `location` field. +- **`evidence-source` block** — the block is present and has a `type` field (`none`, `url`, or + `fileshare`); when `type` is `url` or `fileshare`, it must also include a `location` field + (no `location` field is used with `type: none`). - **Review sets** — each set has an `id`, a `title`, and at least one `paths` entry. - **Duplicate IDs** — no two review sets share the same `id`. diff --git a/test/DemaConsulting.ReviewMark.Tests/Cli/ContextTests.cs b/test/DemaConsulting.ReviewMark.Tests/Cli/ContextTests.cs index 1cb256a..2c94bd3 100644 --- a/test/DemaConsulting.ReviewMark.Tests/Cli/ContextTests.cs +++ b/test/DemaConsulting.ReviewMark.Tests/Cli/ContextTests.cs @@ -200,7 +200,7 @@ public void Context_Create_LogFlag_OpensLogFile() public void Context_Create_UnknownArgument_ThrowsArgumentException() { // Act & Assert - var exception = Assert.Throws(() => Context.Create(["--unknown"])); + var exception = Assert.ThrowsExactly(() => Context.Create(["--unknown"])); Assert.Contains("Unsupported argument", exception.Message); } @@ -211,7 +211,7 @@ public void Context_Create_UnknownArgument_ThrowsArgumentException() public void Context_Create_LogFlag_WithoutValue_ThrowsArgumentException() { // Act & Assert - var exception = Assert.Throws(() => Context.Create(["--log"])); + var exception = Assert.ThrowsExactly(() => Context.Create(["--log"])); Assert.Contains("--log", exception.Message); } @@ -222,7 +222,7 @@ public void Context_Create_LogFlag_WithoutValue_ThrowsArgumentException() public void Context_Create_ResultsFlag_WithoutValue_ThrowsArgumentException() { // Act & Assert - var exception = Assert.Throws(() => Context.Create(["--results"])); + var exception = Assert.ThrowsExactly(() => Context.Create(["--results"])); Assert.Contains("--results", exception.Message); } @@ -412,7 +412,7 @@ public void Context_Create_DefinitionFlag_SetsDefinitionFile() public void Context_Create_DefinitionFlag_WithoutValue_ThrowsArgumentException() { // Act & Assert - --definition with no following value should throw and include the flag name in the message - var exception = Assert.Throws(() => Context.Create(["--definition"])); + var exception = Assert.ThrowsExactly(() => Context.Create(["--definition"])); Assert.Contains("--definition", exception.Message); } @@ -452,7 +452,7 @@ public void Context_Create_PlanDepthFlag_SetsPlanDepth() public void Context_Create_PlanDepthFlag_WithInvalidValue_ThrowsArgumentException() { // Act & Assert - --plan-depth with a non-numeric value should throw - Assert.Throws(() => Context.Create(["--plan-depth", "not-a-number"])); + Assert.ThrowsExactly(() => Context.Create(["--plan-depth", "not-a-number"])); } /// @@ -463,7 +463,7 @@ public void Context_Create_PlanDepthFlag_WithInvalidValue_ThrowsArgumentExceptio public void Context_Create_PlanDepthFlag_WithZeroValue_ThrowsArgumentException() { // Act & Assert - --plan-depth requires a positive integer; zero is not valid - Assert.Throws(() => Context.Create(["--plan-depth", "0"])); + Assert.ThrowsExactly(() => Context.Create(["--plan-depth", "0"])); } /// @@ -501,7 +501,7 @@ public void Context_Create_ReportDepthFlag_SetsReportDepth() public void Context_Create_ReportDepthFlag_NonNumeric_ThrowsArgumentException() { // Act & Assert - creating a context with a non-numeric report depth should fail validation - Assert.Throws(() => Context.Create(["--report-depth", "abc"])); + Assert.ThrowsExactly(() => Context.Create(["--report-depth", "abc"])); } /// @@ -511,7 +511,7 @@ public void Context_Create_ReportDepthFlag_NonNumeric_ThrowsArgumentException() public void Context_Create_ReportDepthFlag_Zero_ThrowsArgumentException() { // Act & Assert - creating a context with a report depth of 0 should fail validation - Assert.Throws(() => Context.Create(["--report-depth", "0"])); + Assert.ThrowsExactly(() => Context.Create(["--report-depth", "0"])); } /// @@ -521,7 +521,7 @@ public void Context_Create_ReportDepthFlag_Zero_ThrowsArgumentException() public void Context_Create_ReportDepthFlag_MissingValue_ThrowsArgumentException() { // Act & Assert - creating a context with --report-depth but no value should fail validation - Assert.Throws(() => Context.Create(["--report-depth"])); + Assert.ThrowsExactly(() => Context.Create(["--report-depth"])); } /// @@ -628,7 +628,7 @@ public void Context_Create_NoArguments_EnforceFalse() public void Context_Create_PlanDepthFlag_WithValueGreaterThanFive_ThrowsArgumentException() { // Act & Assert - --plan-depth cannot exceed 5 (max heading depth supported) - Assert.Throws(() => Context.Create(["--plan-depth", "6"])); + Assert.ThrowsExactly(() => Context.Create(["--plan-depth", "6"])); } /// @@ -638,7 +638,7 @@ public void Context_Create_PlanDepthFlag_WithValueGreaterThanFive_ThrowsArgument public void Context_Create_ReportDepthFlag_WithValueGreaterThanFive_ThrowsArgumentException() { // Act & Assert - --report-depth cannot exceed 5 (max heading depth supported) - Assert.Throws(() => Context.Create(["--report-depth", "6"])); + Assert.ThrowsExactly(() => Context.Create(["--report-depth", "6"])); } /// @@ -675,7 +675,7 @@ public void Context_Create_NoArguments_WorkingDirectoryIsNull() public void Context_Create_DirFlag_MissingValue_ThrowsArgumentException() { // Act & Assert - --dir without a path value should throw - Assert.Throws(() => Context.Create(["--dir"])); + Assert.ThrowsExactly(() => Context.Create(["--dir"])); } /// @@ -712,7 +712,7 @@ public void Context_Create_NoArguments_ElaborateIdIsNull() public void Context_Create_ElaborateFlag_WithoutValue_ThrowsArgumentException() { // Act & Assert - --elaborate without an ID argument should throw - Assert.Throws(() => Context.Create(["--elaborate"])); + Assert.ThrowsExactly(() => Context.Create(["--elaborate"])); } ///