AI Weekly: 3/27–4/1 | Anthropic’s Triple Shock, Arm’s First-Ever Chip, Apple Opens Siri to Rivals

One-line summary: Anthropic stole every headline this week — but half the spotlight was unplanned.

1. Top Story: Anthropic’s Triple Shock

No company dominated the news cycle this week more than Anthropic — yet two of its three appearances were accidents.

IPO Plans Revealed (3/27)

Bloomberg reported that Anthropic is considering going public as early as October, potentially raising over $60 billion. The company has held preliminary talks with Goldman Sachs, JPMorgan, and Morgan Stanley.

Context:

Metric Number
February funding round valuation $380 billion
Paid subscription growth More than doubled since January 2026
Enterprise first-purchase win rate ~70% (vs. OpenAI)
Daily new free users Over 1 million

This isn’t just an IPO — it’s a signal that the AI industry’s competitive axis has shifted from “whose model scores higher” to “who becomes a public company first.” Anthropic and OpenAI are both preparing 2026 listings; capital markets are now pricing the AI duopoly showdown.

Claude Mythos Model Accidentally Exposed (3/26–27)

Fortune broke the story: a draft blog post stored in a publicly accessible data cache leaked Mythos (internal codename Capybara), Anthropic’s next-generation model:

  • Anthropic calls it “the most capable AI model we’ve ever built,” representing “a step change in capabilities”
  • Dramatically outscores Claude Opus 4.6 on coding, academic reasoning, and cybersecurity benchmarks
  • Positioned as a new Capybara tier — larger, smarter, and more expensive than Opus
  • Currently in trial with early-access customers

The safety angle drew the most attention: leaked internal documents acknowledged that Mythos could “significantly heighten cybersecurity risks” by rapidly discovering and exploiting software vulnerabilities. Tech stocks and crypto markets briefly sold off after CoinDesk reported the news.

Claude Code Source Code Fully Exposed (3/31)

On Tuesday, security researcher Chaofan Shou discovered that Anthropic’s entire Claude Code source was publicly accessible via an npm source map file:

  • 1,900 files, 512,000+ lines of TypeScript
  • Root cause: Bun generates source maps by default; .npmignore failed to exclude *.map files, which pointed to an R2 bucket containing the complete source directory

The leak revealed several unannounced features:

Feature Description
KAIROS Persistent background agent mode — doesn’t wait for user input, proactively observes and acts
ULTRAPLAN Offloads complex planning to a cloud-hosted Opus 4.6 instance with up to 30 minutes of thinking time
autoDream Background memory consolidation engine running as a forked sub-agent

Two data breaches in one week — first a model, then a product’s source code — is a serious credibility test for a company whose brand is built on safety. But viewed differently, KAIROS and ULTRAPLAN sketch the next form of AI agents: not waiting for commands, but standing by like a persistent assistant.

2. OpenAI Shuts Down Sora: A Reality Check for AI Video

Announced March 24 and reverberating all week — OpenAI is officially killing Sora, its video generation service, just six months after launch.

Key numbers:

Metric Number
Daily burn rate ~$1 million
Cost per 10-second clip ~$1.30
Total in-app purchase revenue Just $2.1 million
Peak global users ~1 million
After decline <500,000

The drama: Disney had committed $1 billion to a partnership but was informed Sora was shutting down less than an hour before the public announcement.

Sora’s shutdown is a pivotal moment for AI video generation. It proves two things: (1) video generation inference costs are nowhere near commercially viable; (2) “cool tech” does not equal “viable product.” The app and API will wind down on April 26 and September 24 respectively.

3. Apple Opens Siri to Rivals: iOS 27’s AI Pivot

Bloomberg and MacRumors reported this week that Apple is building an entirely new Siri experience for iOS 27:

Three major changes:

  1. Standalone Siri App — text and voice interaction modes, with conversation history
  2. Extensions — third-party AI services (ChatGPT, Gemini, Claude, etc.) can integrate directly into Siri
  3. Redesigned interface — potential Dynamic Island integration, system-wide “Ask Siri” button and a “Write with Siri” button above the keyboard

Expected to debut at WWDC in June, shipping with iOS 27 in September.

This is Apple’s most candid AI strategy statement yet: acknowledging its own LLM capabilities fall short, and pivoting the iPhone into an AI assistant distribution platform. For Anthropic, Google, and OpenAI, this opens an entirely new consumer channel — while Apple retains platform control.

4. Arm’s First-Ever Chip in 35 Years: AGI CPU Redefines Inference Infrastructure

On March 24, Arm unveiled the AGI CPU in San Francisco — the first production chip designed in-house in the company’s 35-year history — marking its leap from IP licensing into silicon products.

Core specs:

Spec Detail
Cores Up to 136 Neoverse V3 cores (dual die)
Process TSMC 3nm
Clock 3.2 GHz all-core / 3.7 GHz boost
TDP 300W
Memory 12-channel DDR5 @ 8800 MT/s, >800 GB/s bandwidth
Per-core bandwidth 6 GB/s, <100ns latency

Partners and customers:

  • Meta is the launch customer and co-developer; AGI CPU will pair with Meta’s in-house MTIA accelerator
  • OpenAI, Cerebras, and Cloudflare are launch partners
  • Arm claims 2x+ performance per rack versus x86 platforms
  • Liquid-cooled configurations support 45,000+ cores per rack

Arm shares surged 16% over two days; management projects AGI CPU could generate approximately $15 billion in annual revenue within five years.

This is the week’s most structurally significant chip event. Arm used to only sell design blueprints — now it’s making its own chips. That’s a direct message to Intel and AMD: I’m coming for the inference-era CPU market. For NVIDIA, Arm remains a partner rather than competitor (Vera Rubin’s CPU is Arm architecture), but the emergence of Arm’s own silicon makes the AI infrastructure power structure more multipolar.

5. Chip Competition Extended: Korea’s Rebellions Bets on Inference

On March 30, Samsung-backed Korean AI chip startup Rebellions closed a $400 million Pre-IPO round at a $2.34 billion valuation:

  • Led by Mirae Asset Financial Group and Korea National Growth Fund
  • $650 million raised in the past six months; $850 million total
  • Launched two new products: RebelRack (inference compute unit) and RebelPOD (large-scale deployment cluster)
  • Aggressively expanding into U.S. and Middle East markets; IPO planned this year

Between Arm’s self-designed chip and Rebellions’ inference-specific silicon, a clear trend emerged this week: the inference market is developing its own dedicated supply chain, moving beyond “repurposing training GPUs for inference.”

6. Space Compute: Starcloud’s Orbital Data Centers

On March 30, Starcloud closed a $170 million Series A at a $1.1 billion valuation — becoming the fastest Y Combinator graduate ever to reach unicorn status (just 17 months).

  • Successfully launched Starcloud-1 in November 2025, completing the first-ever LLM training in orbit
  • Next satellite Starcloud-2 will carry NVIDIA Blackwell B200 chips with 100x the power generation of its predecessor
  • Long-term plan: an 88,000-satellite compute constellation

It sounds like science fiction, but the logic is straightforward: power, cooling, and land on Earth are becoming bottlenecks for AI compute expansion. If satellites can run on solar power and dissipate heat in the vacuum of space, orbital compute economics may prove more rational than building data centers in deserts — at least without land constraints at scale.

7. Policy: White House vs. States in AI Jurisdiction Battle

The White House continued pushing its National AI Policy Framework (released 3/20) through late March, with a core proposition: federal law should fully preempt state-level AI regulations.

  • Opposes creating any new federal AI regulatory body; maintains “sector-specific” approach via existing agencies
  • Calls on Congress to bar states from regulating AI model development or holding developers liable for third-party misuse
  • Seven pillars covering: child protection, intellectual property, free speech, innovation competitiveness, workforce development

Meanwhile, 78 AI-related bills are active across 27 U.S. states, and a political operation called Innovation Council Action is preparing to spend over $100 million in the 2026 midterms backing candidates aligned with AI deregulation.

Structural tension: the federal government wants uniformity, states want autonomy, industry wants speed — all three pulling simultaneously. AI governance uncertainty continues to rise.

This Week in Numbers

Event Number
Anthropic IPO target >$60 billion
Claude daily new users >1 million
OpenAI Sora daily losses ~$1 million
Arm AGI CPU projected annual revenue (5yr) $15 billion
Rebellions Pre-IPO $400M / $2.34B valuation
Starcloud Series A $170M / $1.1B valuation
U.S. state-level AI bills 78 across 27 states

Editorial Take

One thread runs beneath this week’s headlines: the AI industry is shifting from an R&D race to commercialization growing pains.

  • Sora’s shutdown proves inference costs remain generative AI’s fatal bottleneck
  • Anthropic’s back-to-back unplanned leaks expose the operational discipline risks of hypergrowth
  • Arm building its first chip in 35 years directly challenges x86’s data center dominance — inference-specific CPUs are becoming a standalone category
  • Apple redesigning Siri is an admission: even platform giants cannot build competitive LLMs alone
  • Orbital compute moving from science fiction to fundraising — Earth-bound AI expansion is genuinely hitting physical limits

The key observation: AI’s competitive dimensions are fracturing. There’s no longer a single “best model” leaderboard — model capability, infrastructure scale, capital structure, distribution channels, and regulatory compliance are all contested simultaneously across multiple fronts. The companies that can advance on all dimensions at once will define the next decade.

This article covers major AI industry developments from March 27 to April 1, 2026. Corrections and additions welcome in the comments.

Modelagem de Dados no Agro: Unindo fluxo de caixa e Ganho Médio Diário (GMD) no mesmo Banco de Dados

1. Introdução: O Problema Real do Produtor

Quando se fala em tecnologia no agronegócio, é comum pensarmos imediatamente no “Agro 4.0”: tratores autônomos, drones escaneando lavouras e sensores IoT de última geração. Porém, essa é a realidade de uma minoria absoluta. Existe um abismo tecnológico silencioso no campo brasileiro.

A pecuária sozinha representa cerca de 6% a 7% de todo o PIB nacional. No entanto, 77% das propriedades rurais do país pertencem a pequenos e médios produtores. E como a grande maioria desses produtores gerencia a complexidade de uma fazenda — calculando custos de suplementação, flutuação do preço da arroba e o Ganho Médio Diário (GMD) de dezenas ou centenas de animais? Usando cadernos de papel, quadros brancos ou planilhas genéricas que não se conversam.

Para nós, desenvolvedores, o nome disso é Dark Data (dados escuros). São milhares de pontos de dados gerados diariamente que são perdidos ou subutilizados porque não estão estruturados em um banco de dados relacional. O lucro na pecuária moderna está espremido em centavos, e tomar decisões baseadas em dados não estruturados é um gargalo de bilhões de reais.

Neste artigo, quero explorar a arquitetura do Sistema de Gestão de Gado (SGG), um ERP zootécnico que construí para estruturar essa “cauda longa” da pecuária, focando em como resolvi o maior gargalo de performance do projeto.

2. O Desafio Técnico: Python vs. Banco de Dados

2.1. Entendendo o Gargalo (A Matemática do GMD)

O GMD (Ganho Médio Diário), é a estimativa de crescimento diário de um animal, geralmente medido em Kg.
Numa tabela estruturada como:

        CREATE TABLE IF NOT EXISTS pesagens (
        id INT AUTO_INCREMENT PRIMARY KEY,
        animal_id INT NOT NULL,
        data_pesagem DATE NOT NULL,
        peso DECIMAL(10, 2) NOT NULL,
        deleted_at DATETIME NULL DEFAULT NULL,
        FOREIGN KEY (animal_id) REFERENCES animais(id)

O cálculo do GMD é dado como (Último Peso – Primeiro Peso)/Dias entre o primeiro e o último peso.

Numa fazenda, essa estatística afeta diretamente a tomada de decisão. É essa métrica que diz ao pecuarista se a dieta de nutrição está funcionando ou se o animal está apenas dando prejuízo.

O problema real começa quando tentamos transformar essa fórmula simples em código escalável

2.2. A Armadilha de Processar no Backend (O Risco do Python)

A minha primeira ideia foi usar o MySQL apenas para guardar as informações e usar o backend em Python para lidar com os cálculos, algo semelhante a:

cursor.execute("SELECT animal_id, peso, data_pesagem FROM pesagens WHERE deleted_at IS NULL")
todas_pesagens = cursor.fetchall() # <--- O GARGALO COMEÇA AQUI

rebanho_gmd = {}

# 2. O Python tentando fazer o trabalho do Banco de Dados
for registro in todas_pesagens:
    animal_id = registro['animal_id']

    if animal_id not in rebanho_gmd:
        rebanho_gmd[animal_id] = {'pesos': [], 'datas': []}

    rebanho_gmd[animal_id]['pesos'].append(registro['peso'])
    rebanho_gmd[animal_id]['datas'].append(registro['data_pesagem'])

# 3. Calculando o GMD animal por animal com loops (Alto consumo de CPU)
resultados = []
for animal_id, dados in rebanho_gmd.items():
    peso_inicial = min(dados['pesos'])
    peso_final = max(dados['pesos'])

    data_inicial = min(dados['datas'])
    data_final = max(dados['datas'])

    dias = (data_final - data_inicial).days

    if dias > 0:
        gmd = (peso_final - peso_inicial) / dias
        resultados.append({'animal_id': animal_id, 'gmd': gmd})

Porém, após a primeira bateria de testes, percebi que essa escolha era ineficiente, pois o backend ficaria responsável por boa parte dos cálculos brutos, aumentando drasticamente o consumo de RAM pelo servidor. A consequência direta disso seria a lentidão e o travamento do sistema à medida que o projeto escalasse para mais fazendas e rebanhos maiores.

3. A Solução: Inteligência no Banco de Dados

Para resolver o problema do alto consumo de RAM que mostrei no código anterior, decidi inverter a lógica. Em vez de trazer milhares de linhas brutas para o Python e forçar a aplicação a processá-las, por que não fazer a matemática onde os dados já estão armazenados?

Foi assim que estruturei a arquitetura “Performance-First” do Sistema de Gestão de Gado (SGG), delegando o processamento pesado ao MySQL.

3.1. Views SQL Otimizadas e Window Functions

A peça central dessa solução foi a criação de Views SQL. Uma View funciona como uma “tabela virtual”. Em vez de calcular o Ganho Médio Diário (GMD) no backend em tempo real, eu ensino a fórmula matemática ao banco de dados.

Para garantir que o cálculo considerasse a ordem cronológica exata das pesagens (mesmo que um animal perdesse peso no período), utilizei Common Table Expressions (CTEs) e Window Functions (ROW_NUMBER).

Veja como estruturei a View v_gmd_analitico no meu banco para resolver esse cálculo complexo de forma automática:

CREATE OR REPLACE VIEW v_gmd_analitico AS
WITH PesagensOrdenadas AS (
    SELECT 
        animal_id, data_pesagem, peso,
        ROW_NUMBER() OVER(PARTITION BY animal_id ORDER BY data_pesagem ASC) as rn_asc,
        ROW_NUMBER() OVER(PARTITION BY animal_id ORDER BY data_pesagem DESC) as rn_desc
    FROM pesagens
    WHERE deleted_at IS NULL
),
PrimeiraUltima AS (
    SELECT 
        animal_id,
        MAX(CASE WHEN rn_asc = 1 THEN data_pesagem END) AS data_inicial,
        MAX(CASE WHEN rn_asc = 1 THEN peso END) AS peso_inicial,
        MAX(CASE WHEN rn_desc = 1 THEN data_pesagem END) AS data_final,
        MAX(CASE WHEN rn_desc = 1 THEN peso END) AS peso_final
    FROM PesagensOrdenadas
    GROUP BY animal_id
)
SELECT 
    a.user_id, a.id as animal_id, a.brinco,
    p.peso_final,
    (p.peso_final - p.peso_inicial) as ganho_total,
    DATEDIFF(p.data_final, p.data_inicial) as dias,
    CASE 
        WHEN DATEDIFF(p.data_final, p.data_inicial) > 0 
        THEN (p.peso_final - p.peso_inicial) / DATEDIFF(p.data_final, p.data_inicial)
        ELSE 0 
    END as gmd
FROM PrimeiraUltima p
JOIN animais a ON p.animal_id = a.id
WHERE p.data_inicial <> p.data_final
  AND a.deleted_at IS NULL;

“A mágica acontece aqui: Quando o Flask precisa processar os indicadores do pecuarista, ele não faz mais laços de repetição infinitos e não consome RAM desnecessária. A listagem principal consulta diretamente a tabela base de animais de forma paginada, enquanto a View é acionada cirurgicamente para as métricas complexas. O banco já resolve o agrupamento e previne divisões por zero, entregando o dado zootécnico pronto para extração de médias globais (ex: SELECT AVG(gmd) FROM v_gmd_analitico) ou para a montagem instantânea da ficha detalhada de cada animal.

3.2. Índices Compostos para Buscas Instantâneas

Ter a View resolveu o problema do cálculo do GMD, mas e quando o produtor quisesse buscar os custos de um lote específico? Para evitar que o MySQL fizesse um Full Table Scan (varrer a tabela inteira a cada clique), a modelagem precisava ser inteligente.

Implementei índices compostos estruturais, como o idx_pesagens_otimizada e o idx_custos_busca. Isso garantiu que, mesmo que o cliente cadastre milhares de animais ao longo dos anos, os filtros de busca continuem respondendo em milissegundos.

3.3. Paginação Server-Side e Pool de Conexões

Para fechar o “pacote” de alta performance, apliquei duas últimas regras na comunicação entre a API e o Banco:

  1. Server-Side Pagination: O painel principal nunca carrega todos os animais de uma vez. Utilizo LIMIT e OFFSET diretamente nas queries do MySQL, enviando para o navegador apenas o que cabe na tela.
  2. Connection Pooling: Usando a biblioteca mysql-connector-python, configurei um pool de conexões logo no setup inicial do banco. Isso evita o overhead gigantesco de abrir e fechar uma nova conexão TCP a cada nova requisição do usuário.

4. Resultados e Impacto Real

Na engenharia de software, a melhor arquitetura não é a que usa o maior número de ferramentas da moda, mas a que resolve o gargalo do usuário de forma inteligente. Ao adotar essa abordagem focada no banco de dados, o impacto foi imediato em duas frentes:

4.1. O Impacto no Negócio (O fim do Dark Data)

O pecuarista que antes gastava cerca de 2 horas cruzando dados de pesagens de cadernos para planilhas lentas no Excel, agora tem o cenário completo da fazenda. O painel entrega a análise de GMD do rebanho e o fluxo de caixa consolidados em cerca de 3 minutos. O dado escuro finalmente virou decisão.

4.2. O Impacto Técnico (Estabilidade do Servidor)

O meu servidor Flask agora “respira aliviado”. O consumo de RAM da aplicação despencou, porque o Python parou de baixar milhares de linhas pela rede para fazer matemática básica. Com a View SQL resolvendo o cálculo, os índices otimizando os filtros e a paginação segurando o tráfego, o sistema se tornou verdadeiramente escalável. A fazenda pode dobrar ou triplicar o número de animais cadastrados, e o painel continuará abrindo em milissegundos.

5. Conclusão

Sistemas genéricos falham no agronegócio porque tentam tratar o peso de um boi como se fosse apenas o “estoque de uma loja”. A pecuária é um organismo vivo, e seus indicadores, como o Ganho Médio Diário, exigem uma arquitetura desenhada para eles.

No fim das contas, bons sistemas resolvem problemas reais — o resto é exercício. Partir da dor do pecuarista para chegar à solução técnica de uma View avançada é o que diferencia código de prateleira de um software com propósito.

Quer analisar o meu código ou ver essa arquitetura rodando ao vivo?

Debugging a 400 Error: How a Silent API Gateway Update Broke My LLM Agent

As an SDET (Software Development Engineer in Test), I spend a lot of time breaking things. But there is a special kind of frustration when an environment that worked perfectly yesterday suddenly throws a fatal error today, despite zero changes to your local code or configuration.

This is the story of how a silent, server-side API validation update completely broke my local AI agent workflow, and how I debugged it by diving into the raw JSON payloads.

If you are building AI agents using third-party LLM gateways or Model Context Protocol (MCP) tools, this debugging journey might save you hours of pulling your hair out.

The Setup & The Incident

I was using Claude Code CLI (v2.1.69), but instead of routing it to Anthropic’s official API, I pointed the base URL to Zhipu AI’s Anthropic-compatible endpoint (https://open.bigmodel.cn/api/anthropic), powered by their glm-5.1 model. This is a common, cost-effective architecture for developers testing local AI agents.

Everything was running smoothly. I was using MCP tools for image analysis and web searching. Then, suddenly, the agent flatlined.

When I prompted the agent to do a simple task that required tools (e.g., “Check what files are in the current directory”), it instantly crashed with a 400 Bad Request:

API Error: 400
{"error":{"code":"1214","message":"messages[4].content[0].type类型错误"},"request_id":"20260330140839a4b83527b21b4c46"}

(Translation of the error: “Type error in messages[4].content[0].type”)

The most bizarre part? Standard text chats worked perfectly. But the moment the agent tried to touch any MCP tool, it triggered the 1214 loop of death.

The Investigation: Hunting the Ghost in the Machine

My first instinct was to blame context limit limits or a corrupted local cache. I cleared the .claude hidden directories and reset the session. The error persisted.

It was time to stop guessing and look at the raw network traffic.

By inspecting the exact payload Claude Code was sending to the API right before the crash, I found the culprit hidden deep within the Anthropic protocol’s message array:

{
  "role": "user",
  "content": [
    {
      "type": "tool_result",
      "tool_use_id": "call_xxx",
      "content": [
        {
          "type": "tool_reference",
          "tool_name": "Bash"
        }
      ]
    }
  ]
}

The “Aha!” Moment

Claude Code v2.x relies heavily on a feature called ToolSearch. Because it supports dynamic MCP tools, it doesn’t load everything into the context window at once (they are marked as deferred). Instead, it searches for the right tool and then alerts the LLM using a highly specific, internal Anthropic content block: "type": "tool_reference".

The Zhipu API gateway, built to mimic the Anthropic protocol, simply did not know what tool_reference was.

The Root Cause Analysis (RCA)

But wait—if the gateway didn’t support it, why did it work perfectly just hours ago?

I checked my terminal logs and constructed a timeline:

  • March 30, 04:06 AM: Last successful ToolSearch invocation.
  • March 30, 06:05 AM: First appearance of the 1214 error.

The Conclusion: The API provider deployed a silent backend update during that 2-hour window.

Previously, their gateway likely had a lenient validation policy: If you see a JSON field or type you don’t recognize, just ignore it. However, the new update introduced strict Schema Validation. The moment the gateway encountered the unsupported tool_reference type, it rejected the entire payload with a 400 error.

This single, undocumented API regression caused a catastrophic failure downstream:

  1. ToolSearch failed completely.
  2. All deferred MCP tools became undiscoverable.
  3. Image analysis MCPs (mcp__zai-mcp-server__analyze_image) were rendered completely useless.

The Workaround

Until the API provider updates their compatibility layer to support the tool_reference block, the only way to unblock the workflow is to surgically disable the feature generating the payload.

If you are facing this exact issue, you can temporarily bypass it by modifying your ~/.claude/settings.json to disable tool searching:

{
  "env": {
    "ANTHROPIC_BASE_URL": "https://open.bigmodel.cn/api/anthropic",
    "ANTHROPIC_AUTH_TOKEN": "your_api_key",
    "ENABLE_TOOL_SEARCH": "0"
  }
}

Trade-off: This stops the 400 errors, but you will lose access to dynamic MCP tool discovery and image analysis capabilities.

The SDET Takeaway

This incident is a textbook example of why building applications on top of “API masquerading/compatibility layers” is inherently fragile.

When you sit between a rapidly iterating client (Claude Code) and a third-party gateway trying to reverse-engineer its protocol, you are at the mercy of both sides. A minor schema update on the client, or a strict validation patch on the server, will snap the integration in half.

The ultimate lesson? When the UI says “Unknown Error,” don’t just reboot. Grab your network inspector, dive into the raw JSON payloads, and follow the data. The truth is always in the Schema.

Have you encountered similar protocol mismatches while building LLM agents? Let me know in the comments!

My exact Claude Code setup for every new project (copy-paste configs)

Every time I start a new project, I spend the first 10 minutes setting up Claude Code properly. After doing this 20+ times, I’ve got it down to a repeatable system.

Here’s the exact folder structure and config files I use.

The folder structure

your-project/
├── CLAUDE.md              ← project memory
├── .claude/
│   ├── settings.json      ← behavior rules
│   └── commands/          ← custom slash commands
│       ├── review.md
│       ├── test.md
│       └── deploy.md
└── src/

Step 1: CLAUDE.md — project memory

This is the most important file. Claude reads it at the start of every session.

# Project: [your project name]

## What this is
[One sentence description]

## Stack
- Language: Node.js 20
- Framework: Express
- Database: PostgreSQL
- Tests: Jest

## Key conventions
- Use async/await, never .then() chains
- All functions must have JSDoc comments
- Error handling: always throw, never return null for errors
- File naming: kebab-case.js

## What NOT to do
- Don't use var (ESLint will catch it)
- Don't commit .env files
- Don't write tests that hit the real database — use mocks

## Response style
- Short answers. No preamble.
- Show diffs, not full files, when making small changes
- If a change is > 50 lines, ask first

That last section (Response style) alone cuts my token usage by ~40%. Claude stops the “Great! I’ll now refactor your…” preamble and just makes the change.

Step 2: .claude/settings.json — behavior rules

{
  "permissions": {
    "allow": [
      "Bash(git:*)",
      "Bash(npm:*)",
      "Bash(node:*)",
      "Bash(cat:*)",
      "Bash(ls:*)",
      "Bash(grep:*)"
    ],
    "deny": [
      "Bash(rm -rf *)",
      "Bash(git reset --hard*)",
      "Bash(git push --force*)"
    ]
  }
}

The deny rules are non-negotiable after I learned the hard way. Claude Code can and will git reset --hard if you let it. Don’t let it.

Step 3: Custom slash commands

These live in .claude/commands/ and become /project:review, /project:test, etc.

.claude/commands/review.md

Review the code changes since the last commit. Focus on:
1. Security issues (SQL injection, XSS, exposed secrets)
2. Logic errors that tests won't catch
3. Performance problems (N+1 queries, missing indexes)
4. Missing error handling

Format: bullet list, severity HIGH/MED/LOW prefix.
Don't comment on style — ESLint handles that.

.claude/commands/test.md

Look at the files changed since last commit.
Write Jest tests for any new functions that don't have coverage.
Rules:
- Unit tests only — mock all database calls
- Test the failure cases, not just happy path
- Put tests in __tests__/ next to the source file

.claude/commands/deploy.md

Pre-deploy checklist:
1. Run: npm test — stop if any failures
2. Check for console.log statements in src/ — list them if found
3. Verify .env.example is updated if .env changed
4. Check that migrations/ folder has no unrun migrations
Report what you found. Don't deploy anything automatically.

Step 4: One-liner setup script

I have this in my dotfiles:

#!/bin/bash
# claude-init.sh — run in any new project root

mkdir -p .claude/commands

# Copy base configs from template
cp ~/dotfiles/claude-templates/CLAUDE.md ./CLAUDE.md
cp ~/dotfiles/claude-templates/settings.json ./.claude/settings.json
cp ~/dotfiles/claude-templates/commands/* ./.claude/commands/

echo "✓ Claude Code configured. Edit CLAUDE.md with project specifics."

Run claude-init.sh, then spend 3 minutes editing CLAUDE.md with your actual stack and conventions. Done.

The payoff

With this setup:

  • Claude never asks “what framework are you using?” because CLAUDE.md tells it
  • Claude never does dangerous git operations because settings.json blocks them
  • /project:review before every PR takes 10 seconds
  • Token usage drops because response style rules cut the preamble

API cost note

If you’re using Claude Code with direct Anthropic API keys, costs can add up fast with verbose responses. I run mine through SimplyLouie ($2/month flat rate) because the token savings from the CLAUDE.md response style rules become even more meaningful when you’re not paying per token.

The configs above are in my public gist — link in comments.

What’s in your CLAUDE.md? Drop your conventions below — I’m always looking to improve the template.

Day 1339 : Ride

A low-angle view of the

liner notes:

  • Professional : Today I worked on creating another coding exercise. The way I have things set up, I can use another coding exercise that I created previously as a starting point for this new one. I think I’ll be able to get it done tomorrow to have it reviewed and start another one. Spent some time getting caught up since I was gone pretty all last week. Also updated a bunch of stuff that GitHub was saying needed updating with some repos. Had a meeting with my manager and went through the list of things I wanted to discuss. I gave them links to check out the pull request for the new feature I added to the MCP servers and the coding exercise and workshop I created. I also found out that continued updating dependencies. It was also that last day to give points out to folks before the new quarter starts tomorrow. I also tried to finish up the expense report from my work trip, but I’m waiting for one charge to show up. I sent an email, so we’ll see what they say. I also found out that a talk I submitted got accepted. Pretty productive day.

  • Personal : Whew what a ride that past few days since I’ve posted here it has been. I had a pretty successful trip to Dallas for work. Folks were happy with what I got done. I got sick when I got back. Some folks I sent some prototypes to last year placed an order. I did my radio show at https://kNOwBETTERHIPHOP.com . Did the study session at https://untilit.works where I set up the new UV laser I purchased. Then a random emergency popped up which kind of threw me for a loop. Luckily, looks like everything will work out.

A low-angle view of the

Been doing some prep work to get this order started. Looks like I will need to use the UV printer I got which means I’ll need to replace the print head. I know this because I tried to print something and it did not come out how it should even when running a cleaning cycle. I watched someone on YouTube do it and I don’t think it’ll be that bad. While I’m doing that, the machine said some other stuff needed to be replaced so I’ll do that as well. In addition to that, I have been using the UV laser to print different material test arrays on different surfaces, like 3D prints and packaging that I’ll be using for this order. I also have been working on another prototype for my co-host that I’ll be able to finish up now that I have this UV laser. Also, I just got a notification that the tracking number from what I thought was a scam that had no updates for almost 2 months actually had an update that they got the package. We’ll see. Not holding my breath. With my luck, it’ll be a box of bricks or something. haha. Going to go through some tracks for the radio show and possibly work on sketching out a part for that prototype I mentioned, model it and print it tomorrow.

Have a great night!

peace piece
Dwane / conshus
https://dwane.io / https://HIPHOPandCODE.com

From Direct Classification to Agentic Routing: When to Use Local Models vs Azure AI

In many enterprise workflows, classification sounds simple.

An email arrives.

A ticket is created.

A request needs to be routed.

At first glance, it feels like a straightforward model problem:

  • classify the input
  • assign a category
  • trigger the next step

But in practice, enterprise classification is rarely just about model accuracy.

It is also about:

  • latency
  • cost
  • governance
  • data sensitivity
  • operational fit
  • fallback behavior

That is where the architecture becomes more important than the model itself.

In this post, I want to share a practical way to think about classification systems in enterprise environments:

  • when local or department-level models make sense
  • when Azure AI / cloud models are the better fit
  • and how an agentic routing layer changes the design entirely

The Classification Problem Is Everywhere

Classification appears in more places than we often realize:

  • support ticket categorization
  • email triage
  • incident prioritization
  • request type detection
  • business workflow routing
  • document tagging
  • policy or compliance flagging

For years, the common design pattern was simple:

input → model → label

That still works in some cases.

But the moment enterprise conditions enter the picture, things become more nuanced.

Not every request needs the same model.

Not every classification task needs the same level of reasoning.

And not every input should leave a department boundary just because a cloud model is available.

Local Models vs Azure AI: This Is Not a Winner-Takes-All Decision

One of the most useful mindset shifts is this:

The question is not which model is better.

The question is where each model fits in the architecture.

Local / Department-Level Models

Local models are often a strong fit when the classification problem is:

  • repetitive
  • high-volume
  • predictable
  • narrowly scoped
  • sensitive from a data handling perspective

Examples include:

  • routing common internal request types
  • tagging operational alerts
  • classifying structured or semi-structured internal emails
  • recognizing a stable set of departmental categories

Why local models work well here

They can offer:

  • lower latency
  • lower cost
  • stronger control over data locality
  • simpler operational boundaries
  • good performance for known patterns

In other words, local models are often ideal for stable operational classification.

Where Azure AI Adds More Value

Azure AI or cloud-based models become more useful when the problem is less predictable.

That usually happens when inputs are:

  • ambiguous
  • unstructured
  • cross-functional
  • context-heavy
  • changing over time

Examples include:

  • requests that combine multiple intents
  • tickets with incomplete details
  • emails that require contextual interpretation
  • workflows where classification depends on retrieved knowledge
  • scenarios that benefit from reasoning before routing

Why Azure AI helps here

Cloud models can provide:

  • broader language understanding
  • stronger handling of ambiguity
  • easier scale across teams and use cases
  • richer reasoning with context
  • better adaptation when patterns evolve

This becomes especially useful when classification is not just “assign a label,” but also:

  • infer intent
  • structure tasks
  • identify edge cases
  • decide next action

The More Interesting Shift: Classification Is Becoming an Agentic Decision Flow

This is the part I find most interesting.

Classification is moving beyond direct model calls.

It is starting to look more like a decision system.

Instead of asking:

Which model should classify this input?

we start asking:

How should the system decide which model, context, and workflow to use?

That is where an agentic architecture becomes valuable.

A Practical Agentic Pattern for Classification

Here is a simple architecture pattern that works well conceptually:

1. Intake Agent

The intake agent receives the incoming input.

This could be:

  • an email
  • a support request
  • a chat message
  • a portal submission
  • an incident summary

Its job is not deep reasoning.

Its role is to:

  • normalize the input
  • extract obvious metadata
  • identify source and basic context
  • prepare the payload for the next decision step

2. Reasoning Agent

The reasoning agent determines how the request should be handled.

This is where the flow becomes more intelligent.

The reasoning agent can decide:

  • is this a known departmental pattern?
  • is the input ambiguous?
  • does it require more context?
  • should this go to a local model?
  • should this go to Azure AI?
  • should a fallback path be triggered?

This turns the architecture from static classification into routing intelligence.

3. Task Agent

The task agent executes the chosen path.

Depending on the routing decision, it may:

  • invoke a local classifier
  • call an Azure AI model
  • retrieve supporting context
  • query a knowledge base
  • interact with systems such as APIs, databases, or ticketing platforms

The task agent is where the model choice becomes operational.

4. Fallback / Escalation Loop

This layer is often ignored, but it matters a lot.

Good classification systems need a plan for:

  • low confidence scores
  • conflicting signals
  • missing context
  • business-critical ambiguity
  • human review

Without this loop, even a strong model can create weak workflows.

Why This Matters Architecturally

An agentic classification flow gives you something that direct classification often does not:

Flexibility

You can evolve the system without rewriting the whole workflow.

Control

You can enforce rules about where data goes and which models can be used.

Efficiency

You can reserve cloud reasoning for edge cases instead of sending everything there.

Reliability

You can add fallback logic, validation, and system-aware routing.

Better alignment with real enterprise workflows

Because enterprise systems are rarely “one input, one answer.”

A Hybrid Design Often Makes the Most Sense

In many real environments, the best answer is a hybrid one:

  • use local models for routine, high-volume, stable classification
  • use Azure AI for ambiguity, reasoning, and changing context
  • use an agentic layer to decide which path fits the request

That gives you a system that is:

  • cost-aware
  • scalable
  • context-sensitive
  • operationally practical

This is much stronger than treating every classification problem as either:

  • a traditional ML-only problem
    or
  • a cloud-LLM-only problem

Example Enterprise Scenario

Imagine an IT service workflow.

Incoming requests may arrive from:

  • Outlook
  • Teams
  • portal forms
  • ticket queues

Some requests are straightforward:

  • password reset
  • software installation
  • access request

A local model may be enough.

Others are messy:

  • unclear issue descriptions
  • mixed business and technical language
  • incomplete context
  • requests spanning multiple categories

That is where Azure AI can add value.

An agentic decision layer can determine:

  • use the local classifier for known patterns
  • route to Azure AI for ambiguous cases
  • retrieve relevant knowledge if needed
  • escalate if confidence is low
  • push the result into the next enterprise workflow

That is not just classification.

That is classification as part of system design.

What Changes for Engineers and Architects

This shift also changes how we think about solution design.

The focus moves from:

  • optimizing one model in isolation

to:

  • designing the decision logic around models
  • defining routing rules
  • controlling system boundaries
  • handling fallback and exception paths
  • deciding where intelligence should live

In other words:

the architecture becomes the real differentiator.

Final Thought

Local models and Azure AI are not competing answers.

They solve different parts of the problem.

The more useful design question is:

where should each kind of intelligence live?

And once an agentic layer enters the picture, classification stops being just a model call.

It becomes a coordinated decision flow.

That is where things start to get interesting.

Questions for Practitioners

If you are working on enterprise AI or workflow automation, I would be curious to hear your take:

  • Are you centralizing classification in the cloud?
  • Are you keeping some intelligence closer to departmental systems?
  • Have you started introducing routing or agent-based decision layers?

Jetpack Compose fez sentido pra mim quando eu parei de comparar tudo com XML

Image JetpackCompose logo

Image JetpackCompose logo

Quando comecei a mexer com Jetpack Compose, tentei entender tudo com a cabeça do Android antigo. Eu ainda pensava em XML, Activity, Fragment e atualização manual de interface. Por isso, no começo, parecia que estava faltando alguma coisa: cadê o XML, os ids e o findViewById? O que me destravou foi perceber que Compose não era só um jeito novo de fazer a mesma coisa, mas uma forma declarativa de construir UI. E, vindo do React, foi aí que tudo começou a fazer sentido.

A primeira virada de chave

A principal virada de chave para mim foi perceber que o Compose não era “Android sem XML”. No React, a gente já está acostumado com a ideia de que a interface responde ao estado. No Compose, a lógica é parecida: você descreve a UI com funções @Composable, usa estado, e o framework atualiza o que precisa quando esse estado muda. Não é o mesmo modelo do React, mas a ideia de “descrever a interface em vez de montar tudo manualmente” está ali. Quando eu passei a enxergar o Compose assim, ele começou a fazer muito mais sentido.

Row, Column e Box me fizeram parar de brigar com layout

A parte de layout foi uma das primeiras coisas que me deixou confortável. Row e Column são quase autoexplicativos. Um organiza na horizontal, o outro na vertical. E o Box resolve aqueles casos em que você quer empilhar coisas ou alinhar um elemento dentro de uma área. Não é CSS. Mas, se você já trabalhou com flexbox, a sensação não é tão distante.

// código React
export function ProfileHeader() {
  return (
    <div
      style={{
        display: 'flex',
        flexDirection: 'row',
        gap: 12,
        alignItems: 'center',
      }}
    >
      <img
        src="/avatar.png"
        alt="Avatar"
        style={{
          width: 48,
          height: 48,
          borderRadius: 999,
        }}
      />

      <div
        style={{
          display: 'flex',
          flexDirection: 'column',
        }}
      >
        <strong>Marcos</strong>
        <span>Android + Backend</span>
      </div>
    </div>
  )
}
// código Compose
@Composable
fun ProfileHeader() {
    Row(
        modifier = Modifier.fillMaxWidth(),
        verticalAlignment = Alignment.CenterVertically,
        horizontalArrangement = Arrangement.spacedBy(12.dp)
    ) {
        Image(
            painter = painterResource(R.drawable.avatar),
            contentDescription = "Avatar",
            modifier = Modifier
                .size(48.dp)
                .clip(CircleShape)
        )

        Column {
            Text(
                text = "Marcos",
                style = MaterialTheme.typography.titleMedium
            )
            Text(
                text = "Android + Backend",
                style = MaterialTheme.typography.bodyMedium
            )
        }
    }
}

O que eu gostei aqui é que o layout fica muito explícito. Você bate o olho e entende a estrutura da tela sem precisar ficar pulando entre XML, style, tema e código.

Modifier foi a peça que mais me confundiu no começo

Se teve uma coisa que me pegou de verdade foi Modifier. No início eu pensava nele quase como um “style” do Compose. Só que não é só isso. Com Modifier, você mexe em tamanho, espaçamento, comportamento, clique, borda, background, clipping, alinhamento… e o detalhe mais importante: a ordem importa.

Isso parece pequeno até você perceber que:

  • padding antes ou depois de clickable muda a área clicável
  • clip antes ou depois de background muda o resultado visual
  • border, padding e background não são intercambiáveis

Foi uma das partes em que eu mais apanhei, porque no começo eu só encadeava as coisas até “ficar com cara certa”. Depois entendi que ali existe uma lógica mesmo.

Um exemplo simples

@Composable
fun SelectableCard(
    selected: Boolean,
    onClick: () -> Unit,
    content: @Composable () -> Unit
) {
    val borderColor = if (selected) Color(0xFF4F46E5) else Color(0xFFE5E7EB)

    Box(
        modifier = Modifier
            .clip(RoundedCornerShape(12.dp))
            .border(1.dp, borderColor, RoundedCornerShape(12.dp))
            .clickable(onClick = onClick)
            .padding(16.dp)
    ) {
        content()
    }
}

Depois que eu entendi que Modifier não era enfeite, mas parte da lógica visual e interativa da UI, muita coisa ficou mais previsível.

O lado bom: if, when e loops são só Kotlin

Uma coisa que eu gostei rápido no Compose foi não precisar entrar naquele modo mental de “template + expressão” toda hora. No React, embora tudo esteja dentro do JavaScript, ainda existe aquela mistura constante entre JSX e lógica de renderização. No Compose, a interface já é código Kotlin desde o começo. Então você usa if, when, for e outras estruturas da linguagem de forma direta, sem sentir que está alternando entre marcação e lógica o tempo todo.

Input controlado continua sendo input controlado

Essa parte foi das mais tranquilas pra mim porque lembra muito o React. Se você já trabalhou com input controlado, não tem muito mistério. O valor vem do estado, o onValueChange atualiza esse estado, e a UI reage.

@Composable
fun NameForm() {
    var name by remember { mutableStateOf("") }

    Column(
        modifier = Modifier.padding(16.dp)
    ) {
        OutlinedTextField(
            value = name,
            onValueChange = { name = it },
            label = { Text("Nome") }
        )

        if (name.isNotBlank()) {
            Text("Hello, $name")
        }
    }
}

Pra mim, o que muda de verdade aqui não é o conceito de estado, e sim o vocabulário que o Compose usa para lidar com isso. Se no React a gente pensa em useState e no ciclo de renderização do componente, no Compose essa conversa passa muito por remember, mutableStateOf e rememberSaveable. O remember eu comecei a enxergar como aquele estado que continua existindo entre recomposições. Já o rememberSaveable fica mais próximo daqueles casos em que você quer preservar melhor a informação da tela, por exemplo em uma rotação, sem simplesmente perder tudo que o usuário já fez.

LazyColumn foi onde o Compose começou a ficar familiar

Quando cheguei em lista, tudo começou a parecer menos “mundo novo”. No Compose, LazyColumn é aquele componente que você naturalmente usa pra listas maiores, renderizando só o necessário. E aí entra uma coisa que pra quem vem do React já deveria soar conhecida a identidade do item importa. Se existe um id, usa esse id.

data class Hero(
    val id: String,
    val name: String
)

@Composable
fun HeroList(
    heroes: List<Hero>,
    selectedId: String?,
    onSelect: (String) -> Unit
) {
    LazyColumn(
        contentPadding = PaddingValues(8.dp),
        verticalArrangement = Arrangement.spacedBy(12.dp)
    ) {
        items(
            items = heroes,
            key = { it.id }
        ) { hero ->
            SelectableCard(
                selected = hero.id == selectedId,
                onClick = { onSelect(hero.id) }
            ) {
                Text(hero.name)
            }
        }
    }
}

O padrão stateful/stateless me ajudou bastante

Outra coisa que eu trouxe do React foi separar melhor o que segura estado do que só renderiza, me ajudou muito pensar assim:

  • a tela principal segura o estado
  • os componentes menores recebem dados e callbacks
@Composable
fun ChooseHeroScreen(heroes: List<Hero>) {
    var selectedId by rememberSaveable { mutableStateOf<String?>(null) }

    Scaffold(
        topBar = {
            TopAppBar(
                title = { Text("Qual é seu herói favorito?") }
            )
        }
    ) { innerPadding ->
        HeroList(
            heroes = heroes,
            selectedId = selectedId,
            onSelect = { selectedId = it },
            modifier = Modifier.padding(innerPadding)
        )
    }
}

Isso conversa muito com state hoisting, que é outro conceito que no começo parece nome bonito pra algo simples deixar o estado subir para um nível mais apropriado.

O que mais me atrapalhou vindo do React

Algumas coisas me fizeram perder mais tempo do que eu gostaria:

Achar que Modifier era só estilo

  • Não é. Ele mexe com layout, interação e até semântica.

Ignorar a ordem das coisas

  • No Compose, a ordem muda bastante o resultado.

Colocar estado cedo demais no componente errado

  • Quando eu deixava componente pequeno segurando regra demais, rapidamente ficava chato de reaproveitar.

Subestimar rememberSaveable

  • Tem tela em que perder estado por besteira é irritante.

Tratar Compose como XML com sintaxe diferente

  • Esse foi provavelmente o maior erro de todos.

No fim das contas

Hoje, se eu tivesse que explicar Jetpack Compose de um jeito simples, eu diria que ele me pareceu uma mistura de três coisas:

  • a mentalidade declarativa que eu já conhecia de React
  • a estrutura forte de uma UI nativa Android
  • a praticidade de escrever tudo em Kotlin, sem ficar alternando demais de contexto

O que mais me ajudou a sair da trava inicial foi parar de tentar decorar API e focar em entender alguns blocos básicos:

  • Row, Column e Box
  • Modifier
  • remember e rememberSaveable
  • LazyColumn
  • state hoisting

Depois disso, o resto começou a parecer menos intimidador.

Compose ainda tem muita coisa que eu estou aprendendo. Mas, honestamente, depois que esse modelo mental encaixa, a experiência fica bem melhor do que eu imaginava quando comecei.

ClawMoat — Protecting Your Machine from AI Agent Threats

ClawMoat is an open‑source runtime security layer designed to protect your computer, credentials, and sensitive data from malicious or careless actions by AI agents — especially those built on platforms like OpenClaw. It acts like a “security moat” around your machine, blocking dangerous behaviors before they ever reach your system.

https://medium.com/@hasanmcse/clawmoat-protecting-your-machine-from-ai-agent-threats-173147cd570a

TCJSgame Tutorial #1: Setting Up Your First Game Engine

TCJSgame Tutorial #1: Setting Up Your First Game Engine

Welcome to the first tutorial in the TCJSgame series. If you have ever wanted to create your own browser games but found engines like Phaser or Unity overwhelming, you are in the right place.

TCJSgame is a lightweight, beginner-friendly JavaScript game engine that lets you create 2D games with just a few lines of code. No build tools. No complex setups. Just HTML, JavaScript, and your creativity.

https://images.unsplash.com/photo-1555066931-4365d14bab8c?ixlib=rb-4.0.3&auto=format&fit=crop&w=1000&q=80

In this tutorial, you will learn:

· What TCJSgame is and how its core classes work
· How to get the engine file and set it up locally
· How to create your first canvas and game loop
· How to draw and move a simple object

By the end, you will have a working game window with a moving square. Let us dive in.

What is TCJSgame?

TCJSgame (Terra Codes JavaScript Game) is an open-source 2D game engine created by Owolabi Kehinde. It was built to make game development accessible, especially for beginners and educators who want to focus on game logic rather than complex setup.

https://images.unsplash.com/photo-1461749280684-dccba630e2f6?ixlib=rb-4.0.3&auto=format&fit=crop&w=1000&q=80

Core Classes You Will Use

Based on the engine file, here are the main classes you will work with:

· Display – Creates and manages the game canvas, handles the game loop, tracks keyboard, mouse, and touch input, and manages camera and scenes.
· Component – The base class for all game objects (rectangles, images, text). Handles position, size, color, physics, movement, rotation, and collision.
· Camera – Allows you to follow a target component and scroll the world.
· Sound – Simple audio player for sound effects and music.
· TileMap – For creating tile-based levels (platformers, RPGs).

The engine powers everything from simple platformers to complex tile-based games, and it is perfect for learning game development concepts.

Getting the Engine File

TCJSgame is hosted on Vercel, not on a traditional CDN. You need to download the file directly.

Option 1: Download from the Official Website

Open your browser and go to tcjsgame.vercel.app. Look for the Download section on the homepage. Click to download tcjsgame-v3.js and save the file in your project folder.

https://images.unsplash.com/photo-1581291518633-83b4ebd1d83e?ixlib=rb-4.0.3&auto=format&fit=crop&w=1000&q=80

Option 2: Save the File from the Direct Link

If you have the direct link to the file, open it in your browser, right-click and select Save As, then save it as tcjsgame-v3.js in your project folder.

Option 3: Copy the Code Directly

You can also create a new file called tcjsgame-v3.js and paste the entire engine code into it using any text editor like Notepad or VS Code.

https://images.unsplash.com/photo-1542831371-29b0f74f9713?ixlib=rb-4.0.3&auto=format&fit=crop&w=1000&q=80

Creating Your First HTML File

Once you have the engine file locally, create a new file called index.html in the same folder.

Paste this code into the file:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>My First TCJSgame</title>
    <style>
        body {
            margin: 0;
            min-height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
            background: linear-gradient(135deg, #1a1a2e, #16213e);
            font-family: system-ui, 'Segoe UI', monospace;
        }
        canvas {
            box-shadow: 0 10px 25px rgba(0,0,0,0.3);
            border-radius: 8px;
        }
    </style>
</head>
<body>
    <script src="tcjsgame-v3.js"></script>

    <script>
        const display = new Display();
        display.start(800, 600, document.body);

        const player = new Component(50, 50, "limegreen", 375, 275, "rect");
        display.add(player);

        function update() {
            if (display.keys[39]) {
                player.x += 5;
            }
            if (display.keys[37]) {
                player.x -= 5;
            }
            if (display.keys[38]) {
                player.y -= 5;
            }
            if (display.keys[40]) {
                player.y += 5;
            }

            if (player.x < 0) player.x = 0;
            if (player.x > display.canvas.width - player.width) player.x = display.canvas.width - player.width;
            if (player.y < 0) player.y = 0;
            if (player.y > display.canvas.height - player.height) player.y = display.canvas.height - player.height;
        }

        const instruction = new Component("16px", "Arial", "white", 20, 30, "text");
        instruction.text = "Arrow keys to move the green square";
        display.add(instruction);

        console.log("Game is running. Use arrow keys to move.");
    </script>
</body>
</html>

Save the file and open it in your browser. You should see a green square that moves when you press the arrow keys.

https://images.unsplash.com/photo-1581091226033-d5c48150dbaa?ixlib=rb-4.0.3&auto=format&fit=crop&w=1000&q=80

Understanding the Code

Let us break down what each part does based on the actual engine file.

Loading the engine

<script src="tcjsgame-v3.js"></script>

This makes all the classes available globally.

Creating the Display

const display = new Display();

This creates a new game display object. The Display constructor sets up the canvas element, input tracking arrays, and initializes the camera.

Starting the game

display.start(800, 600, document.body);

The start method sets canvas width and height, inserts the canvas into the specified container, starts the interval timer that runs updat() every 20 milliseconds, and adds event listeners for keyboard, mouse, and touch input.

Creating a Component

const player = new Component(50, 50, "limegreen", 375, 275, "rect");

The Component constructor stores width, height, color, x, y, type. It also sets default values for speedX, speedY, gravity, bounce, and physics flag. If type is “image”, it loads the image from the color parameter.

Adding to the display

display.add(player);

This pushes the component into the global comm array. The updat() method then automatically calls move() and update() for every component in comm.

The update function

function update() { ... }

This is a special function that the engine calls every frame. It does not require any parameters. Inside it, you can check input and update component properties.

Keyboard input

if (display.keys[39]) { ... }

The Display class tracks all key presses. When a key is pressed, it sets display.keys[keyCode] = true. When released, it sets it to false. Key code 39 is the right arrow.

Text component

const instruction = new Component("16px", "Arial", "white", 20, 30, "text");
instruction.text = "Arrow keys to move...";

For text components, the width parameter becomes the font size, and height becomes the font family. The actual text is stored in the .text property.

https://images.unsplash.com/photo-1516116216624-53e697fedbea?ixlib=rb-4.0.3&auto=format&fit=crop&w=1000&q=80

Adding Simple Physics

The engine has built-in physics support. To make your square fall with gravity, enable the physics property.

Replace your player creation code with this:

const player = new Component(50, 50, "limegreen", 375, 100, "rect");
player.physics = true;
player.gravity = 0.5;
player.bounce = 0.7;

Then change your update function to this:

function update() {
    if (display.keys[39]) {
        player.speedX = 5;
    } else if (display.keys[37]) {
        player.speedX = -5;
    } else {
        player.speedX = 0;
    }

    player.hitBottom();
}

The move() method, when physics is true, adds gravitySpeed to y each frame. The hitBottom() method checks if the component has reached the bottom of the canvas and applies bounce.

https://images.unsplash.com/photo-1635070041078-e363dbe005cb?ixlib=rb-4.0.3&auto=format&fit=crop&w=1000&q=80

Troubleshooting

Issue Solution
Canvas does not appear Make sure tcjsgame-v3.js is in the same folder as your HTML file. Check the file name spelling. Open browser console (F12) to see error messages.
Arrow keys do not move the square Click on the canvas first to give it focus. Add display.canvas.focus() at the end of your script. Check that display.keys[39] is being set by adding console.log(display.keys) inside update.
Player falls through the ground Make sure you are calling player.hitBottom() inside update(). The hitBottom method uses display.canvas.height to detect the bottom edge.
“display is not defined” error You likely forgot to create the Display instance with new Display(). Or you placed your script before loading the engine file.
“update is not defined” error This is not an error – the engine checks for update() and calls it if it exists. If you see this, it means update() is not defined, but it is optional.

https://images.unsplash.com/photo-1558494949-ef010cbdcc31?ixlib=rb-4.0.3&auto=format&fit=crop&w=1000&q=80

Folder Structure

Your project folder should look like this:

my-first-game/
├── index.html
└── tcjsgame-v3.js

That is all you need. No build tools, no package managers, no complex setup.

What’s Next

You have just created your first interactive canvas with TCJSgame. In future tutorials, we will build on this foundation:

· Topic 2: Drawing images, sprites, and animations
· Topic 3: Handling mouse and touch input
· Topic 4: Using the built-in collision detection
· Topic 5: Building a simple platformer with tilemaps

https://images.unsplash.com/photo-1513258496099-48168024aec0?ixlib=rb-4.0.3&auto=format&fit=crop&w=1000&q=80

Resources

· Official TCJSgame Website: tcjsgame.vercel.app
· Discord Community: discord.gg/j45mn75q
· GitHub Repository: github.com/terracodes004/tcjsgame

Share Your Creation

I would love to see what you build. Share your first game in the comments or reach out on Discord with your creation.

Next tutorial: Drawing Images, Sprites, and Animations – Bringing Your Game to Life

Happy coding. 🎮✨

First Quarter 2026 Goal Update

1) Continue doing hands on labs through SkillBuilder to refine my skills. I’ve learned a great deal in the world of enterprise scale for Cloud Operations, Generative AI, and Security and I’m going to double down on those while also seeking Quantum Computing learning opportunities. <- I doubled down, but not necessarily on SkillBuilder. I participated in the BeSA Batch 9 on Agentic AI, built my own personal agent to help with homeschool learning activies for my kids, and continued with TryHackMe SOC2 Learning Path.

2) Make my learning actionable by creating a technology roadmap and strategy to continue to build a secure, dynamic, and innovative cloud platform. <- This is hard. The challenge here is that there are more people also looking to make a roadmap for their capabilities. Sometimes it involves the platform that my team and I support, but we’re not always in the conversation on how we make sure the “how” meets everyones needs. I also see the asks that we have to others can also impede others from their own roadmap. I don’t take for granted the opportunities to work with busy people to accomplish big goals, but I probably underestimated the effort needed to actually do it. I think this is a stretch area for me in Q2

3) Refine skills in leadership so I can strengthen my people-centered approach as a technical lead for our cloud platform team. <- My biggest focus this year has been on continuing to learn and develop both relational and emotional intelligences. The surprise here for me was that people actually are open to me being a mentor, another thing I don’t take for granted. With this opportunity, I get to share some lessons learned through my experience of applying what I’ve learned from leadership programs and podcasts from my favorites like Dr. Dharius Daniels, John Maxwell, and Craig Groeschel. Really excited about continuing this for the rest of the year.