Browse pages
LS5002: Mark Names No Usable Record
LS5002 is a compiler check that reports a mark naming no record, a record of a type its code element does not take, or a mark on a type or method that takes no mark, because a mark pointing nowhere leaves the element unlinked. This page states what the rule reports, where the error sits, and how to name a record the element takes.
Reference#
What the Rule Enforces#
LS5002 enforces Point Code and Tests up to Records (litespec/core/system#point-code-and-tests-up-to-records) and Point Every Technical Class up to Its Record or Choice (litespec/core/system#point-every-technical-class-up-to-its-record-or-choice). It runs in every layer, on every marked code element and test. Its category is LiteSpec.Traceability.
What the Rule Reports#
The rule reports a mark whose identifier matches no record, or matches a record of a type its code element does not take, such as a handler marking a rule. The marks table on the model page states which record type each kind takes. A test proves the lowest record it verifies: a scenario, a rule, a quality requirement, a technical decision, an external system, an integration event, or a provision of the carried release, read from the provision ids the package embeds. A proves mark naming a functional or policy requirement, a product decision, or any other kind a test does not take reports, as does one naming an unknown provision. The rule also reports a mark on a type or method that takes no mark: a reading method, a private helper, or an unrecognized class carrying a mark, or a proves mark on a method with no test attribute. A method that only reads or computes performs no act and carries no mark, following Give Each Use Case Its Own Act. On a technical class an implements mark names a Behavior record it serves, and a follows mark names a provision of the carried release, read from the provision ids the package embeds, or a decision or exception. A follows mark on any element is checked and never stray. The diagnostic sits on the mark.
Failing Example#
/// <implements>use-case/spending.submit-expences</implements>
internal sealed class SubmitExpenseCommandHandler(...)
: ICommandHandler<SubmitExpenseCommand, SubmitExpenseCommandResult>
{
// Loads the expense, submits it, stages it, and returns the result.
}A mark on a method that takes no mark reports as well:
public sealed class ExpenseCalculator
{
/// <implements>use-case/spending.submit-expense</implements>
public int ApprovedCount() => 0;
}An implements mark naming a non-Behavior record on a technical class reports as well:
/// <implements>decision/0002-store-expenses-as-documents</implements>
internal sealed class SubmitExpenseValidator
{
public Task ValidateAsync(CancellationToken cancellationToken) => Task.CompletedTask;
}A follows mark naming no decision reports as well:
/// <follows>decision/0002-store-expences-as-documents</follows>
internal sealed class ExpenseRepository
{
public Task StoreAsync(object expense, CancellationToken cancellationToken) => Task.CompletedTask;
}A proves mark naming a functional requirement reports as well:
public sealed class ExpenseTests
{
/// <proves>requirement/approval-reminders.receipt-threshold</proves>
[Fact]
public void Submitting_without_a_receipt_fails()
{
}
}Passing Example#
/// <implements>use-case/spending.submit-expense</implements>
internal sealed class SubmitExpenseCommandHandler(...)
: ICommandHandler<SubmitExpenseCommand, SubmitExpenseCommandResult>
{
// Loads the expense, submits it, stages it, and returns the result.
}The same method with no mark passes:
public sealed class ExpenseCalculator
{
public int ApprovedCount() => 0;
}The same validator with a use case it serves passes:
/// <implements>use-case/spending.submit-expense</implements>
internal sealed class SubmitExpenseValidator
{
public Task ValidateAsync(CancellationToken cancellationToken) => Task.CompletedTask;
}The same repository with a decision that exists passes:
/// <follows>decision/0002-store-expenses-as-documents</follows>
internal sealed class ExpenseRepository
{
public Task StoreAsync(object expense, CancellationToken cancellationToken) => Task.CompletedTask;
}The same test proving a quality requirement passes:
public sealed class ExpenseTests
{
/// <proves>requirement/expense-submission.month-end-submissions</proves>
[Fact]
public void Month_end_submissions_stay_fast()
{
}
}How to Fix#
Name a record of the type the element takes, spelled as the record states it. A test names the lowest record it verifies: a quality requirement rather than a functional or policy one, and a technical decision rather than a product one. A code fix corrects a mistyped id, offering the three nearest records of the expected type by edit distance, one action each, with no Fix All. Remove the mark from a type or method that takes no mark, or add the test attribute the proves mark needs. A follows mark names a provision of the carried release, or a decision or exception.
Notes#
What the Rule Ignores#
The rule does not report an element with no mark; LS5001 owns that finding. It does not report a mark naming a record of the type its element takes, or an unmarked method that takes no mark. It does not report whether a provision's page applies to the project; litespec model check owns that finding. The rule stays silent when no .litespec/project.json is in reach, or when the docs root it names cannot be resolved; the CLI owns that configuration error.
Severity#
The rule reports an error, whether the mark names a record or not. A finding that a live exception covers stays silent. An .editorconfig entry for the rule id overrides the computed severity.