Skip to main content

koyu validate — architectural judgement

koyu check says only that what is written is not self-contradictory as data. Whether the daylight is sufficient, whether the envelope closes, whether the stair can be climbed, whether a car can leave the garage, whether the building fits on its site — none of that is in there. koyu validate says it, and this volume documents it.

koyu validate examples/tower/main.muro
✔ Nothing caught by validation (this is a judgement, not a guarantee about the composition)

Every bundled building (two-rooms, office, house, mansion, tower, basement, complex, twin) currently comes back with nothing caught.

The two greens cannot be spoken of in the same words

So that a judgement is never read as a guarantee about the composition, the two are different types.

structural diagnosticarchitectural finding
typeDiagnosticFinding
identifiercode: "BND04"rule: "daylight.ratio"
weightseverity: "error" | "warning"level: "violation" | "caution"
entry pointcheckDiagnostics(model) / koyu checkvalidate(model) / koyu validate
spellingfour capitals plus two digitsa chapter and a subject joined by .
versionfrozennot frozen — rules are added, sharpened, dropped

The field names differ, so the two arrays cannot be confused; concatenate them and the type collapses. Nobody mistakes ENV01 for envelope.gap — that is exactly why the spellings were split.

level is an invariant property of the rule. The same rule is never a violation in one model and a caution in another. Changing the weight means minting a new rule name.

Levels and exit codes

levelmeaningkoyu validate exit code
violationthe rule is not met1
cautionsuspicious, or not fully counted0

This is a different axis from check's error / warning. Validation can be green while the composition is broken, and the other way round. --json does not change the exit rule.

The ledger — fifteen rules

The order below is the order of the scan, and the order the output comes out in.

rulelevelwhat it says
envelope.gapcautionpart of the outline faces nothing — a hole in the envelope
daylight.ratioviolationeffective window area is below one seventh of the floor area
daylight.unknowncautiona window without h: means the window area is not fully counted
stair.proportioncautionthe derived step is cramped (going under 240mm, or 2×riser+going outside 550–700mm)
run.slopecautionthe derived slope is steeper than the declared slope:, or outside the usual escalator band
run.disconnectedcautionthe vertical run has a shape but no vertical boundary joining the levels
access.unreachableviolationa space with a region cannot reach the outside
access.voidonlyviolationthe doors open only onto a void, where there is no floor
access.throughtenantcautionescape from a stair necessarily passes through a tenancy
access.parkingviolationa car cannot get out of the parking
access.backofhousecautiona vertical run is not reachable from a common corridor without crossing back-of-house
column.blocksdoorviolationa derived column overlaps a derived door
site.escapeviolationthe building escapes the site outline
site.areacautionthe declared and derived site areas disagree
site.frontageviolationthe road frontage is under 2 m

The chapters (envelope, daylight, stair, run, access, column, site) name subjects, not jurisdictions. When a second jurisdiction arrives — Japanese building law and somebody else's — it is added underneath a chapter.

Thresholds

The thresholds are numbers on the architectural side, not invariants the written composition must satisfy. That is why they are gathered in one place.

constantvaluewhich rule reads it
DAYLIGHT_DIVISOR7daylight.ratio
TREAD_MIN240mmstair.proportion
STEP_RULE550–700mmstair.proportion
ESCALATOR_SLOPE1/2.3 – 1/1.4run.slope
CAR_WIDTH_MIN2400mmaccess.parking
FRONTAGE_MIN2000mmsite.frontage
site area tolerance±0.05 m²site.area
site shape tolerance1mm (on the line counts as inside)site.escape

The 0.7 semi-outdoor daylight factor is not in this table. It is a factor, not a threshold — it belongs to the derivation of what a window faces. See daylight.

Three entry points

koyu validate <file.muro> # for people. 0 = no violations / 1 = violations
koyu validate <file.muro> --json # Finding[] as JSON
import { validate, VALIDATION_RULES, type Finding } from "@kensnzk/koyu";
// there is also a subpath entry: import { validate } from "@kensnzk/koyu/validate";

const findings: Finding[] = validate(model);
const violations = findings.filter((f) => f.level === "violation");

One finding out of --json has this shape.

{
"rule": "daylight.unknown",
"level": "caution",
"message": "Window area is not fully counted: /L1/a has a window without h: (write h: on it)",
"line": 6,
"file": "/path/to/main.muro",
"path": [
"/L1/a"
]
}

line is the source line, file is the composed layer that wrote the value, and path is the space or zone concerned. Some judgements have no position, so line and file can be absent.

In the koyu-mcp server the validate tool returns the same Finding[] alongside the violations and cautions counts. Every judgement is callable over MCP — a judgement a machine cannot call does not exist as far as the machine is concerned.

If you arrived here holding a retired diagnostic code

Some codes that check once emitted were architectural judgements, and moved to this surface. The spellings are never reused, because that would make old output unreadable.

old coderule today
ENV01envelope.gap
RUN06stair.proportion
RUN07run.slope
RUN08run.disconnected
SIT03site.escape
SIT05site.area

The full list of retired numbers is at retired diagnostic codes.

This surface does not freeze

The rules may be coarse, the jurisdiction may be a single country, the precision may be short — this surface can still grow, and rules can still be dropped. It is cheap precisely because it does not freeze. Adding a judgement does not move the language version: one line in the ledger, one section in this volume.

There is exactly one condition on being allowed to be untidy here. A judgement must never be confused with a guarantee about the composition. That is why the types are separate, the spellings are separate, and this volume stands apart from the check volume.