Skip to main content

Verifying — check / validate

The two you call after an edit. They say different things.

  • check — does what is written hold together as data. The gatekeeper.
  • validate — is it sound as architecture. A judgement, not a gate.

They differ down to the type. check's diagnostics carry {code, severity}; validate's findings carry {rule, level}. The spellings differ and the two arrays cannot be concatenated. Do not claim the building works because check is green.

Every piece of output on this page was obtained by actually running it. Absolute paths are shortened to <abs>.


check

The gatekeeper of the build: composes the layers and checks structural consistency. Errors and warnings carry layer:line. Call it after every edit. This says nothing about architectural soundness — that is the validate tool

file only, required. Call it after every edit.

When it is green

{
 "ok": true,
 "spaces": 3,
 "boundaries": 3,
 "errors": [],
 "warnings": [],
 "diagnostics": []
}
FieldContents
okWhether errors is empty. warnings do not make ok false
spacesHow many spaces after composition
boundariesHow many boundaries after derivation
errors warningsArrays of strings carrying their provenance
diagnosticsThe structured diagnostics. Same count and same order as errors plus warnings

If you want to stop on warnings, look yourself. There is no flag here corresponding to the CLI's --strict. Read warnings.length and decide.

When there are warnings

koyu 1.0
name 警告
unit mm
grid X 0 3600 7200
grid Y 0 4500
level L1 0 h:2400
space /L1/a room X1..X2 Y1..Y2 name:居室A
space /L1/b room X2..X3 Y1..Y2 name:居室B
space /out exterior name:外部
{
 "ok": true,
 "spaces": 3,
 "boundaries": 1,
 "errors": [],
 "warnings": [
  "<abs>/warn.muro:line 6: Level L1 has no slab:, so not one floor is generated on this storey"
 ],
 "diagnostics": [
  {
   "code": "SUF03",
   "severity": "warning",
   "message": "Level L1 has no slab:, so not one floor is generated on this storey",
   "line": 6,
   "file": "<abs>/warn.muro"
  }
 ]
}

ok stays true.

When there are errors

koyu 1.0
name 二重宣言
unit mm
grid X 0 3600 7200
grid Y 0 4500
level L1 0 h:2400 slab:150
space /L1/a room X1..X2 Y1..Y2 name:居室A
space /L1/b room X2..X3 Y1..Y2 name:居室B
space /out exterior name:外部
boundary /L1/a /L1/b t:120
boundary /L1/a /L1/b t:150
{
 "ok": false,
 "spaces": 3,
 "boundaries": 2,
 "errors": [
  "<abs>/dup.muro:line 11: Duplicate boundary: /L1/a | /L1/b (first seen at <abs>/dup.muro:line 10)"
 ],
 "warnings": [],
 "diagnostics": [
  {
   "code": "BND02",
   "severity": "error",
   "message": "Duplicate boundary: /L1/a | /L1/b (first seen at <abs>/dup.muro:line 10)",
   "line": 11,
   "file": "<abs>/dup.muro",
   "path": [
    "/L1/a",
    "/L1/b"
   ],
   "related": [
    {
     "line": 10,
     "file": "<abs>/dup.muro"
    }
   ]
  }
 ]
}

The strings in errors and the entries in diagnostics are the same things. The first is the human form with the position glued onto the front of the message; the second is the machine form. Same count, same order. An agent reads diagnostics.

The shape of a diagnostic

FieldWhen it appearsContents
codealwaysThree letters plus two digits. There are 65 of them
severityalways"error" or "warning"
messagealwaysThe message alone. No position prefix
linewhen the provenance is known1-based line number
filewhen the provenance is knownAbsolute path of the layer the declaration is in
pathwhen the subject is a space or zoneThe subject paths
relatedwhen the diagnostic has a counterpart{line, file} for the other end

severity is an attribute of the code. The same code is never an error sometimes and a warning other times. The table from a code to its cause and its fix is on Diagnostic codes.

The order is scan order. Nothing regroups them by code family. The same model always yields the same order.

Syntax and composition errors do not arrive here

When a file cannot be read, the syntax is broken, or the composition does not hold, no check result comes back at all. The tool is treated as having thrown: the response carries isError: true and the message alone.

<abs>/bad.muro:line 8: The region has zero width

This is where it differs from koyu check --json. The CLI maps this onto a single SYN01 diagnostic and still returns valid JSON; over MCP no diagnostics array comes back. The agent notices isError, reads the one line, and fixes it. The details are on The protocol.

What check does not say

check guarantees only that what is written holds together as data. It has not looked at whether the building is usable.

The default between touching spaces is a wall, and a wall is impassable without a door. So a building with no door written at all stays sealed and green.

koyu 1.0
name 密封
unit mm
grid X 0 3600 7200
grid Y 0 4500
level L1 0 h:2400 slab:150
space /L1/a room X1..X2 Y1..Y2 name:居室A daylight:1
space /L1/b room X2..X3 Y1..Y2 name:居室B
space /out exterior name:外部
{
 "ok": true,
 "spaces": 3,
 "boundaries": 1,
 "errors": [],
 "warnings": [],
 "diagnostics": []
}

Hand the same file to validate and three violations come out.


validate

Architectural verdicts: daylight, envelope continuity, stair proportions, slopes, reachability, column/door collisions, and the site. This is a different surface from the check guarantee — findings carry rule/level, never code/severity. The surface grows and is not frozen

file only, required. Against the sealed building above:

{
 "findings": [
  {
   "rule": "daylight.ratio",
   "level": "violation",
   "message": "Insufficient daylight: /L1/a — effective window 0.00 m2 < required 2.31 m2 (1/7 of the 16.20 m2 floor)",
   "line": 7,
   "file": "<abs>/sealed.muro",
   "path": [
    "/L1/a"
   ]
  },
  {
   "rule": "access.unreachable",
   "level": "violation",
   "message": "Cannot reach the exterior: /L1/a (no passable boundary leads out — write a door)",
   "line": 7,
   "file": "<abs>/sealed.muro",
   "path": [
    "/L1/a"
   ]
  },
  {
   "rule": "access.unreachable",
   "level": "violation",
   "message": "Cannot reach the exterior: /L1/b (no passable boundary leads out — write a door)",
   "line": 8,
   "file": "<abs>/sealed.muro",
   "path": [
    "/L1/b"
   ]
  }
 ],
 "violations": 3,
 "cautions": 0,
 "note": "These are verdicts, not the structural-consistency guarantee of koyu check"
}
FieldContents
findingsThe findings
violationsHow many have level "violation"
cautionsHow many have level "caution"
noteA fixed sentence saying this is not what check guarantees

No ok comes back. This tool declares no pass or fail. Reading the counts and deciding is the caller's job.

The shape of a finding

FieldWhen it appearsContents
rulealwaysThe rule name, spelled family.namedaylight.ratio, say
levelalways"violation" (it was not met) or "caution" (it is suspect)
messagealwaysThe message alone; no position prefix
line filewhen the provenance is knownThe declaring line and layer
pathwhen the subject is knownThe subject paths

level is an attribute of the rule. The same rule never gets heavier or lighter with circumstance.

Violations and cautions mix freely in one response.

koyu 1.0
name 窓の高さ
unit mm
grid X 0 3600 7200
grid Y 0 4500
level L1 0 h:2400 slab:150
space /L1/a room X1..X2 Y1..Y2 name:居室A daylight:1
space /L1/b room X2..X3 Y1..Y2 name:居室B
space /out exterior name:外部
boundary /L1/a /L1/b t:120
  door w:780 h:2000
boundary /L1/a /out t:150
  window w:2600 edge:S name:腰窓
boundary /L1/b /out t:150
  door w:900 h:2100 edge:S name:玄関
{
 "findings": [
  {
   "rule": "daylight.ratio",
   "level": "violation",
   "message": "Insufficient daylight: /L1/a — effective window 0.00 m2 < required 2.31 m2 (1/7 of the 16.20 m2 floor)",
   "line": 7,
   "file": "<abs>/win.muro",
   "path": [
    "/L1/a"
   ]
  },
  {
   "rule": "daylight.unknown",
   "level": "caution",
   "message": "Window area is not fully counted: /L1/a has a window without h: (write h: on it)",
   "line": 7,
   "file": "<abs>/win.muro",
   "path": [
    "/L1/a"
   ]
  }
 ],
 "violations": 1,
 "cautions": 1,
 "note": "These are verdicts, not the structural-consistency guarantee of koyu check"
}

The window has no h:, so the effective window area could not be fully counted (caution), and what could be counted does not reach 1/7 (violation). Both come out at once.

The rules that can come back

There are 15. level is fixed per rule.

RulelevelWhat it looks at
daylight.ratioviolationEffective window area below one seventh of the floor
daylight.unknowncautionA window with no h:, so the window area is not fully counted
envelope.gapcautionA hole in the envelope — perimeter facing nothing
stair.proportioncautionThe derived steps are cramped
run.slopecautionThe derived slope is too steep, or outside normal use
run.disconnectedcautionA vertical run exists but no vertical boundary connects the storeys
access.unreachableviolationA room with a region cannot reach the exterior
access.voidonlyviolationA door opens only onto a void
access.throughtenantcautionEscape from a stair core passes through a tenancy
access.parkingviolationA car cannot get out of the parking
access.backofhousecautionA vertical run cannot be reached from the common corridor without crossing back-of-house
column.blocksdoorviolationA derived column collides with a derived door
site.escapeviolationThe building escapes the site shape
site.areacautionThe declared and derived site areas disagree
site.frontageviolationRoad frontage under 2 m

Each one, read closely with its fix, is on Judgement — koyu validate.

This surface grows

validate's rules are not frozen. Rules get added, and rules can be dropped. That is a different footing from check's 65 diagnostic codes, which are a frozen surface.

So if you gate CI on a finding count, either accept going red when a rule is added, or filter by rule name.

See also