Skip to main content

koyu validate — architectural judgement

For mode spatial, see spatial models: unwritten boundaries stay unresolved, heights and slabs may be omitted, and architectural assessment is not evaluated.

koyu check says only that what is written is not self-contradictory as data. Whether the daylight is sufficient, 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 --profile koyu.profile.schematic-screen --as-of 2026-08-03
✔ 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.

Nothing runs that you did not name​

There is no default profile, and no rule runs because it was imported. A rule pack is a value: you compose a registry, name one profile and one context, and pass them in. Leave the profile out and the run stops before it starts — a judgement whose grounds were guessed cannot be reproduced, and one that quietly picked a jurisdiction is worse than one that refused.

The date is explicit for the same reason. koyu never reads the clock to fill it in, so the same file judges the same way twice.

koyu validate main.muro # exit 2 — no profile
koyu validate main.muro --profile koyu.profile.schematic-screen # exit 2 — no date

This pack is design lint, not code compliance​

koyu ships the schematic-screen profile and its rule set.

rule setkoyu.ruleset.schematic-screen@1
profilekoyu.profile.schematic-screen@1
purposedesign-lint
jurisdictionnone

The names say what they are worth. Some of the constants below recall statute or custom, but nothing here reads a jurisdiction, an effective date, a use-class condition, an exception or an administrative interpretation — so no rule is named after a law, and none of them cites an authority. What this pack is good for is an early, reproducible screen at the resolution of a scheme design.

A pack that does apply a law is somebody's to write, and it uses the same public SPI these rules use. See the API reference.

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
typeDiagnosticAssessmentFinding wrapping a RuleOutcome
identifiercode: "BND04"rule: { id: "koyu.schematic.daylight.ratio", revision: "1" }
weightseverity: "error" | "warning"level: "violation" | "caution"
per subject—status: "pass" | "fail" | "indeterminate"
entry pointcheckDiagnostics(model) / koyu checkassess(model, …) / koyu validate
spellingthree capitals plus two digitsa dotted, namespaced identifier
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 SIT03 for koyu.schematic.site.escape — 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 identity.

Silence is not a pass​

A bare list of failures cannot tell you whether a rule passed, never applied, or could not run at all. The report keeps them apart, and so does the exit code.

rule statemeaning
evaluatedthe rule had subjects and reached a verdict on each
not-applicablenothing in this building is the kind of thing the rule is about
indeterminatean input was missing, so no verdict was reached
errorthe rule or its analysis failed to run
outcome statusmeaning
passthis subject meets the rule
failit does not — this is what becomes a finding
indeterminatethis subject could not be decided

Levels and exit codes​

levelmeaningexit code
violationthe rule is not met1
cautionsuspicious, or not fully counted0

koyu validate exits 0 only when the whole set ran and nothing was violated. An indeterminate rule, a rule that errored, or an inconsistent model exits 1 — not being able to judge is not the same as passing. A missing or unknown profile, a missing date, or malformed context exits 2, like any other usage error. --json does not change the exit rule.

This is a different axis from check's error / warning. Validation can be green while the composition is broken, and the other way round.

The ledger​

The rows follow the order the rule set declares them in. That is not the order of the output within a chapter — outcomes come out one scan unit at a time.

rulelevelwhat it says
koyu.schematic.daylight.ratioviolationeffective window area is below one seventh of the floor area
koyu.schematic.daylight.unknowncautiona window without h: means the window area is not fully counted
koyu.schematic.stair.proportioncautionthe derived step is cramped (going under 240mm, or 2×riser+going outside 550–700mm)
koyu.schematic.ramp.declared-slopecautionthe derived slope is steeper than the ramp's own declared slope:
koyu.schematic.escalator.usual-slopecautionthe derived escalator pitch is outside the usual band
koyu.schematic.run.disconnectedcautionthe vertical run has a shape but no vertical boundary joining the levels
koyu.schematic.access.unreachableviolationa space with a region cannot reach the outside
koyu.schematic.access.voidonlyviolationthe doors open only onto a void, where there is no floor
koyu.schematic.access.throughtenantcautionescape from a stair necessarily passes through a tenancy
koyu.schematic.access.parkingviolationa car cannot get out of the parking
koyu.schematic.access.backofhousecautiona vertical run is not reachable from a common corridor without crossing back-of-house
koyu.schematic.column.blocksdoorviolationa derived column overlaps a derived door
koyu.schematic.opening.storage-leaves-spaceviolationa sliding leaf parks outside the space selected for its operation
koyu.schematic.opening.storage-overlaps-openingviolationa parked sliding leaf covers another aperture on the same wall axis
koyu.schematic.site.escapeviolationthe building escapes the site outline
koyu.schematic.site.areacautionthe declared and derived site areas disagree
koyu.schematic.site.frontageviolationthe road frontage is under 2 m

The chapters name subjects, not jurisdictions.

A ramp and an escalator are two rules, not one. They both leave a slope band, but one band is the limit the designer wrote in the file and the other is a custom this pack carries. Different grounds, so either can be revised without touching the other.

The analyses​

A rule never reads the model. It reads an analysis — a versioned computation that returns facts and no verdict at all.

analysiswhat it observes
koyu.analysis.daylight@1floor area, effective window area, windows with no h:
koyu.analysis.vertical-runs@1derived rise, run, tread, riser, slope, and whether a vertical boundary links the levels
koyu.analysis.access@1which spaces reach the exterior, and by what route
koyu.analysis.door-column-collisions@1derived door and column geometry, and where they intersect
koyu.analysis.opening-operations@1placed sliding-leaf storage, its intended space, crossed spaces and covered apertures
koyu.analysis.site@1site area, footprint, total floor, coverage, floor-area ratio, frontage, containment

An artifact carries measurements, evidence pointing back at the line that produced them, and whatever input was missing. It carries no pass, no fail and no level. That separation is what lets somebody else's rule pack reuse koyu's arithmetic and draw its own line.

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_MIN_MM240mmstair.proportion
STEP_RULE_MM550–700mmstair.proportion
ESCALATOR_SLOPE_BAND1/2.3 – 1/1.4escalator.usual-slope
CAR_WIDTH_MIN2400mmaccess.parking
SITE_FRONTAGE_MIN_MM2000mmsite.frontage
SITE_AREA_TOLERANCE_M2±0.05 m²site.area
SITE_CONTAINMENT_TOLERANCE_MM1mm (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.

Calling validation​

koyu validate <file.muro> --profile koyu.profile.schematic-screen --as-of 2026-08-03
koyu validate <file.muro> --profile … --as-of … --json # the whole AssessmentReport as JSON
import { assess } from "@kensnzk/koyu/validate";
import { createSchematicRegistry, SCHEMATIC_PROFILE_ID } from "@kensnzk/koyu/validate/builtin";

const report = assess(model, {
registry: createSchematicRegistry(),
profile: SCHEMATIC_PROFILE_ID,
context: { schema: "koyu-context/1", asOf: "2026-08-03", values: {} },
});

const violations = report.findings.filter((f) => f.level === "violation");

In the koyu-mcp server the validate tool takes the same profile and asOf and returns the same report. Every judgement is callable over MCP — a judgement a machine cannot call does not exist as far as the machine is concerned.

One finding out of --json has this shape.

{
"rule": { "id": "koyu.schematic.daylight.unknown", "revision": "1" },
"ruleSet": { "id": "koyu.ruleset.schematic-screen", "revision": "1" },
"level": "caution",
"outcome": {
"id": "/L1/a",
"status": "fail",
"subjects": [ { "kind": "space", "ref": "/L1/a" } ],
"message": "Window area is not fully counted: /L1/a has a window without h: (write h: on it)"
}
}

The evidence behind each outcome names the analysis that produced it and the source line it came from, so a verdict can always be walked back to the declaration that caused it.

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
RUN06koyu.schematic.stair.proportion
RUN07koyu.schematic.ramp.declared-slope / koyu.schematic.escalator.usual-slope
RUN08koyu.schematic.run.disconnected
SIT03koyu.schematic.site.escape
SIT05koyu.schematic.site.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 absent, 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.

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.