Multilingual semantic programming for application code and GPU work · MIT
Write compute programs
in the language you think in.
One typed program stays readable in the language you work in, then lowers toward application targets and a measured GPU path. Support is stated target by target.
The language, public libraries (including Gradus), examples, and tooling ship under the MIT license. Radix, the compiler, is closed only while it is under active development. That is temporary, not a permanent fence.
import from "gradus:loss" loss
main {
const tensor<f32, []> seed ← empty
const list<int> shape_2x2 ← [2, 2]
const tensor<f32, [2, 2]> prediction ← seed.from_flat([1.0, 2.0, 3.0, 4.0], shape_2x2)
const tensor<f32, [2, 2]> target ← seed.from_flat([1.0, 2.0, 3.0, 3.0], shape_2x2)
const f32 value ← loss.mse_2x2(prediction, target)
print value
}
/llms.txt.
Readable in your language. Same meaning.
Faber’s reader locales change keywords, types, and diagnostics without changing program meaning. This example constructs two typed matrices, multiplies them, and reduces the product to a scalar. Pick a tab and that same compute program remains the same program. Identifiers and string literals stay intact, so teams can review durable code across language surfaces without a translation service in the middle.
faber convert --to en — English reader surface — the base spelling for everyday sourcemain {
const tensor<f32, [2, 3]> a ← empty
const tensor<f32, [3, 4]> b ← empty
const tensor<f32, [2, 4]> product ← a · b
print product
}faber convert --to la — canonical Faber — the classical surface the language is named forincipit {
fixum tensor<f32, [2, 3]> a ← vacua
fixum tensor<f32, [3, 4]> b ← vacua
fixum tensor<f32, [2, 4]> product ← a · b
nota product
}faber convert --to th-TH — Thai — spaceless scriptเริ่ม {
คงที่ เทนเซอร์<f32, [2, 3]> a ← เซตว่าง
คงที่ เทนเซอร์<f32, [3, 4]> b ← เซตว่าง
คงที่ เทนเซอร์<f32, [2, 4]> product ← a · b
บันทึก product
}faber convert --to zh-Hans — Simplified Chinese入口 {
常量 张量<f32, [2, 3]> a ← 空集
常量 张量<f32, [3, 4]> b ← 空集
常量 张量<f32, [2, 4]> product ← a · b
显示 product
}faber convert --to zh-Hant — Traditional Chinese入口 {
定值 張量<f32, [2, 3]> a ← 空集
定值 張量<f32, [3, 4]> b ← 空集
定值 張量<f32, [2, 4]> product ← a · b
註記 product
}faber convert --to vi — Vietnamesebắt_đầu {
hằng ten_xo<f32, [2, 3]> a ← tập_rỗng
hằng ten_xo<f32, [3, 4]> b ← tập_rỗng
hằng ten_xo<f32, [2, 4]> product ← a · b
ghi_chú product
}faber convert --to ar — Arabic — right-to-left, bidi isolatedبداية {
ثابت موتر<f32, [2, 3]> a ← فارغ
ثابت موتر<f32, [3, 4]> b ← فارغ
ثابت موتر<f32, [2, 4]> product ← a · b
اعرض product
}faber convert --to hi — Hindi — Devanagariआरंभ {
स्थिर टेंसर<f32, [2, 3]> a ← खाली
स्थिर टेंसर<f32, [3, 4]> b ← खाली
स्थिर टेंसर<f32, [2, 4]> product ← a · b
दिखाओ product
}$ faber run --interpret <package> 76.25
A reviewer sets their locale once. This is the compiler’s own rendering, so the program you approve is the program that ships.
One semantic program for applications and GPU work
The same analyzed program can feed application targets or a device program. Every target is a projection of HIR/MIR meaning — support is stated target by target. The target matrix is the source of truth, not a promise that every backend behaves the same way.
Every panel below is literal radix emit output. The matrix
records where a target emits, validates, runs, or remains limited. See
target matrix
for the current boundary.
radix emit --target rust main.fab — HIR projection — reviewable source; package product path via Cargo// Generated by radix - do not edit
// Requires the faber language-runtime crate (add to Cargo.toml):
// faber = { path = "../faber" } # adjust path for your layout
fn main() {
let a: faber::Tensor<f32> /* tensor<fractus<f32>, [2, 3]> */ = faber::Tensor::vacua();
let b: faber::Tensor<f32> /* tensor<fractus<f32>, [3, 4]> */ = faber::Tensor::vacua();
let product: faber::Tensor<f32> /* tensor<fractus<f32>, [2, 4]> */ = { let t6 = &a; t6.matmul(&(b)) }.expect("tensor matmul failed");
println!("{:?}", product);
}radix emit --target go main.fab — HIR projection — file emission + e2e floors// Generated by radix - do not edit
package main
import "fmt"
type faberTensor[T any] struct {
data []T
shape []int
}
func faberTensorElementCount(shape []int) int {
const maxInt = int(^uint(0) >> 1)
total := 1
for _, dim := range shape {
if dim < 0 { panic("tensor shape dimension must be non-negative") }
if dim > 0 && total > maxInt/dim { panic("tensor shape element count overflow") }
total *= dim
}
return total
}
func faberIndexSlice(indices any) []int {
switch values := indices.(type) {
case []int:
return append([]int{}, values...)
case []uint32:
out := make([]int, len(values)); for i, value := range values { out[i] = int(value) }; return out
case []uint64:
out := make([]int, len(values)); for i, value := range values { out[i] = int(value) }; return out
case []int32:
out := make([]int, len(values)); for i, value := range values { out[i] = int(value) }; return out
case []int64:
out := make([]int, len(values)); for i, value := range values { out[i] = int(value) }; return out
default:
panic("tensor index must be a numeric list")
}
}
func faberTensorOffset(shape []int, rawIndices any) *int {
const maxInt = int(^uint(0) >> 1)
indices := faberIndexSlice(rawIndices)
if len(indices) != len(shape) { return nil }
offset := 0
stride := 1
for axis := len(shape) - 1; axis >= 0; axis-- {
idx := indices[axis]
dim := shape[axis]
if dim < 0 || idx < 0 || idx >= dim { return nil }
if idx > 0 && stride > (maxInt-offset)/idx { return nil }
offset += idx * stride
if dim > 0 && stride > maxInt/dim { return nil }
stride *= dim
}
return &offset
}
func (t faberTensor[T]) Crea(fill T, shape []int) faberTensor[T] {
data := make([]T, faberTensorElementCount(shape))
for i := range data { data[i] = fill }
return faberTensor[T]{data: data, shape: append([]int{}, shape...)}
}
func (t faberTensor[T]) Strue(data []T, shape []int) faberTensor[T] {
if faberTensorElementCount(shape) != len(data) { panic("tensor structa element count does not match shape") }
return faberTensor[T]{data: append([]T{}, data...), shape: append([]int{}, shape...)}
}
func (t faberTensor[T]) Longitudo() int { return len(t.shape) }
func (t faberTensor[T]) Magnitudines() []int { return append([]int{}, t.shape...) }
func (t faberTensor[T]) Planata() []T { return append([]T{}, t.data...) }
func (t faberTensor[T]) Materialize() faberTensor[T] { return faberTensor[T]{data: append([]T{}, t.data...), shape: append([]int{}, t.shape...)} }
func faberTensorAdd[T any](left T, right T) T {
switch value := any(left).(type) {
case int: return any(value + any(right).(int)).(T)
case int32: return any(value + any(right).(int32)).(T)
case int64: return any(value + any(right).(int64)).(T)
case uint: return any(value + any(right).(uint)).(T)
case uint32: return any(value + any(right).(uint32)).(T)
case uint64: return any(value + any(right).(uint64)).(T)
case float32: return any(value + any(right).(float32)).(T)
case float64: return any(value + any(right).(float64)).(T)
default: panic("tensor arithmetic requires numeric elements")
}
}
func faberTensorMul[T any](left T, right T) T {
switch value := any(left).(type) {
case int: return any(value * any(right).(int)).(T)
case int32: return any(value * any(right).(int32)).(T)
case int64: return any(value * any(right).(int64)).(T)
case uint: return any(value * any(right).(uint)).(T)
case uint32: return any(value * any(right).(uint32)).(T)
case uint64: return any(value * any(right).(uint64)).(T)
case float32: return any(value * any(right).(float32)).(T)
case float64: return any(value * any(right).(float64)).(T)
default: panic("tensor arithmetic requires numeric elements")
}
}
func faberTensorSub[T any](left T, right T) T {
switch value := any(left).(type) {
case int: return any(value - any(right).(int)).(T)
case int32: return any(value - any(right).(int32)).(T)
case int64: return any(value - any(right).(int64)).(T)
case uint: return any(value - any(right).(uint)).(T)
case uint32: return any(value - any(right).(uint32)).(T)
case uint64: return any(value - any(right).(uint64)).(T)
case float32: return any(value - any(right).(float32)).(T)
case float64: return any(value - any(right).(float64)).(T)
default: panic("tensor arithmetic requires numeric elements")
}
}
func faberTensorShapeEqual(left []int, right []int) bool {
if len(left) != len(right) { return false }
for i, dim := range left { if dim != right[i] { return false } }
return true
}
func faberTensorMean[T any](data []T) T {
if len(data) == 0 { panic("tensor media requires non-empty data") }
switch any(data[0]).(type) {
case float32:
var total float32
for _, value := range data { total += any(value).(float32) }
return any(total / float32(len(data))).(T)
case float64:
var total float64
for _, value := range data { total += any(value).(float64) }
return any(total / float64(len(data))).(T)
default: panic("tensor media requires floating-point elements")
}
}
func (t faberTensor[T]) Summa() T {
var total T
for _, value := range t.data { total = faberTensorAdd(total, value) }
return total
}
func (t faberTensor[T]) Media() T { return faberTensorMean(t.data) }
func (a faberTensor[T]) Addita(b faberTensor[T]) faberTensor[T] {
if !faberTensorShapeEqual(a.shape, b.shape) { panic("tensor elementwise arithmetic requires equal shapes") }
data := make([]T, len(a.data))
for i := range data { data[i] = faberTensorAdd(a.data[i], b.data[i]) }
return faberTensor[T]{data: data, shape: append([]int{}, a.shape...)}
}
func (a faberTensor[T]) Subtrahe(b faberTensor[T]) faberTensor[T] {
if !faberTensorShapeEqual(a.shape, b.shape) { panic("tensor elementwise arithmetic requires equal shapes") }
data := make([]T, len(a.data))
for i := range data { data[i] = faberTensorSub(a.data[i], b.data[i]) }
return faberTensor[T]{data: data, shape: append([]int{}, a.shape...)}
}
func (a faberTensor[T]) Multiplica(b faberTensor[T]) faberTensor[T] {
if !faberTensorShapeEqual(a.shape, b.shape) { panic("tensor elementwise arithmetic requires equal shapes") }
data := make([]T, len(a.data))
for i := range data { data[i] = faberTensorMul(a.data[i], b.data[i]) }
return faberTensor[T]{data: data, shape: append([]int{}, a.shape...)}
}
func (a faberTensor[T]) Matmul(b faberTensor[T]) faberTensor[T] {
if len(a.shape) != 2 || len(b.shape) != 2 || a.shape[1] != b.shape[0] { panic("tensor matmul requires compatible rank-2 shapes") }
rows, inner, cols := a.shape[0], a.shape[1], b.shape[1]
data := make([]T, rows*cols)
for row := 0; row < rows; row++ {
for col := 0; col < cols; col++ {
var sum T
for k := 0; k < inner; k++ { sum = faberTensorAdd(sum, faberTensorMul(a.data[row*inner+k], b.data[k*cols+col])) }
data[row*cols+col] = sum
}
}
return faberTensor[T]{data: data, shape: []int{rows, cols}}
}
func (t faberTensor[T]) Forma(shape []int) faberTensor[T] {
if faberTensorElementCount(shape) != len(t.data) { panic("tensor forma (reshape) element count mismatch") }
return faberTensor[T]{data: append([]T{}, t.data...), shape: append([]int{}, shape...)}
}
func (t faberTensor[T]) Accipe(indices any) *T {
offset := faberTensorOffset(t.shape, indices)
if offset == nil || *offset < 0 || *offset >= len(t.data) { return nil }
return &t.data[*offset]
}
func (t *faberTensor[T]) Ponde(indices any, value T) {
offset := faberTensorOffset(t.shape, indices)
if offset == nil || *offset < 0 || *offset >= len(t.data) { panic("tensor ponde invalid index") }
t.data[*offset] = value
}
func (t *faberTensor[T]) Reple(value T) {
for i := range t.data { t.data[i] = value }
}
func (t faberTensor[T]) Sectio(start int, end int) faberTensor[T] {
if len(t.shape) == 0 || start < 0 || end < start || end > t.shape[0] { panic("tensor sectio invalid slice bounds") }
inner := faberTensorElementCount(t.shape[1:])
shape := append([]int{end - start}, t.shape[1:]...)
return faberTensor[T]{data: append([]T{}, t.data[start*inner:end*inner]...), shape: shape}
}
func main() {
a := faberTensor[float32]{}.Crea(*new(float32), []int{2, 3})
b := faberTensor[float32]{}.Crea(*new(float32), []int{3, 4})
product := a.Matmul(b)
fmt.Println(product)
}radix emit --target ts main.fab — HIR projection — file emission + e2e floors// … 242 lines of generated display/runtime shim elided …
const a: FaberTensor<number> = FaberTensor.empty<number>([2, 3]);
const b: FaberTensor<number> = FaberTensor.empty<number>([3, 4]);
const product: FaberTensor<number> = a.matmul(b);
console.log(__faberDisplay(product, { kind: "tensor", element: "fractus" }));
}})();radix emit --target llvm-text main.fab — MIR staging text for external LLVM tools — not embedded native codegen; Generated by radix MIR LLVM IR probe - experimental artifact.
%FaberRtSliceV1 = type { ptr, i64 }
%FaberRtExitV1 = type i64
%FaberRtPtrResultV1 = type { i32, ptr }
%FaberRtStatusV1 = type { i32 }
@__faber_rt_v1_context = linkonce_odr global ptr null
@__faber_rt_v1_status = linkonce_odr global i32 0
declare void @__faber_rt_v1_fatal(ptr, %FaberRtSliceV1) noreturn
declare void @__faber_rt_v1_numerus_overflow(ptr) noreturn
; @runtime __faber_rt_v1_diagnostic_nota_ptr category=host-integration
declare i32 @__faber_rt_v1_diagnostic_nota_ptr(ptr, ptr)
; @runtime __faber_rt_v1_tensor_matmul category=core-semantics
declare %FaberRtPtrResultV1 @__faber_rt_v1_tensor_matmul(ptr, ptr, ptr)
; @runtime __faber_rt_v1_tensor_new category=core-semantics
declare %FaberRtPtrResultV1 @__faber_rt_v1_tensor_new(ptr, i32)
define void @incipit() {
entry:
%l0.addr = alloca ptr
%l1.addr = alloca ptr
%l2.addr = alloca ptr
%t0.addr = alloca ptr
%t1.addr = alloca ptr
%t2.addr = alloca ptr
br label %b0
b0:
%faber.context0 = load ptr, ptr @__faber_rt_v1_context
%faber.tensor.result0 = call %FaberRtPtrResultV1 @__faber_rt_v1_tensor_new(ptr %faber.context0, i32 5)
%faber.tensor.status0 = extractvalue %FaberRtPtrResultV1 %faber.tensor.result0, 0
%faber.tensor.value0 = extractvalue %FaberRtPtrResultV1 %faber.tensor.result0, 1
%faber.old.status0 = load i32, ptr @__faber_rt_v1_status
%faber.has.error0 = icmp ne i32 %faber.old.status0, 0
%faber.latched.status0 = select i1 %faber.has.error0, i32 %faber.old.status0, i32 %faber.tensor.status0
store i32 %faber.latched.status0, ptr @__faber_rt_v1_status
store ptr %faber.tensor.value0, ptr %t0.addr
%load1 = load ptr, ptr %t0.addr
store ptr %load1, ptr %l0.addr
%faber.context2 = load ptr, ptr @__faber_rt_v1_context
%faber.tensor.result2 = call %FaberRtPtrResultV1 @__faber_rt_v1_tensor_new(ptr %faber.context2, i32 5)
%faber.tensor.status2 = extractvalue %FaberRtPtrResultV1 %faber.tensor.result2, 0
%faber.tensor.value2 = extractvalue %FaberRtPtrResultV1 %faber.tensor.result2, 1
%faber.old.status2 = load i32, ptr @__faber_rt_v1_status
%faber.has.error2 = icmp ne i32 %faber.old.status2, 0
%faber.latched.status2 = select i1 %faber.has.error2, i32 %faber.old.status2, i32 %faber.tensor.status2
store i32 %faber.latched.status2, ptr @__faber_rt_v1_status
store ptr %faber.tensor.value2, ptr %t1.addr
%load3 = load ptr, ptr %t1.addr
store ptr %load3, ptr %l1.addr
%load5 = load ptr, ptr %l0.addr
%faber.context4 = load ptr, ptr @__faber_rt_v1_context
%load6 = load ptr, ptr %l1.addr
%faber.tensor.result4 = call %FaberRtPtrResultV1 @__faber_rt_v1_tensor_matmul(ptr %faber.context4, ptr %load5, ptr %load6)
%faber.tensor.status4 = extractvalue %FaberRtPtrResultV1 %faber.tensor.result4, 0
%faber.tensor.value4 = extractvalue %FaberRtPtrResultV1 %faber.tensor.result4, 1
%faber.old.status4 = load i32, ptr @__faber_rt_v1_status
%faber.has.error4 = icmp ne i32 %faber.old.status4, 0
%faber.latched.status4 = select i1 %faber.has.error4, i32 %faber.old.status4, i32 %faber.tensor.status4
store i32 %faber.latched.status4, ptr @__faber_rt_v1_status
store ptr %faber.tensor.value4, ptr %t2.addr
%load7 = load ptr, ptr %t2.addr
store ptr %load7, ptr %l2.addr
%faber.context8 = load ptr, ptr @__faber_rt_v1_context
%faber.diag.status8 = call i32 @__faber_rt_v1_diagnostic_nota_ptr(ptr %faber.context8, ptr null)
ret void
}
define %FaberRtExitV1 @__faber_program_entry_v1(ptr %context) {
entry:
store ptr %context, ptr @__faber_rt_v1_context
call void @incipit()
%faber.entry.status = load i32, ptr @__faber_rt_v1_status
%faber.entry.status.ext = zext i32 %faber.entry.status to i64
%faber.entry.shifted = shl i64 %faber.entry.status.ext, 32
%faber.entry.packed = or i64 0, %faber.entry.shifted
ret %FaberRtExitV1 %faber.entry.packed
}Compiler lanes
| Lane | Targets / outputs |
|---|---|
| Locale | en (base surface) · la (canonical classical) · th-TH · zh-Hans · zh-Hant · ar · vi · hi |
| HIR | Rust · Faber · TypeScript · Go · Swift |
| AIR (autograd) | Typed HIR → reverse-mode AD / fusion → MIR |
| MIR | LLVM · WASM · WGSL · S-expression · FMIR |
| GPU | Metal · CUDA |
| Packaging | FHIR · FMIR |
Training through Metal or CUDA
The ordinary faber run --backend metal|cuda route executes
a bounded device-program subset on accepted Metal and CUDA machines.
The accepted dual-backend MLP training path runs device-resident
forward, AIR-generated backward, and optimizer update steps with
gradient mapping and per-element numeric comparison against a pinned
CPU oracle.
$ faber run --backend metal <package> $ faber run --backend cuda <package>
This is a bounded training proof, not a claim of a general training framework, broad hardware coverage, or a released package surface. Device execution is explicit and fail-closed: a requested backend does not silently fall back to CPU.
Read the device execution contract · Open the training proof
One kernel, backend-specific output
A function marked @ nucleum is a compute kernel. The source
stays small while Faber emits backend-specific shader code. These panels
show the lowering surface; the real-device route above is the narrower
product proof.
@ nucleum
functio multiplico(tf32[16, 8] a, tf32[8, 16] b, tf32[16, 16] out, u32 id) → vacuum {
fixum tf32[16, 16] c ← a.matmul(b)
}
radix emit --target wgsl-text kernel.fab — WebGPU compute shader// Generated by radix wgsl-text (supported-with-limitations compute source).
var<workgroup> shared_a: array<f32, 64u>;
var<workgroup> shared_b: array<f32, 64u>;
@group(0) @binding(0) var<storage, read> a_in: array<f32>;
@group(0) @binding(1) var<storage, read> b_in: array<f32>;
@group(0) @binding(2) var<storage, read_write> output: array<f32>;
@compute @workgroup_size(8, 8, 1)
fn multiplico(@builtin(global_invocation_id) id: vec3<u32>, @builtin(local_invocation_id) local_id: vec3<u32>) {
let i: u32 = id.x;
var acc: f32 = 0.0;
let row = id.y;
let col = id.x;
let ty = local_id.y;
let tx = local_id.x;
for (var k_tile: u32 = 0u; k_tile < 1u; k_tile++) {
let k_start = k_tile * 8u;
let a_idx = row * 8u + (k_start + tx);
if (a_idx < 16u * 8u) { shared_a[ty * 8u + tx] = a_in[a_idx]; }
if (a_idx >= 16u * 8u) { shared_a[ty * 8u + tx] = 0.0; }
let b_idx = col * 8u + (k_start + ty);
if (col < 16u && (k_start + ty) < 8u) { shared_b[ty * 8u + tx] = b_in[b_idx]; }
if (col >= 16u || (k_start + ty) >= 8u) { shared_b[ty * 8u + tx] = 0.0; }
workgroupBarrier();
for (var kk: u32 = 0u; kk < 8u; kk++) {
acc += shared_a[ty * 8u + kk] * shared_b[kk * 8u + tx];
}
workgroupBarrier();
}
let out_idx = row * 16u + col;
if (row < 16u && col < 16u) { output[out_idx] = acc; }
}radix emit --target metal-text kernel.fab — Apple GPU compute shader// Generated by radix metal-text (supported-with-limitations compute source).
#include <metal_stdlib>
using namespace metal;
kernel void multiplico(
device const float* a_in [[buffer(0)]],
device const float* b_in [[buffer(1)]],
device float* output [[buffer(2)]],
uint3 id [[thread_position_in_grid]],
uint3 local_id [[thread_position_in_threadgroup]]
) {
uint i = id.x;
threadgroup float shared_a[64];
threadgroup float shared_b[64];
float acc = 0.0;
uint row = id.y;
uint col = id.x;
uint ty = local_id.y;
uint tx = local_id.x;
for (uint k_tile = 0u; k_tile < 1u; k_tile++) {
uint k_start = k_tile * 8u;
uint a_idx = row * 8u + (k_start + tx);
if (a_idx < 16u * 8u) { shared_a[ty * 8u + tx] = a_in[a_idx]; }
if (a_idx >= 16u * 8u) { shared_a[ty * 8u + tx] = 0.0; }
uint b_idx = col * 8u + (k_start + ty);
if (col < 16u && (k_start + ty) < 8u) { shared_b[ty * 8u + tx] = b_in[b_idx]; }
if (col >= 16u || (k_start + ty) >= 8u) { shared_b[ty * 8u + tx] = 0.0; }
threadgroup_barrier(mem_flags::mem_threadgroup);
for (uint kk = 0u; kk < 8u; kk++) {
acc += shared_a[ty * 8u + kk] * shared_b[kk * 8u + tx];
}
threadgroup_barrier(mem_flags::mem_threadgroup);
}
uint out_idx = row * 16u + col;
if (row < 16u && col < 16u) { output[out_idx] = acc; }
}Inference is being built next
Faber-owned GPU inference is in active development behind a pinned model contract and a correctness oracle. The CPU oracle track (admission, dequant, decoder ops, greedy decode agreement) is engineering-real; end-to-end device inference is not shipped, and this is not a broad GGUF product claim.
Follow the AI and GPU examples while the persistent inference path is built.
Build the rest of the application around it
Triga is a graphics and geometry engine written in Faber. These frames are supporting evidence that the same language can carry application and GPU-shaped work — not a replacement for the training and inference path above.
triga-budapest
triga:geometriaFast enough to use like a script
Faber also runs with no build step. faber run --interpret
takes source through parse, typecheck and MIR lowering, then steps the
MIR in-process — no rustc, no linker, no build directory.
| Command | Wall clock |
|---|---|
faber run --interpret (incl. full typecheck) | 4.4 ms |
python3 script.py (no typecheck) | 13.3 ms |
Reproduce with the scripting docs. A statically typed language should not be slower to start than a dynamic one, and it isn't.
Where to go
faber check.
Five-minute tour
The shape of the language, start to finish.
Language
Types, control flow, generics, glyphs, errors.
Reader locales
How the rendering actually works.
Target matrix
Measured lowerability, every term × every backend.
Libraries
Norma, Gradus, Triga, Cista, the language corpus.
Reading this as a model?
Machine surfaces are locale-less and live at the root:
/llms.txt for the index,
/agents/index.md for the
learning path, and
/.well-known/agent-skills/
for focused skill guides.