Skip to content

Style Rules

llms.txt lists every page for an agent

Core style rules check structure and naming in every chant project, whatever the lexicon.


Resource constructor properties must not contain inline object literals or arrays-of-objects. Each config value should be its own named, exported const.

Severity: warning

// ❌ Triggers COR001
export const bucket = new _.Bucket({
bucketEncryption: {
serverSideEncryptionConfiguration: [
{ serverSideEncryptionByDefault: { sseAlgorithm: "AES256" } },
],
},
});
// ✅ Fixed — extract each config value
export const encryptionDefault = new _.ServerSideEncryptionByDefault({
sseAlgorithm: "AES256",
});
export const encryptionRule = new _.ServerSideEncryptionRule({
serverSideEncryptionByDefault: encryptionDefault,
});
export const bucketEncryption = new _.BucketEncryption({
serverSideEncryptionConfiguration: [encryptionRule],
});
export const bucket = new _.Bucket({
bucketEncryption: bucketEncryption,
});

Simple string, number, and boolean properties are allowed inline. Only nested object literals and arrays containing objects are flagged.

The rule looks only at resource constructors. A property-kind declarable is one a lexicon builds with createProperty and lists in its plugin’s propertyClassNames(). It is already a nested value inside the resource that holds it, so the objects passed to it are not flagged. Grafana declares its panel classes this way, so a panel can carry its fieldConfig and options inline.

// OK: TimeSeriesPanel is property-kind
export const overview = new Dashboard({
title: "Overview",
panels: [
new TimeSeriesPanel({ title: "Latency", fieldConfig: { defaults: { unit: "ms" } } }),
],
});

A lexicon that does not implement propertyClassNames() gets the rule on every constructor, as before.


Exported declarable instances should use camelCase naming. PascalCase and UPPER_SNAKE_CASE are reserved for types and constants.

Severity: warning

// ❌ Triggers COR005
export const DataBucket = new _.Bucket({
bucketName: "my-data",
});
export const DATA_BUCKET = new _.Bucket({
bucketName: "my-data",
});
// ✅ Fixed
export const dataBucket = new _.Bucket({
bucketName: "my-data",
});

Only applies to exported variables initialized with new expressions.


A file may hold up to 8 resource declarations by default.

Only resources count. The classes a lexicon lists in its plugin’s propertyClassNames() are property-kind and are left out. A Grafana dashboard file with twenty panels inside one dashboard counts as one declarable. A lexicon that does not implement propertyClassNames() has every declarable counted, as before.

Severity: warning

⚠ COR009: File contains 12 Declarable instances (limit: 8) — consider splitting into separate files by concern

Fix: Split the file by concern. For example, separate networking resources from compute resources.

Configure the limit:

chant.config.ts
export default {
lint: {
rules: {
COR009: ["warning", { max: 12 }],
},
},
};

When a namespace import already exists for a package, a separate import type from the same package is redundant.

Severity: warning

// ❌ Triggers COR012
import { isDeclarable } from "@intentius/chant";
import type { Declarable } from "@intentius/chant";
// ✅ Fixed — import the type as a named import
import { isDeclarable, type Declarable } from "@intentius/chant";
const d: Declarable = /* ... */;

chant lint --fix removes the redundant import type line.


Keep resources (e.g., Bucket, Function, Role) and properties (e.g., BucketEncryption, VersioningConfiguration, PublicAccessBlockConfiguration) in separate files.

Severity: info

// ❌ Triggers COR013 — mixes resource and configuration
export const encryptionDefault = new _.ServerSideEncryptionByDefault({
sseAlgorithm: "AES256",
});
export const bucket = new _.Bucket({
bucketEncryption: _.bucketEncryption,
});

Fix: Move configuration declarations to a defaults.ts file and resources to their own files.


AttrRef and other intrinsic types already satisfy Value<T>, so casting with as Value<...> is redundant.

Severity: warning

// ❌ Triggers COR015
export const fn = new _.Function({
role: functionRole.arn as Value<string>,
});
// ✅ Fixed — use directly without cast
export const fn = new _.Function({
role: functionRole.arn,
});

chant