fn main() {
    println("  ==========================================")
    println("  SILKIE HUMAN TRAINER — SCYLA EDITION")
    println("  Aislinn teaches Silkie directly.")
    println("  ==========================================")
    println("")
    println("  Commands after Silkie responds:")
    println("    /great  - Genuine response (big reward)")
    println("    /good   - Decent attempt (moderate)")
    println("    /ok     - She tried (small)")
    println("    /bad    - Off topic (gentle stress)")
    println("    /quit   - Save and exit")
    println("")

    let n = load_model("examples/silkie_full.safetensors")
    let ns = create_neuron_stack(1, 512, 64)
    let cs = create_cascade()
    let ts = load_tokenizer("examples/silkie_tokenizer.json")
    let tp = load_topos("examples/silkie_topos.json")
    let sc = load_schemas("examples/silkie_schemas.json")
    println("  Model + neurons + cascade + topos + schemas loaded")
    println("  Type a message to Silkie!")
    println("")

    let embed_w = model_get("token_embedding.weight")
    let total_great = 0
    let total_good = 0
    let total_ok = 0
    let total_bad = 0
    let lr = 0.000003

    for turn in 0..1000 {
        let user_msg = input("  You: ")

        if str_eq(user_msg, "/quit") {
            println("")
            println("  Session complete!")
            println("  Great: " + total_great + " Good: " + total_good + " Ok: " + total_ok + " Bad: " + total_bad)
            return
        }

        if str_eq(user_msg, "/help") {
            println("  /great /good /ok /bad /quit")
            let skip = 1
        }

        let generated = tokenize(user_msg)
        let seq_len = tensor_len(generated)

        // Generate Silkie's response
        neuron_stack_reset()
        cascade_reset()
        for step in 0..8 {
            let flat = t_reshape(generated, seq_len)
            let x = embedding_lookup(embed_w, flat)
            let x = t_reshape(x, 1, seq_len, 512)
            for layer_idx in 0..8 {
                let prefix = "layers." + layer_idx
                let x = transformer_block(x,
                    model_get(prefix + ".attention.in_proj_weight"),
                    model_get(prefix + ".attention.in_proj_bias"),
                    model_get(prefix + ".attention.out_proj.weight"),
                    model_get(prefix + ".attention.out_proj.bias"),
                    model_get(prefix + ".ff.0.weight"),
                    model_get(prefix + ".ff.0.bias"),
                    model_get(prefix + ".ff.3.weight"),
                    model_get(prefix + ".ff.3.bias"),
                    model_get(prefix + ".norm1.weight"),
                    model_get(prefix + ".norm1.bias"),
                    model_get(prefix + ".norm2.weight"),
                    model_get(prefix + ".norm2.bias"), 8)
            }
            neuron_stack_apply_noise(1.0)
            let x = neuron_stack_forward(x)
            let pooled = t_mean_seq(x)
            let emotion = t_slice(pooled, -1, 0, 34)
            topos_observe(emotion)
            let x = layer_norm(x, model_get("layers.0.norm1.weight"), model_get("layers.0.norm1.bias"))
            let logits = matmul(x, transpose(embed_w))
            let logits_2d = t_reshape(logits, seq_len, 4100)
            let last_logits = t_slice(logits_2d, 0, seq_len - 1, seq_len)
            let masked = topk_mask(last_logits, 50)
            let probs = softmax(masked)
            let next_token = t_multinomial(probs)
            neuron_stack_suppress_repeat(0.85)
            let generated = t_cat(generated, t_reshape(next_token, 1, 1))
            let seq_len = seq_len + 1
        }

        let gen_flat = t_reshape(generated, seq_len)
        let response = detokenize(gen_flat)
        println("  Silkie: " + response)
        println("")

        // Wait for score
        let score = input("  Score (/great /good /ok /bad): ")

        let reward = 0.0
        if str_eq(score, "/great") {
            let reward = 1.0
            let total_great = total_great + 1
            println("  *Silkie chirps happily*")
        }
        if str_eq(score, "/good") {
            let reward = 0.5
            let total_good = total_good + 1
            println("  *Silkie tilts head*")
        }
        if str_eq(score, "/ok") {
            let reward = 0.1
            let total_ok = total_ok + 1
            println("  *soft chirp*")
        }
        if str_eq(score, "/bad") {
            let reward = -0.5
            let total_bad = total_bad + 1
            println("  *Silkie goes quiet*")
        }

        // Train on this interaction
        gc()
        neuron_stack_reset()
        cascade_reset()
        begin_tape()

        let qkv_0 = register_param("qkv_0", model_get("layers.0.attention.in_proj_weight"))
        let qkv_1 = register_param("qkv_1", model_get("layers.1.attention.in_proj_weight"))
        let qkv_2 = register_param("qkv_2", model_get("layers.2.attention.in_proj_weight"))
        let qkv_3 = register_param("qkv_3", model_get("layers.3.attention.in_proj_weight"))
        let qkv_4 = register_param("qkv_4", model_get("layers.4.attention.in_proj_weight"))
        let qkv_5 = register_param("qkv_5", model_get("layers.5.attention.in_proj_weight"))
        let qkv_6 = register_param("qkv_6", model_get("layers.6.attention.in_proj_weight"))
        let qkv_7 = register_param("qkv_7", model_get("layers.7.attention.in_proj_weight"))

        let flat = t_reshape(generated, seq_len)
        let x = embedding_lookup(embed_w, flat)
        let x = t_reshape(x, 1, seq_len, 512)

        let x = transformer_block_train(x, qkv_0,
            model_get("layers.0.attention.in_proj_bias"),
            model_get("layers.0.attention.out_proj.weight"),
            model_get("layers.0.attention.out_proj.bias"),
            model_get("layers.0.ff.0.weight"), model_get("layers.0.ff.0.bias"),
            model_get("layers.0.ff.3.weight"), model_get("layers.0.ff.3.bias"),
            model_get("layers.0.norm1.weight"), model_get("layers.0.norm1.bias"),
            model_get("layers.0.norm2.weight"), model_get("layers.0.norm2.bias"), 8)
        let x = transformer_block_train(x, qkv_1,
            model_get("layers.1.attention.in_proj_bias"),
            model_get("layers.1.attention.out_proj.weight"),
            model_get("layers.1.attention.out_proj.bias"),
            model_get("layers.1.ff.0.weight"), model_get("layers.1.ff.0.bias"),
            model_get("layers.1.ff.3.weight"), model_get("layers.1.ff.3.bias"),
            model_get("layers.1.norm1.weight"), model_get("layers.1.norm1.bias"),
            model_get("layers.1.norm2.weight"), model_get("layers.1.norm2.bias"), 8)
        let x = transformer_block_train(x, qkv_2,
            model_get("layers.2.attention.in_proj_bias"),
            model_get("layers.2.attention.out_proj.weight"),
            model_get("layers.2.attention.out_proj.bias"),
            model_get("layers.2.ff.0.weight"), model_get("layers.2.ff.0.bias"),
            model_get("layers.2.ff.3.weight"), model_get("layers.2.ff.3.bias"),
            model_get("layers.2.norm1.weight"), model_get("layers.2.norm1.bias"),
            model_get("layers.2.norm2.weight"), model_get("layers.2.norm2.bias"), 8)
        let x = transformer_block_train(x, qkv_3,
            model_get("layers.3.attention.in_proj_bias"),
            model_get("layers.3.attention.out_proj.weight"),
            model_get("layers.3.attention.out_proj.bias"),
            model_get("layers.3.ff.0.weight"), model_get("layers.3.ff.0.bias"),
            model_get("layers.3.ff.3.weight"), model_get("layers.3.ff.3.bias"),
            model_get("layers.3.norm1.weight"), model_get("layers.3.norm1.bias"),
            model_get("layers.3.norm2.weight"), model_get("layers.3.norm2.bias"), 8)
        let x = transformer_block_train(x, qkv_4,
            model_get("layers.4.attention.in_proj_bias"),
            model_get("layers.4.attention.out_proj.weight"),
            model_get("layers.4.attention.out_proj.bias"),
            model_get("layers.4.ff.0.weight"), model_get("layers.4.ff.0.bias"),
            model_get("layers.4.ff.3.weight"), model_get("layers.4.ff.3.bias"),
            model_get("layers.4.norm1.weight"), model_get("layers.4.norm1.bias"),
            model_get("layers.4.norm2.weight"), model_get("layers.4.norm2.bias"), 8)
        let x = transformer_block_train(x, qkv_5,
            model_get("layers.5.attention.in_proj_bias"),
            model_get("layers.5.attention.out_proj.weight"),
            model_get("layers.5.attention.out_proj.bias"),
            model_get("layers.5.ff.0.weight"), model_get("layers.5.ff.0.bias"),
            model_get("layers.5.ff.3.weight"), model_get("layers.5.ff.3.bias"),
            model_get("layers.5.norm1.weight"), model_get("layers.5.norm1.bias"),
            model_get("layers.5.norm2.weight"), model_get("layers.5.norm2.bias"), 8)
        let x = transformer_block_train(x, qkv_6,
            model_get("layers.6.attention.in_proj_bias"),
            model_get("layers.6.attention.out_proj.weight"),
            model_get("layers.6.attention.out_proj.bias"),
            model_get("layers.6.ff.0.weight"), model_get("layers.6.ff.0.bias"),
            model_get("layers.6.ff.3.weight"), model_get("layers.6.ff.3.bias"),
            model_get("layers.6.norm1.weight"), model_get("layers.6.norm1.bias"),
            model_get("layers.6.norm2.weight"), model_get("layers.6.norm2.bias"), 8)
        let x = transformer_block_train(x, qkv_7,
            model_get("layers.7.attention.in_proj_bias"),
            model_get("layers.7.attention.out_proj.weight"),
            model_get("layers.7.attention.out_proj.bias"),
            model_get("layers.7.ff.0.weight"), model_get("layers.7.ff.0.bias"),
            model_get("layers.7.ff.3.weight"), model_get("layers.7.ff.3.bias"),
            model_get("layers.7.norm1.weight"), model_get("layers.7.norm1.bias"),
            model_get("layers.7.norm2.weight"), model_get("layers.7.norm2.bias"), 8)

        let logits = t_matmul(x, transpose(embed_w))
        end_tape()
        backward(logits)

        // Update all 8 layers
        model_set("layers.0.attention.in_proj_weight", sgd_step(qkv_0, get_grad("qkv_0"), lr))
        model_set("layers.1.attention.in_proj_weight", sgd_step(qkv_1, get_grad("qkv_1"), lr))
        model_set("layers.2.attention.in_proj_weight", sgd_step(qkv_2, get_grad("qkv_2"), lr))
        model_set("layers.3.attention.in_proj_weight", sgd_step(qkv_3, get_grad("qkv_3"), lr))
        model_set("layers.4.attention.in_proj_weight", sgd_step(qkv_4, get_grad("qkv_4"), lr))
        model_set("layers.5.attention.in_proj_weight", sgd_step(qkv_5, get_grad("qkv_5"), lr))
        model_set("layers.6.attention.in_proj_weight", sgd_step(qkv_6, get_grad("qkv_6"), lr))
        model_set("layers.7.attention.in_proj_weight", sgd_step(qkv_7, get_grad("qkv_7"), lr))

        println("  [trained on your score]")
        println("")
    }
}
