Skip to Content

Evidence Tags

One target, one reason, on the declaration that owes the answer.

@evidence <target> <reason> @evidenceExclude <target> <reason> @evidenceReview <target> <description> @evidenceReview <target> #<fingerprint> <description> @evidenceExcludeReview <target> <description> @evidenceExcludeReview <target> #<fingerprint> <description>

The target is one whitespace-delimited token, except that one opening with {@link, {@linkcode, or {@linkplain runs to its closing brace. A declaration may carry any number of tags, and each is validated on its own.

The four tags

@evidence is a positive citation. It says this declaration implements, verifies, or documents the target, and the reason says why.

@evidenceExclude is the opposite decision, recorded rather than assumed. It says nothing here owes the target, and the reason is what a reviewer reads to agree or disagree. A reference that declares noEvidenceExclude refuses this answer outright, and a claim that declares evidenceExcludeCarriers accepts it only from the files it names.

@evidenceReview and @evidenceExcludeReview annotate a citation and an exclusion respectively. They are not a third kind of acknowledgement: a review discharges no coverage, contributes no host, and never answers a unit on its own.

The two review forms stay separate because they answer different questions. Whether a screen truly renders a limit and whether it truly owes nothing are not the same verification, and one tag for both would let the easier one discharge the harder.

A review is therefore paired by acknowledgement kind together with target, which also lets one host cite a target for one claim and exclude it for another.

Writing the reason

The reason is the only part a reviewer can disagree with, so it has to say what this declaration does about the target rather than what the target says.

// unfalsifiable: true of anything in the same folder /** @evidence docs/requirements/discount.md#coupon-stacking Handles coupon stacking. */ // checkable: names what a reader can look for and fail to find /** @evidence docs/requirements/discount.md#coupon-stacking Renders the per-issuer limit this section defines, in the buyer's words. */

A restated heading is worse than no citation. It passes every check, spends the reviewer’s attention, and returns a sentence that cannot be wrong.

An exclusion’s reason carries more, because it records a decision rather than describing work. Where another population owns the target, name it and the condition that would bring the target back: “Settlement owns coupon combination; reject this exclusion once a screen displays a stacked total.”

Where the target does not apply at all, say what makes it inapplicable rather than asserting that it is. A principle about fixing root causes does not reach a component that only renders, and “No failure to fix.” is the whole reason, because a reviewer can check it against the declaration in one look.

Nothing scores any of this, and that is deliberate. A graded reason becomes a reason written for the grader, and the sentence stops being worth a reviewer’s time.

Targets

/** @evidence docs/requirements/pricing.md#sale-price Sale price derives from this section. */ /** @evidence prisma:Sale.price The stored column this figure is read from. */ /** @evidence POST:/members Member creation follows this API operation. */ /** @evidence {@link ISale} The complete sale contract is mirrored here. */
KindAddress
Markdown<path> for a file, <path>#<anchor> for an H1-H4 section
Prismaprisma:<Model>, prisma:<Model>.<member>
Swagger<UPPERCASE_METHOD>:<path>, such as GET:/members/{id}
TypeScript{@link QualifiedName}, resolved through your imports

Those are two resolvers, and the token itself says which. A path address resolves against the configured populations, while an inline link resolves through the citing module’s imports, so a symbol citation is a real reference rather than a string that happens to spell a name.

The braces are load-bearing rather than decoration. TypeScript resolves a name inside an inline link and counts it as a use, so an import that exists only to support a citation survives noUnusedLocals; an unbraced name leaves that import unreferenced and raises TS6133. Prefer import type for a citation-only import, since it is erased at emit and creates no runtime dependency or cycle.

Keeping the discrimination inside the token is also why a path target must stay one token. POST /members cannot be read as a two-token target without reinterpreting a TypeScript target named POST whose reason begins with /members.

Markdown anchors

A file target is its path inside the reference root, written with / or \. A heading target appends its anchor.

An explicit {#anchor} suffix on the heading wins, and it must start with an ASCII letter or digit and may then contain letters, digits, ., _, :, and -. Without one, the heading becomes a lowercase slug: letters, numbers, and _ survive, whitespace and - collapse to -, and other punctuation is dropped.

Two selected headings that slug to the same target are ambiguous and need distinct explicit anchors. A source path containing whitespace cannot be addressed at all, so the rule reports it with a rename diagnostic rather than creating an impossible obligation.

TypeScript identities

A top-level identity uses its public export name, so export { Local as Public } is addressed as Public. A namespace member prepends its namespace, as in Orders.create. A static class member is ClassName.member and an instance member is ClassName.prototype.member, which is also how a constructor parameter property is addressed.

Targets deliberately carry no file path. If two selected files expose the same qualified name, a citation of it is ambiguous and the fix is to rename or further qualify the symbol. A re-export creates no second unit in the barrel file.

Where a tag may live

A tag is read only from a comment the parser attached to a declaration. One written anywhere else is reported rather than silently ignored, because a tag that does nothing is the exact failure this plugin exists to remove.

ArtifactHostNot a host
TypeScriptA JSDoc block on the declaration// lines, comments inside a binding pattern
PrismaA /// run or block comment above the declaration// lines, comments above a closing brace
MarkdownAn HTML commentRendered prose
/// @evidence docs/requirements/pricing.md#sale-price Sale price derives from this section. model Sale { /// @evidence docs/requirements/discount.md#coupon-stacking The stacking limit is stored here. coupon_limit Int }
<!-- @evidence docs/ideas/discount.md#discount-policy Carries over the per-issuer limit recorded in the idea notes. -->

Markdown uses an HTML comment so the citation renders invisibly and the document a reader sees stays clean. The tag must be the first content on its line, though a bullet or quote marker in front of it is read through, so a sentence merely mentioning a tag goes on describing it. Anything that renders as code stays silent, which is what lets this page print tags in fenced blocks without answering for them.

Prisma follows Prisma’s own rule that a comment documents the declaration immediately below it. A blank line before a top-level block detaches the comment, and a comment above a block attribute or a closing brace documents nothing. One exception exists on the exclusion side: an unattached top-level /// @evidenceExclude run is a file-level carrier, which supports a lint-only .schema ledger outside Prisma generation.

Editor completions

evidence/graph publishes the configured targets as editor completions through ttscserver, so a citation can be picked from the real address list instead of typed from memory.

The host withholds a rule’s completions on any cycle where that rule reports something, which is deliberate rather than a limitation: a target list computed from a graph that failed to load would suggest addresses that do not exist. Clear the diagnostics and the completions come back.

Scope

Units contain other units. A Markdown file contains its outline and a heading contains the lower headings until the next one of equal or higher level. A Prisma model contains its columns and relations. An interface or object-shaped type alias contains its members, a class contains its selected members, and a namespace contains everything public nested below it.

An @evidence target acknowledges the unit it names and every selected descendant, and @evidenceExclude does the same unless its reference refuses exclusions. That is what lets one citation of an H2 answer for the H3s beneath it when the whole section is what the declaration implements.

The reference’s symbol selector defines the denominator, not the set of addressable targets. Every structural ancestor of a selected unit stays resolvable as an aggregate scope even when its own kind is not selected.

A checklist reference is the one place this cascade is cut back, because the whole point of a checklist is that each item was answered on purpose.

Withdrawing a declaration

A declaration whose documentation carries @internal, @hidden, or @ignore materializes no unit, and neither does anything nested inside it. The three are equivalent statements that the declaration is not API, and the graph honors the decision the source already made.

Withdrawal applies to both sides. A withdrawn declaration is neither a selected reference unit nor a selected claim host, and it cannot serve as an exclusion carrier either. TypeScript JSDoc and Prisma /// comments behave identically, and a withdrawn Prisma model takes its columns and relations with it.

The tag has to open its own line, so prose that mentions one is describing it rather than declaring it. A citation naming a withdrawn declaration is answered with the tag as the cause, rather than as an unresolved target that would send you hunting for a typo that is not there.

Note that stripInternal deletes a withdrawn declaration from emitted declaration files. Writing one of these tags inside a published doc comment removes the API it documents from what consumers see.

Reviews and fingerprints

/** * @evidence docs/requirements/discount.md#coupon-stacking States the per-issuer limit. * @evidenceReview docs/requirements/discount.md#coupon-stacking #a1b2c3d Verified against policy section 3. * @evidenceExclude .agents/skills/principles/SKILL.md#fix-root-causes-not-symptoms No failure to fix. * @evidenceExcludeReview .agents/skills/principles/SKILL.md#fix-root-causes-not-symptoms #f0e1d2c Confirmed the component only renders. */ export function CouponStackingNotice(props: IProps): JSX.Element;

A review matches the same declaration and the same target as the acknowledgement it annotates, and the two kinds pair separately: the citation above is answered by @evidenceReview, the exclusion beneath it by @evidenceExcludeReview. A review filed against a target nothing on that declaration cites is its own finding, because the author did the work and an orphan message would send them looking for a typo instead.

The #-prefixed fingerprint is seven lowercase hex characters, optional in the grammar and required by a reference that declares requireReview. It digests the cited scope’s content, so editing that content expires the review and the diagnostic hands you the value that replaces it. Write the new one only after re-reading what moved; copying it from the diagnostic is how a review becomes a formality.

The # is doing the same work the braces do around an inline link. The token discriminates itself rather than being guessed at, which matters because a requirement anchor such as #req-search-policies opens a description in exactly that shape.

What a review proves is narrow and worth stating plainly. It proves that a separately addressed statement exists and was written against the cited content as it now stands. It does not prove anyone read that content, and no rule judges whether the prose is sincere.

When a tag is wrong

A tag is data the compiler reads, so every way of getting one wrong has its own diagnostic rather than silence. A target that resolves nowhere, a tag with no reason, a review naming a target its declaration never cites, an exclusion written outside the declared carriers, and a tag sitting where nothing can attach it are each reported where they sit, with the repair named in the message.

What the tags cannot do

A citation cannot cross from another artifact kind into TypeScript evidence, because an inline link needs the citing module’s import scope. Documentation therefore cannot cite code, and the inverse obligation is a different one rather than a substitute.

A tag also cannot make a false statement true. It removes the error, not the problem, which is why evidence/review exists and why the overview draws the line where it does: the compiler catches omissions, and review catches falsehoods.

Last updated on