Skip to content

Diagnostics & rules reference

English(this file) · 日本語

karasu reports problems with two layers of vocabulary:

  • A rule (規則) is a concept — what the language allows and forbids (“an edge originates within its enclosing block”). Rules are how authors and this spec talk about the constraint.
  • A diagnostic (診断) is a mechanism — a specific, named check that fires when one concrete violation of a rule is detected (edge-source-mismatch).

One rule is often enforced by several diagnostics, and a single diagnostic belongs to exactly one rule. This document is the catalog that maps the two.

  • Diagnostic codes are stable API. The code string (e.g. edge-source-mismatch) is consumed by the LSP, the app, and downstream tooling. Codes are not renamed to match a rule’s wording; the rule name is conceptual, the code is the contract. When a rule reads more naturally under a different name than its diagnostic, that is expected — they sit at different altitudes.
  • Every diagnostic code lives under exactly one rule family below, and every code defined in core (DiagnosticParamsByCode, WarningKind) appears here. This completeness is enforced by a meta-test (see Catalog completeness), so a new code cannot ship without a catalog entry.
  • The fires when column states the concrete trigger. Severities are listed as emitted by core.

A diagnostic has a severity: error, warning, or info.

  • error — the model is malformed; the offending construct is rejected.
  • warning — a real defect the author should fix (a dangling reference, a conflicting style).
  • info — a fact, not a defect. karasu surfaces something true about the model that an external school of thought may call a smell (a shared database, a dispersed domain), without asserting it is wrong. This is the fact vs. style register split — see TPL-1386.

karasu also follows warn-don’t-error for unresolved references (spec §S6): an unresolved relation is dropped while the node it points from is preserved, and the drop is reported as a warning rather than failing the whole render.

Where a construct may be declared, and what an edge’s origin may be. An edge declared inside a service / domain block originates from that block’s id; infra blocks and legend have fixed placement; sync edges must not form a cycle.

Code Severity Fires when
edge-source-mismatch error An explicit edge source inside a service / domain / entity block does not equal the enclosing block id (the edge origin scope rule; for an entity this enforces the relation direction — origin = the reference-holding entity).
edge-endpoint-not-at-scope warning An edge endpoint resolves to a node that exists in the merged model but is not a peer at the scope where the edge is declared (the edge endpoint scope rule) — e.g. A -> B written at system scope where A and B are domains inside a service. The edge renders on no view; author it inside its source block, or qualify a cross-domain entity target. Skipped for dotted refs and for ids absent from the model (unresolved-edge-endpoint owns those); a domaindomain edge is exempt because it is derived up to an implicit service edge.
ambiguous-edge-base warning Multiple edges share the same from → to base with no distinguishing author id.
service-outside-system warning A service is declared outside any system.
infra-not-in-context error An infra block (database / queue / storage) is not a direct child of system.
boundary-not-in-context error A boundary block is declared inside a node kind that draws no canvas of its own (entity, resource, user, client, or an infra leaf), so it would have no peers to frame.
entity-not-in-domain error An entity is declared somewhere other than as a child of a domain.
node-not-in-context warning A logical node is nested inside a kind whose May contain column does not list it (e.g. a usecase inside a client). The node is kept and still renders; it simply carries no defined meaning there. Scheduled to become an error in .krs language v2.0 (see roadmap §Syntax 2.0).
legend-not-top-level error A legend block is declared somewhere other than the top level.
top-level-declaration error A user or an edge is declared at the top level instead of inside a system block.
system-property-conflict warning A system label / description conflicts between merged imports.
cyclic-dependency warning Sync edges (->) form a dependency cycle.

Ids must be unique within their declaring scope; ownership assigns at most one primary owner.

Code Severity Fires when
duplicate-edge-id error An author-supplied edge id collides with another edge’s id.
duplicate-node-id-parent error A node id is duplicated within its immediate parent (covers a usecase and entity sharing an id under one domain).
entity-anchor-collision warning An id is claimed by more than one target in the entity deep-link namespace ({all domain ids} ∪ {all entity ids}) — an entity id duplicated across domains, or an entity id equal to a domain id. Deep-links resolve ambiguously; the model still renders.
duplicate-node-in-system error A node id is duplicated within a system.
duplicate-node-in-deploy error A node id is duplicated within a deploy block.
duplicate-team-id error A team id is duplicated.
duplicate-team-in-organization error A team id is duplicated within an organization.
duplicate-resource-operation warning A CRUD verb is listed more than once on one resource.
duplicate-crud-decoration-target warning A CRUD decoration targets the same operation more than once.
duplicate-owner-assignment info A node is assigned as owned by more than one team (a fact; see ADR-1566).
duplicate-boundary-assignment info A node belongs to more than one boundary (a fact; membership is 1:N — see syntax.md for how a view resolves it).
boundary-membership-not-drawn info Under Group by: boundary, a boundary’s frame could not be widened to enclose one of its members without covering a non-member, so the membership is marked on the card as a tab instead. States what this drawing did, unlike duplicate-boundary-assignment, which states a fact about the model; it therefore carries no source location and appears only on that axis.
duplicate-boundary-id error Two boundary blocks in the same enclosing node declare the same id, so the second cannot be addressed. Top-level blocks are unaffected.
duplicate-facet-id error Two facet blocks declare the same id, so a facets reference cannot say whose metadata it means. Decided on the merged model, so a duplicate split across two files is caught; the first declaration is the one references resolve to.
positional-label-removed error A boundary, facet, organization, team or member id is followed by a positional label string. ADR-19 made label a property, and the positional form was never in the spec: boundary / facet lost it outright as experimental constructs (#2133), the rest after a deprecation window (#2208). Recovery differs — boundary / facet discard the string, while organization / team / member keep it as the label so the org chart still reads while the file is fixed.
node-id-multiple-locations warning The same node id appears in more than one location.

Cross-reference resolution (warn-don’t-error, §S6)

Section titled “Cross-reference resolution (warn-don’t-error, §S6)”

A referenced id must resolve to a declared node. When it does not, the source node is preserved and the unresolved relation is reported (it is not a fatal error) — see syntax spec §S6.

Code Severity Fires when
owns-target-not-found warning A team owns an id that names no node in the merged model — any kind, any depth, so a declared user or entity is found here and refused by kind in invalid-owns instead. A capability is a property rather than a node, so it resolves to nothing and lands here. Derived from the merged tree, so the verdict depends neither on the import form nor on where the block was declared. Import-coupled: a document that still has imports to resolve does not decide it at all (the LSP’s single-document context stays silent; the App / CLI decide it on the merged model).
invalid-owns warning An owns target resolves to a node whose kind cannot be owned; the message names that kind. An id that resolves to nothing is not this diagnostic’s business — owns-target-not-found reports that instead, so one mistake draws exactly one of the two. Import-coupled as a consequence: in a single-document context a cross-file target resolves to nothing, so nothing is reported. The ownable kinds are service / domain / client / infra blocks at any depth (OWNS_TARGET_KINDS, which only this check reads — existence asks nothing about kinds); an infra leaf (table / queue-item / bucket) and a capability exist but are not ownership units, so this is the check that rejects them. Note the system-view card draws a team chip only for the logical kinds; an owned infra block still reads under Group by: team (frames resolve by id) and in the org view.
contains-target-not-found warning Import-coupled: a document that still has imports to resolve does not decide it at all — the member may be declared in an imported file, and a cross-file system reopen can add the very child a scoped contains names. Otherwise: a boundary contains a node that does not exist — for a top-level block, anywhere in the merged system hierarchy (existence is checked after cross-file merge, not per file); for a scoped block, among the declaring node’s direct children.
facet-not-declared warning A facets reference names no declared facet block (existence is checked on the merged model, so a declaration in an imported file counts). Unlike the near-miss annotation hint, the declared set makes this check complete: a typo between two author-defined names is caught too.
import-id-not-found error A named import id path fails to resolve.
import-path-not-found error An import path fails to resolve at some segment.
unresolved-edge-endpoint warning An edge endpoint id is not found anywhere in the merged model.
unresolved-handles warning A handles domain is not reachable through the one-hop expose rule.
unresolved-realizes warning A deploy node realizes a target absent from the logical layer.
legend-ref-unresolved warning A legend ref matches no style rule and no node.
cross-system-ref-unresolved warning A cross-system edge (Sys.Svc) target is not found.
cross-system-ref-implicit-external warning A cross-system edge crosses into a system not tagged [external].
delivers-target-not-client warning A delivers target is not a client node.

Infra nodes are declared once; a store referenced by several services is a fact worth surfacing.

Code Severity Fires when
infra-redeclared-across-files info The same database / queue / storage id is declared in more than one merged file.
infra-leaf-redeclared-silently info A table / queue-item / bucket leaf is redeclared within its parent infra.
shared-infra-fan-in info Two or more services depend on the same store within one system (a fact, not a defect).
cross-domain-store-access info A usecase reads/writes an infra leaf owned by another domain, in one system (a boundary-crossing fact, not a defect). Ownership is derived from entity mappings; keyed at leaf granularity; [external] and role-tagged ([index] / [cache] / [analytics]) stores excluded. Orthogonal to shared-infra-fan-in.

The grammar of operation / CRUD decoration on resources.

Code Severity Fires when
invalid-crud-decoration error A CRUD decoration uses an unrecognised verb / letter.
empty-crud-decoration warning A verb: decoration has an empty right-hand side.
unknown-resource-operation warning A resource operation verb is not one of create / read / update / delete.

Whether structural nodes are assigned to an owner / parent, and cohesion facts about how domains and deploy targets are wired.

Code Severity Fires when
unassigned-service warning A service sits at top level with no team assignment.
unassigned-domain warning A domain is not assigned to a service — it sits at top level, or directly inside a system. Both placements express the same modelling state, so both fire (#2184); only the top-level form is additionally wrapped in the (Unassigned) pseudo-system.
unassigned-usecase warning A usecase is a direct child of a service with no domain parent.
unassigned-client warning A client sits at top level with no team assignment.
unassigned-database warning A database sits at top level with no team assignment.
unassigned-queue warning A queue sits at top level with no team assignment.
unassigned-storage warning A storage sits at top level with no team assignment.
unassigned-resource warning A bare resource <id> resolves to no store: not dot-notation, not [external], and not a unique entity. Model-wide (resolver, not parser), so it is promoted away — with zero edits — once a matching entity is declared. An ambiguous bare id (>1 matching entity) stays unresolved; the collision is reported by entity-anchor-collision.
domain-dispersal info One domain id appears under multiple services in scope (a fact).
missing-realizes info A deploy node lacks a realizes property.
missing-runtime info A deploy node lacks a runtime property.

Annotation parameters, removed / deprecated properties, and the v1.x deprecation of non-builtin tag / annotation vocabulary (syntax v2.0 accepts tool vocabulary only — see tags-annotations.md).

Code Severity Fires when
annotation-param-unsupported warning An annotation parameter key is not recognised for that annotation.
annotation-possible-typo info An annotation name is a near-match to a builtin (typo hint).
tag-not-builtin warning A tag name is outside the tool vocabulary (builtin + system-assigned tags). Deprecated in v1.x; no suppression condition.
tag-not-applicable warning A builtin tag is written on a node kind outside its applicability (e.g. service Api [index][index] applies to database). The tag has no effect there. Never fires together with tag-not-builtin: a non-builtin name has no applicability to violate.
annotation-not-builtin warning An annotation name is outside the builtin set. Deprecated in v1.x; no suppression condition.
style-tag-selector-not-builtin warning A .krs.style selector targets a tag name outside the tool vocabulary (e.g. [pci] { … }). Deprecated in v1.x — the rule still matches; syntax v2.0 matches tool vocabulary only. Migrate to a facet selector ([facets=<id>]). Fires per selector, independently of the model-side tag-not-builtin: the two name two edits, and warning once would leave the other site unfound. Never fires for the builtin theme or injected system sheets.
style-annotation-selector-not-builtin warning A .krs.style selector targets an annotation name outside the builtin set (e.g. @canary { … }). Same contract as style-tag-selector-not-builtin.
team-property-removed error The removed team property is used (see ADR-1564).

Resolving import declarations and style imports against the filesystem.

Code Severity Fires when
circular-import warning A node import forms a cycle.
circular-style-import warning A style import forms a cycle.
file-not-found error An imported file does not exist.
directory-not-found error An imported directory does not exist.
style-file-not-found warning An imported style file does not exist.

Validating .krs.style property names and values.

Code Severity Fires when
style-unknown-property warning A style property name is not recognised.
style-invalid-enum-value error A style value is not in the allowed enum.
style-invalid-hex-color error A style hex color is malformed.
style-invalid-length-unit error A style length uses a disallowed unit.
style-missing-length-unit error A style length is missing its required unit.
style-out-of-range error A style numeric value is outside its min / max bounds.
style-token-type-mismatch error A style token does not match the expected type.
expected-style-property-name error The style parser expected a property name.
expected-semicolon-between-properties error The style parser expected a ; between properties.
unknown-edge-selector-attribute error A selector uses a bracket attribute other than from / to / facets (e.g. edge[source=X]). The code name predates facets, which is accepted on node selectors too, not only on edge.
style-conflict warning A selector is defined in more than one user style sheet.
style-column-invalid-value warning A style column value is not left / center / right.
style-column-ignored-non-system-view warning A column hint is applied to a deploy / org view (ignored).
style-grid-columns-invalid-value warning A style grid-columns value is not a positive integer (the hint is dropped; layout auto-balances).

The client sub-language: storage kinds and capabilities.

Code Severity Fires when
client-resource-invalid-kind error A client resource storage kind is not one of the reserved values.
client-capability-duplicate warning A client declares the same capability name twice.

Low-level parser errors raised when tokens do not form a valid construct. These are mechanism-level by nature; the “rule” is the grammar itself.

Code Severity Fires when
token-type-mismatch error A token does not match the type the parser expected.
unexpected-token-root error An unexpected token appears at the root level.
unexpected-token-in-block error An unexpected token appears inside a block.
expected-brace-or-string error The parser expected a { or a string literal.
expected-identifier error The parser expected an identifier.
expected-string-after error The parser expected a string after a property keyword.
expected-id-or-string error The parser expected an id or a string.
expected-node-id error The parser expected a node id.
expected-property-value error The parser expected a property value.
expected-id-after error The parser expected an id after a property keyword.
invalid-node-kind error A node kind keyword is not recognised.
property-not-for-node-kind error A property is not valid for the node kind it appears on.
link-url-scheme-not-allowed warning A link URL scheme is not in the allowed set (http / https / mailto).

Synthetic codes the app uses when wrapping a thrown compile / parse error.

Code Severity Fires when
app-project-compile-error error compile() threw and the app reports a generic compile failure.
app-org-parse-error error Org parsing threw and the app reports a generic parse failure.
generic-text error A pre-built fallback message string with no structured params.

Every member of DiagnosticParamsByCode and WarningKind (in packages/core/src/types) must appear as a code in this document. A meta-test (packages/core/src/types/diagnostics-catalog.test.ts) asserts this in both directions, so the catalog cannot silently drift from the emitted codes. The discipline behind it is recorded as TPL-1623.

Related TPLs: TPL-1623 (catalog ↔ code completeness), TPL-1386 (fact vs style register), TPL-2171 (spec-promised diagnostics are implemented), TPL-1296 (spec ↔ source-of-truth sync).

© 2026 Hiroki Kondo · Licensed underApache-2.0

Built with Cloudflare