Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/design/cli/context.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
21 changes: 14 additions & 7 deletions docs/design/program.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
11 changes: 7 additions & 4 deletions docs/reqstream/reviewmark-system.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,20 @@ 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

- id: ReviewMark-System-Enforce
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

Expand Down
5 changes: 3 additions & 2 deletions docs/user_guide/introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`.

Expand Down
26 changes: 13 additions & 13 deletions test/DemaConsulting.ReviewMark.Tests/Cli/ContextTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ public void Context_Create_LogFlag_OpensLogFile()
public void Context_Create_UnknownArgument_ThrowsArgumentException()
{
// Act & Assert
var exception = Assert.Throws<ArgumentException>(() => Context.Create(["--unknown"]));
var exception = Assert.ThrowsExactly<ArgumentException>(() => Context.Create(["--unknown"]));
Assert.Contains("Unsupported argument", exception.Message);
}

Expand All @@ -211,7 +211,7 @@ public void Context_Create_UnknownArgument_ThrowsArgumentException()
public void Context_Create_LogFlag_WithoutValue_ThrowsArgumentException()
{
// Act & Assert
var exception = Assert.Throws<ArgumentException>(() => Context.Create(["--log"]));
var exception = Assert.ThrowsExactly<ArgumentException>(() => Context.Create(["--log"]));
Assert.Contains("--log", exception.Message);
}

Expand All @@ -222,7 +222,7 @@ public void Context_Create_LogFlag_WithoutValue_ThrowsArgumentException()
public void Context_Create_ResultsFlag_WithoutValue_ThrowsArgumentException()
{
// Act & Assert
var exception = Assert.Throws<ArgumentException>(() => Context.Create(["--results"]));
var exception = Assert.ThrowsExactly<ArgumentException>(() => Context.Create(["--results"]));
Assert.Contains("--results", exception.Message);
}

Expand Down Expand Up @@ -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<ArgumentException>(() => Context.Create(["--definition"]));
var exception = Assert.ThrowsExactly<ArgumentException>(() => Context.Create(["--definition"]));
Assert.Contains("--definition", exception.Message);
}

Expand Down Expand Up @@ -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<ArgumentException>(() => Context.Create(["--plan-depth", "not-a-number"]));
Assert.ThrowsExactly<ArgumentException>(() => Context.Create(["--plan-depth", "not-a-number"]));
}

/// <summary>
Expand All @@ -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<ArgumentException>(() => Context.Create(["--plan-depth", "0"]));
Assert.ThrowsExactly<ArgumentException>(() => Context.Create(["--plan-depth", "0"]));
}

/// <summary>
Expand Down Expand Up @@ -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<ArgumentException>(() => Context.Create(["--report-depth", "abc"]));
Assert.ThrowsExactly<ArgumentException>(() => Context.Create(["--report-depth", "abc"]));
}

/// <summary>
Expand All @@ -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<ArgumentException>(() => Context.Create(["--report-depth", "0"]));
Assert.ThrowsExactly<ArgumentException>(() => Context.Create(["--report-depth", "0"]));
}

/// <summary>
Expand All @@ -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<ArgumentException>(() => Context.Create(["--report-depth"]));
Assert.ThrowsExactly<ArgumentException>(() => Context.Create(["--report-depth"]));
}

/// <summary>
Expand Down Expand Up @@ -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<ArgumentException>(() => Context.Create(["--plan-depth", "6"]));
Assert.ThrowsExactly<ArgumentException>(() => Context.Create(["--plan-depth", "6"]));
}

/// <summary>
Expand All @@ -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<ArgumentException>(() => Context.Create(["--report-depth", "6"]));
Assert.ThrowsExactly<ArgumentException>(() => Context.Create(["--report-depth", "6"]));
}

/// <summary>
Expand Down Expand Up @@ -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<ArgumentException>(() => Context.Create(["--dir"]));
Assert.ThrowsExactly<ArgumentException>(() => Context.Create(["--dir"]));
}

/// <summary>
Expand Down Expand Up @@ -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<ArgumentException>(() => Context.Create(["--elaborate"]));
Assert.ThrowsExactly<ArgumentException>(() => Context.Create(["--elaborate"]));
}

/// <summary>
Expand Down
Loading