Type to search pages and rules.

Browse pages

LS5003: Code Element Carries the Wrong Number of Marks

LS5003 is a compiler check that reports a code element carrying more or fewer marks than its kind takes, because an extra mark answers the element to a record it does not serve. This page states what the rule reports, where the error sits, and how to carry exactly the marks the kind takes.

Markdown

Reference#

What the Rule Enforces#

LS5003 enforces Point Code and Tests up to Records (litespec/core/system#point-code-and-tests-up-to-records). 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 code element whose mark count differs from what its kind takes: a handler, event handler, job, endpoint, aggregate root, aggregate method, domain event, rule exception, or domain service carrying more than one mark, or an element naming one record twice, which counts as two marks. Each aggregate root method that changes state performs the act of exactly one use case, following Give Each Use Case Its Own Act. The marks table on the model page states the count each kind takes. The diagnostic sits on the type or method name.

Failing Example#

C#
/// <implements>use-case/spending.submit-expense</implements>
/// <implements>use-case/spending.approve-expense</implements>
internal sealed class SubmitExpenseCommandHandler(...)
    : ICommandHandler<SubmitExpenseCommand, SubmitExpenseCommandResult>
{
    // Loads the expense, submits it, stages it, and returns the result.
}

A root method shared by two use cases reports as well:

C#
/// <implements>aggregate/spending.expense</implements>
public sealed class Expense : AggregateRoot<ExpenseState>
{
    /// <implements>use-case/spending.approve-expense</implements>
    /// <implements>use-case/spending.approve-expense-on-behalf</implements>
    public void Approve(Guid approverId, DateTimeOffset decidedAt)
    {
        // Checks the submitter, replaces the state, and records the approval.
    }
}

Passing Example#

C#
/// <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.
}

One method per act with a private shared helper passes:

C#
/// <implements>aggregate/spending.expense</implements>
public sealed class Expense : AggregateRoot<ExpenseState>
{
    /// <implements>use-case/spending.approve-expense</implements>
    public void Approve(Guid approverId, DateTimeOffset decidedAt) => RecordApproval(approverId, decidedAt);

    /// <implements>use-case/spending.approve-expense-on-behalf</implements>
    public void ApproveOnBehalf(Guid approverId, DateTimeOffset decidedAt) => RecordApproval(approverId, decidedAt);

    private void RecordApproval(Guid approverId, DateTimeOffset decidedAt)
    {
        // Checks the submitter, replaces the state, and records the approval.
    }
}

How to Fix#

Remove the mark the kind does not take, so the element carries exactly the marks its kind takes.

Notes#

What the Rule Ignores#

The rule does not report an element with no mark; LS5001 owns that finding. It does not report an element carrying the marks its kind takes, including a port, adapter, or test carrying several marks. 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. A finding that a live exception covers stays silent. An .editorconfig entry for the rule id overrides the computed severity.